File Coverage

blib/lib/Spreadsheet/XLSX/Reader/LibXML/CellToColumnRow.pm
Criterion Covered Total %
statement 67 79 84.8
branch 30 38 78.9
condition 6 6 100.0
subroutine 10 10 100.0
pod 2 2 100.0
total 115 135 85.1


line stmt bran cond sub pod time code
1             package Spreadsheet::XLSX::Reader::LibXML::CellToColumnRow;
2             our $AUTHORITY = 'cpan:JANDREW';
3 1     1   719715 use version; our $VERSION = version->declare('v0.38.22');
  1         1  
  1         8  
4             ###LogSD warn "You uncovered internal logging statements for Spreadsheet::XLSX::Reader::LibXML::CellToColumnRow-$VERSION";
5              
6 1     1   814 use Moose::Role;
  1         4820  
  1         4  
7             requires
8             'set_error',
9             ###LogSD 'get_all_space'
10             ;
11 1     1   5513 use Types::Standard qw( Bool );
  1         2  
  1         10  
12             ###LogSD use Log::Shiras::Telephone;
13              
14             #########1 Dispatch Tables 3#########4#########5#########6#########7#########8#########9
15              
16             my $lookup_ref ={
17             A => 1, B => 2, C => 3, D => 4, E => 5, F => 6, G => 7, H => 8, I => 9, J => 10,
18             K => 11, L => 12, M => 13, N => 14, O => 15, P => 16, Q => 17, R => 18, S => 19,
19             T => 20, U => 21, V => 22, W => 23, X => 24, Y => 25, Z => 26,
20             };
21             my $lookup_list =[ qw( A B C D E F G H I J K L M N O P Q R S T U V W X Y Z ) ];
22              
23             #########1 Public Attributes 3#########4#########5#########6#########7#########8#########9
24              
25              
26              
27             #########1 Public Methods 3#########4#########5#########6#########7#########8#########9
28              
29             sub parse_column_row{#? add the manual conversion to used vs excel on the next two
30 38     38 1 4390 my ( $self, $cell ) = @_;
31             ###LogSD my $phone = Log::Shiras::Telephone->new( name_space =>
32             ###LogSD $self->get_all_space . '::parse_column_row', );
33             ###LogSD $phone->talk( level => 'debug', message =>[
34             ###LogSD "Parsing file row number and file column number from: $cell" ] );
35 38         95 my ( $column, $row ) = $self->_parse_column_row( $cell );
36             ###LogSD $phone->talk( level => 'debug', message =>[
37             ###LogSD 'File Column: ' . ($column//''), 'File Row: ' . ($row//'') ] );
38             ###LogSD use warnings 'uninitialized';
39 38         275 return( $column, $row );
40             }
41              
42             sub build_cell_label{
43 32     32 1 16252 my ( $self, $column, $row ) = @_;
44             ###LogSD my $phone = Log::Shiras::Telephone->new( name_space =>
45             ###LogSD ($self->get_log_space . '::build_cell_label' ), );
46             ###LogSD $phone->talk( level => 'debug', message =>[
47             ###LogSD "Converting file column -$column- and file row -$row- to a cell ID" ] );
48 32         73 my $cell_label = $self->_build_cell_label( $column, $row );
49             ###LogSD $phone->talk( level => 'debug', message =>[
50             ###LogSD "Cell label is: $cell_label" ] );
51 32         211 return $cell_label;
52             }
53              
54             #########1 Private Attributes 3#########4#########5#########6#########7#########8#########9
55              
56              
57              
58             #########1 Private Methods 3#########4#########5#########6#########7#########8#########9
59              
60             sub _parse_column_row{
61 38     38   63 my ( $self, $cell ) = @_;
62 38         47 my ( $column, $error_list_ref );
63             ###LogSD my $phone = Log::Shiras::Telephone->new( name_space =>
64             ###LogSD $self->get_all_space . '::_parse_column_row', );
65             ###LogSD $phone->talk( level => 'debug', message =>[
66             ###LogSD "Parsing excel row and column number from: $cell" ] );
67 38         123 my $regex = qr/^([A-Z])?([A-Z])?([A-Z])?([0-9]*)$/;
68 38         256 my ( $one_column, $two_column, $three_column, $row ) = $cell =~ $regex;
69 1     1   945 no warnings 'uninitialized';
  1         1  
  1         266  
70 38         87 my $column_text = $one_column . $two_column . $three_column;
71             ###LogSD $phone->talk( level => 'debug', message =>[
72             ###LogSD "Regex result is: ( $one_column, $two_column, $three_column, $row )" ] );
73            
74 38 100       119 if( !defined $one_column ){
    100          
    100          
75 2         7 push @$error_list_ref, "Could not parse the column component from -$cell-";
76             }elsif( !defined $two_column ){
77 29         59 $column = $lookup_ref->{$one_column};
78             }elsif( !defined $three_column ){
79 5         13 $column = $lookup_ref->{$two_column} + 26 * $lookup_ref->{$one_column};
80             }else{
81 2         8 $column = $lookup_ref->{$three_column} + 26 * $lookup_ref->{$two_column} + 26 * 26 * $lookup_ref->{$one_column};
82             }
83             ###LogSD $phone->talk( level => 'debug', message =>[
84             ###LogSD "Result of initial parse is column text: $column_text",
85             ###LogSD "Column number: $column", "Row number: $row" ] );
86 38 100 100     168 if( $column_text and $column > 16384 ){
87 1         7 push @$error_list_ref, "The column text -$column_text- points to a position at " .
88             "-$column- past the excel limit of: 16,384";
89 1         2 $column = undef;
90             }
91 38 100 100     230 if( !defined $row or $row eq '' ){
    100          
    100          
92 2         10 push @$error_list_ref, "Could not parse the row component from -$cell-";
93 2         4 $row = undef;
94             }elsif( $row < 1 ){
95 1         3 push @$error_list_ref, "The requested row cannot be less than one - you requested: $row";
96 1         2 $row = undef;
97             }elsif( $row > 1048576 ){
98 1         5 push @$error_list_ref, "The requested row cannot be greater than 1,048,576 " .
99             "- you requested: $row";
100 1         2 $row = undef;
101             }
102 38 100       78 if( $error_list_ref ){
103 6 100       16 if( scalar( @$error_list_ref ) > 1 ){
104 1         8 $self->set_error( "The regex $regex could not match -$cell-" );
105             }else{
106 5         20 $self->set_error( $error_list_ref->[0] );
107             }
108             }
109             ###LogSD no warnings 'uninitialized';
110             ###LogSD $phone->talk( level => 'debug', message =>[
111             ###LogSD "Column: $column", "Row: $row" ] );
112 1     1   5 use warnings 'uninitialized';
  1         1  
  1         367  
113 38         133 return( $column, $row );
114             }
115              
116             sub _build_cell_label{
117 32     32   49 my ( $self, $column, $row ) = @_;
118             ###LogSD my $phone = Log::Shiras::Telephone->new( name_space =>
119             ###LogSD $self->get_all_space . '::build_cell_label', );
120             ###LogSD no warnings 'uninitialized';
121             ###LogSD $phone->talk( level => 'debug', message =>[
122             ###LogSD "Converting column -$column- and row -$row- to a cell ID" ] );
123             ###LogSD use warnings 'uninitialized';
124 32         35 my $error_list;
125 32 50       72 if( !defined $column ){
126             ###LogSD $phone->talk( level => 'debug', message =>[
127             ###LogSD "The column is not defined" ] );
128 0         0 $column = '';
129 0         0 push @$error_list, 'missing column';
130             }else{
131             ###LogSD $phone->talk( level => 'debug', message =>[
132             ###LogSD "Excel column: $column" ] );
133 32         38 $column -= 1;
134             ###LogSD $phone->talk( level => 'debug', message =>[
135             ###LogSD "From zero: $column" ] );
136 32 50       87 if( $column > 16383 ){
    50          
137 0         0 push @$error_list, 'column too large';
138 0         0 $column = '';
139             }elsif( $column < 0 ){
140 0         0 push @$error_list, 'column too small';
141 0         0 $column = '';
142             }else{
143 32         66 my $first_letter = int( $column / (26 * 26) );
144 32         43 $column = $column - $first_letter * (26 * 26);
145 32 100       62 $first_letter = ( $first_letter ) ? $lookup_list->[$first_letter - 1] : '';
146             ###LogSD $phone->talk( level => 'debug', message =>[
147             ###LogSD "First letter is: $first_letter", "New column is: $column" ] );
148 32         41 my $second_letter = int( $column / 26 );
149 32         41 $column = $column - $second_letter * 26;
150 32 50       79 $second_letter =
    100          
151             ( $second_letter ) ? $lookup_list->[$second_letter - 1] :
152             ( $first_letter ne '' ) ? 'A' : '' ;
153             ###LogSD $phone->talk( level => 'debug', message =>[
154             ###LogSD "Second letter is: $second_letter", "New column is: $column" ] );
155 32         52 my $third_letter = $lookup_list->[$column];
156 32         72 $column = $first_letter . $second_letter . $third_letter;
157             }
158             }
159             ###LogSD $phone->talk( level => 'debug', message =>[
160             ###LogSD "Column letters are: $column" ] );
161            
162 32 50       59 if( !defined $row ){
163 0         0 $row = '';
164 0         0 push @$error_list, 'missing row';
165             }else{
166 32 50       91 if( $row > 1048576 ){
    50          
167 0         0 push @$error_list, 'row too large';
168 0         0 $row = '';
169             }elsif( $row < 1 ){
170 0         0 push @$error_list, 'row too small';
171 0         0 $row = '';
172             }
173             }
174 32 50       57 $self->set_error(
175             "Failures in build_cell_label include: " . join( ' - ', @$error_list )
176             ) if $error_list;
177             ###LogSD $phone->talk( level => 'debug', message =>[
178             ###LogSD "Row is: $row" ] );
179            
180 32         46 my $cell_label = "$column$row";
181             ###LogSD $phone->talk( level => 'debug', message =>[
182             ###LogSD "Cell label is: $cell_label" ] );
183 32         76 return $cell_label;
184             }
185              
186             #########1 Phinish 3#########4#########5#########6#########7#########8#########9
187              
188 1     1   5 no Moose::Role;
  1         2  
  1         13  
