The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
=head1 NAME

Class::Composite - Implements Composite patterns

=head1 SYNOPSIS

  =========================
  Collection implementation
  =========================

  use Class::Composite;
  my $collection = Class::Composite::Container->new();
  my $element = Class::Composite::Element->new();
  $collection->addElement( $elem );
  $elements = $collection->getElements();


  ========================
  Composite implementation
  ========================

  package graphicBase; # Base for graphics containers and elements
  sub display {
    my $self = shift;
    foreach my $elem (@{$self->getElements()}) {
      $elem->display();
    }
    paint($elem);
  }


  package graphicElement;
  use base qw( Class::Composite::Element graphicBase );


  package graphicContainer;
  use base qw( Class::Composite::Container graphicBase );


  package main;
  use graphicElement;
  use graphicContainer;
  my $element   = graphicElement->new();
  my $container = graphicContainer->new();
  $container->addElement( $element );
  $container->display();

=head1 DESCRIPTION

C<Class::Composite> is used to provide mechanisms used by C<Class::Composite::Container>
and C<Class::Composite::Element>. Class::Composite::* implements a Composite pattern (see OO Patterns books and http://www.uni-paderborn.de/cs/ag-schaefer/Lehre/Lehrveranstaltungen/Vorlesungen/Entwurfsmuster/WS0102/DPSA-IVb.pdf for example).
A composite pattern is a collection implementation which provides same methods to the container and elements.
The reason for using a Composite pattern is to have the same interface to deal with different objects and their containers (collections).

If you only need a collection implementation, then you can inherite from Class::Composite::Container and Class::Composite::Element directly.
If you need specific method that should be applied to both your container and your elements (which is what the Class::Composite is made for),
then you isolate the methods you want to apply on both elements and containers in a specific package.
Then, you inherite from both your package and Class::Composite::Element for elements, and Class::Composite::Container for containers.

=head1 INHERITANCE

Class::Base

=cut
package Class::Composite;

use strict;
use warnings::register;
use Scalar::Util  qw( blessed );

use base  qw( Class::Base );

our $VERSION = 0.2;


=head2 getAll()

Returns an array ref of all elements below, whatever their depth or type.

=cut
sub getAll : method {
  my $self = shift;
  my @elems = ();
  foreach my $junior ( @{$self->getElements()} ) {
    push @elems, $junior;
    push @elems, @{$junior->getAll} if defined($junior);
  }
  \@elems;
}


=head2 getLeaves(start, end)

Returns all Class::Composite::Element contained in the collection, whatever their depth.

=cut
sub getLeaves : method {
  my ($self, $start, $last) = @_;
  my @elements = ();
  foreach my $elem ( @{$self->getElements($start, $last)} ) {
    defined $elem or next;
    if ($elem->isa('Class::Composite::Element')) {
      push @elements, $elem;
    }
    else {
      my $subElems = $elem->getLeaves() || [];
      push @elements, @$subElems if (@$subElems);
    }
  }
  \@elements;
}


=head2 getElements()

Returns the elements just below the current object.
Returns []
must probably be overriden by child classes.

=cut
sub getElements () : method { [] }


=head2 getElement()

Returns undef
must probably be overriden by child classes

=cut
sub getElement () : method { undef }


=head2 nOfElements()

Returns undef, to be overriden by child class

=cut
sub nOfElements { }


=head2 elementType()

Returns the class the element must belongs to, default is
Class::Composite.
Sets it to undef if you don't want any checking to occur.
To be overriden in Child class.

=cut
sub elementType () : method { __PACKAGE__ }


=head2 applyToAll( $sub )

Applies the subroutine $sub to all elements.
The subroutine will receive a collection element as a parameter.

=cut
sub applyToAll : method {
  my ($self, $sub) = @_;
  $sub->( $_ ) foreach ( @{$self->getElements} );
  $self;
}


##
## Helper method
##
sub _warn {
  warn $_[1].' - '.caller(1)." " . caller(2) . "\n";
  undef;
}


1;


__END__

=head1 SEE ALSO

Class::Composite::Container, Class::Composite::Element
http://opensource.fotango.com/ for other goodies

=head1 AUTHOR

"Pierre Denis" <pdenis@fotango.com>

=head1 ACKNOWLEDGEMENTS

Thanks to Leon Brocard and James Duncan for their input and suggestions.

=head1 COPYRIGHT

Copyright (C) 2002, Fotango Ltd. All rights reserved.

This is free software. This software
may be modified and/or distributed under the same terms as Perl
itself.

=cut