und die restlichen .pm Module.
[kivitendo-erp.git] / SL / Template / Plugin / MultiColumnIterator.pm
1 package SL::Template::Plugin::MultiColumnIterator;
2
3 use base 'Template::Plugin';
4 use Template::Constants;
5 use Template::Exception;
6 use Template::Iterator;
7 use SL::LXDebug;
8 use Data::Dumper;
9
10 use strict;
11
12 our $AUTOLOAD;
13
14 sub new {
15     $main::lxdebug->enter_sub();
16     my $class   = shift;
17     my $context = shift;
18     my $data    = shift || [ ];
19     my $dim     = shift || 1;
20
21     $dim        = 1 if $dim < 1;
22
23     use vars qw(@ISA);
24     push @ISA, "Template::Iterator";
25
26     if (ref $data eq 'HASH') {
27         # map a hash into a list of { key => ???, value => ??? } hashes,
28         # one for each key, sorted by keys
29         $data = [ map { { key => $_, value => $data->{ $_ } } }
30                   sort keys %$data ];
31     }
32     elsif (UNIVERSAL::can($data, 'as_list')) {
33         $data = $data->as_list();
34     }
35     elsif (ref $data ne 'ARRAY') {
36         # coerce any non-list data into an array reference
37         $data  = [ $data ] ;
38     }
39
40     $main::lxdebug->leave_sub();
41
42     bless {
43         _DATA  => $data,
44         _ERROR => '',
45         _DIM   => $dim,
46     }, $class;
47 }
48
49
50 sub get_first {
51     $main::lxdebug->enter_sub();
52     my $self  = shift;
53     my $data  = $self->{ _DATA };
54     my $dim   = $self->{ _DIM  };
55
56     $self->{ _DATASET } = $self->{ _DATA };
57     my $size = int ((scalar @$data - 1) / $dim) + 1;
58     my $index = 0;
59
60     return (undef, Template::Constants::STATUS_DONE) unless $size;
61
62     # initialise various counters, flags, etc.
63     @$self{ qw( SIZE MAX INDEX COUNT FIRST LAST ) } = ( $size, $size - 1, $index, 1, 1, $size > 1 ? 0 : 1, undef );
64     @$self{ qw( PREV ) } = ( undef );
65     $$self{ qw( NEXT ) } = [ @{ $self->{ _DATASET }  }[ map { $index + 1 + $_ * $size } 0 .. ($dim - 1) ] ];
66
67     $main::lxdebug->leave_sub();
68     return [ @{ $self->{ _DATASET } }[ map { $index + $_ * $size } 0 .. ($dim - 1) ] ];
69 }
70
71 sub get_next {
72     $main::lxdebug->enter_sub();
73     my $self = shift;
74     my ($max, $index) = @$self{ qw( MAX INDEX ) };
75     my $data = $self->{ _DATASET };
76     my $dim  = $self->{ _DIM  };
77     my $size = $self->{ SIZE  };
78
79     # warn about incorrect usage
80     unless (defined $index) {
81         my ($pack, $file, $line) = caller();
82         warn("iterator get_next() called before get_first() at $file line $line\n");
83         return (undef, Template::Constants::STATUS_DONE);   ## RETURN ##
84     }
85
86     # if there's still some data to go...
87     if ($index < $max) {
88         # update counters and flags
89         $index++;
90         @$self{ qw( INDEX COUNT FIRST LAST ) } = ( $index, $index + 1, 0, $index == $max ? 1 : 0 );
91         $$self{ qw( PREV ) } = [ @{ $self->{ _DATASET } }[ map { $index - 1 + $_ * $size } 0 .. ($dim - 1) ] ];
92         $$self{ qw( NEXT ) } = [ @{ $self->{ _DATASET } }[ map { $index + 1 + $_ * $size } 0 .. ($dim - 1) ] ];
93         $main::lxdebug->leave_sub();
94         return [ @{ $self->{ _DATASET } }[ map { $index + $_ * $size } 0 .. ($dim - 1) ] ];
95     }
96     else {
97         $main::lxdebug->leave_sub();
98         return (undef, Template::Constants::STATUS_DONE);   ## RETURN ##
99     }
100 }
101
102 sub get_all {
103     my $self = shift;
104     my ($max, $index, $dim, $size) = @$self{ qw( MAX INDEX _DIM SIZE) };
105     my (@data, $i);
106
107     # if there's still some data to go...
108     if ($index < $max) {
109         $index++;
110         @data = map do{ !($i = $_) || +[ @{ $self->{ _DATASET } }[ map { $i + $_ * $size } 0 .. ($dim - 1) ] ] }, $index .. $max;
111         # update counters and flags
112         @$self{ qw( INDEX COUNT FIRST LAST ) } = ( $max, $max + 1, 0, 1 );
113         $main::lxdebug->leave_sub();
114         return \@data;                                      ## RETURN ##
115     }
116     else {
117         $main::lxdebug->leave_sub();
118         return (undef, Template::Constants::STATUS_DONE);   ## RETURN ##
119     }
120 }
121
122 sub AUTOLOAD {
123     my $self = shift;
124     my $item = $AUTOLOAD;
125     $item =~ s/.*:://;
126     return if $item eq 'DESTROY';
127
128     # alias NUMBER to COUNT for backwards compatability
129     $item = 'COUNT' if $item =~ /NUMBER/i;
130
131     return $self->{ uc $item };
132 }
133
134 sub dump {
135     $main::lxdebug->enter_sub();
136     my $self = shift;
137     $main::lxdebug->leave_sub();
138     return join('',
139          "<pre>",
140          "  Data: ", Dumper($self->{ _DATA  }), "\n",
141          " Index: ", $self->{ INDEX  }, "\n",
142          "Number: ", $self->{ NUMBER }, "\n",
143          "   Max: ", $self->{ MAX    }, "\n",
144          "  Size: ", $self->{ SIZE   }, "\n",
145          " First: ", $self->{ FIRST  }, "\n",
146          "  Last: ", $self->{ LAST   }, "\n",
147          "\n",
148          "</pre>"
149      );
150 }
151
152 sub index {
153   $main::lxdebug->enter_sub();
154   my ($self) = @_;
155   $main::lxdebug->leave_sub();
156   return $self->{ INDEX };
157 }
158
159 sub number {
160   $main::lxdebug->enter_sub();
161   my ($self) = @_;
162   $main::lxdebug->leave_sub();
163   return $self->{ NUMBER };
164 }
165
166 sub count {
167   $main::lxdebug->enter_sub();
168   my ($self) = @_;
169   $main::lxdebug->leave_sub();
170   return $self->{ COUNT };
171 }
172 sub max {
173   $main::lxdebug->enter_sub();
174   my ($self) = @_;
175   $main::lxdebug->leave_sub();
176   return $self->{ MAX };
177 }
178
179 sub size {
180   $main::lxdebug->enter_sub();
181   my ($self) = @_;
182   $main::lxdebug->leave_sub();
183   return $self->{ SIZE };
184 }
185
186 sub first {
187   $main::lxdebug->enter_sub();
188   my ($self) = @_;
189   $main::lxdebug->leave_sub();
190   return $self->{ FIRST };
191 }
192
193 sub last {
194   $main::lxdebug->enter_sub();
195   my ($self) = @_;
196   $main::lxdebug->leave_sub();
197   return $self->{ LAST};
198 }
199
200 1;