- Fixed bugs that were:
Rob Kinyon [Thu, 4 Nov 2004 21:14:14 +0000 (21:14 +0000)]
  - preventing a worksheet from using a variable name in a loop
  - allowing two worksheets to have the same name
  - preventing a worksheet from being called '0' or '0.0'
- Added back-references. This allows for one cell to refer to another
  cell in an Excel-aware way, especially in formulas.
- Added the following nodes:
  - BACKREF
  - RANGE

12 files changed:
Changes
MANIFEST
Makefile.PL
lib/Excel/Template.pm
lib/Excel/Template/Base.pm
lib/Excel/Template/Container/Worksheet.pm
lib/Excel/Template/Context.pm
lib/Excel/Template/Element/Backref.pm [new file with mode: 0755]
lib/Excel/Template/Element/Cell.pm_
lib/Excel/Template/Element/Range.pm [new file with mode: 0755]
lib/Excel/Template/Factory.pm
lib/Excel/Template/TextObject.pm_

diff --git a/Changes b/Changes
index 58ad3d1..05e7456 100644 (file)
--- a/Changes
+++ b/Changes
@@ -1,6 +1,17 @@
-Revision history for Perl module Excel::Template
-
-0.14 Wed Nov 04 13:30:00 2004
+Revision history for Perl distribution Excel::Template
+
+0.15 Thu Nov 04 16:15:00 2004
+        - Fixed bugs that were:
+          - preventing a worksheet from using a variable name in a loop
+          - allowing two worksheets to have the same name
+          - preventing a worksheet from being called '0' or '0.0'
+        - Added back-references. This allows for one cell to refer to another
+          cell in an Excel-aware way, especially in formulas.
+        - Added the following nodes:
+          - BACKREF
+          - RANGE
+
+0.14 Thu Nov 04 13:30:00 2004
         - Added new format support for (nearly) all formats supported by
           Spreadsheet::WriteExcel
         - Fixed email address everywhere
index 6275da5..88d5c49 100644 (file)
--- a/MANIFEST
+++ b/MANIFEST
@@ -26,8 +26,11 @@ 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/Backref.pm
 lib/Excel/Template/Element/Cell.pm_
 lib/Excel/Template/Element/Formula.pm
+lib/Excel/Template/Element/Range.pm
 lib/Excel/Template/Element/Var.pm
 t/001_load.t
 Makefile.PL
+META.yml                                 Module meta-data (added by MakeMaker)
index 6fdd34a..a7aefc3 100644 (file)
@@ -8,6 +8,7 @@ my $prereqs = {
     'Test::Simple'            => 0.44,
     'XML::Parser'             => 0.01,
     'IO::File'                => 0.01,
+    'IO::Scalar'              => 0.01,
     'File::Basename'          => 0.01,
     'Spreadsheet::WriteExcel' => 0.42,
 };
index 00e7531..1d7626e 100644 (file)
@@ -6,7 +6,7 @@ BEGIN {
     use Excel::Template::Base;
     use vars qw ($VERSION @ISA);
 
-    $VERSION  = '0.14';
+    $VERSION  = '0.15';
     @ISA      = qw( Excel::Template::Base );
 }
 
@@ -123,11 +123,11 @@ sub parse_xml
                 my $node = Excel::Template::Factory->create_node($name, @_);
                 die "'$name' (@_) didn't make a node!\n" unless defined $node;
 
-                if ($name eq 'WORKBOOK')
+                if ( $node->isa( 'WORKBOOK' ) )
                 {
                     push @{$self->{WORKBOOKS}}, $node;
                 }
-                elsif ($name eq 'VAR')
+                elsif ( $node->is_embedded )
                 {
                     return unless @stack;
                                                                                 
@@ -347,6 +347,16 @@ but I expect to be adding on to it very soon.
 
 There is a mailing list at http://groups-beta.google.com/group/ExcelTemplate
 
+Robert Graff -
+
+=over 4
+
+=item * Finishing formats
+
+=item * Fixing several bugs in worksheet naming
+
+=back 4
+
 =head1 COPYRIGHT
 
 This program is free software; you can redistribute
index 9788c11..9215ac5 100644 (file)
@@ -25,6 +25,7 @@ sub new
 }
                                                                                 
 sub isa { Excel::Template::Factory::isa(@_) }
