Added new tests, fixed a bug, and cleaned up more code: Intermediate check-in
[p5sagit/Excel-Template.git] / lib / Excel / Template / Factory.pm
1 package Excel::Template::Factory;
2
3 use strict;
4
5 my %Manifest = (
6
7 # These are the instantiable nodes
8     'IF'        => 'Excel::Template::Container::Conditional',
9     'LOOP'      => 'Excel::Template::Container::Loop',
10     'ROW'       => 'Excel::Template::Container::Row',
11     'SCOPE'     => 'Excel::Template::Container::Scope',
12     'WORKBOOK'  => 'Excel::Template::Container::Workbook',
13     'WORKSHEET' => 'Excel::Template::Container::Worksheet',
14
15     'BACKREF'   => 'Excel::Template::Element::Backref',
16     'CELL'      => 'Excel::Template::Element::Cell',
17     'FORMULA'   => 'Excel::Template::Element::Formula',
18     'RANGE'     => 'Excel::Template::Element::Range',
19     'VAR'       => 'Excel::Template::Element::Var',
20
21     'FORMAT'    => 'Excel::Template::Container::Format',
22
23 # These are all the Format short-cut objects
24 # They are also instantiable
25     'BOLD'      => 'Excel::Template::Container::Bold',
26     'HIDDEN'    => 'Excel::Template::Container::Hidden',
27     'ITALIC'    => 'Excel::Template::Container::Italic',
28     'LOCKED'    => 'Excel::Template::Container::Locked',
29     'OUTLINE'   => 'Excel::Template::Container::Outline',
30     'SHADOW'    => 'Excel::Template::Container::Shadow',
31     'STRIKEOUT' => 'Excel::Template::Container::Strikeout',
32
33 # These are the helper objects
34 # They are also in here to make E::T::Factory::isa() work.
35     'CONTEXT'    => 'Excel::Template::Context',
36     'ITERATOR'   => 'Excel::Template::Iterator',
37     'TEXTOBJECT' => 'Excel::Template::TextObject',
38
39     'CONTAINER'  => 'Excel::Template::Container',
40     'ELEMENT'    => 'Excel::Template::Element',
41
42     'BASE'       => 'Excel::Template::Base',
43 );
44
45 my %isBuildable = map { $_ => ~~1 } qw(
46     WORKBOOK WORKSHEET
47     FORMAT BOLD HIDDEN ITALIC LOCKED OUTLINE SHADOW STRIKEOUT
48     IF ROW LOOP SCOPE
49     CELL FORMULA
50     VAR BACKREF RANGE
51 );
52
53 {
54     my %Loaded;
55     sub _load_class
56     {
57         my $self = shift;
58         my ($class) = @_;
59
60         unless ( exists $Loaded{$class} )
61         {
62             (my $filename = $class) =~ s!::!/!g;
63             eval {
64                 require "$filename.pm";
65             }; if ($@) {
66                 die "Cannot find or compile PM file for '$class' ($filename)\n";
67             }
68
69             $Loaded{$class} = ~~1;
70         }
71
72         return ~~1;
73     }
74 }
75
76 {
77     my @param_names = qw(name class isa);
78     sub register
79     {
80         my $self = shift;
81         my %params = @_;
82
83         for (@param_names)
84         {
85             unless ($params{$_})
86             {
87                 warn "$_ was not supplied to register()\n" if $^W;
88                 return;
89             }
90         }
91
92         my $name = uc $params{name};
93         if (exists $Manifest{$name})
94         {
95             warn "$params{name} already exists in the manifest.\n" if $^W;
96             return;
97         }
98
99         my $isa = uc $params{isa};
100         unless (exists $Manifest{$isa})
101         {
102             warn "$params{isa} does not exist in the manifest.\n" if $^W;
103             return;
104         }
105
106         {
107             no strict 'refs';
108             unshift @{"$params{class}::ISA"}, $Manifest{$isa};
109         }
110
111         $self->_load_class( $Manifest{$isa} );
112         $self->_load_class( $params{class} );
113
114         $Manifest{$name} = $params{class};
115         $isBuildable{$name} = ~~1;
116
117         return ~~1;
118     }
119 }
120
121 sub _create
122 {
123     my $self = shift;
124     my $name = uc shift;
125
126     return unless exists $Manifest{$name};
127
128     $self->_load_class( $Manifest{$name} );
129  
130     return $Manifest{$name}->new(@_);
131 }
132
133 sub _create_node
134 {
135     my $self = shift;
136     my $name = uc shift;
137
138     return unless exists $isBuildable{$name};
139
140     return $self->_create($name, @_);
141 }
142
143 sub isa
144 {
145     return unless @_ >= 2;
146     exists $Manifest{uc $_[1]}
147         ? UNIVERSAL::isa($_[0], $Manifest{uc $_[1]})
148         : UNIVERSAL::isa(@_)
149 }
150
151 sub is_embedded
152 {
153     return unless @_ >= 1;
154
155     isa( $_[0], $_ ) && return ~~1 for qw( VAR BACKREF RANGE );
156     return;
157 }
158
159 1;
160 __END__
161
162 =head1 NAME
163
164 Excel::Template::Factory
165
166 =head1 PURPOSE
167
168 To provide a common way to instantiate Excel::Template nodes
169
170 =head1 USAGE
171
172 =head2 register()
173
174 Use this to register your own nodes.
175
176 Example forthcoming.
177
178 =head1 METHODS
179
180 =head2 isa
181
182 This is a customized isa() wrapper for syntactic sugar
183
184 =head2 is_embedded
185
186 =head1 AUTHOR
187
188 Rob Kinyon (rob.kinyon@gmail.com)
189
190 =head1 SEE ALSO
191
192 =cut