189             1;
190              
191             #########1 Documentation 3#########4#########5#########6#########7#########8#########9
192             __END__
193              
194             =head1 NAME
195              
196             Spreadsheet::XLSX::Reader::LibXML::CellToColumnRow - Translate Excel cell IDs to column row
197              
198             =head1 SYNOPSIS
199            
200             #!/usr/bin/env perl
201             package MyPackage;
202             use Moose;
203             with 'Spreadsheet::XLSX::Reader::LibXML::CellToColumnRow';
204              
205             sub set_error{} #Required method of this role
206             sub error{ print "Missing the column or row\n" }
207            
208             sub my_method{
209             my ( $self, $cell ) = @_;
210             my ($column, $row ) = $self->parse_column_row( $cell );
211             print $self->error if( !defined $column or !defined $row );
212             return ($column, $row );
213             }
214              
215             package main;
216              
217             my $parser = MyPackage->new;
218             print '(' . join( ', ', $parser->my_method( 'B2' ) ) . ")'\n";
219            
220             ###########################
221             # SYNOPSIS Screen Output
222             # 01: (2, 2)
223             ###########################
224            
225             =head1 DESCRIPTION
226              
227             This documentation is written to explain ways to use this module when writing your
228             own excel parser. To use the general package for excel parsing out of the box please
229             review the documentation for L<Workbooks|Spreadsheet::XLSX::Reader::LibXML>,
230             L<Worksheets|Spreadsheet::XLSX::Reader::LibXML::Worksheet>, and
231             L<Cells|Spreadsheet::XLSX::Reader::LibXML::Cell>
232              
233             This is a L<Moose Role|Moose::Manual::Roles>. The role provides methods to convert back
234             and forth betwee Excel Cell ID and ($column $row) lists. This role also provides a layer
235             of abstraction so that it is possible to implement
236             L<around|Moose::Manual::MethodModifiers/Around modifiers> mofifiers on these methods so
237             that the data provided by the user can be in the user context and the method implementation
238             will still be in the Excel context. For example this package uses this abstraction to allow
239             the user to call or receive row column numbers in either the
240             L<count-from-zero|Spreadsheet::XLSX::Reader::LibXML/count_from_zero> context used by
241             L<Spreadsheet::ParseExcel> or the count-from-one context used by Excel. It is important
242             to note that column letters do not equal digits in a modern 26 position numeral system
243             since the excel implementation is effectivly zeroless.
244              
245             The module counts from 1 (the excel convention) without implementation of around modifiers.
246             Meaning that cell ID 'A1' is equal to (1, 1) and column row (3, 2) is equal to the cell ID
247             'C2'.
248              
249             =head2 requires
250              
251             These are methods used by this role but not provided by the role. Any class consuming this
252             role will not build unless it first provides these methods prior to loading this role.
253              
254             =head3 set_error( $error_string )
255              
256             =over
257              
258             B<Definition:> Used to set errors that occur in code from this role. See
259             L<Spreadsheet::XLSX::Reader::LibXML::Error> for the default implementation of this functionality.
260              
261             =back
262            
263             =head2 Methods
264              
265             Methods are object methods (not functional methods)
266              
267             =head3 parse_column_row( $excel_cell_id )
268              
269             =over
270              
271             B<Definition:> This is the way to turn an alpha numeric Excel cell ID into column and row
272             integers. This method uses a count from 1 methodology. Since this method is actually just
273             a layer of abstraction above the real method for the calculation you can wrap it in an
274             L<around|Moose::Manual::MethodModifiers/Around modifiers> block to modify the output to
275             the desired user format without affecting other parts of the package that need the
276             unfiltered conversion. If you want both then use the following call when unfiltered results
277             are required;
278              
279             $self->_parse_column_row( $excel_cell_id )
280              
281             B<Accepts:> $excel_cell_id
282              
283             B<Returns:> ( $column_number, $row_number )
284              
285             =back
286              
287             =head3 build_cell_label( $column, $row, )
288              
289             =over
290              
291             B<Definition:> This is the way to turn a (column, row) pair into an Excel Cell ID. The
292             underlying method uses a count from 1 methodology. Since this method is actually just
293             a layer of abstraction above the real method for the calculation you can wrap it in an
294             L<around|Moose::Manual::MethodModifiers/Around modifiers> block to modify the input from
295             the implemented user format to the count from one methodology without affecting other parts
296             of the package that need the unfiltered conversion. If you want both then use the following
297             call when unfiltered results are required;
298              
299             $self->_build_cell_label( $column, $row )
300            
301             The column and the row must be provided in that order for both public and private methods.
302              
303             B<Accepts:> ($column, $row) I<in that order>
304              
305             B<Returns:> ( $excel_cell_id ) I<qr/[A-Z]{1,3}\d+/>
306              
307             =back
308              
309             =head1 SUPPORT
310              
311             =over
312              
313             L<github Spreadsheet::XLSX::Reader::LibXML/issues
314             |https://github.com/jandrew/Spreadsheet-XLSX-Reader-LibXML/issues>
315              
316             =back
317              
318             =head1 TODO
319              
320             =over
321              
322             B<1.> Nothing L<yet|/SUPPORT>
323              
324             =back
325              
326             =head1 AUTHOR
327              
328             =over
329              
330             Jed Lund
331              
332             jandrew@cpan.org
333              
334             =back
335              
336             =head1 COPYRIGHT
337              
338             This program is free software; you can redistribute
339             it and/or modify it under the same terms as Perl itself.
340              
341             The full text of the license can be found in the
342             LICENSE file included with this module.
343              
344             This software is copyrighted (c) 2014, 2015 by Jed Lund
345              
346             =head1 DEPENDENCIES
347              
348             =over
349              
350             L<version> - 0.77
351              
352             L<Moose::Role>
353              
354             L<Types::Standard>
355              
356             =back
357              
358             =head1 SEE ALSO
359              
360             =over
361              
362             L<Spreadsheet::ParseExcel> - Excel 2003 and earlier
363              
364             L<Spreadsheet::XLSX> - 2007+
365              
366             L<Spreadsheet::ParseXLSX> - 2007+
367              
368             L<Log::Shiras|https://github.com/jandrew/Log-Shiras>
369              
370             =over
371              
372             All lines in this package that use Log::Shiras are commented out
373              
374             =back
375              
376             =back
377              
378             =cut
379              
380             #########1#########2 main pod documentation end 5#########6#########7#########8#########9