+sub is_embedded { Excel::Template::Factory::is_embedded(@_) }
 
 sub calculate { ($_[1])->get(@_[0,2]) }
 #{
index 4b96638..6a1edaf 100644 (file)
@@ -14,7 +14,7 @@ sub render
     my $self = shift;
     my ($context) = @_;
 
-    $context->new_worksheet($self->{NAME});
+    $context->new_worksheet( $self );
 
     return $self->SUPER::render($context);
 }
index ea5d8f3..7a66225 100644 (file)
@@ -15,7 +15,7 @@ use Excel::Template::Format;
 # represent an XML object. Rather, every container will use this object to
 # maintain the context for its children.
 
-my %isAbsolute = map { $_ => 1 } qw(
+my %isAbsolute = map { $_ => !!1 } qw(
     ROW
     COL
 );
@@ -27,6 +27,7 @@ sub new
 
     $self->{ACTIVE_WORKSHEET} = undef;
     $self->{ACTIVE_FORMAT}    = Excel::Template::Format->blank_format($self);
+    $self->{WORKSHEET_NAMES}  = undef;
 
     UNIVERSAL::isa($self->{$_}, 'ARRAY') || ($self->{$_} = [])
         for qw( STACK PARAM_MAP NAME_MAP );
@@ -43,15 +44,13 @@ sub _find_param_in_map
     $param = uc $param;
     $depth ||= 0;
 
-    my $val = undef;
-    my $found = 0;
-
+    my ($val, $found);
     for my $map (reverse @{$self->{$map}})
     {
         next unless exists $map->{$param};
         $depth--, next if $depth;
 
-        $found = 1;
+        $found = !!1;
         $val = $map->{$param};
         last;
     }
@@ -145,7 +144,7 @@ sub enter_scope
         $self->{$key} = $self->resolve($obj, $key);
     }
 
-    return 1;
+    return !!1;
 }
 
 sub exit_scope
@@ -161,7 +160,7 @@ sub exit_scope
 
     pop @{$self->{STACK}};
 
-    return 1;
+    return !!1;
 }
 
 sub get
@@ -209,14 +208,31 @@ sub active_format
 sub new_worksheet
 {
     my $self = shift;
-    my ($name) = @_;
+    my ($worksheet) = @_;
 
     $self->{ROW} = $self->{COL} = 0;
+    $self->{REFERENCES} = {};
+
+    my $name = $self->get( $worksheet, 'NAME' );
+
+    if ( defined $name && length $name )
+    {
+        if ( exists $self->{WORKSHEET_NAMES}{$name} )
+        {
+            $name = '';
+        }
+        else
+        {
+            $self->{WORKSHEET_NAMES}{$name} = undef;
+        }
+    }
+    else
+    {
+        $name = '';
+    }
 
     $self->active_worksheet(
-        $self->{XLS}->add_worksheet(
-            $name || '',
-        ),
+        $self->{XLS}->add_worksheet( $name ),
     );
 }
 
@@ -230,6 +246,38 @@ sub active_worksheet
     $self->{ACTIVE_WORKSHEET};
 }
 
+sub add_reference
+{
+    my $self = shift;
+    my ($ref, $row, $col) = @_;
+
+    $self->{REFERENCES}{$ref} ||= [];
+
+    push @{$self->{REFERENCES}{$ref}}, [ $row, $col ];
+
+    return !!1;
+}
+
+sub get_all_references
+{
+    my $self = shift;
+    my $ref = uc shift;
+
+    $self->{REFERENCES}{$ref} ||= [];
+
+    return @{ $self->{REFERENCES}{$ref} };
+}
+
+sub get_last_reference
+{
+    my $self = shift;
+    my $ref = uc shift;
+
+    $self->{REFERENCES}{$ref} ||= [];
+
+    return @{ $self->{REFERENCES}{$ref}[-1] };
+}
+
 1;
 __END__
 
