Initial Import
Rob Kinyon [Wed, 27 Oct 2004 12:59:22 +0000 (12:59 +0000)]
33 files changed:
trunk/Changes [new file with mode: 0644]
trunk/LICENSE [new file with mode: 0644]
trunk/MANIFEST [new file with mode: 0644]
trunk/Makefile.PL [new file with mode: 0644]
trunk/README [new file with mode: 0644]
trunk/Todo [new file with mode: 0644]
trunk/lib/Excel/Template.pm [new file with mode: 0644]
trunk/lib/Excel/Template/Base.pm [new file with mode: 0644]
trunk/lib/Excel/Template/Container.pm [new file with mode: 0644]
trunk/lib/Excel/Template/Container/Bold.pm [new file with mode: 0644]
trunk/lib/Excel/Template/Container/Conditional.pm [new file with mode: 0644]
trunk/lib/Excel/Template/Container/Format.pm [new file with mode: 0644]
trunk/lib/Excel/Template/Container/Hidden.pm [new file with mode: 0644]
trunk/lib/Excel/Template/Container/Italic.pm [new file with mode: 0644]
trunk/lib/Excel/Template/Container/Locked.pm [new file with mode: 0644]
trunk/lib/Excel/Template/Container/Loop.pm [new file with mode: 0644]
trunk/lib/Excel/Template/Container/Outline.pm [new file with mode: 0644]
trunk/lib/Excel/Template/Container/Row.pm [new file with mode: 0644]
trunk/lib/Excel/Template/Container/Scope.pm [new file with mode: 0644]
trunk/lib/Excel/Template/Container/Shadow.pm [new file with mode: 0644]
trunk/lib/Excel/Template/Container/Strikeout.pm [new file with mode: 0644]
trunk/lib/Excel/Template/Container/Workbook.pm [new file with mode: 0644]
trunk/lib/Excel/Template/Container/Worksheet.pm [new file with mode: 0644]
trunk/lib/Excel/Template/Context.pm [new file with mode: 0644]
trunk/lib/Excel/Template/Element.pm [new file with mode: 0644]
trunk/lib/Excel/Template/Element/Cell.pm [new file with mode: 0644]
trunk/lib/Excel/Template/Element/Formula.pm [new file with mode: 0644]
trunk/lib/Excel/Template/Element/Var.pm [new file with mode: 0644]
trunk/lib/Excel/Template/Factory.pm [new file with mode: 0644]
trunk/lib/Excel/Template/Format.pm [new file with mode: 0644]
trunk/lib/Excel/Template/Iterator.pm [new file with mode: 0644]
trunk/lib/Excel/Template/TextObject.pm [new file with mode: 0644]
trunk/t/001_load.t [new file with mode: 0644]

