#!/usr/local/bin/perl

package Text::PORE::Object;

use Exporter;
@Text::PORE::Object::ISA = qw(Exporter);
$Text::PORE::Object::VERSION = "0.05";

sub new
{
        my ($type) = shift;
        my (%att_list) = @_;
        my ($self) = {};
 
        foreach $key (keys %att_list) {
                $self->{"\L$key\E"} = $att_list{$key};
        }
 
        bless $self;
        $self;
}
 

#######################################
# getAttribute($name)
#######################################
sub getAttribute() {
	return GetAttribute(@_);
};

#######################################
# setAttribute($name=>$value)
#######################################
sub setAttribute() {
	LoadAttributes(@_);
}

#######################################
# setAttributes($name1=>$value1, $name2=>$value2, ..., $nameN=>$valueN)
#######################################
sub setAttributes() {
	LoadAttributes(@_);
}


sub GetClassType
{	
	my($type) = shift;
	my($dummy);
	($type,$dummy) = split(/\=/,$type);
	return ($type);
}


#######################################
# Given the attribute name,
# return the value of itself, closest ancestor or default
#######################################
sub GetAttribute
{
	my ($self) = shift; 
	my ($att) = @_;
	$att = "\L$att\E";
	return ($self->{$att}) if (defined $self->{$att});

	my ($obj_id);
	if ($obj_id = $self->{"ID\_$att"}) {
		#####################################
		# this attribute exists, but the object is now allocated yet
		# allocate the object now
		#####################################
		my($class) = $::obj_type_2_class{$self->{"TYPE_$att"}};	
		if ($class) {
			require "$class.pm"; import $class;
			if ($::_PRINT_NEW_) {
				print "new $class(id=>$obj_id)\n";
			}
			$self->{$att} = $class->new (id=>$obj_id);
		}
		return ($self->{$att});
	}
	elsif ($self->{'parent'}) {
		#####################################
		# this attribute dosn't exists, 
		# look one level up
		#####################################
		return ($self->{'parent'}->GetAttribute($att));
	}
	else {
		#########################################
		# this attribute is not found anywhere within myself and
		# my ancestors, return the default one
		#########################################
		#print "default:[$att]=[$::default_attribs{$att}]";
		return ($::default_attribs{$att});

	}
}

######################################
# returns a reference to a list of all attribute names
######################################
sub GetAllAttributeNames
{
	my($self) = shift;
	my ($att,@att_list);

	$self->FinalizeAllAttributes;
	foreach $att (sort keys %{$self}) {
		if ($att =~ /^ID_|^TYPE_/) { next; }
		push (@att_list, $att);
	}
	return (\@att_list);
}

######################################
# Finalize All the Attributes
# Internal Function, outsiders should not care about it.
######################################
sub FinalizeAllAttributes
{
	my($self) = shift;
	my ($att,$class,$obj_id);
	foreach $att(keys %{$self}) {
	    if ($att =~ /^ID_(.+)$/) {
		$obj_id = $self->{$att};
		$att = $1;
		$class = $::obj_type_2_class{$self->{"TYPE_$att"}};	
		if ($class) {
			require "$class.pm"; import $class;
			if ($::_PRINT_NEW_) {
				print "new $class(id=>$obj_id)\n";
			}
			$self->{$att} = $class->new (id=>$obj_id);
		}
	    	$self->{"ID_$att"} = $self->{"TYPE_$att"} = undef;
	    }
	}
}

######################################
# returns a reference to a hash 
# the hash keys are the attribute names
# the hash values are the attribute values
# an example:
#	$page = new Page(id=>$id);
#	$hash_ref = $page->GetAllAttributes;
#	foreach $attr (keys %$hash_ref) {
#		print "name: $attr, value: $hash_ref->{$attr}";
#	....
######################################
sub GetAllAttributes
{
    my($self) = shift;
	my ($att,%obj);
	$self->FinalizeAllAttributes;
	foreach $att (sort keys %{$self}) {
		if ($att =~ /^ID_|^TYPE_/) { next; }
		else {
		  $obj{$att} = $self->{$att};
		}
	}
        return (\%obj);
}
 


######################################
# Print All Attributes and Values for debugging purpose
######################################
sub PrintAllAttributes
{
    my($self) = shift;
	my $att_ref = $self->GetAllAttributes;
	my ($attr,$val);
	while (($attr,$val) = (each %{$att_ref})) {
		print "'$attr'=[";
		if (ref $val eq 'ARRAY') {
			### multi-value attribute
			print "multi: @$val";
		}
		else { print $val; }
		print "]<br>\n";
	}
}