diff --git a/lib/Excel/Template/Element/Backref.pm b/lib/Excel/Template/Element/Backref.pm
new file mode 100755 (executable)
index 0000000..fe82f1b
--- /dev/null
@@ -0,0 +1,89 @@
+package Excel::Template::Element::Backref;
+
+use strict;
+use Spreadsheet::WriteExcel::Utility;
+
+BEGIN {
+    use vars qw(@ISA);
+    @ISA = qw(Excel::Template::Element);
+
+    use Excel::Template::Element;
+}
+
+sub resolve
+{ 
+    my $self = shift;
+    my ($context) = @_;
+
+    my $ref_name = $context->resolve($self, 'REF');
+
+    my ($row, $col) = $context->get_last_reference( $ref_name );
+    return '' unless defined $row && defined $col;
+
+    return xl_rowcol_to_cell( $row, $col );
+}
+
+1;
+__END__
+
+=head1 NAME
+
+Excel::Template::Element::Ref
+
+=head1 PURPOSE
+
+Returns the cell location (i.e. B2) of the last cell to name this reference.  To
+return the location of the entire range of cells to name this reference see RANGE.
+
+=head1 NODE NAME
+
+REF
+
+=head1 INHERITANCE
+
+Excel::Template::Element
+
+=head1 ATTRIBUTES
+
+=over 4
+
+=item * REF
+
+This is the name of the reference to look up.
+
+=back 4
+
+=head1 CHILDREN
+
+None
+
+=head1 EFFECTS
+
+None
+
+=head1 DEPENDENCIES
+
+This will only be used within CELL tags.
+
+=head1 USAGE
+
+In the example...
+
+  <row>
+    <cell ref="this_cell"/><cell ref="that_cell"><cell ref="that_cell">
+  </row>
+  <row>
+    <formula>=<ref ref="this_cell">+<ref ref="that_cell"></formula>
+  </row>
+
+The formula in row 2 would be =A1+C1.  C1 is the last to reference "that_cell".
+
+=head1 AUTHOR
+
+Rob Kinyon (rkinyon@columbus.rr.com)
+
+=head1 SEE ALSO
+
+CELL, RANGE
+
+=cut
index f291c4b..e09a4c2 100755 (executable)
@@ -49,8 +49,16 @@ sub render
     my $self = shift;
     my ($context) = @_;
 
+    my ($row, $col) = map { $context->get($self, $_) } qw(ROW COL);
+
+    my $ref = uc $context->get( $self, 'REF' );
+    if (defined $ref && length $ref)
+    {
+        $context->add_reference( $ref, $row, $col );
+    }
+
     $context->active_worksheet->write(
-        (map { $context->get($self, $_) } qw(ROW COL)),
+        $row, $col,
         $self->get_text($context),
         $context->active_format,
     );
