Web::Chain project:    Web/Node.pm


     package Web::Node;
#                                doom@kzsu.stanford.edu
#                                07 Aug 2004
#                                Tue Aug 24, 2004

=pod

=head1 NAME

Web::Node - Basic doomfiles node object, a bundle of all the 
                  information that makes up a doomfiles page

=head1 SYNOPSIS

  use Web::Node;

  $node1 = Web::Node->new($node_name_1);
  $node1->body($main_body_ref);

  print $node1->prev(); # undef

  $node2 = Web::Node->new();  # node name left blank for now
  $node2->name($node_name_2);

  $node2->prev($node1);
  $node1->next($node2);

  print $node2->next(); # shows undef

  # Chained methods access:
  $name_from_node4 = $node1->next->next->next->name;

=head1 DESCRIPTION

Basic node class for the DF project: holds the data that make up a 
DF page.  The methods here consist entirely of accessor/getters 
(and some deprecated mutators).

Every node should have a name which matches the 
   $DF_NODE_NAME_PINNED_RULE
which is defined in Web::Definitions. 
A typical example of a matching name: UPPERCASE_WITH_UNDERSCORES

TODO - make it impossible to create a node without a name.

This is used by the "Chain" class, which has methods to
generate and parse different formats, to manipulate
chains, and to validate them.  (The Chain class is 
reponsible for ensuring that the names are unique 
for each node of the chain.)

=head1 METHODS

=over

=cut

use 5.006;
use strict; 
use warnings;
use Carp;
use Data::Dumper; 
use Web::Definitions qw( $DF_NODE_NAME_PINNED_RULE $DF_VERSION );

our $DEBUG = 0;
our $VERBOSE = 1;

our $VERSION = $DF_VERSION;

=item B<new> - creates a new node object with given name if 
 supplied (this can be deferred and set with "name" later). 

=cut

sub new {
  my $class = shift;
  my $name = shift if $_[0];
  ($DEBUG) && print STDERR "new called with $name\n";
  ($VERBOSE) && print STDERR "  Creating new doomfiles node: $name\n";

  _check_name($name); 

  bless { 
         _name => $name,   # unique node name                 (scalar, string)
         _body => undef,   # content of doomfile node         (ref, string)
         _prev => undef,   # previous node in browse sequence (Node object - aka hashref)
         _next => undef,   # next     node in browse sequence (Node object - aka hashref)
        } , $class
}

=back 

=head2 SETTERS and GETTERS

Simple routines that either assign values to an object
attribute (named set_*), or return existing values (named
get_*).

Originally I thought mutators would be more convenient but
(a) there's a clear need to be able to assign undefs, so the
undef can't be used to signal a get; (b) I believe the code
reads a little more clearly if assignment is flagged with
the prefix "set_".  (Though to my eye, the need for "get_"
is a little less clear.)

The setters (which all take Web::Node objects as arguments): 

=over

=item B<set_prev> - sets the pointer to the previous node in the chain

=cut

sub set_prev {
   my ($self, $prev) = @_;
   my $subname = ( caller(0) )[3];

   if ( ref($prev) eq ref($self) ) {  # $prev is a node object
     $self -> {_prev} =  $prev;

   } elsif ($prev) {  # $prev is defined (but not a node object)
     croak "$subname: $prev is not a Web::Node object";

   } else {  # set to undefined (also if arg was blank, or 0)
     $self -> {_prev} =  undef;

   }

   return( $self->{_prev} ); # return what was just set
}

=item B<set_next> - sets the pointer to the next node in the chain

=cut

sub set_next {
   my ($self, $next) = @_;
   my $subname = ( caller(0) )[3];

   if ( ref($next) eq ref($self) ) {  # $next is a node object
     $self -> {_next} =  $next;

   } elsif ($next) {  # $next is ~ defined (but not a node object)
     croak "$subname: $next is not a Web::Node object";

   } else {  # set to undefined (also if arg was blank, or 0)
     $self -> {_next} =  undef;

   }

   return( $self->{_next} ); # return what was just set
}

=back

Setters that take references, and croak if they're given anything else. 

=over 

=item B<set_body> - sets the main body (the text content) of the doomfiles
             node expected to be a reference to a string.

=cut

sub set_body {
   my ($self, $body) = @_;
   my $subname = ( caller(0) )[3];
   ($DEBUG) && print STDERR "$subname: argument ref is ". ref($body) . "\n";

   if (ref $body) {   # TODO further, could make sure it's SCALAR...
     $self -> {_body} =  $body;

   } elsif ($body) {  # $body is ~ defined but not a ref
     croak "$subname: $body is not a reference";

   } else {  # set to undefined (also if arg was blank, or 0)
     $self -> {_next} =  undef;

   }
   return( $self->{_body} );
}

=back

=head2 GETTERS

Methods that return the value of an object attribute.

=over

=item B<get_name> - return the name of the node 
  (A short string, typically uppercase with underscores).

=cut

sub get_name {
   my $self = shift;
   return( $self->{_name} );
}

=item B<get_prev> - returns the pointer to the previous node in the chain
    (Another node object)
    
=cut