diff --git a/trunk/Changes b/trunk/Changes
new file mode 100644 (file)
index 0000000..f1e84ad
--- /dev/null
@@ -0,0 +1,55 @@
+Revision history for Perl module Excel::Template
+
+0.12 Thu Apr 08 07:30:00 2004
+        - Fixed bug regarding empty arrays as loop variables
+
+0.11 Wed Mar 17 16:00:00 2004
+        - Fixed bug introduced in 0.10 (Loops were not case-insensitive)
+
+0.10 Wed Mar 17 16:00:00 2004
+        - Parameters are now case-insensitive
+
+0.09 Mon Feb 02 16:00:00 2004
+        - Fixed bug with multiple worksheets
+
+0.08 Fri Jan 30 14:00:00 2004
+        - Added Base to the params for XML::Parser to allow for entity includes
+
+0.07 Fri Jan 23 08:30:00 2004
+        - Fixed the MANIFEST to account for missing files
+
+0.06 Mon Jan 20 11:00:00 2004
+        - Added formulas (no back-references yet)
+        - Improved POD a little
+
+0.05 Wed Jan 16 12:30:00 2004
+        - Fixed a bug in formats
+
+0.04 Wed Jan 16 12:00:00 2004
+        - Added BIG_FILES as an option, which will use
+          Spreadsheet::WriteExcel::Big as the renderer (yet unimplemented)
+        - Changed the output() method to use a tied IO::Scalar (which is
+          now a requirement.
+        - Firmed up the infrastructure in Excel::Template::Format
+        - Added the following tags
+            - FORMAT
+            - HIDDEN
+            - LOCKED
+            - OUTLINE
+            - SHADOW
+            - STRIKEOUT
+
+0.03 Wed Dec 03 20:30:00 2003
+        - Added XML::Parser as a required pre-requisite module
+        - Added Italic format
+        - Removed $VERSION from Excel::Template::Base (Unneeded)
+        - No documentation or testing changes
+
+0.02 Sun Nov 30 17:00:00 2003
+        - documentation improvements
+        - No actual functional changes
+
+0.01 Tue Nov 18 14:23:42 2003
+        - original version; created by ExtUtils::ModuleMaker 0.32
+
+
diff --git a/trunk/LICENSE b/trunk/LICENSE
new file mode 100644 (file)
index 0000000..9d0305b
--- /dev/null
@@ -0,0 +1,383 @@
+Terms of Perl itself
+
+a) the GNU General Public License as published by the Free
+   Software Foundation; either version 1, or (at your option) any
+   later version, or
+b) the "Artistic License"
+
+---------------------------------------------------------------------------
+
+The General Public License (GPL)
+Version 2, June 1991
+
+Copyright (C) 1989, 1991 Free Software Foundation, Inc. 675 Mass Ave,
+Cambridge, MA 02139, USA. Everyone is permitted to copy and distribute
+verbatim copies of this license document, but changing it is not allowed.
+
+Preamble
+
+The licenses for most software are designed to take away your freedom to share
+and change it. By contrast, the GNU General Public License is intended to
+guarantee your freedom to share and change free software--to make sure the
+software is free for all its users. This General Public License applies to most of
+the Free Software Foundation's software and to any other program whose
+authors commit to using it. (Some other Free Software Foundation software is
+covered by the GNU Library General Public License instead.) You can apply it to
+your programs, too.
+
+When we speak of free software, we are referring to freedom, not price. Our
+General Public Licenses are designed to make sure that you have the freedom
+to distribute copies of free software (and charge for this service if you wish), that
+you receive source code or can get it if you want it, that you can change the
+software or use pieces of it in new free programs; and that you know you can do
+these things.
+
+To protect your rights, we need to make restrictions that forbid anyone to deny
+you these rights or to ask you to surrender the rights. These restrictions
+translate to certain responsibilities for you if you distribute copies of the
+software, or if you modify it.
+
+For example, if you distribute copies of such a program, whether gratis or for a
+fee, you must give the recipients all the rights that you have. You must make
+sure that they, too, receive or can get the source code. And you must show
+them these terms so they know their rights.
+
+We protect your rights with two steps: (1) copyright the software, and (2) offer
+you this license which gives you legal permission to copy, distribute and/or
+modify the software.
+
+Also, for each author's protection and ours, we want to make certain that
+everyone understands that there is no warranty for this free software. If the
+software is modified by someone else and passed on, we want its recipients to
+know that what they have is not the original, so that any problems introduced by
+others will not reflect on the original authors' reputations.
+
+Finally, any free program is threatened constantly by software patents. We wish
+to avoid the danger that redistributors of a free program will individually obtain
+patent licenses, in effect making the program proprietary. To prevent this, we
+have made it clear that any patent must be licensed for everyone's free use or
+not licensed at all.
+
+The precise terms and conditions for copying, distribution and modification
+follow.
+
+GNU GENERAL PUBLIC LICENSE
+TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND
+MODIFICATION
+
+0. This License applies to any program or other work which contains a notice
+placed by the copyright holder saying it may be distributed under the terms of
+this General Public License. The "Program", below, refers to any such program
+or work, and a "work based on the Program" means either the Program or any
+derivative work under copyright law: that is to say, a work containing the
+Program or a portion of it, either verbatim or with modifications and/or translated
+into another language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not covered by
+this License; they are outside its scope. The act of running the Program is not
+restricted, and the output from the Program is covered only if its contents
+constitute a work based on the Program (independent of having been made by
+running the Program). Whether that is true depends on what the Program does.
+
+1. You may copy and distribute verbatim copies of the Program's source code as
+you receive it, in any medium, provided that you conspicuously and appropriately
+publish on each copy an appropriate copyright notice and disclaimer of warranty;
+keep intact all the notices that refer to this License and to the absence of any
+warranty; and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and you may at
+your option offer warranty protection in exchange for a fee.
+
+2. You may modify your copy or copies of the Program or any portion of it, thus
+forming a work based on the Program, and copy and distribute such
+modifications or work under the terms of Section 1 above, provided that you also
+meet all of these conditions:
+
+a) You must cause the modified files to carry prominent notices stating that you
+changed the files and the date of any change.
+
+b) You must cause any work that you distribute or publish, that in whole or in
+part contains or is derived from the Program or any part thereof, to be licensed
+as a whole at no charge to all third parties under the terms of this License.
+
+c) If the modified program normally reads commands interactively when run, you
+must cause it, when started running for such interactive use in the most ordinary
+way, to print or display an announcement including an appropriate copyright
+notice and a notice that there is no warranty (or else, saying that you provide a
+warranty) and that users may redistribute the program under these conditions,
+and telling the user how to view a copy of this License. (Exception: if the
+Program itself is interactive but does not normally print such an announcement,
+your work based on the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If identifiable
+sections of that work are not derived from the Program, and can be reasonably
+considered independent and separate works in themselves, then this License,
+and its terms, do not apply to those sections when you distribute them as
+separate works. But when you distribute the same sections as part of a whole
+which is a work based on the Program, the distribution of the whole must be on
+the terms of this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest your rights to
+work written entirely by you; rather, the intent is to exercise the right to control
+the distribution of derivative or collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program with the
+Program (or with a work based on the Program) on a volume of a storage or
+distribution medium does not bring the other work under the scope of this
+License.
+
+3. You may copy and distribute the Program (or a work based on it, under
+Section 2) in object code or executable form under the terms of Sections 1 and 2
+above provided that you also do one of the following:
+
+a) Accompany it with the complete corresponding machine-readable source
+code, which must be distributed under the terms of Sections 1 and 2 above on a
+medium customarily used for software interchange; or,
+
+b) Accompany it with a written offer, valid for at least three years, to give any
+third party, for a charge no more than your cost of physically performing source
+distribution, a complete machine-readable copy of the corresponding source
+code, to be distributed under the terms of Sections 1 and 2 above on a medium
+customarily used for software interchange; or,
+
+c) Accompany it with the information you received as to the offer to distribute
+corresponding source code. (This alternative is allowed only for noncommercial
+distribution and only if you received the program in object code or executable
+form with such an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for making
+modifications to it. For an executable work, complete source code means all the
+source code for all modules it contains, plus any associated interface definition
+files, plus the scripts used to control compilation and installation of the
+executable. However, as a special exception, the source code distributed need
+not include anything that is normally distributed (in either source or binary form)
+with the major components (compiler, kernel, and so on) of the operating system
+on which the executable runs, unless that component itself accompanies the
+executable.
+
+If distribution of executable or object code is made by offering access to copy
+from a designated place, then offering equivalent access to copy the source
+code from the same place counts as distribution of the source code, even though
+third parties are not compelled to copy the source along with the object code.
+
+4. You may not copy, modify, sublicense, or distribute the Program except as
+expressly provided under this License. Any attempt otherwise to copy, modify,
+sublicense or distribute the Program is void, and will automatically terminate
+your rights under this License. However, parties who have received copies, or
+rights, from you under this License will not have their licenses terminated so long
+as such parties remain in full compliance.
+
+5. You are not required to accept this License, since you have not signed it.
+However, nothing else grants you permission to modify or distribute the Program
+or its derivative works. These actions are prohibited by law if you do not accept
+this License. Therefore, by modifying or distributing the Program (or any work
+based on the Program), you indicate your acceptance of this License to do so,
+and all its terms and conditions for copying, distributing or modifying the
+Program or works based on it.
+
+6. Each time you redistribute the Program (or any work based on the Program),
+the recipient automatically receives a license from the original licensor to copy,
+distribute or modify the Program subject to these terms and conditions. You
+may not impose any further restrictions on the recipients' exercise of the rights
+granted herein. You are not responsible for enforcing compliance by third parties
+to this License.
+
+7. If, as a consequence of a court judgment or allegation of patent infringement
+or for any other reason (not limited to patent issues), conditions are imposed on
+you (whether by court order, agreement or otherwise) that contradict the
+conditions of this License, they do not excuse you from the conditions of this
+License. If you cannot distribute so as to satisfy simultaneously your obligations
+under this License and any other pertinent obligations, then as a consequence
+you may not distribute the Program at all. For example, if a patent license would
+not permit royalty-free redistribution of the Program by all those who receive
+copies directly or indirectly through you, then the only way you could satisfy
+both it and this License would be to refrain entirely from distribution of the
+Program.
+
+If any portion of this section is held invalid or unenforceable under any particular
+circumstance, the balance of the section is intended to apply and the section as
+a whole is intended to apply in other circumstances.
+
+It is not the purpose of this section to induce you to infringe any patents or other
+property right claims or to contest validity of any such claims; this section has
+the sole purpose of protecting the integrity of the free software distribution
+system, which is implemented by public license practices. Many people have
+made generous contributions to the wide range of software distributed through
+that system in reliance on consistent application of that system; it is up to the
+author/donor to decide if he or she is willing to distribute software through any
+other system and a licensee cannot impose that choice.
+
+This section is intended to make thoroughly clear what is believed to be a
+consequence of the rest of this License.
+
+8. If the distribution and/or use of the Program is restricted in certain countries
+either by patents or by copyrighted interfaces, the original copyright holder who
+places the Program under this License may add an explicit geographical
+distribution limitation excluding those countries, so that distribution is permitted
+only in or among countries not thus excluded. In such case, this License
+incorporates the limitation as if written in the body of this License.
+
+9. The Free Software Foundation may publish revised and/or new versions of the
+General Public License from time to time. Such new versions will be similar in
+spirit to the present version, but may differ in detail to address new problems or
+concerns.
+
+Each version is given a distinguishing version number. If the Program specifies a
+version number of this License which applies to it and "any later version", you
+have the option of following the terms and conditions either of that version or of
+any later version published by the Free Software Foundation. If the Program does
+not specify a version number of this License, you may choose any version ever
+published by the Free Software Foundation.
+
+10. If you wish to incorporate parts of the Program into other free programs
+whose distribution conditions are different, write to the author to ask for
+permission. For software which is copyrighted by the Free Software Foundation,
+write to the Free Software Foundation; we sometimes make exceptions for this.
+Our decision will be guided by the two goals of preserving the free status of all
+derivatives of our free software and of promoting the sharing and reuse of
+software generally.
+
+NO WARRANTY
+
+11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS
+NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE
+COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM
+"AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR
+IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE
+ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
+PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE,
+YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR
+CORRECTION.
+
+12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED
+TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY
+WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS
+PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES
+ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM
+(INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
+RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY
+OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS
+BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
+
+END OF TERMS AND CONDITIONS
+
+
+---------------------------------------------------------------------------
+
+The Artistic License
+
+Preamble
+
+The intent of this document is to state the conditions under which a Package
+may be copied, such that the Copyright Holder maintains some semblance of
+artistic control over the development of the package, while giving the users of the
+package the right to use and distribute the Package in a more-or-less customary
+fashion, plus the right to make reasonable modifications.
+
+Definitions:
+
+-    "Package" refers to the collection of files distributed by the Copyright
+     Holder, and derivatives of that collection of files created through textual
+     modification. 
+-    "Standard Version" refers to such a Package if it has not been modified,
+     or has been modified in accordance with the wishes of the Copyright
+     Holder. 
+-    "Copyright Holder" is whoever is named in the copyright or copyrights for
+     the package. 
+-    "You" is you, if you're thinking about copying or distributing this Package.
+-    "Reasonable copying fee" is whatever you can justify on the basis of
+     media cost, duplication charges, time of people involved, and so on. (You
+     will not be required to justify it to the Copyright Holder, but only to the
+     computing community at large as a market that must bear the fee.) 
+-    "Freely Available" means that no fee is charged for the item itself, though
+     there may be fees involved in handling the item. It also means that
+     recipients of the item may redistribute it under the same conditions they
+     received it. 
+
+1. You may make and give away verbatim copies of the source form of the
+Standard Version of this Package without restriction, provided that you duplicate
+all of the original copyright notices and associated disclaimers.
+
+2. You may apply bug fixes, portability fixes and other modifications derived from
+the Public Domain or from the Copyright Holder. A Package modified in such a
+way shall still be considered the Standard Version.
+
+3. You may otherwise modify your copy of this Package in any way, provided
+that you insert a prominent notice in each changed file stating how and when
+you changed that file, and provided that you do at least ONE of the following:
+
+     a) place your modifications in the Public Domain or otherwise
+     make them Freely Available, such as by posting said modifications
+     to Usenet or an equivalent medium, or placing the modifications on
+     a major archive site such as ftp.uu.net, or by allowing the
+     Copyright Holder to include your modifications in the Standard
+     Version of the Package.
+
+     b) use the modified Package only within your corporation or
+     organization.
+
+     c) rename any non-standard executables so the names do not
+     conflict with standard executables, which must also be provided,
+     and provide a separate manual page for each non-standard
+     executable that clearly documents how it differs from the Standard
+     Version.
+
+     d) make other distribution arrangements with the Copyright Holder.
+
+4. You may distribute the programs of this Package in object code or executable
+form, provided that you do at least ONE of the following:
+
+     a) distribute a Standard Version of the executables and library
+     files, together with instructions (in the manual page or equivalent)
+     on where to get the Standard Version.
+
+     b) accompany the distribution with the machine-readable source of
+     the Package with your modifications.
+
+     c) accompany any non-standard executables with their
+     corresponding Standard Version executables, giving the
+     non-standard executables non-standard names, and clearly
+     documenting the differences in manual pages (or equivalent),
+     together with instructions on where to get the Standard Version.
+
+     d) make other distribution arrangements with the Copyright Holder.
+
+5. You may charge a reasonable copying fee for any distribution of this Package.
+You may charge any fee you choose for support of this Package. You may not
+charge a fee for this Package itself. However, you may distribute this Package in
+aggregate with other (possibly commercial) programs as part of a larger
+(possibly commercial) software distribution provided that you do not advertise
+this Package as a product of your own.
+
+6. The scripts and library files supplied as input to or produced as output from
+the programs of this Package do not automatically fall under the copyright of this
+Package, but belong to whomever generated them, and may be sold
+commercially, and may be aggregated with this Package.
+
+7. C or perl subroutines supplied by you and linked into this Package shall not
+be considered part of this Package.
+
+8. Aggregation of this Package with a commercial distribution is always permitted
+provided that the use of this Package is embedded; that is, when no overt attempt
+is made to make this Package's interfaces visible to the end user of the
+commercial distribution. Such use shall not be construed as a distribution of
+this Package.
+
+9. The name of the Copyright Holder may not be used to endorse or promote
+products derived from this software without specific prior written permission.
+
+10. THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR
+IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
+PURPOSE.
+
+The End
+
+
diff --git a/trunk/MANIFEST b/trunk/MANIFEST
new file mode 100644 (file)
index 0000000..0ff82b1
--- /dev/null
@@ -0,0 +1,33 @@
+MANIFEST
+LICENSE
+README
+Todo
+Changes
+lib/Excel/Template.pm
+lib/Excel/Template/Base.pm
+lib/Excel/Template/Container.pm
+lib/Excel/Template/Context.pm
+lib/Excel/Template/Element.pm
+lib/Excel/Template/Factory.pm
+lib/Excel/Template/Format.pm
+lib/Excel/Template/Iterator.pm
+lib/Excel/Template/TextObject.pm
+lib/Excel/Template/Container/Bold.pm
+lib/Excel/Template/Container/Conditional.pm
+lib/Excel/Template/Container/Format.pm
+lib/Excel/Template/Container/Hidden.pm
+lib/Excel/Template/Container/Italic.pm
+lib/Excel/Template/Container/Loop.pm
+lib/Excel/Template/Container/Locked.pm
+lib/Excel/Template/Container/Outline.pm
+lib/Excel/Template/Container/Row.pm
+lib/Excel/Template/Container/Scope.pm
+lib/Excel/Template/Container/Strikeout.pm
+lib/Excel/Template/Container/Shadow.pm
+lib/Excel/Template/Container/Workbook.pm
+lib/Excel/Template/Container/Worksheet.pm
+lib/Excel/Template/Element/Cell.pm
+lib/Excel/Template/Element/Formula.pm
+lib/Excel/Template/Element/Var.pm
+t/001_load.t
+Makefile.PL
diff --git a/trunk/Makefile.PL b/trunk/Makefile.PL
new file mode 100644 (file)
index 0000000..89504ef
--- /dev/null
@@ -0,0 +1,16 @@
+use ExtUtils::MakeMaker;
+# See lib/ExtUtils/MakeMaker.pm for details of how to influence
+# the contents of the Makefile that is written.
+WriteMakefile(
+    NAME         => 'Excel::Template',
+    VERSION_FROM => 'lib/Excel/Template.pm', # finds $VERSION
+    AUTHOR       => 'Rob Kinyon (rkinyon@columbus.rr.com',
+    ABSTRACT     => 'Excel::Template',
+    PREREQ_PM    => {
+                     'Test::Simple' => 0.44,
+                     'Spreadsheet::WriteExcel' => 0.42,
+                     'XML::Parser' => 0.01,
+                     'IO::Scalar' => 0.01,
+                     'IO::File' => 0.01,
+                    },
+);
diff --git a/trunk/README b/trunk/README
new file mode 100644 (file)
index 0000000..b5fedff
--- /dev/null
@@ -0,0 +1,11 @@
+Excel::Template is a layout system to use the data structure from HTML::Template
+and create a Microsoft Excel file. A few caveats:
+1) All limitations stated in Spreadsheet::WriteExcel are in force, as that is
+   the module used for rendering.
+2) The output() method is not extremely well-tested.
+3) Not all the methods have been tested in all situations. I fully expect bugs
+   to come back and have to be fixed.
+
+Barring all of that, and a slight lack of features, it's a good module. Enjoy!
+
+Rob
diff --git a/trunk/Todo b/trunk/Todo
new file mode 100644 (file)
index 0000000..297e3fd
--- /dev/null
@@ -0,0 +1,9 @@
+TODO list for Perl module Excel::Template
+
+- Add more formatting options, such as italic
+- Add fonts
+- Add numeric/string formats
+- Figure out how to add support for formulas, especially in <LOOP>s
+- Figure out if and how to add support for charts
+- Anything else people suggest
+
diff --git a/trunk/lib/Excel/Template.pm b/trunk/lib/Excel/Template.pm
new file mode 100644 (file)
index 0000000..665405b
--- /dev/null
@@ -0,0 +1,358 @@
+package Excel::Template;
+
+use strict;
+
+BEGIN {
+    use Excel::Template::Base;
+    use vars qw ($VERSION @ISA);
+
+    $VERSION  = '0.12';
+    @ISA      = qw( Excel::Template::Base );
+}
+
+use File::Basename;
+use XML::Parser;
+use IO::File;
+use IO::Scalar;
+
+sub new
+{
+    my $class = shift;
+    my $self = $class->SUPER::new(@_);
+
+    $self->parse_xml($self->{FILENAME})
+        if defined $self->{FILENAME};
+
+    my @renderer_classes = ( 'Spreadsheet::WriteExcel' );
+    if (exists $self->{BIG_FILE} && $self->{BIG_FILE})
+    {
+        unshift @renderer_classes, 'Spreadsheet::WriteExcel::Big';
+    }
+
+    $self->{RENDERER} = undef;
+    foreach my $class (@renderer_classes)
+    {
+        (my $filename = $class) =~ s!::!/!g;
+        eval {
+            require "$filename.pm";
+            $class->import;
+        };
+        if ($@) {
+            warn "Could not find or compile '$class'\n";
+        } else {
+            $self->{RENDERER} = $class;
+            last;
+        }
+    }
+
+    defined $self->{RENDERER} ||
+        die "Could not find a renderer class. Tried:\n\t" .
+            join("\n\t", @renderer_classes) .
+            "\n";
+
+    return $self;
+}
+
+sub param
+{
+    my $self = shift;
+
+    # Allow an arbitrary number of hashrefs, so long as they're the first things    # into param(). Put each one onto the end, de-referenced.
+    push @_, %{shift @_} while UNIVERSAL::isa($_[0], 'HASH');
+
+    (@_ % 2)
+        && die __PACKAGE__, "->param() : Odd number of parameters to param()\n";
+
+    my %params = @_;
+    $params{uc $_} = delete $params{$_} for keys %params;
+    @{$self->{PARAM_MAP}}{keys %params} = @params{keys %params};
+
+    return 1;
+}
+
+sub write_file
+{
+    my $self = shift;
+    my ($filename) = @_;
+
+    my $xls = $self->{RENDERER}->new($filename)
+        || die "Cannot create XLS in '$filename': $!\n";
+
+    $self->_prepare_output($xls);
+
+    $xls->close;
+
+    return 1;
+}
+
+sub output
+{
+    my $self = shift;
+
+    my $output;
+    tie *XLS, 'IO::Scalar', \$output;
+
+    $self->write_file(\*XLS);
+
+    return $output;
+}
+
+sub parse
+{
+    my $self = shift;
+
+    $self->parse_xml(@_);
+}
+
+sub parse_xml
+{
+    my $self = shift;
+    my ($fname) = @_;
+
+    my ($filename, $dirname) = fileparse($fname);
+    my @stack;
+    my $parser = XML::Parser->new(
+        Base => $dirname,
+        Handlers => {
+            Start => sub {
+                shift;
+
+                my $name = uc shift;
+
+                my $node = Excel::Template::Factory->create_node($name, @_);
+                die "'$name' (@_) didn't make a node!\n" unless defined $node;
+
+                if ($name eq 'WORKBOOK')
+                {
+                    push @{$self->{WORKBOOKS}}, $node;
+                }
+                elsif ($name eq 'VAR')
+                {
+                    return unless @stack;
+                                                                                
+                    if (exists $stack[-1]{TXTOBJ} &&
+                        $stack[-1]{TXTOBJ}->isa('TEXTOBJECT'))
+                    {
+                        push @{$stack[-1]{TXTOBJ}{STACK}}, $node;
+                    }
+                }
+                else
+                {
+                    push @{$stack[-1]{ELEMENTS}}, $node
+                        if @stack;
+                }
+                push @stack, $node;
+            },
+            Char => sub {
+                shift;
+                return unless @stack;
+
+                my $parent = $stack[-1];
+
+                if (
+                    exists $parent->{TXTOBJ}
+                        &&
+                    $parent->{TXTOBJ}->isa('TEXTOBJECT')
+                ) {
+                    push @{$parent->{TXTOBJ}{STACK}}, @_;
+                }
+            },
+            End => sub {
+                shift;
+                return unless @stack;
+
+                pop @stack if $stack[-1]->isa(uc $_[0]);
+            },
+        },
+    );
+
+    {
+        my $fh = IO::File->new($fname)
+            || die "Cannot open '$fname' for reading: $!\n";
+
+        $parser->parse(do { local $/ = undef; <$fh> });
+
+        $fh->close;
+    }
+
+    return 1;
+}
+
+sub _prepare_output
+{
+    my $self = shift;
+    my ($xls) = @_;
+
+    my $context = Excel::Template::Factory->create(
+        'CONTEXT',
+
+        XLS       => $xls,
+        PARAM_MAP => [ $self->{PARAM_MAP} ],
+    );
+
+    $_->render($context) for @{$self->{WORKBOOKS}};
+
+    return 1;
+}
+
+sub register { shift; Excel::Template::Factory::register(@_) }
+
+1;
+__END__
+
+=head1 NAME
+
+Excel::Template - Excel::Template
+
+=head1 SYNOPSIS
+
+First, make a template. This is an XML file, describing the layout of the
+spreadsheet.
+
+For example, test.xml:
+
+  <workbook>
+      <worksheet name="tester">
+          <cell text="$HOME"/>
+          <cell text="$PATH"/>
+      </worksheet>
+  </workbook>
+
+Now, create a small program to use it:
+
+  #!/usr/bin/perl -w
+  use Excel::Template
+
+  # Create the Excel template
+  my $template = Excel::Template->new(
+      filename => 'test.xml',
+  );
+
+  # Add a few parameters
+  $template->param(
+      HOME => $ENV{HOME},
+      PATH => $ENV{PATH},
+  );
+
+  $template->write_file('test.xls');
+
+If everything worked, then you should have a spreadsheet in your work directory
+that looks something like:
+
+             A                B                C
+    +----------------+----------------+----------------
+  1 | /home/me       | /bin:/usr/bin  |
+    +----------------+----------------+----------------
+  2 |                |                |
+    +----------------+----------------+----------------
+  3 |                |                |
+
+=head1 DESCRIPTION
+
+This is a module used for templating Excel files. Its genesis came from the
+need to use the same datastructure as HTML::Template, but provide Excel files
+instead. The existing modules don't do the trick, as they require replication
+of logic that's already been done within HTML::Template.
+
+Currently, only a small subset of the planned features are supported. This is
+meant to be a test of the waters, to see what features people actually want.
+
+=head1 MOTIVATION
+
+I do a lot of Perl/CGI for reporting purposes. In nearly every place I've been,
+I've been asked for HTML, PDF, and Excel. HTML::Template provides the first, and
+PDF::Template does the second pretty well. But, generating Excel was the
+sticking point. I already had the data structure for the other templating
+modules, but I just didn't have an easy mechanism to get that data structure
+into an XLS file.
+
+=head1 USAGE
+
+=head2 new()
+
+This creates a Excel::Template object. If passed a filename parameter, it will
+parse the template in the given file. (You can also use the parse() method,
+described below.)
+
+=head2 param()
+
+This method is exactly like HTML::Template's param() method. Although I will
+be adding more to this section later, please see HTML::Template's description
+for info right now.
+
+=head2 parse() / parse_xml()
+
+This method actually parses the template file. It can either be called
+separately or through the new() call. It will die() if it runs into a situation
+it cannot handle.
+
+=head2 write_file()
+
+Create the Excel file and write it to the specified filename, if possible. (This
+is when the actual merging of the template and the parameters occurs.)
+
+=head2 output()
+
+It will act just like HTML::Template's output() method, returning the resultant
+file as a stream, usually for output to the web. (This is when the actual
+merging of the template and the parameters occurs.)
+
+=head1 SUPPORTED NODES
+
+This is just a list of nodes. See the other classes in this distro for more
+details on specific parameters and the like.
+
+Every node can set the ROW and COL parameters. These are the actual ROW/COL
+values that the next CELL tag will write into.
+
+=over 4
+
+=item * WORKBOOK
+
+=item * WORKSHEET
+
+=item * IF
+
+=item * LOOP
+
+=item * ROW
+
+=item * CELL
+
+=item * FORMULA
+
+=item * BOLD
+
+=item * ITALIC
+
+=back 4
+
+=head1 BUGS
+
+None, that I know of.
+
+=head1 SUPPORT
+
+This is currently beta-quality software. The featureset is extremely limited,
+but I expect to be adding on to it very soon.
+
+=head1 AUTHOR
+
+    Rob Kinyon
+    rkinyon@columbus.rr.com
+
+=head1 COPYRIGHT
+
+This program is free software; you can redistribute
+it and/or modify it under the same terms as Perl itself.
+
+The full text of the license can be found in the
+LICENSE file included with this module.
+
+=head1 SEE ALSO
+
+perl(1), HTML::Template, Spreadsheet::WriteExcel.
+
+=cut
diff --git a/trunk/lib/Excel/Template/Base.pm b/trunk/lib/Excel/Template/Base.pm
new file mode 100644 (file)
index 0000000..9788c11
--- /dev/null
@@ -0,0 +1,78 @@
+package Excel::Template::Base;
+
+use strict;
+
+BEGIN {
+}
+
+use Excel::Template::Factory;
+
+sub new
+{
+    my $class = shift;
+                                                                                
+    push @_, %{shift @_} while UNIVERSAL::isa($_[0], 'HASH');
+    (@_ % 2) 
+        and die "$class->new() called with odd number of option parameters\n";
+                                                                                
+    my %x = @_;
+                                                                                
+    # Do not use a hashref-slice here because of the uppercase'ing
+    my $self = {};
+    $self->{uc $_} = $x{$_} for keys %x;
+                                                                                
+    bless $self, $class;
+}
+                                                                                
+sub isa { Excel::Template::Factory::isa(@_) }
+
+sub calculate { ($_[1])->get(@_[0,2]) }
+#{
+#    my $self = shift;
+#    my ($context, $attr) = @_;
+#
+#    return $context->get($self, $attr);
+#}
+                                                                                
+sub enter_scope { ($_[1])->enter_scope($_[0]) }
+#{
+#    my $self = shift;
+#    my ($context) = @_;
+#
+#    return $context->enter_scope($self);
+#}
+                                                                                
+sub exit_scope { ($_[1])->exit_scope(@_[0, 2]) }
+#{
+#    my $self = shift;
+#    my ($context, $no_delta) = @_;
+#
+#    return $context->exit_scope($self, $no_delta);
+#}
+                                                                                
+sub deltas
+{
+#    my $self = shift;
+#    my ($context) = @_;
+                                                                                
+    return {};
+}
+                                                                                
+sub resolve
+{
+#    my $self = shift;
+#    my ($context) = @_;
+                                                                                
+    '';
+}
+
+sub render
+{
+#    my $self = shift;
+#    my ($context) = @_;
+
+    1;
+}
+
+1;
+__END__
diff --git a/trunk/lib/Excel/Template/Container.pm b/trunk/lib/Excel/Template/Container.pm
new file mode 100644 (file)
index 0000000..97d6972
--- /dev/null
@@ -0,0 +1,147 @@
+package Excel::Template::Container;
+
+use strict;
+
+BEGIN {
+    use vars qw(@ISA);
+    @ISA = qw(Excel::Template::Base);
+
+    use Excel::Template::Base;
+}
+
+# Containers are objects that can contain arbitrary elements, such as
+# PageDefs or Loops.
+
+sub new
+{
+    my $class = shift;
+    my $self = $class->SUPER::new(@_);
+
+    $self->{ELEMENTS} = [] unless UNIVERSAL::isa($self->{ELEMENTS}, 'ARRAY');
+
+    return $self;
+}
+
+sub _do_page
+{
+    my $self = shift;
+    my ($method, $context) = @_;
+
+    for my $e (@{$self->{ELEMENTS}})
+    {
+        $e->enter_scope($context);
+        $e->$method($context);
+        $e->exit_scope($context, 1);
+    }
+
+    return 1;
+}
+
+sub begin_page { _do_page 'begin_page', @_ }
+sub end_page   { _do_page 'end_page', @_   }
+
+sub iterate_over_children
+{
+    my $self = shift;
+    my ($context) = @_;
+
+    my $continue = 1;
+
+    for my $e (
+        @{$self->{ELEMENTS}})
+    {
+        $e->enter_scope($context);
+
+        my $rc = $e->render($context);
+        $continue = $rc if $continue;
+
+        $e->exit_scope($context);
+    }
+
+    return $continue;
+}
+
+sub render { $_[0]->iterate_over_children($_[1]) }
+#{
+#    my $self = shift;
+#    my ($context) = @_;
+#
+#    return $self->iterate_over_children($context);
+#}
+
+sub max_of
+{
+    my $self = shift;
+    my ($context, $attr) = @_;
+
+    my $max = $context->get($self, $attr);
+
+    ELEMENT:
+    foreach my $e (@{$self->{ELEMENTS}})
+    {
+        $e->enter_scope($context);
+
+        my $v = $e->isa('CONTAINER')
+            ? $e->max_of($context, $attr)
+            : $e->calculate($context, $attr);
+
+        $max = $v if $max < $v;
+
+        $e->exit_scope($context, 1);
+    }
+
+    return $max;
+}
+
+sub total_of
+{
+    my $self = shift;
+    my ($context, $attr) = @_;
+
+    my $total = 0;
+
+    ELEMENT:
+    foreach my $e (@{$self->{ELEMENTS}})
+    {
+        $e->enter_scope($context);
+
+        $total += $e->isa('CONTAINER')
+            ? $e->total_of($context, $attr)
+            : $e->calculate($context, $attr);
+
+        $e->exit_scope($context, 1);
+    }
+
+    return $total;
+}
+
+1;
+__END__
+
+=head1 NAME
+
+Excel::Template::Container
+
+=head1 PURPOSE
+
+=head1 NODE NAME
+
+=head1 INHERITANCE
+
+=head1 ATTRIBUTES
+
+=head1 CHILDREN
+
+=head1 AFFECTS
+
+=head1 DEPENDENCIES
+
+=head1 USAGE
+
+=head1 AUTHOR
+
+Rob Kinyon (rkinyon@columbus.rr.com)
+
+=head1 SEE ALSO
+
+=cut
diff --git a/trunk/lib/Excel/Template/Container/Bold.pm b/trunk/lib/Excel/Template/Container/Bold.pm
new file mode 100644 (file)
index 0000000..f4b345f
--- /dev/null
@@ -0,0 +1,75 @@
+package Excel::Template::Container::Bold;
+
+use strict;
+
+BEGIN {
+    use vars qw(@ISA);
+    @ISA = qw( Excel::Template::Container::Format );
+
+    use Excel::Template::Container::Format;
+}
+
+sub new
+{
+    my $class = shift;
+    my $self = $class->SUPER::new(@_);
+
+    $self->{BOLD} = 1;
+
+    return $self;
+}
+
+1;
+__END__
+
+=head1 NAME
+
+Excel::Template::Container::Bold - Excel::Template::Container::Bold
+
+=head1 PURPOSE
+
+To format all children in bold
+
+=head1 NODE NAME
+
+BOLD
+
+=head1 INHERITANCE
+
+Excel::Template::Container::Format
+
+=head1 ATTRIBUTES
+
+None
+
+=head1 CHILDREN
+
+None
+
+=head1 EFFECTS
+
+None
+
+=head1 DEPENDENCIES
+
+None
+
+=head1 USAGE
+
+  <bold>
+    ... Children here
+  </bold>
+
+In the above example, the children will be displayed (if they are displaying
+elements) in a bold format. All other formatting will remain the same and the
+"bold"-ness will end at the end tag.
+
+=head1 AUTHOR
+
+Rob Kinyon (rkinyon@columbus.rr.com)
+
+=head1 SEE ALSO
+
+FORMAT
+
+=cut
diff --git a/trunk/lib/Excel/Template/Container/Conditional.pm b/trunk/lib/Excel/Template/Container/Conditional.pm
new file mode 100644 (file)
index 0000000..cd7ec75
--- /dev/null
@@ -0,0 +1,189 @@
+package Excel::Template::Container::Conditional;
+
+#GGG Convert <conditional> to be a special case of <switch>?
+
+use strict;
+
+BEGIN {
+    use vars qw(@ISA);
+    @ISA = qw(Excel::Template::Container);
+
+    use Excel::Template::Container;
+}
+
+my %isOp = (
+    '='  => '==',
+    (map { $_ => $_ } ( '>', '<', '==', '!=', '>=', '<=' )),
+    (map { $_ => $_ } ( 'gt', 'lt', 'eq', 'ne', 'ge', 'le' )),
+);
+
+sub conditional_passes
+{
+    my $self = shift;
+    my ($context) = @_;
+
+    my $name = $context->get($self, 'NAME');
+    return 0 unless $name =~ /\S/;
+
+    my $val = $context->param($name);
+    $val = @{$val} while UNIVERSAL::isa($val, 'ARRAY');
+    $val = ${$val} while UNIVERSAL::isa($val, 'SCALAR');
+
+    my $value = $context->get($self, 'VALUE');
+    if (defined $value)
+    {
+        my $op = $context->get($self, 'OP');
+        $op = defined $op && exists $isOp{$op}
+            ? $isOp{$op}
+            : '==';
+
+        # Force numerical context on both values;
+        $value *= 1;
+        $val *= 1;
+
+        my $res;
+        for ($op)
+        {
+            /^>$/  && do { $res = ($val > $value);  last };
+            /^<$/  && do { $res = ($val < $value);  last };
+            /^==$/ && do { $res = ($val == $value); last };
+            /^!=$/ && do { $res = ($val != $value); last };
+            /^>=$/ && do { $res = ($val >= $value); last };
+            /^<=$/ && do { $res = ($val <= $value); last };
+            /^gt$/ && do { $res = ($val gt $value); last };
+            /^lt$/ && do { $res = ($val lt $value); last };
+            /^eq$/ && do { $res = ($val eq $value); last };
+            /^ne$/ && do { $res = ($val ne $value); last };
+            /^ge$/ && do { $res = ($val ge $value); last };
+            /^le$/ && do { $res = ($val le $value); last };
+
+            die "Unknown operator in conditional resolve", $/;
+        }
+
+        return 0 unless $res;
+    }
+    elsif (my $is = uc $context->get($self, 'IS'))
+    {
+        my $istrue = $val && 1;
+        if ($is eq 'TRUE')
+        {
+            return 0 unless $istrue;
+        }
+        else
+        {
+            warn "Conditional 'is' value was [$is], defaulting to 'FALSE'" . $/
+                if $is ne 'FALSE';
+
+            return 0 if $istrue;
+        }
+    }
+
+    return 1;
+}
+
+sub render
+{
+    my $self = shift;
+    my ($context) = @_;
+
+    return 1 unless $self->conditional_passes($context);
+
+    return $self->iterate_over_children($context);
+}
+
+sub max_of
+{
+    my $self = shift;
+    my ($context, $attr) = @_;
+
+    return 0 unless $self->conditional_passes($context);
+
+    return $self->SUPER::max_of($context, $attr);
+}
+
+sub total_of
+{
+    my $self = shift;
+    my ($context, $attr) = @_;
+
+    return 0 unless $self->conditional_passes($context);
+
+    return $self->SUPER::total_of($context, $attr);
+}
+
+1;
+__END__
+
+=head1 NAME
+
+Excel::Template::Container::Conditional - Excel::Template::Container::Conditional
+
+=head1 PURPOSE
+
+To provide conditional execution of children nodes
+
+=head1 NODE NAME
+
+IF
+
+=head1 INHERITANCE
+
+Excel::Template::Container
+
+=head1 ATTRIBUTES
+
+=over 4
+
+=item * NAME
+
+This is the name of the parameter to be testing. It is resolved like any other
+parameter. 
+
+=item * VALUE
+
+If VALUE is set, then a comparison operation is done. The value of NAME is
+compared to VALUE using the value of OP.
+
+=item * OP
+
+If VALUE is set, then this is checked. If it isn't present, then '==' (numeric
+equality) is assumed. OP must be one of the numeric comparison operators or the
+string comparison operators. All 6 of each kind is supported.
+
+=item * IS
+
+If VALUE is not set, then IS is checked. IS is allowed to be either "TRUE" or
+"FALSE". The boolean value of NAME is checked against IS.
+
+=back 4
+
+=head1 CHILDREN
+
+None
+
+=head1 EFFECTS
+
+None
+
+=head1 DEPENDENCIES
+
+None
+
+=head1 USAGE
+
+  <if name="__ODD__" is="false">
+    ... Children here
+  </if>
+
+In the above example, the children will be executed if the value of __ODD__
+(which is set by the LOOP node) is false. So, for all even iterations.
+
+=head1 AUTHOR
+
+Rob Kinyon (rkinyon@columbus.rr.com)
+
+=head1 SEE ALSO
+
+LOOP
+
+=cut
diff --git a/trunk/lib/Excel/Template/Container/Format.pm b/trunk/lib/Excel/Template/Container/Format.pm
new file mode 100644 (file)
index 0000000..3574362
--- /dev/null
@@ -0,0 +1,116 @@
+package Excel::Template::Container::Format;
+
+use strict;
+
+BEGIN {
+    use vars qw(@ISA);
+    @ISA = qw( Excel::Template::Container );
+
+    use Excel::Template::Container;
+}
+
+use Excel::Template::Format;
+
+sub render
+{
+    my $self = shift;
+    my ($context) = @_;
+
+    my $old_format = $context->active_format;
+    my $format = Excel::Template::Format->copy(
+        $context, $old_format,
+
+        %{$self},
+    );
+    $context->active_format($format);
+
+    my $child_success = $self->iterate_over_children($context);
+
+    $context->active_format($old_format);
+}
+
+1;
+__END__
+
+=head1 NAME
+
+Excel::Template::Container::Format - Excel::Template::Container::Format
+
+=head1 PURPOSE
+
+To format all children according to the parameters
+
+=head1 NODE NAME
+
+FORMAT
+
+=head1 INHERITANCE
+
+Excel::Template::Container
+
+=head1 ATTRIBUTES
+
+=over 4
+
+=item * bold
+
+This will set bold to on or off, depending on the boolean value.
+
+=item * hidden
+
+This will set whether the cell is hidden to on or off, depending on the boolean
+value. (q.v. BOLD tag)
+
+=item * italic
+
+This will set italic to on or off, depending on the boolean value. (q.v. ITALIC
+tag)
+
+=item * font_outline
+
+This will set font_outline to on or off, depending on the boolean value. (q.v.
+OUTLINE tag)
+
+=item * font_shadow
+
+This will set font_shadow to on or off, depending on the boolean value. (q.v.
+SHADOW tag)
+
+=item * font_strikeout
+
+This will set font_strikeout to on or off, depending on the boolean value. (q.v.
+STRIKEOUT tag)
+
+=back 4
+
+=head1 CHILDREN
+
+None
+
+=head1 EFFECTS
+
+None
+
+=head1 DEPENDENCIES
+
+None
+
+=head1 USAGE
+
+  <format bold="1">
+    ... Children here
+  </format>
+
+In the above example, the children will be displayed (if they are displaying
+elements) in a bold format. All other formatting will remain the same and the
+"bold"-ness will end at the end tag.
+
+=head1 AUTHOR
+
+Rob Kinyon (rkinyon@columbus.rr.com)
+
+=head1 SEE ALSO
+
+BOLD, HIDDEN, ITALIC, OUTLINE, SHADOW, STRIKEOUT
+
+=cut
diff --git a/trunk/lib/Excel/Template/Container/Hidden.pm b/trunk/lib/Excel/Template/Container/Hidden.pm
new file mode 100644 (file)
index 0000000..97f8f08
--- /dev/null
@@ -0,0 +1,75 @@
+package Excel::Template::Container::Hidden;
+
+use strict;
+
+BEGIN {
+    use vars qw(@ISA);
+    @ISA = qw( Excel::Template::Container::Format );
+
+    use Excel::Template::Container::Format;
+}
+
+sub new
+{
+    my $class = shift;
+    my $self = $class->SUPER::new(@_);
+
+    $self->{HIDDEN} = 1;
+
+    return $self;
+}
+
+1;
+__END__
+
+=head1 NAME
+
+Excel::Template::Container::Hidden - Excel::Template::Container::Hidden
+
+=head1 PURPOSE
+
+To format all children in hidden
+
+=head1 NODE NAME
+
+HIDDEN
+
+=head1 INHERITANCE
+
+Excel::Template::Container::Format
+
+=head1 ATTRIBUTES
+
+None
+
+=head1 CHILDREN
+
+None
+
+=head1 EFFECTS
+
+None
+
+=head1 DEPENDENCIES
+
+None
+
+=head1 USAGE
+
+  <hidden>
+    ... Children here
+  </hidden>
+
+In the above example, the children will be displayed (if they are displaying
+elements) in a hidden format. All other formatting will remain the same and the
+"hidden"-ness will end at the end tag.
+
+=head1 AUTHOR
+
+Rob Kinyon (rkinyon@columbus.rr.com)
+
+=head1 SEE ALSO
+
+FORMAT
+
+=cut
diff --git a/trunk/lib/Excel/Template/Container/Italic.pm b/trunk/lib/Excel/Template/Container/Italic.pm
new file mode 100644 (file)
index 0000000..f4f4e00
--- /dev/null
@@ -0,0 +1,75 @@
+package Excel::Template::Container::Italic;
+
+use strict;
+
+BEGIN {
+    use vars qw(@ISA);
+    @ISA = qw( Excel::Template::Container::Format );
+
+    use Excel::Template::Container::Format;
+}
+
+sub new
+{
+    my $class = shift;
+    my $self = $class->SUPER::new(@_);
+
+    $self->{ITALIC} = 1;
+
+    return $self;
+}
+
+1;
+__END__
+
+=head1 NAME
+
+Excel::Template::Container::Italic - Excel::Template::Container::Italic
+
+=head1 PURPOSE
+
+To format all children in italic
+
+=head1 NODE NAME
+
+ITALIC
+
+=head1 INHERITANCE
+
+Excel::Template::Container::Format
+
+=head1 ATTRIBUTES
+
+None
+
+=head1 CHILDREN
+
+None
+
+=head1 EFFECTS
+
+None
+
+=head1 DEPENDENCIES
+
+None
+
+=head1 USAGE
+
+  <italic>
+    ... Children here
+  </italic>
+
+In the above example, the children will be displayed (if they are displaying
+elements) in a italic format. All other formatting will remain the same and the
+"italic"-ness will end at the end tag.
+
+=head1 AUTHOR
+
+Rob Kinyon (rkinyon@columbus.rr.com)
+
+=head1 SEE ALSO
+
+FORMAT
+
+=cut
diff --git a/trunk/lib/Excel/Template/Container/Locked.pm b/trunk/lib/Excel/Template/Container/Locked.pm
new file mode 100644 (file)
index 0000000..55cab43
--- /dev/null
@@ -0,0 +1,75 @@
+package Excel::Template::Container::Locked;
+
+use strict;
+
+BEGIN {
+    use vars qw(@ISA);
+    @ISA = qw( Excel::Template::Container::Format );
+
+    use Excel::Template::Container::Format;
+}
+
+sub new
+{
+    my $class = shift;
+    my $self = $class->SUPER::new(@_);
+
+    $self->{LOCKED} = 1;
+
+    return $self;
+}
+
+1;
+__END__
+
+=head1 NAME
+
+Excel::Template::Container::Locked - Excel::Template::Container::Locked
+
+=head1 PURPOSE
+
+To format all children in locked
+
+=head1 NODE NAME
+
+LOCKED
+
+=head1 INHERITANCE
+
+Excel::Template::Container::Format
+
+=head1 ATTRIBUTES
+
+None
+
+=head1 CHILDREN
+
+None
+
+=head1 EFFECTS
+
+None
+
+=head1 DEPENDENCIES
+
+None
+
+=head1 USAGE
+
+  <locked>
+    ... Children here
+  </locked>
+
+In the above example, the children will be displayed (if they are displaying
+elements) in a locked format. All other formatting will remain the same and the
+"locked"-ness will end at the end tag.
+
+=head1 AUTHOR
+
+Rob Kinyon (rkinyon@columbus.rr.com)
+
+=head1 SEE ALSO
+
+FORMAT
+
+=cut
diff --git a/trunk/lib/Excel/Template/Container/Loop.pm b/trunk/lib/Excel/Template/Container/Loop.pm
new file mode 100644 (file)
index 0000000..574ca5f
--- /dev/null
@@ -0,0 +1,179 @@
+package Excel::Template::Container::Loop;
+
+use strict;
+
+BEGIN {
+    use vars qw(@ISA);
+    @ISA = qw(Excel::Template::Container);
+
+    use Excel::Template::Container;
+}
+
+sub new
+{
+    my $class = shift;
+    my $self = $class->SUPER::new(@_);
+
+    if (exists $self->{MAXITERS} && $self->{MAXITERS} < 1)
+    {
+        die "<loop> MAXITERS must be greater than or equal to 1", $/;
+    }
+    else
+    {
+        $self->{MAXITERS} = 0;
+    }
+
+    return $self;
+}
+
+sub make_iterator
+{
+    my $self = shift;
+    my ($context) = @_;
+
+    return Excel::Template::Factory->create('ITERATOR',
+        NAME     => $context->get($self, 'NAME'),
+        MAXITERS => $context->get($self, 'MAXITERS'),
+        CONTEXT  => $context,
+    );
+}
+
+sub render
+{
+    my $self = shift;
+    my ($context) = @_;
+
+    unless ($self->{ITERATOR} && $self->{ITERATOR}->more_params)
+    {
+        $self->{ITERATOR} = $self->make_iterator($context);
+    }
+    my $iterator = $self->{ITERATOR};
+
+    $iterator->enter_scope;
+
+    while ($iterator->can_continue)
+    {
+        $iterator->next;
+
+        unless ($self->iterate_over_children($context))
+        {
+            $iterator->back_up;
+            last;
+        }
+    }
+
+    $iterator->exit_scope;
+
+    return 0 if $iterator->more_params;
+
+    return 1;
+}
+
+sub total_of
+{
+    my $self = shift;
+    my ($context, $attr) = @_;
+
+    my $iterator = $self->make_iterator($context);
+
+    my $total = 0;
+
+    $iterator->enter_scope;
+    while ($iterator->can_continue)
+    {
+        $iterator->next;
+        $total += $self->SUPER::total_of($context, $attr);
+    }
+    $iterator->exit_scope;
+
+    return $total;
+}
+
+sub max_of
+{
+    my $self = shift;
+    my ($context, $attr) = @_;
+
+    my $iterator = $self->make_iterator($context);
+
+    my $max = $context->get($self, $attr);
+
+    $iterator->enter_scope;
+    while ($iterator->can_continue)
+    {
+        $iterator->next;
+        my $v = $self->SUPER::max_of($context, $attr);
+
+        $max = $v if $max < $v;
+    }
+    $iterator->exit_scope;
+
+    return $max;
+}
+
+1;
+__END__
+
+=head1 NAME
+
+Excel::Template::Container::Loop
+
+=head1 PURPOSE
+
+To provide looping
+
+=head1 NODE NAME
+
+LOOP
+
+=head1 INHERITANCE
+
+Excel::Template::Container
+
+=head1 ATTRIBUTES
+
+=over 4
+
+=item * NAME
+
+This is the name of the loop. It's used to identify within the parameter set
+what variables to expose to the children nodes each iteration.
+
+=back 4
+
+=head1 CHILDREN
+
+None
+
+=head1 EFFECTS
+
+None
+
+=head1 DEPENDENCIES
+
+None
+
+=head1 USAGE
+
+  <loop name="LOOPY">
+    ... Children here ...
+  </loop>
+
+In the above example, the children nodes would have access to the LOOPY array
+of hashes as parameters. Each iteration through the array would expose a
+different hash of parameters to the children.
+
+These loops work just like HTML::Template's loops. (I promise I'll give more
+info here!)
+
+There is one difference - I prefer using Perl-like scoping, so accessing of
+variables outside the LOOP scope from within is perfectly acceptable. You can
+also hide outside variables with inner values, if you desire, just like Perl.
+
+=head1 AUTHOR
+
+Rob Kinyon (rkinyon@columbus.rr.com)
+
+=head1 SEE ALSO
+
+=cut
diff --git a/trunk/lib/Excel/Template/Container/Outline.pm b/trunk/lib/Excel/Template/Container/Outline.pm
new file mode 100644 (file)
index 0000000..a13447b
--- /dev/null
@@ -0,0 +1,75 @@
+package Excel::Template::Container::Outline;
+
+use strict;
+
+BEGIN {
+    use vars qw(@ISA);
+    @ISA = qw( Excel::Template::Container::Format );
+
+    use Excel::Template::Container::Format;
+}
+
+sub new
+{
+    my $class = shift;
+    my $self = $class->SUPER::new(@_);
+
+    $self->{FONT_OUTLINE} = 1;
+
+    return $self;
+}
+
+1;
+__END__
+
+=head1 NAME
+
+Excel::Template::Container::Outline - Excel::Template::Container::Outline
+
+=head1 PURPOSE
+
+To format all children in outline
+
+=head1 NODE NAME
+
+OUTLINE
+
+=head1 INHERITANCE
+
+Excel::Template::Container::Format
+
+=head1 ATTRIBUTES
+
+None
+
+=head1 CHILDREN
+
+None
+
+=head1 EFFECTS
+
+None
+
+=head1 DEPENDENCIES
+
+None
+
+=head1 USAGE
+
+  <outline>
+    ... Children here
+  </outline>
+
+In the above example, the children will be displayed (if they are displaying
+elements) in a outline format. All other formatting will remain the same and the
+"outline"-ness will end at the end tag.
+
+=head1 AUTHOR
+
+Rob Kinyon (rkinyon@columbus.rr.com)
+
+=head1 SEE ALSO
+
+FORMAT
+
+=cut
diff --git a/trunk/lib/Excel/Template/Container/Row.pm b/trunk/lib/Excel/Template/Container/Row.pm
new file mode 100644 (file)
index 0000000..cc5c3d1
--- /dev/null
@@ -0,0 +1,81 @@
+package Excel::Template::Container::Row;
+
+use strict;
+
+BEGIN {
+    use vars qw(@ISA);
+    @ISA = qw(Excel::Template::Container);
+
+    use Excel::Template::Container;
+}
+
+sub render
+{
+    my $self = shift;
+    my ($context) = @_;
+
+    $context->{COL} = 0;
+
+    return $self->SUPER::render($context);
+}
+
+sub deltas
+{
+    return {
+        ROW => +1,
+    };
+}
+
+1;
+__END__
+
+=head1 NAME
+
+Excel::Template::Container::Row - Excel::Template::Container::Row
+
+=head1 PURPOSE
+
+To provide a row context for CELL tags
+
+=head1 NODE NAME
+
+ROW
+
+=head1 INHERITANCE
+
+Excel::Template::Container
+
+=head1 ATTRIBUTES
+
+None
+
+=head1 CHILDREN
+
+None
+
+=head1 EFFECTS
+
+Each ROW tag will consume one row of the workbook. When the ROW tag starts, it
+will set the COL value to 0.
+
+=head1 DEPENDENCIES
+
+None
+
+=head1 USAGE
+
+  <row>
+    ... Children here
+  </row>
+
+Generally, you will have CELL and/or FORMULA tags within a ROW.
+
+=head1 AUTHOR
+
+Rob Kinyon (rkinyon@columbus.rr.com)
+
+=head1 SEE ALSO
+
+CELL, FORMULA
+
+=cut
diff --git a/trunk/lib/Excel/Template/Container/Scope.pm b/trunk/lib/Excel/Template/Container/Scope.pm
new file mode 100644 (file)
index 0000000..97c4124
--- /dev/null
@@ -0,0 +1,66 @@
+package Excel::Template::Container::Scope;
+
+use strict;
+
+BEGIN {
+    use vars qw(@ISA);
+    @ISA = qw(Excel::Template::Container);
+
+    use Excel::Template::Container;
+}
+
+# This is used as a placeholder for scoping values across any number
+# of children. It does nothing on its own.
+
+1;
+__END__
+
+=head1 NAME
+
+Excel::Template::Container::Scope
+
+=head1 PURPOSE
+
+To provide scoping of parameters for children
+
+=head1 NODE NAME
+
+SCOPE
+
+=head1 INHERITANCE
+
+Excel::Template::Container
+
+=head1 ATTRIBUTES
+
+None
+
+=head1 CHILDREN
+
+None
+
+=head1 EFFECTS
+
+None
+
+=head1 DEPENDENCIES
+
+None
+
+=head1 USAGE
+
+  <scope param1="value1" param2="value2">
+    ... Children here ...
+  </scope>
+
+In the above example, the children would all have access to the parameters
+param1 and param2. This is useful if you have a section of your template that
+all has the same set of parameter values, but don't have a common parent.
+
+=head1 AUTHOR
+
+Rob Kinyon (rkinyon@columbus.rr.com)
+
+=head1 SEE ALSO
+
+=cut
diff --git a/trunk/lib/Excel/Template/Container/Shadow.pm b/trunk/lib/Excel/Template/Container/Shadow.pm
new file mode 100644 (file)
index 0000000..8597218
--- /dev/null
@@ -0,0 +1,75 @@
+package Excel::Template::Container::Shadow;
+
+use strict;
+
+BEGIN {
+    use vars qw(@ISA);
+    @ISA = qw( Excel::Template::Container::Format );
+
+    use Excel::Template::Container::Format;
+}
+
+sub new
+{
+    my $class = shift;
+    my $self = $class->SUPER::new(@_);
+
+    $self->{FONT_SHADOW} = 1;
+
+    return $self;
+}
+
+1;
+__END__
+
+=head1 NAME
+
+Excel::Template::Container::Shadow - Excel::Template::Container::Shadow
+
+=head1 PURPOSE
+
+To format all children in shadow
+
+=head1 NODE NAME
+
+SHADOW
+
+=head1 INHERITANCE
+
+Excel::Template::Container::Format
+
+=head1 ATTRIBUTES
+
+None
+
+=head1 CHILDREN
+
+None
+
+=head1 EFFECTS
+
+None
+
+=head1 DEPENDENCIES
+
+None
+
+=head1 USAGE
+
+  <shadow>
+    ... Children here
+  </shadow>
+
+In the above example, the children will be displayed (if they are displaying
+elements) in a shadow format. All other formatting will remain the same and the
+"shadow"-ness will end at the end tag.
+
+=head1 AUTHOR
+
+Rob Kinyon (rkinyon@columbus.rr.com)
+
+=head1 SEE ALSO
+
+FORMAT
+
+=cut
diff --git a/trunk/lib/Excel/Template/Container/Strikeout.pm b/trunk/lib/Excel/Template/Container/Strikeout.pm
new file mode 100644 (file)
index 0000000..bc6b96b
--- /dev/null
@@ -0,0 +1,75 @@
+package Excel::Template::Container::Strikeout;
+
+use strict;
+
+BEGIN {
+    use vars qw(@ISA);
+    @ISA = qw( Excel::Template::Container::Format );
+
+    use Excel::Template::Container::Format;
+}
+
+sub new
+{
+    my $class = shift;
+    my $self = $class->SUPER::new(@_);
+
+    $self->{FONT_STRIKEOUT} = 1;
+
+    return $self;
+}
+
+1;
+__END__
+
+=head1 NAME
+
+Excel::Template::Container::Strikeout - Excel::Template::Container::Strikeout
+
+=head1 PURPOSE
+
+To format all children in bold
+
+=head1 NODE NAME
+
+STRIKEOUT
+
+=head1 INHERITANCE
+
+Excel::Template::Container::Format
+
+=head1 ATTRIBUTES
+
+None
+
+=head1 CHILDREN
+
+None
+
+=head1 EFFECTS
+
+None
+
+=head1 DEPENDENCIES
+
+None
+
+=head1 USAGE
+
+  <bold>
+    ... Children here
+  </bold>
+
+In the above example, the children will be displayed (if they are displaying
+elements) in a bold format. All other formatting will remain the same and the
+"bold"-ness will end at the end tag.
+
+=head1 AUTHOR
+
+Rob Kinyon (rkinyon@columbus.rr.com)
+
+=head1 SEE ALSO
+
+FORMAT
+
+=cut
diff --git a/trunk/lib/Excel/Template/Container/Workbook.pm b/trunk/lib/Excel/Template/Container/Workbook.pm
new file mode 100644 (file)
index 0000000..2defd37
--- /dev/null
@@ -0,0 +1,62 @@
+package Excel::Template::Container::Workbook;
+
+use strict;
+
+BEGIN {
+    use vars qw(@ISA);
+    @ISA = qw( Excel::Template::Container );
+
+    use Excel::Template::Container;
+}
+
+1;
+__END__
+
+=head1 NAME
+
+Excel::Template::Container::Workbook - Excel::Template::Container::Workbook
+
+=head1 PURPOSE
+
+The root node
+
+=head1 NODE NAME
+
+WORKBOOK
+
+=head1 INHERITANCE
+
+Excel::Template::Container
+
+=head1 ATTRIBUTES
+
+Currently, none. There will be attributes added here, regarding how the
+workbook as a whole will behave.
+
+=head1 CHILDREN
+
+None
+
+=head1 EFFECTS
+
+None
+
+=head1 DEPENDENCIES
+
+None
+
+=head1 USAGE
+
+  <workbook>
+    ... Children here
+  </workbook>
+
+=head1 AUTHOR
+
+Rob Kinyon (rkinyon@columbus.rr.com)
+
+=head1 SEE ALSO
+
+Nothing
+
+=cut
diff --git a/trunk/lib/Excel/Template/Container/Worksheet.pm b/trunk/lib/Excel/Template/Container/Worksheet.pm
new file mode 100644 (file)
index 0000000..c707779
--- /dev/null
@@ -0,0 +1,80 @@
+package Excel::Template::Container::Worksheet;
+
+use strict;
+
+BEGIN {
+    use vars qw(@ISA);
+    @ISA = qw(Excel::Template::Container);
+
+    use Excel::Template::Container;
+}
+
+sub render
+{
+    my $self = shift;
+    my ($context) = @_;
+
+    $context->new_worksheet($self->{NAME});
+
+    return $self->SUPER::render($context);
+}
+
+1;
+__END__
+
+=head1 NAME
+
+Excel::Template::Container::Worksheet - Excel::Template::Container::Worksheet
+
+=head1 PURPOSE
+
+To provide a new worksheet.
+
+=head1 NODE NAME
+
+WORKSHEET
+
+=head1 INHERITANCE
+
+Excel::Template::Container
+
+=head1 ATTRIBUTES
+
+=over 4
+
+=item * NAME
+
+This is the name of the worksheet to be added.
+
+=back 4
+
+=head1 CHILDREN
+
+None
+
+=head1 EFFECTS
+
+None
+
+=head1 DEPENDENCIES
+
+None
+
+=head1 USAGE
+
+  <worksheet name="My Taxes">
+    ... Children here
+  </worksheet>
+
+In the above example, the children will be executed in the context of the
+"My Taxes" worksheet.
+
+=head1 AUTHOR
+
+Rob Kinyon (rkinyon@columbus.rr.com)
+
+=head1 SEE ALSO
+
+ROW, CELL, FORMULA
+
+=cut
diff --git a/trunk/lib/Excel/Template/Context.pm b/trunk/lib/Excel/Template/Context.pm
new file mode 100644 (file)
index 0000000..79c5415
--- /dev/null
@@ -0,0 +1,262 @@
+package Excel::Template::Context;
+
+use strict;
+
+BEGIN {
+    use vars qw(@ISA);
+    @ISA = qw(Excel::Template::Base);
+
+    use Excel::Template::Base;
+}
+
+use Excel::Template::Format;
+
+# This is a helper object. It is not instantiated by the user, nor does it
+# represent an XML object. Rather, every container will use this object to
+# maintain the context for its children.
+
+my %isAbsolute = map { $_ => 1 } qw(
+    ROW
+    COL
+);
+
+sub new
+{
+    my $class = shift;
+    my $self = $class->SUPER::new(@_);
+
+    $self->{ACTIVE_WORKSHEET} = undef;
+    $self->{ACTIVE_FORMAT}    = Excel::Template::Format->blank_format($self);
+
+    UNIVERSAL::isa($self->{$_}, 'ARRAY') || ($self->{$_} = [])
+        for qw( STACK PARAM_MAP NAME_MAP );
+
+    $self->{$_} = 0 for keys %isAbsolute;
+
+    return $self;
+}
+
+sub _find_param_in_map
+{
+    my $self = shift;
+    my ($map, $param, $depth) = @_;
+    $param = uc $param;
+    $depth ||= 0;
+
+    my $val = undef;
+    my $found = 0;
+
+    for my $map (reverse @{$self->{$map}})
+    {
+        next unless exists $map->{$param};
+        $depth--, next if $depth;
+
+        $found = 1;
+        $val = $map->{$param};
+        last;
+    }
+
+    die "Parameter '$param' not found\n"
+        if !$found && $self->{DIE_ON_NO_PARAM};
+
+    return $val;
+}
+
+sub param
+{
+    my $self = shift;
+    $self->_find_param_in_map(
+        'PARAM_MAP',
+        @_,
+    );
+}
+
+sub named_param
+{
+    my $self = shift;
+    $self->_find_param_in_map(
+        'NAME_MAP',
+        @_,
+    );
+}
+
+sub resolve
+{
+    my $self = shift;
+    my ($obj, $key, $depth) = @_;
+    $key = uc $key;
+    $depth ||= 0;
+
+    my $obj_val = $obj->{$key};
+
+    $obj_val = $self->param($1)
+        if $obj_val =~ /^\$(\S+)$/o;
+
+#GGG Remove this once NAME_MAP is working
+#    $obj_val = $self->named_param($1)
+#        if $obj_val =~ /^\\(\S+)$/o;
+
+#GGG Does this adequately test values to make sure they're legal??
+    # A value is defined as:
+    #    1) An optional operator (+, -, *, or /)
+    #    2) A decimal number
+
+#GGG Convert this to use //x
+    my ($op, $val) = $obj_val =~ m!^\s*([\+\*\/\-])?\s*([\d.]*\d)\s*$!oi;
+
+    # Unless it's a relative value, we have what we came for.
+    return $obj_val unless $op;
+
+    my $prev_val = $isAbsolute{$key}
+        ? $self->{$key}
+        : $self->get($obj, $key, $depth + 1);
+
+    return $obj_val unless defined $prev_val;
+    return $prev_val unless defined $obj_val;
+
+    # Prevent divide-by-zero issues.
+    return $val if $op eq '/' and $val == 0;
+
+    my $new_val;
+    for ($op)
+    {
+        /^\+$/ && do { $new_val = ($prev_val + $val); last; };
+        /^\-$/ && do { $new_val = ($prev_val - $val); last; };
+        /^\*$/ && do { $new_val = ($prev_val * $val); last; };
+        /^\/$/ && do { $new_val = ($prev_val / $val); last; };
+
+        die "Unknown operator '$op' in arithmetic resolve\n";
+    }
+
+    return $new_val if defined $new_val;
+    return;
+}
+
+sub enter_scope
+{
+    my $self = shift;
+    my ($obj) = @_;
+
+    push @{$self->{STACK}}, $obj;
+
+    for my $key (keys %isAbsolute)
+    {
+        next unless exists $obj->{$key};
+        $self->{$key} = $self->resolve($obj, $key);
+    }
+
+    return 1;
+}
+
+sub exit_scope
+{
+    my $self = shift;
+    my ($obj, $no_delta) = @_;
+
+    unless ($no_delta)
+    {
+        my $deltas = $obj->deltas($self);
+        $self->{$_} += $deltas->{$_} for keys %$deltas;
+    }
+
+    pop @{$self->{STACK}};
+
+    return 1;
+}
+
+sub get
+{
+    my $self = shift;
+    my ($dummy, $key, $depth) = @_;
+    $depth ||= 0;
+    $key = uc $key;
+
+    return unless @{$self->{STACK}};
+
+    my $obj = $self->{STACK}[-1];
+
+    return $self->{$key} if $isAbsolute{$key};
+
+    my $val = undef;
+    my $this_depth = $depth;
+    foreach my $e (reverse @{$self->{STACK}})
+    {
+        next unless exists $e->{$key};
+        next if $this_depth-- > 0;
+
+        $val = $self->resolve($e, $key, $depth);
+        last;
+    }
+
+    $val = $self->{$key} unless defined $val;
+    return $val unless defined $val;
+
+    return $self->param($1, $depth) if $val =~ /^\$(\S+)$/o;
+
+    return $val;
+}
+
+sub active_format
+{
+    my $self = shift;
+    
+    $self->{ACTIVE_FORMAT} = $_[0]
+        if @_;
+
+    $self->{ACTIVE_FORMAT};
+}
+
+sub new_worksheet
+{
+    my $self = shift;
+    my ($name) = @_;
+
+    $self->{ROW} = $self->{COL} = 0;
+
+    $self->active_worksheet(
+        $self->{XLS}->add_worksheet(
+            $name || '',
+        ),
+    );
+}
+
+sub active_worksheet
+{
+    my $self = shift;
+    
+    $self->{ACTIVE_WORKSHEET} = $_[0]
+        if @_;
+
+    $self->{ACTIVE_WORKSHEET};
+}
+
+1;
+__END__
+
+=head1 NAME
+
+Excel::Template::Context
+
+=head1 PURPOSE
+
+=head1 NODE NAME
+
+=head1 INHERITANCE
+
+=head1 ATTRIBUTES
+
+=head1 CHILDREN
+
+=head1 AFFECTS
+
+=head1 DEPENDENCIES
+
+=head1 USAGE
+
+=head1 AUTHOR
+
+Rob Kinyon (rkinyon@columbus.rr.com)
+
+=head1 SEE ALSO
+
+=cut
diff --git a/trunk/lib/Excel/Template/Element.pm b/trunk/lib/Excel/Template/Element.pm
new file mode 100644 (file)
index 0000000..4ca8284
--- /dev/null
@@ -0,0 +1,41 @@
+package Excel::Template::Element;
+
+use strict;
+
+BEGIN {
+    use vars qw(@ISA);
+    @ISA = qw(Excel::Template::Base);
+
+    use Excel::Template::Base;
+}
+
+1;
+__END__
+
+=head1 NAME
+
+Excel::Template::Element
+
+=head1 PURPOSE
+
+=head1 NODE NAME
+
+=head1 INHERITANCE
+
+=head1 ATTRIBUTES
+
+=head1 CHILDREN
+
+=head1 AFFECTS
+
+=head1 DEPENDENCIES
+
+=head1 USAGE
+
+=head1 AUTHOR
+
+Rob Kinyon (rkinyon@columbus.rr.com)
+
+=head1 SEE ALSO
+
+=cut
diff --git a/trunk/lib/Excel/Template/Element/Cell.pm b/trunk/lib/Excel/Template/Element/Cell.pm
new file mode 100644 (file)
index 0000000..88a0140
--- /dev/null
@@ -0,0 +1,147 @@
+package Excel::Template::Element::Cell;
+
+use strict;
+
+BEGIN {
+    use vars qw(@ISA);
+    @ISA = qw(Excel::Template::Element);
+
+    use Excel::Template::Element;
+}
+
+sub new
+{
+    my $class = shift;
+    my $self = $class->SUPER::new(@_);
+                                                                                
+    $self->{TXTOBJ} = Excel::Template::Factory->create('TEXTOBJECT');
+                                                                                
+    return $self;
+}
+
+sub get_text
+{
+    my $self = shift;
+    my ($context) = @_;
+                                                                                
+    my $txt = $context->get($self, 'TEXT');
+    if (defined $txt)
+    {
+        my $txt_obj = Excel::Template::Factory->create('TEXTOBJECT');
+        push @{$txt_obj->{STACK}}, $txt;
+        $txt = $txt_obj->resolve($context);
+    }
+    elsif ($self->{TXTOBJ})
+    {
+        $txt = $self->{TXTOBJ}->resolve($context)
+    }
+    else
+    {
+#        $txt = Unicode::String::utf8('');
+        $txt = '';
+    }
+                                                                                
+    return $txt;
+}
+
+sub render
+{
+    my $self = shift;
+    my ($context) = @_;
+
+    $context->active_worksheet->write(
+        (map { $context->get($self, $_) } qw(ROW COL)),
+        $self->get_text($context),
+        $context->active_format,
+    );
+
+    return 1;
+}
+
+sub deltas
+{
+    return {
+        COL => +1,
+    };
+}
+
+1;
+__END__
+
+=head1 NAME
+
+Excel::Template::Element::Cell - Excel::Template::Element::Cell
+
+=head1 PURPOSE
+
+To actually write stuff to the worksheet
+
+=head1 NODE NAME
+
+CELL
+
+=head1 INHERITANCE
+
+Excel::Template::Element
+
+=head1 ATTRIBUTES
+
+=over 4
+
+=item * TEXT
+
+This is the text to write to the cell. This can either be text or a parameter
+with a dollar-sign in front of the parameter name.
+
+=item * COL
+
+Optionally, you can specify which column you want this cell to be in. It can be
+either a number (zero-based) or an offset. See Excel::Template for more info on
+offset-based numbering.
+
+=back 4
+
+There will be more parameters added, as features are added.
+
+=head1 CHILDREN
+
+Excel::Template::Element::Formula
+
+=head1 EFFECTS
+
+This will consume one column on the current row. 
+
+=head1 DEPENDENCIES
+
+None
+
+=head1 USAGE
+
+  <cell text="Some Text Here"/>
+  <cell>Some other text here</cell>
+
+  <cell text="$Param2"/>
+  <cell>Some <var name="Param"> text here</cell>
+
+In the above example, four cells are written out. The first two have text hard-
+coded. The second two have variables. The third and fourth items have another
+thing that should be noted. If you have text where you want a variable in the
+middle, you have to use the latter form. Variables within parameters are the
+entire parameter's value.
+
+Please see Spreadsheet::WriteExcel for what constitutes a legal formula.
+
+=head1 BACK-REFERENCES
+
+Currently, you can only use a hard-coded formula. The next release will add the
+capability to have a formula reference other nodes in the template dynamically.
+
+=head1 AUTHOR
+
+Rob Kinyon (rkinyon@columbus.rr.com)
+
+=head1 SEE ALSO
+
+ROW, VAR, FORMULA
+
+=cut
diff --git a/trunk/lib/Excel/Template/Element/Formula.pm b/trunk/lib/Excel/Template/Element/Formula.pm
new file mode 100644 (file)
index 0000000..094326e
--- /dev/null
@@ -0,0 +1,109 @@
+package Excel::Template::Element::Formula;
+
+use strict;
+
+BEGIN {
+    use vars qw(@ISA);
+    @ISA = qw(Excel::Template::Element::Cell);
+
+    use Excel::Template::Element::Cell;
+}
+
+sub get_text
+{
+    my $self = shift;
+    my ($context) = @_;
+
+    my $text = $self->SUPER::get_text($context);
+
+# At this point, we must do back-reference dereferencing
+
+    return $text;
+}
+
+sub render
+{
+    my $self = shift;
+    my ($context) = @_;
+
+    $context->active_worksheet->write_formula(
+        (map { $context->get($self, $_) } qw(ROW COL)),
+        $self->get_text($context),
+    );
+
+    return 1;
+}
+
+1;
+__END__
+
+=head1 NAME
+
+Excel::Template::Element::Formula - Excel::Template::Element::Formula
+
+=head1 PURPOSE
+
+To write formulas to the worksheet
+
+=head1 NODE NAME
+
+FORMULA
+
+=head1 INHERITANCE
+
+Excel::Template::Element::Cell
+
+=head1 ATTRIBUTES
+
+=over 4
+
+=item * TEXT
+
+This is the formula to write to the cell. This can either be text or a parameter
+with a dollar-sign in front of the parameter name.
+
+=item * COL
+
+Optionally, you can specify which column you want this cell to be in. It can be
+either a number (zero-based) or an offset. See Excel::Template for more info on
+offset-based numbering.
+
+=back 4
+
+There will be more parameters added, as features are added.
+
+=head1 CHILDREN
+
+None
+
+=head1 EFFECTS
+
+This will consume one column on the current row. 
+
+=head1 DEPENDENCIES
+
+None
+
+=head1 USAGE
+
+  <formula text="=(1 + 2)"/>
+  <formula>=SUM(A1:A5)</formula>
+
+  <formula text="$Param2"/>
+  <formula>=(A1 + <var name="Param">)</formula>
+
+In the above example, four formulas are written out. The first two have the
+formula hard-coded. The second two have variables. The third and fourth items
+have another thing that should be noted. If you have a formula where you want a
+variable in the middle, you have to use the latter form. Variables within
+parameters are the entire parameter's value.
+
+=head1 AUTHOR
+
+Rob Kinyon (rkinyon@columbus.rr.com)
+
+=head1 SEE ALSO
+
+ROW, VAR, CELL
+
+=cut
diff --git a/trunk/lib/Excel/Template/Element/Var.pm b/trunk/lib/Excel/Template/Element/Var.pm
new file mode 100644 (file)
index 0000000..b4eac2f
--- /dev/null
@@ -0,0 +1,75 @@
+package Excel::Template::Element::Var;
+
+use strict;
+
+BEGIN {
+    use vars qw(@ISA);
+    @ISA = qw(Excel::Template::Element);
+
+    use Excel::Template::Element;
+}
+
+sub resolve { ($_[1])->param($_[1]->resolve($_[0], 'NAME')) }
+
+1;
+__END__
+
+=head1 NAME
+
+Excel::Template::Element::Var
+
+=head1 PURPOSE
+
+To provide parameter substitution.
+
+=head1 NODE NAME
+
+VAR
+
+=head1 INHERITANCE
+
+Excel::Template::Element
+
+=head1 ATTRIBUTES
+
+=over 4
+
+=item * NAME
+
+This is the name of the parameter to substitute here.
+
+=back 4
+
+=head1 CHILDREN
+
+None
+
+=head1 EFFECTS
+
+None
+
+=head1 DEPENDENCIES
+
+This will only be used within CELL tags.
+
+=head1 USAGE
+
+This is used exactly like HTML::Template's TMPL_VAR. There is one exception -
+since you can have variable names inside the parameters, you can do something
+like:
+
+  <loop name="LOOPY">
+    <var name="$SomeParam"/>
+  </loop>
+
+Where the actual name to be substituted is, itself, a parameter.
+
+=head1 AUTHOR
+
+Rob Kinyon (rkinyon@columbus.rr.com)
+
+=head1 SEE ALSO
+
+CELL
+
+=cut
diff --git a/trunk/lib/Excel/Template/Factory.pm b/trunk/lib/Excel/Template/Factory.pm
new file mode 100644 (file)
index 0000000..6b806c9
--- /dev/null
@@ -0,0 +1,167 @@
+package Excel::Template::Factory;
+
+use strict;
+
+BEGIN {
+    use vars qw(%Manifest %isBuildable);
+}
+
+%Manifest = (
+
+# These are the instantiable nodes
+    'IF'        => 'Excel::Template::Container::Conditional',
+    'LOOP'      => 'Excel::Template::Container::Loop',
+    'ROW'       => 'Excel::Template::Container::Row',
+    'SCOPE'     => 'Excel::Template::Container::Scope',
+    'WORKBOOK'  => 'Excel::Template::Container::Workbook',
+    'WORKSHEET' => 'Excel::Template::Container::Worksheet',
+
+    'CELL'      => 'Excel::Template::Element::Cell',
+    'FORMULA'   => 'Excel::Template::Element::Formula',
+    'VAR'       => 'Excel::Template::Element::Var',
+
+    'FORMAT'    => 'Excel::Template::Container::Format',
+
+# These are all the Format short-cut objects
+    'BOLD'      => 'Excel::Template::Container::Bold',
+    'HIDDEN'    => 'Excel::Template::Container::Hidden',
+    'ITALIC'    => 'Excel::Template::Container::Italic',
+    'LOCKED'    => 'Excel::Template::Container::Locked',
+    'OUTLINE'   => 'Excel::Template::Container::Outline',
+    'SHADOW'    => 'Excel::Template::Container::Shadow',
+    'STRIKEOUT' => 'Excel::Template::Container::Strikeout',
+
+# These are the helper objects
+
+    'CONTEXT'    => 'Excel::Template::Context',
+    'ITERATOR'   => 'Excel::Template::Iterator',
+    'TEXTOBJECT' => 'Excel::Template::TextObject',
+
+    'CONTAINER'  => 'Excel::Template::Container',
+    'ELEMENT'    => 'Excel::Template::Element',
+
+    'BASE'       => 'Excel::Template::Base',
+);
+
+%isBuildable = map { $_ => 1 } qw(
+    BOLD
+    CELL
+    FORMAT
+    FORMULA
+    IF
+    ITALIC
+    OUTLINE
+    LOOP
+    ROW
+    SHADOW
+    STRIKEOUT
+    VAR
+    WORKBOOK
+    WORKSHEET
+);
+
+sub register
+{
+    my %params = @_;
+
+    my @param_names = qw(name class isa);
+    for (@param_names)
+    {
+        unless ($params{$_})
+        {
+            warn "$_ was not supplied to register()\n";
+            return 0;
+        }
+    }
+
+    my $name = uc $params{name};
+    if (exists $Manifest{$name})
+    {
+        warn "$params{name} already exists in the manifest.\n";
+        return 0;
+    }
+
+    my $isa = uc $params{isa};
+    unless (exists $Manifest{$isa})
+    {
+        warn "$params{isa} does not exist in the manifest.\n";
+        return 0;
+    }
+
+    $Manifest{$name} = $params{class};
+    $isBuildable{$name} = 1;
+
+    {
+        no strict 'refs';
+        unshift @{"$params{class}::ISA"}, $Manifest{$isa};
+    }
+
+    return 1;
+}
+
+sub create
+{
+    my $class = shift;
+    my $name = uc shift;
+
+    return unless exists $Manifest{$name};
+
+    (my $filename = $Manifest{$name}) =~ s!::!/!g;
+    eval {
+        require "$filename.pm";
+    }; if ($@) {
+        die "Cannot find or compile PM file for '$name' ($filename)\n";
+    }
+    return $Manifest{$name}->new(@_);
+}
+
+sub create_node
+{
+    my $class = shift;
+    my $name = uc shift;
+
+    return unless exists $isBuildable{$name};
+
+    return $class->create($name, @_);
+}
+
+sub isa
+{
+    return unless @_ >= 2;
+    exists $Manifest{uc $_[1]}
+        ? UNIVERSAL::isa($_[0], $Manifest{uc $_[1]})
+        : UNIVERSAL::isa(@_)
+}
+
+1;
+__END__
+
+=head1 NAME
+
+Excel::Template::Factory
+
+=head1 PURPOSE
+
+=head1 NODE NAME
+
+=head1 INHERITANCE
+
+=head1 ATTRIBUTES
+
+=head1 CHILDREN
+
+=head1 AFFECTS
+
+=head1 DEPENDENCIES
+
+=head1 USAGE
+
+=head1 AUTHOR
+
+Rob Kinyon (rkinyon@columbus.rr.com)
+
+=head1 SEE ALSO
+
+=cut
diff --git a/trunk/lib/Excel/Template/Format.pm b/trunk/lib/Excel/Template/Format.pm
new file mode 100644 (file)
index 0000000..dcd2a1f
--- /dev/null
@@ -0,0 +1,141 @@
+package Excel::Template::Format;
+
+use strict;
+
+# This is the format repository. Spreadsheet::WriteExcel does not cache the
+# known formats. So, it is very possible to continually add the same format
+# over and over until you run out of RAM or addressability in the XLS file. In
+# real life, less than 10-20 formats are used, and they're re-used in various
+# places in the file. This provides a way of keeping track of already-allocated
+# formats and making new formats based on old ones.
+
+{
+    # %_Parameters is a hash with the key being the format name and the value
+    # being the index/length of the format in the bit-vector.
+    my %_Formats = ( 
+        bold   => [ 0, 1 ],
+        italic => [ 1, 1 ],
+        locked => [ 2, 1 ],
+        hidden => [ 3, 1 ],
+        font_outline   => [ 4, 1 ],
+        font_shadow    => [ 5, 1 ],
+        font_strikeout => [ 6, 1 ],
+    );
+    sub _params_to_vec
+    {
+        my %params = @_;
+        $params{lc $_} = delete $params{$_} for keys %params;
+        my $vec = '';
+        vec( $vec, $_Formats{$_}[0], $_Formats{$_}[1] ) = ($params{$_} && 1)  
+            for grep { exists $_Formats{$_} } 
+                map { lc } keys %params;
+        $vec;
+    }
+    sub _vec_to_params
+    {
+        my ($vec) = @_;
+        my %params;
+        while (my ($k, $v) = each %_Formats) 
+        {
+            next unless vec( $vec, $v->[0], $v->[1] );
+            $params{$k} = 1;
+        }
+        %params;
+    }
+}
+
+{
+    my %_Formats;
+
+    sub _assign {
+        $_Formats{$_[0]} = $_[1] unless exists $_Formats{$_[0]};
+        $_Formats{$_[1]} = $_[0] unless exists $_Formats{$_[1]};
+    }
+
+    sub _retrieve_vec    { ref($_[0]) ? ($_Formats{$_[0]}) : ($_[0]); }
+    sub _retrieve_format { ref($_[0]) ? ($_[0]) : ($_Formats{$_[0]}); }
+}
+
+sub blank_format
+{
+    shift;
+    my ($context) = @_;
+
+    my $blank_vec = _params_to_vec();
+
+    my $format = _retrieve_format($blank_vec);
+    return $format if $format;
+
+    $format = $context->{XLS}->add_format;
+    _assign($blank_vec, $format);
+    $format;
+}
+
+sub copy
+{
+    shift;
+    my ($context, $old_format, %properties) = @_;
+
+    defined(my $vec = _retrieve_vec($old_format))
+        || die "Internal Error: Cannot find vector for format '$old_format'!\n";
+
+    my $new_vec = _params_to_vec(%properties);
+
+    $new_vec |= $vec;
+
+    my $format = _retrieve_format($new_vec);
+    return $format if $format;
+
+    $format = $context->{XLS}->add_format(_vec_to_params($new_vec));
+    _assign($new_vec, $format);
+    $format;
+}
+
+1;
+__END__
+
+Category   Description       Property        Method Name          Implemented
+--------   -----------       --------        -----------          -----------
+Font       Font type         font            set_font()
+           Font size         size            set_size()
+           Font color        color           set_color()
+           Bold              bold            set_bold()              YES
+           Italic            italic          set_italic()            YES
+           Underline         underline       set_underline()
+           Strikeout         font_strikeout  set_font_strikeout()    YES
+           Super/Subscript   font_script     set_font_script()
+           Outline           font_outline    set_font_outline()      YES
+           Shadow            font_shadow     set_font_shadow()       YES
+
+Number     Numeric format    num_format      set_num_format()
+
+Protection Lock cells        locked          set_locked()            YES
+           Hide formulas     hidden          set_hidden()            YES
+
+Alignment  Horizontal align  align           set_align()
+           Vertical align    valign          set_align()
+           Rotation          rotation        set_rotation()
+           Text wrap         text_wrap       set_text_wrap()
+           Justify last      text_justlast   set_text_justlast()
+           Merge             merge           set_merge()
+
+Pattern    Cell pattern      pattern         set_pattern()
+           Background color  bg_color        set_bg_color()
+           Foreground color  fg_color        set_fg_color()
+
+Border     Cell border       border          set_border()
+           Bottom border     bottom          set_bottom()
+           Top border        top             set_top()
+           Left border       left            set_left()
+           Right border      right           set_right()
+           Border color      border_color    set_border_color()
+           Bottom color      bottom_color    set_bottom_color()
+           Top color         top_color       set_top_color()
+           Left color        left_color      set_left_color()
+           Right color       right_color     set_right_color()
diff --git a/trunk/lib/Excel/Template/Iterator.pm b/trunk/lib/Excel/Template/Iterator.pm
new file mode 100644 (file)
index 0000000..bdd74e2
--- /dev/null
@@ -0,0 +1,211 @@
+package Excel::Template::Iterator;
+
+use strict;
+
+BEGIN {
+    use vars qw(@ISA);
+    @ISA = qw(Excel::Template::Base);
+
+    use Excel::Template::Base;
+}
+
+sub new
+{
+    my $class = shift;
+    my $self = $class->SUPER::new(@_);
+
+    unless (Excel::Template::Factory::isa($self->{CONTEXT}, 'CONTEXT'))
+    {
+        die "Internal Error: No context object passed to ", __PACKAGE__, $/;
+    }
+
+    $self->{MAXITERS} ||= 0;
+
+    # This is the index we will work on NEXT, in whatever direction the
+    # iterator is going.
+    $self->{INDEX} = -1;
+
+    # This is a short-circuit parameter to let the iterator function in a
+    # null state.
+    $self->{NO_PARAMS} = 0;
+    unless ($self->{NAME} =~ /\w/)
+    {
+        $self->{NO_PARAMS} = 1;
+
+        warn "INTERNAL ERROR: 'NAME' was blank was blank when passed to ", __PACKAGE__, $/;
+
+        return $self;
+    }
+
+    # Cache the reference to the appropriate data.
+    $self->{DATA} = $self->{CONTEXT}->param($self->{NAME});
+
+    unless (UNIVERSAL::isa($self->{DATA}, 'ARRAY'))
+    {
+        $self->{NO_PARAMS} = 1;
+        warn "'$self->{NAME}' does not have a list of parameters", $/;
+
+        return $self;
+    }
+
+    unless (@{$self->{DATA}})
+    {
+        $self->{NO_PARAMS} = 1;
+    }
+
+    $self->{MAX_INDEX} = $#{$self->{DATA}};
+
+    return $self;
+}
+
+sub enter_scope
+{
+    my $self = shift;
+
+    return 0 if $self->{NO_PARAMS};
+
+    for my $x ($self->{DATA}[$self->{INDEX}])
+    {
+        $x->{uc $_} = delete $x->{$_} for keys %$x;
+    }
+
+    push @{$self->{CONTEXT}{PARAM_MAP}}, $self->{DATA}[$self->{INDEX}];
+
+    return 1;
+}
+
+sub exit_scope
+{
+    my $self = shift;
+
+    return 0 if $self->{NO_PARAMS};
+
+    # There has to be the base parameter map and at least the one that
+    # Iterator::enter_scope() added on top.
+    @{$self->{CONTEXT}{PARAM_MAP}} > 1 ||
+        die "Internal Error: ", __PACKAGE__, "'s internal param_map off!", $/;
+
+    pop @{$self->{CONTEXT}{PARAM_MAP}};
+
+    return 1;
+}
+
+sub can_continue
+{
+    my $self = shift;
+
+    return 0 if $self->{NO_PARAMS};
+
+    return 1 if $self->more_params;
+
+    return 0;
+}
+
+sub more_params
+{
+    my $self = shift;
+
+    return 0 if $self->{NO_PARAMS};
+
+    return 1 if $self->{MAX_INDEX} > $self->{INDEX};
+
+    return 0;
+}
+
+# Call this method BEFORE incrementing the index to the next value.
+sub _do_globals
+{
+    my $self = shift;
+
+    my $data = $self->{DATA}[$self->{INDEX}];
+
+    # Perl's arrays are 0-indexed. Thus, the first element is at index "0".
+    # This means that odd-numbered elements are at even indices, and vice-versa.
+    # This also means that MAX (the number of elements in the array) can never
+    # be the value of an index. It is NOT the last index in the array.
+
+    $data->{'__FIRST__'} ||= ($self->{INDEX} == 0);
+    $data->{'__INNER__'} ||= (0 < $self->{INDEX} && $self->{INDEX} < $self->{MAX_INDEX});
+    $data->{'__LAST__'}  ||= ($self->{INDEX} == $self->{MAX_INDEX});
+    $data->{'__ODD__'}   ||= !($self->{INDEX} % 2);
+
+    return 1;
+}
+
+sub next
+{
+    my $self = shift;
+
+    return 0 if $self->{NO_PARAMS};
+
+    return 0 unless $self->more_params;
+
+    $self->exit_scope;
+
+    $self->{INDEX}++;
+
+    $self->_do_globals;
+
+    $self->enter_scope;
+
+    return 1;
+}
+
+sub back_up
+{
+    my $self = shift;
+
+    return 0 if $self->{NO_PARAMS};
+
+    $self->exit_scope;
+
+    $self->{INDEX}--;
+
+    $self->_do_globals;
+
+    $self->enter_scope;
+
+    return 1;
+}
+
+sub reset
+{
+    my $self = shift;
+
+    return 0 if $self->{NO_PARAMS};
+
+    $self->{INDEX} = -1;
+
+    return 1;
+}
+
+1;
+__END__
+
+=head1 NAME
+
+Excel::Template::Iterator
+
+=head1 PURPOSE
+
+=head1 NODE NAME
+
+=head1 INHERITANCE
+
+=head1 ATTRIBUTES
+
+=head1 CHILDREN
+
+=head1 AFFECTS
+
+=head1 DEPENDENCIES
+
+=head1 USAGE
+
+=head1 AUTHOR
+
+Rob Kinyon (rkinyon@columbus.rr.com)
+
+=head1 SEE ALSO
+
+=cut
diff --git a/trunk/lib/Excel/Template/TextObject.pm b/trunk/lib/Excel/Template/TextObject.pm
new file mode 100644 (file)
index 0000000..ac7a3d7
--- /dev/null
@@ -0,0 +1,79 @@
+package Excel::Template::TextObject;
+
+use strict;
+
+BEGIN {
+    use vars qw(@ISA);
+    @ISA = qw(Excel::Template::Base);
+
+    use Excel::Template::Base;
+
+#    use Unicode::String;
+}
+
+# This is a helper object. It is not instantiated by the user,
+# nor does it represent an XML object. Rather, certain elements,
+# such as <textbox>, can use this object to do text with variable
+# substitutions.
+
+sub new
+{
+    my $class = shift;
+    my $self = $class->SUPER::new(@_);
+
+    $self->{STACK} = [] unless UNIVERSAL::isa($self->{STACK}, 'ARRAY');
+
+    return $self;
+}
+
+sub resolve
+{
+    my $self = shift;
+    my ($context) = @_;
+
+#    my $t = Unicode::String::utf8('');
+    my $t = '';
+
+    for my $tok (@{$self->{STACK}})
+    {
+        my $val = $tok;
+        $val = $val->resolve($context)
+            if Excel::Template::Factory::isa($val, 'VAR');
+
+#        $t .= Unicode::String::utf8("$val");
+        $t .= $val;
+    }
+
+    return $t;
+}
+
+1;
+__END__
+
+=head1 NAME
+
+Excel::Template::TextObject
+
+=head1 PURPOSE
+
+=head1 NODE NAME
+
+=head1 INHERITANCE
+
+=head1 ATTRIBUTES
+
+=head1 CHILDREN
+
+=head1 AFFECTS
+
+=head1 DEPENDENCIES
+
+=head1 USAGE
+
+=head1 AUTHOR
+
+Rob Kinyon (rkinyon@columbus.rr.com)
+
+=head1 SEE ALSO
+
+=cut
diff --git a/trunk/t/001_load.t b/trunk/t/001_load.t
new file mode 100644 (file)
index 0000000..86fd2f1
--- /dev/null
@@ -0,0 +1,12 @@
+# -*- perl -*-
+
+# t/001_load.t - check module loading and create testing directory
+
+use Test::More tests => 2;
+
+BEGIN { use_ok( 'Excel::Template' ); }
+
+my $object = Excel::Template->new ();
+isa_ok ($object, 'Excel::Template');
+
+