diff --git a/lib/Excel/Template/Element/Range.pm b/lib/Excel/Template/Element/Range.pm
new file mode 100755 (executable)
index 0000000..c880654
--- /dev/null
@@ -0,0 +1,107 @@
+package Excel::Template::Element::Range;
+
+use strict;
+use Spreadsheet::WriteExcel::Utility;
+
+BEGIN {
+    use vars qw(@ISA);
+    @ISA = qw(Excel::Template::Element);
+
+    use Excel::Template::Element;
+}
+
+sub min { $_[0] < $_[1] ? $_[0] : $_[1] }
+sub max { $_[0] > $_[1] ? $_[0] : $_[1] }
+
+sub resolve
+{ 
+    my $self = shift;
+    my ($context) = @_;
+
+    my $ref_name = $context->resolve($self, 'REF');
+
+    my @refs = $context->get_all_references( $ref_name );
+    return '' unless @refs;
+
+    my ($top, $left, $bottom, $right) =
+        ( $refs[0][0], $refs[0][1] ) x 2;
+
+    shift @refs;
+    foreach my $ref ( @refs )
+    {
+        $top    = min( $top,    $ref->[0]);
+        $bottom = max( $bottom, $ref->[0]);
+        $left   = min( $left,   $ref->[1]);
+        $right  = max( $right,  $ref->[1]);
+    }
+
+    return join( ':',
+        xl_rowcol_to_cell($top, $left),
+        xl_rowcol_to_cell($bottom, $right)
+    );
+}
+
+1;
+__END__
+
+=head1 NAME
+
+Excel::Template::Element::Range
+
+=head1 PURPOSE
+
+Returns a range of cell locations (i.e. B2:C2) that contains all calls using this
+reference. To return the location of the last cell, use REF.
+
+=head1 NODE NAME
+
+REF
+
+=head1 INHERITANCE
+
+Excel::Template::Element
+
+=head1 ATTRIBUTES
+
+=over 4
+
+=item * REF
+
+This is the name of the reference to look up.
+
+=back 4
+
+=head1 CHILDREN
+
+None
+
+=head1 EFFECTS
+
+None
+
+=head1 DEPENDENCIES
+
+This will only be used within CELL tags.
+
+=head1 USAGE
+
+In the example...
+
+  <row>
+    <cell ref="this_cell"/><cell ref="that_cell"><cell ref="that_cell">
+  </row>
+  <row>
+    <formula>=SUM(<range ref="that_cell">)</formula>
+  </row>
+
+The formula in row 2 would be =SUM(B1:C1).
+
+=head1 AUTHOR
+
+Rob Kinyon (rkinyon@columbus.rr.com)
+
+=head1 SEE ALSO
+
+CELL, REF
+
+=cut
index 1fc9cd3..7a0a6a4 100644 (file)
@@ -16,13 +16,16 @@ BEGIN {
     'WORKBOOK'  => 'Excel::Template::Container::Workbook',
     'WORKSHEET' => 'Excel::Template::Container::Worksheet',
 
+    'BACKREF'   => 'Excel::Template::Element::Backref',
     'CELL'      => 'Excel::Template::Element::Cell',
     'FORMULA'   => 'Excel::Template::Element::Formula',
+    'RANGE'     => 'Excel::Template::Element::Range',
     'VAR'       => 'Excel::Template::Element::Var',
 
     'FORMAT'    => 'Excel::Template::Container::Format',
 
 # These are all the Format short-cut objects
+# They are also instantiable
     'BOLD'      => 'Excel::Template::Container::Bold',
     'HIDDEN'    => 'Excel::Template::Container::Hidden',
     'ITALIC'    => 'Excel::Template::Container::Italic',
@@ -32,7 +35,7 @@ BEGIN {
     'STRIKEOUT' => 'Excel::Template::Container::Strikeout',
 
 # These are the helper objects
-
+# They are also in here to make E::T::Factory::isa() work.
     'CONTEXT'    => 'Excel::Template::Context',
     'ITERATOR'   => 'Excel::Template::Iterator',
     'TEXTOBJECT' => 'Excel::Template::TextObject',
@@ -52,6 +55,8 @@ BEGIN {
     ITALIC
     OUTLINE
     LOOP
+    BACKREF
+    RANGE
     ROW
     SHADOW
     STRIKEOUT
@@ -135,6 +140,14 @@ sub isa
         : UNIVERSAL::isa(@_)
 }
 
+sub is_embedded
+{
+    return unless @_ >= 1;
+
+    isa( $_[0], $_ ) && return !!1 for qw( VAR BACKREF RANGE );
+    return;
+}
+
 1;
 __END__
 
@@ -144,19 +157,15 @@ Excel::Template::Factory
 
 =head1 PURPOSE
 
-=head1 NODE NAME
+To provide a common way to instantiate Excel::Template nodes
 
-=head1 INHERITANCE
-
-=head1 ATTRIBUTES
-
-=head1 CHILDREN
+=head1 USAGE
 
-=head1 AFFECTS
+=head2 register()
 
-=head1 DEPENDENCIES
+Use this to register your own nodes.
 
-=head1 USAGE
+Example forthcoming.
 
 =head1 AUTHOR
 
index db555d5..8fa6b47 100755 (executable)
@@ -38,7 +38,7 @@ UNI_NO    my $t = '';
     {
         my $val = $tok;
         $val = $val->resolve($context)
-            if Excel::Template::Factory::isa($val, 'VAR');
+            if Excel::Template::Factory::is_embedded( $val );
 
 UNI_YES        $t .= Unicode::String::utf8("$val");
 UNI_NO        $t .= $val;