sub get_prev {
   my $self = shift;
   return( $self->{_prev} );
}


=item B<get_next> - returns the pointer to the next node in the chain
    (Another node object)
    
=cut

sub get_next {
   my $self = shift;
   return( $self->{_next} );
}

=item B<body> - gets the main body (the text content) of the doomfiles node 
             expected to be a reference to a string.  

=cut

sub get_body {
   my $self = shift;
   return( $self->{_body} );
}


=back

=head2 MUTATORS

Mutators set arguments if provided, and if not just return existing setting.
First, mutators that take simple strings (not a ref):

TODO: make sure that these are no longer in use, and delete them. 
Then rename getters from get_foo to just foo.

=over

=item B<name> - 

=cut

sub name { 
   my ($self, $name) = @_;
   my $subname = ( caller(0) )[3];
   if ($name and not ref $name) {
     _check_name($name); # must match the node name pattern 
     $self->{_name} =  $name;
   } elsif (ref $name) { 
     carp "$subname: $name should be a simple string, not a reference";
   } 
   return( $self->{_name} );
}

=back

Mutators that take Web::Node objects: 

=over

=item B<prev> - sets or gets the pointer to the previous node in the chain

=cut

sub prev {
   my ($self, $prev) = @_;
   my $subname = ( caller(0) )[3];
   if ( ref($prev) eq ref($self) ) {  
     $self -> {_prev} =  $prev;
   } elsif ($prev) { 
     carp "$subname: $prev is not a Web::Node object";
   }
   return( $self->{_prev} );
}

=item B<next> - sets or gets the pointer to the next node in the chain

=cut

sub next {
   my ($self, $next) = @_;
   my $subname = ( caller(0) )[3];
   if ( ref($next) eq ref($self) ) {  
     $self -> {_next} =  $next;
   } elsif ($next) { 
     carp "$subname: $next is not a Web::Node object";
   }
   return( $self->{_next} );
}

=back

Mutators that take references, 
and carp if they're given anything else. 

=over 

=item B<body> - sets or gets the main body (the text content) of the doomfiles node

=cut

sub body {
   my ($self, $body) = @_;
   my $subname = ( caller(0) )[3];
   ($DEBUG) && print STDERR "$subname: argument ref is ". ref($body) . "\n";
   if (ref $body) {   # further, could make sure it's SCALAR...
     $self -> {_body} =  $body;
   } elsif ($body) { 
     carp "$subname: $body is not a reference";
   }
   return( $self->{_body} );
}

=back

=head2 INTERNAL ROUTINES 

Note, some of the following are used internally 
by this package, some are used "internally" by the 
project. 

=over 

=item B<_dump_contents_to_stderr> - used internally by this project to assist 
  in debugging.  Dumps node contents (name of node, name of previous and next nodes, 
  the node body) in a simple textual form to STDERR.

=cut 

sub _dump_contents_to_stderr {
  my $node = shift;
     
  # stub code, dumps node contents to stderr
  print STDERR '|<|' . $node->get_prev->get_name . '|<|' . "\n"  if $node->get_prev;
  print STDERR '|||' . $node->get_name . '|||' . "\n";
  print STDERR '|b>|'. "\n";
  print STDERR ${ $node->get_body } . "\n";
  print STDERR '|<b|' . "\n";
  print STDERR '|>|' . $node->get_next->get_name . '|>|' . "\n" if $node->get_next;;
  print STDERR '===' . "\n\n";
}

=back

=head2 PROCEEDURAL FUNCTIONS 

Some  proceedural (i.e. not OOP) utilities , used internally by this package

=over

=item B<_check_name> - 
 Makes sure the name is in doomfiles node name format
 Croaks in the event of a bad name argument.

=cut

sub _check_name {  
   my $name = shift;
   my $subname = ( caller(1) )[3];  # Name of for sub that calls this _check_name routine
   unless( $name =~ qr/$DF_NODE_NAME_PINNED_RULE/ ) {  # Exported from Web::Definitions
     croak "$subname: $name is not in doomfiles node name format (e.g. all caps with underscores)";
   }
}

1;
__END__

=back

=head1 FUTURE DEVELOPMENT

Notes for future development:

As written nothing prevents next or prev from pointing 
at any node.  Possibly the linear chain idea should be 
enforced on the level of the Chain object...

Idea: in the future, add a capability to store the names
of identified links in the body, perhaps with locations, 
e.g. byte offset(s).  This "out of band" link info might 
be used for a link checking in the Chain object

=head1 EXPORT

None by default.

=head1 SEE ALSO

=over 

=item L<Project Documentation|Web::Project>

=item L<Web::Chain>

=back 

=head1 AUTHOR

Joseph Brenner, E<lt>doom@kzsu.stanford.eduE<gt>

=head1 COPYRIGHT AND LICENSE

Copyright (C) 2004 by Joseph Brenner

This library is free software; you can redistribute it and/or modify
it under the same terms as Perl itself, either Perl version 5.8.2 or,
at your option, any later version of Perl 5 you may have available.

=head1 BUGS

None reported... yet.

=cut

     

Joseph Brenner, Sat Nov 6 17:04:11 2004