######################################
# Return 1 if these multi-value attribute 
#		$attr has value $value
# Return 0 otherwise
######################################
sub MultiValAttrHas
{
    my($self) = shift;
	my($attr,$val) = @_;

	if (ref $self->{$attr} ne 'ARRAY') { return 0; }
	foreach (@{$self->{$attr}}) {
		if ($_ eq $val) { return 1; }
	}
	return 0;
}

	
#################################################
# Load Attributes 
# In: pair(s) of attribute_name and attribute_value
# Example:
#	$object->LoadAttributes($name1=>$value1,$name2=>$value2,...);
#################################################
sub LoadAttributes
{
	my($self) = shift;
	my(%att_list) = @_;

	foreach $key (keys %att_list) {
		$self->{"\L$key\E"} = $att_list{$key};
	}

	return $self;
}

###############################################
# given an attribute name, an id and a type, 
# create a object, which is my child
# if id is 0, then the object is a scalar, use
# type as its value
###############################################
sub MakeChild 
{
	my $self = shift;
        my($child_name, $id, $type) = @_;
 
        ###############################################
        # it's not a object but a value, return value($type)
        ###############################################
        if (!$id) {
                $self->{$child_name} = $type;
        }
 
        ###############################################
        # it's a object
        ###############################################
        my $class = $::obj_type_2_class{$type};
	if ($class) {
		require "$class.pm"; import $class;
		if ($::_PRINT_NEW_) {
			print "new $class(id=>$obj_id)\n";
		}
		my $obj = $class->new(id=>$id,parent=>$self);
		$self->{$child_name} = $obj;
	}
	else { return undef; }
}


###############################################
# Atts2QueryString
#	convert attributes to QueryString
###############################################
sub Atts2QueryString
{
	my $self = shift;
	my %atts = @_;
	$self->LoadAttributes(%atts);
 
	my $string = undef;
	my $key;
	my $value;
        foreach $key (keys %{$self}) {
                if ($key eq 'parent') { next; }
		$value = urlencode ($self->{$key});
		$key = urlencode_word ($key);
                $string .= "$key=$value\&";
        }
 
        return $string;
}

1;
__END__

=head1 NAME

Text::PORE::Object - PORE Objects

=head1 SYNOPSIS

	$obj = new Text::PORE::Object('name'=>'Joe Smith');
	@chilren = (
		new Text::PORE::Object('name'=>'John Smith', 'age'=>10, 'gender'=>'M'),
		new Text::PORE::Object('name'=>'Jack Smith', 'age'=>15, 'gender'=>'M'),
		new Text::PORE::Object('name'=>'Joan Smith', 'age'=>20, 'gender'=>'F'),
		new Text::PORE::Object('name'=>'Jim Smith', 'age'=>25, 'gender'=>'M'),
	);
	$obj->{'children'} = \@chilren;

=head1 DESCRIPTION

PORE::Object is the superclass of all renderable objects. That is, if you want to render an object, the
object must be an instance of PORE::Object or an instance of its subclass.

The purpose of this class is to provide methods to create and access attributes. Attributes can be
created via the constructor C<new> and setters C<setAttribute()> and C<setAttributes()>. Attributes can be 
retrieve via the getter C<getAttribute()>.

=head1 METHODS

=over 4

=item new

Usage:

	new Text::PORE::Object();
	new Text::PORE::Object($name1=>$value1, $name2=>$value2, ..., $nameN=>$valueN);

The constructor can take no argument or a list of name-value pairs. If a list of name-value pairs is
provided, the object is created with the given attributes.

=item getAttribute()

Usage:

	$obj->getAttribute($name);

This method retrieves the value of the given attribute. If the attribute is an object, its reference is
returned.

=item setAttribute()

Usage:

	$obj->setAttribute($name=>$value);

This method takes a name-value pair. It sets the attribute for the given name to the given value.
If the attribute previously has an old value, the new value overrides the old one.

=item setAttributes()

Usage:

	$obj->setAttributes($name1=>$value1, $name2=>$value2, ..., $nameN=>$valueN);

This method takes a list of name-value pairs. It sets the attribute for each given name to its
corresponding value.
If the attribute previously has an old value, the new value overrides the old one.

=back

=head1 AUTHOR

Zhengrong Tang, ztang@cpan.org

=head1 COPYRIGHT

Copyright 2004 by Zhengrong Tang

This library is free software; you can redistribute it and/or modify
it under the same terms as Perl itself. 

=cut

