File Coverage

blib/lib/Class/CodeStyler.pm
Criterion Covered Total %
statement 13 15 86.6
branch n/a
condition n/a
subroutine 5 5 100.0
pod n/a
total 18 20 90.0


line stmt bran cond sub pod time code
1             # vim:ts=4 sw=4
2             # ----------------------------------------------------------------------------------------------------
3             # Name : Class::CodeStyler.pm
4             # Created : 24 April 2006
5             # Author : Mario Gaffiero (gaffie)
6             #
7             # Copyright 2006-2007 Mario Gaffiero.
8             #
9             # This file is part of Class::CodeStyler(TM).
10             #
11             # Class::CodeStyler is free software; you can redistribute it and/or modify
12             # it under the terms of the GNU General Public License as published by
13             # the Free Software Foundation; version 2 of the License.
14             #
15             # Class::CodeStyler is distributed in the hope that it will be useful,
16             # but WITHOUT ANY WARRANTY; without even the implied warranty of
17             # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18             # GNU General Public License for more details.
19             #
20             # You should have received a copy of the GNU General Public License
21             # along with Class::CodeStyler; if not, write to the Free Software
22             # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
23             # ----------------------------------------------------------------------------------------------------
24             # Modification History
25             # When Version Who What
26             # ----------------------------------------------------------------------------------------------------
27             package Class::CodeStyler;
28             require 5.005_62;
29 1     1   6101 use strict;
  1         3  
  1         40  
30 1     1   6 use warnings;
  1         2  
  1         39  
31 1     1   5 use vars qw($VERSION $BUILD);
  1         7  
  1         90  
32             $VERSION = 0.27;
33             $BUILD = 'Tue May 01 18:32:42 GMTDT 2007';
34 1     1   6 use Carp qw(confess);
  1         2  
  1         54  
35 1     1   88934 use stl;
  0            
  0            
36             # ----------------------------------------------------------------------------------------------------
37             {
38             package Class::CodeStyler::Element::Abstract;
39             use base qw(Class::STL::Element);
40             use Class::STL::ClassMembers qw(owner);
41             use Class::STL::ClassMembers::Constructor;
42             sub prepare
43             {
44             confess __PACKAGE__ . "::prepare() -- pure virtual function must be overridden.";
45             }
46             }
47             # ----------------------------------------------------------------------------------------------------
48             {
49             package Class::CodeStyler::CodeText;
50             use base qw(Class::STL::Containers::Stack);
51             use Class::STL::ClassMembers
52             Class::STL::ClassMembers::DataMember->new(name => 'newline_is_on', default => 1),
53             Class::STL::ClassMembers::DataMember->new(name => 'indent_is_on', default => 1),
54             Class::STL::ClassMembers::DataMember->new(name => '_indent_next', default => 1),
55             Class::STL::ClassMembers::DataMember->new(name => 'current_tab', default => 0),
56             Class::STL::ClassMembers::DataMember->new(name => 'tab_type', default => 'spaces', validate => '(hard|spaces)'),
57             Class::STL::ClassMembers::DataMember->new(name => 'tab_size', default => 2),
58             Class::STL::ClassMembers::DataMember->new(name => 'debug', default => 0),
59             Class::STL::ClassMembers::DataMember->new(name => 'raw_is_on', default => 0);
60             use Class::STL::ClassMembers::Constructor;
61             sub append_newline
62             {
63             my $self = shift;
64             return if ($self->raw_is_on());
65             $self->push($self->factory(data => "\n"));
66             $self->_indent_next(1);
67             print STDERR "NEWLINE:\n" if ($self->debug());
68             }
69             sub append_text
70             {
71             my $self = shift;
72             my $code = shift || '';
73             $self->push($self->factory(data => $self->current_indent() . $code));
74             print STDERR "CODE :@{[ $self->current_indent() ]}${code}\n" if ($self->debug());
75             $self->_indent_next(0);
76             }
77             sub current_indent
78             {
79             my $self = shift;
80             return '' if ($self->raw_is_on());
81             return '' if (!$self->indent_is_on());
82             return '' unless ($self->_indent_next());
83             my $tabchar = $self->tab_type() eq 'hard' ? "\t" : ' ';
84             return ($tabchar x ($self->current_tab() * $self->tab_size()));
85             }
86             }
87             # ----------------------------------------------------------------------------------------------------
88             {
89             package Class::CodeStyler::FindName;
90             use base qw(Class::STL::Utilities::FunctionObject::UnaryFunction);
91             use Class::STL::ClassMembers qw( name );
92             use Class::STL::ClassMembers::Constructor;
93             sub function_operator
94             {
95             my $self = shift;
96             my $arg = shift; # element object
97             return $arg->program_name() eq $self->name() ? $arg : 0;
98             }
99             }
100             # ----------------------------------------------------------------------------------------------------
101             {
102             package Class::CodeStyler::Program::Abstract;
103             use base qw(Class::CodeStyler::Element::Abstract);
104             use stl qw( find_if stack list iterator find for_each mem_fun );
105             use UNIVERSAL qw(isa can);
106             use Class::STL::ClassMembers
107             (
108             qw(
109             program_name segments code_text
110             _bracket_stack _parent _insert_point _jump_stack _over_stack _anchor_stack
111             ),
112             Class::STL::ClassMembers::DataMember->new(name => 'suppress_comments', default => 0),
113             Class::STL::ClassMembers::DataMember->new(name => 'tab_size', default => 2),
114             Class::STL::ClassMembers::DataMember->new(name => 'tab_type', default => 'spaces', validate => '(hard|spaces)'),
115             Class::STL::ClassMembers::DataMember->new(name => 'debug', default => 0),
116             Class::STL::ClassMembers::DataMember->new(name => 'divider_length', default => 70),
117             Class::STL::ClassMembers::DataMember->new(name => 'divider_char', default => '-'),
118             Class::STL::ClassMembers::DataMember->new(name => 'comment_start_char', default => '#'),
119             Class::STL::ClassMembers::DataMember->new(name => 'comment_block_begin_char', default => ''),
120             Class::STL::ClassMembers::DataMember->new(name => 'comment_block_end_char', default => ''),
121             Class::STL::ClassMembers::DataMember->new(name => 'comment_block_char', default => ''),
122             Class::STL::ClassMembers::DataMember->new(name => 'disable_newline', default => 0),
123             Class::STL::ClassMembers::DataMember->new(name => 'print_bookmarks', default => 0),
124             Class::STL::ClassMembers::DataMember->new(name => 'open_block_on_newline', default => 1),
125             );
126             use Class::STL::ClassMembers::Constructor;
127             sub exists
128             {
129             my $self = shift;
130             my $name = shift;
131             my @l = grep($_->program_name() eq $name, $self->to_array());
132             return $l[0] if (@l);
133             return 0;
134             }
135             #? sub exists #TODO: memleak? slow?
136             #? {
137             #? my $self = shift;
138             #? my $name = shift;
139             #? my $s;
140             #? return $s->p_element()
141             #? if ($s = find_if($self->segments()->begin(), $self->segments()->end(),
142             #? Class::CodeStyler::FindName->new(name => $name)));
143             #? return 0;
144             #? }
145             sub new_extra
146             {
147             my $self = shift;
148             $self->code_text(Class::CodeStyler::CodeText->new(
149             debug => $self->debug(),
150             tab_size => $self->tab_size(),
151             tab_type => $self->tab_type(),
152             ))
153             unless (defined($self->code_text()));
154             $self->segments(list(element_type => 'Class::CodeStyler::Element::Abstract'));
155             $self->_bracket_stack(stack());
156             $self->_jump_stack(stack());
157             $self->_anchor_stack(stack());
158             $self->_over_stack(stack());
159             $self->_insert_point(iterator($self->segments()->begin()));
160             return $self;
161             }
162             sub add
163             {
164             my $self = shift;
165             foreach my $code (@_)
166             {
167             confess "@{[ __PACKAGE__ ]}->add(): Undefined object!" unless (defined($code));
168             if (ref($code) && $code->isa(__PACKAGE__))
169             {
170             $code->code_text($self->code_text());
171             $code->_parent($self);
172             map
173             (
174             $self->add($_),
175             grep
176             (
177             !$_->isa('Class::CodeStyler::Bookmark') || !find($self->segments()->begin(), $self->segments()->end(), $_->data()),
178             $code->segments()->to_array()
179             )
180             );
181             next;
182             }
183             elsif (ref($code) && $code->isa('Class::CodeStyler::Element::Abstract'))
184             {
185             $code->owner($self);
186             $self->segments()->insert($self->_insert_point(), 1, $code);
187             }
188             elsif (!ref($code))
189             {
190             $self->add(Class::CodeStyler::Code->new(code => $code));
191             next;
192             }
193             else
194             {
195             next;
196             }
197             }
198             }
199             sub code
200             {
201             my $self = shift;
202             my $code = @_ ? shift : '';
203             $self->add(Class::CodeStyler::Code->new(code => $code));
204             }
205             sub open_block
206             {
207             my $self = shift;
208             my $bracket = shift || '{';
209             my %_bracket_pairs = ( '(' => ')', '{' => '}', '[' => ']', '<' => '>' );
210             $self->add(Class::CodeStyler::OpenBlock->new(bracket_char => $bracket));
211             $self->_bracket_stack()->push($self->_bracket_stack()->factory($_bracket_pairs{$bracket}));
212             return;
213             }
214             sub close_block
215             {
216             my $self = shift;
217             return unless ($self->_bracket_stack()->size()); #ignore unmatched 'open_block'
218             my $bracket = $self->_bracket_stack()->top()->data();
219             $self->_bracket_stack()->pop();
220             $self->add(Class::CodeStyler::CloseBlock->new(bracket_char => $bracket));
221             return;
222             }
223             sub newline_on
224             {
225             my $self = shift;
226             $self->add(Class::CodeStyler::ToggleNewline->new(on => 1));
227             }
228             sub newline_off
229             {
230             my $self = shift;
231             $self->add(Class::CodeStyler::ToggleNewline->new(on => 0));
232             }
233             sub indent_on
234             {
235             my $self = shift;
236             $self->add(Class::CodeStyler::ToggleIndent->new(on => 1));
237             }
238             sub indent_off
239             {
240             my $self = shift;
241             $self->add(Class::CodeStyler::ToggleIndent->new(on => 0));
242             }
243             sub over
244             {
245             my $self = shift;
246             my $indent = shift || 1;
247             $self->add(Class::CodeStyler::Indent->new(indent => $indent));
248             $self->_over_stack()->push($self->_over_stack()->factory($indent));
249             return;
250             }
251             sub back
252             {
253             my $self = shift;
254             return unless ($self->_over_stack()->size()); #ignore unmatched 'back'
255             $self->add(Class::CodeStyler::Indent->new(indent => -($self->_over_stack()->top()->data())));
256             $self->_over_stack()->pop();
257             return;
258             }
259             sub comment
260             {
261             my $self = shift;
262             my $txt = shift;
263             $self->add(Class::CodeStyler::Comment->new(data => $txt));
264             }
265             sub divider
266             {
267             my $self = shift;
268             $self->add(Class::CodeStyler::Divider->new());
269             }
270             sub anchor_set
271             {
272             my $self = shift;
273             $self->add(Class::CodeStyler::Anchor->new(data => "@{[
274             $self->_insert_point()->at_end()
275             ? $self->_insert_point()->p_container()->size()
276             : $self->_insert_point()->arr_idx()
277             ]}"));
278             $self->_anchor_stack()->push($self->_insert_point()->prev()->clone());
279             }
280             sub anchor_return
281             {
282             my $self = shift;
283             return unless($self->_anchor_stack()->size());
284             $self->_insert_point($self->_anchor_stack()->top()->clone());
285             $self->_anchor_stack()->pop();
286             }
287             sub bookmark
288             {
289             my $self = shift;
290             my $id = shift;
291             $self->add(Class::CodeStyler::Bookmark->new(data => $id));
292             }
293             sub jump
294             {
295             my $self = shift;
296             my $id = shift;
297             my $found;
298             #TODO: potential bug if comment.data same as bookmark.data (id)!
299             if ($found = find($self->segments()->begin(), $self->segments()->end(), $id))
300             {
301             $self->_jump_stack()->push($self->_insert_point()->clone());
302             $self->_insert_point($found);
303             return $found;
304             }
305             return 0;
306             }
307             sub return
308             {
309             my $self = shift;
310             return unless($self->_jump_stack()->size());
311             $self->_insert_point($self->_jump_stack()->top()->clone());
312             $self->_jump_stack()->pop();
313             }
314             sub clear
315             {
316             my $self = shift;
317             $self->code_text()->clear();
318             return $self;
319             }
320             sub prepare
321             {
322             my $self = shift;
323             # This works because all 'segments' elements are (ultimately) derived
324             # from Class::CodeStyler::Element::Abstract. Recursion via this prepare() will
325             # occure if the element is a Class::CodeStyler::Program.
326             #? for_each($self->segments()->begin(), $self->segments()->end(), mem_fun('prepare')); #TODO: memleak? slow?
327             map($_->prepare(), $self->segments()->to_array());
328             return $self;
329             }
330             sub print
331             {
332             my $self = shift;
333             return $self->code_text()->join('');
334             # Class::STL::Containers function -- joins print() return for all elements;
335             }
336             sub raw
337             {
338             my $self = shift;
339             $self->code_text()->raw_is_on(1);
340             $self->code_text()->clear();
341             $self->prepare();
342             my $txt = $self->print();
343             $self->code_text()->raw_is_on('0');
344             return $txt;
345             }
346             sub save
347             {
348             my $self = shift;
349             my $filename = shift || $self->program_name();
350             confess "save() -- Unable to save -- 'program_name' is not defined."
351             unless defined($filename);
352             open(SAVE, ">@{[ $filename ]}");
353             print SAVE $self->print();
354             }
355             sub display
356             {
357             my $self = shift;
358             my $line_number = 1;
359             my @p;
360             foreach (split(/\n/, $self->print()))
361             {
362             push(@p, sprintf(" %5d %s", $line_number++, $_));
363             }
364             return join("\n", @p);
365             }
366             sub syntax_check
367             {
368             my $self = shift;
369             $self->save("@{[ $self->program_name() ]}.DEBUG");
370             my $check = `perl -cw @{[ $self->program_name() ]}.DEBUG 2>&1`;
371             chomp($check);
372             if ($check !~ /syntax OK/i)
373             {
374             $self->code("__END__");
375             $self->code("Syntax check summary follows:");
376             $self->code("$check");
377             $self->clear();
378             $self->prepare();
379             $self->save("@{[ $self->program_name() ]}.DEBUG");
380             }
381             else
382             {
383             unlink "@{[ $self->program_name() ]}.DEBUG";
384             }
385             return $check;
386             }
387             sub exec
388             {
389             my $self = shift;
390             $self->save("@{[ $self->program_name() ]}.EXEC");
391             exec("perl @{[ $self->program_name() ]}.EXEC");
392             }
393             sub run
394             {
395             my $self = shift;
396             $self->save("@{[ $self->program_name() ]}.EXEC");
397             system("perl @{[ $self->program_name() ]}.EXEC");
398             }
399             sub eval
400             {
401             my $self = shift;
402             my $code = $self->print();
403             eval($code);
404             confess "**Error in eval:\n$@" if ($@);
405             }
406             }
407             # ----------------------------------------------------------------------------------------------------
408             {
409             package Class::CodeStyler::Program::Perl;
410             use base qw(Class::CodeStyler::Program::Abstract);
411             use Class::STL::ClassMembers;
412             use Class::STL::ClassMembers::Constructor;
413             sub new_extra
414             {
415             my $self = shift;
416             $self->comment_start_char('#');
417             }
418             }
419             # ----------------------------------------------------------------------------------------------------
420             {
421             package Class::CodeStyler::Program::C;
422             use base qw(Class::CodeStyler::Program::Abstract);
423             use Class::STL::ClassMembers;
424             use Class::STL::ClassMembers::Constructor;
425             sub new_extra
426             {
427             my $self = shift;
428             $self->comment_start_char('//');
429             $self->comment_block_begin_char('/*');
430             $self->comment_block_char (' *');
431             $self->comment_block_end_char (' */');
432             }
433             }
434             # ----------------------------------------------------------------------------------------------------
435             {
436             package Class::CodeStyler::Program::Pod;
437             use base qw(Class::CodeStyler::Program::Abstract);
438             use Class::STL::ClassMembers qw( title version type user_email author pdf );
439             use Class::STL::ClassMembers::Constructor;
440             sub new_extra
441             {
442             my $self = shift;
443             $self->comment_start_char('=cut ');
444             $self->code("=pod");
445             $self->code();
446             }
447             sub head1
448             {
449             my $self = shift;
450             my $code = shift || '';
451             $self->code('=head1 ' . $code);
452             $self->code();
453             }
454             sub head2
455             {
456             my $self = shift;
457             my $code = shift || '';
458             $self->code('=head2 ' . $code);
459             $self->code();
460             }
461             sub head3
462             {
463             my $self = shift;
464             my $code = shift || '';
465             $self->code('=head3 ' . $code);
466             $self->code();
467             }
468             sub head4
469             {
470             my $self = shift;
471             my $code = shift || '';
472             $self->code('=head4 ' . $code);
473             $self->code();
474             }
475             sub begin
476             {
477             my $self = shift;
478             my $code = shift || '';
479             $self->code('=begin ' . $code);
480             $self->code();
481             }
482             sub end
483             {
484             my $self = shift;
485             my $code = shift || '';
486             $self->code('=end ' . $code);
487             $self->code();
488             }
489             sub item
490             {
491             my $self = shift;
492             my $code = shift || '';
493             $self->code();
494             $self->code('=item ' . $code);
495             $self->code();
496             }
497             sub literal
498             {
499             my $self = shift;
500             my $code = shift || '';
501             $self->code(' ' . $code);
502             $self->code();
503             }
504             sub page
505             {
506             my $self = shift;
507             $self->code('=page');
508             $self->code();
509             }
510             sub over
511             {
512             my $self = shift;
513             my $indent = shift;
514             $self->code("=over @{[ defined($indent) ? $indent : '' ]}");
515             $self->code();
516             }
517             sub back
518             {
519             my $self = shift;
520             $self->code('=back ');
521             $self->code();
522             }
523             sub to_pdf
524             {
525             #> $self->pdf(Class::CodeStyler::Pod2Pdf->new(
526             #> title => $self->title(),
527             #> version => $self->version(),
528             #> type => $self->type(),
529             #> email => $self->user_email(),
530             #> author => $self->author(),
531             #> ));
532             #> $self->pdf()->produce();
533             }
534             }
535             # ----------------------------------------------------------------------------------------------------
536             {
537             package Class::CodeStyler::Anchor;
538             use base qw(Class::CodeStyler::Element::Abstract);
539             use Class::STL::ClassMembers;
540             use Class::STL::ClassMembers::Constructor;
541             sub prepare
542             {
543             my $self = shift;
544             return unless ($self->owner()->print_bookmarks());
545             return if ($self->owner()->code_text()->raw_is_on());
546             $self->owner()->code_text()->append_text("# ANCHOR ---- @{[ $self->data() ]}");
547             $self->owner()->code_text()->append_newline();
548             }
549             }
550             # ----------------------------------------------------------------------------------------------------
551             {
552             package Class::CodeStyler::Bookmark;
553             use base qw(Class::CodeStyler::Element::Abstract);
554             use Class::STL::ClassMembers;
555             use Class::STL::ClassMembers::Constructor;
556             sub prepare
557             {
558             my $self = shift;
559             return unless ($self->owner()->print_bookmarks());
560             return if ($self->owner()->code_text()->raw_is_on());
561             $self->owner()->code_text()->append_text("# BOOKMARK ---- @{[ $self->data() ]}");
562             $self->owner()->code_text()->append_newline();
563             }
564             }
565             # ----------------------------------------------------------------------------------------------------
566             {
567             package Class::CodeStyler::OpenBlock;
568             use base qw(Class::CodeStyler::Element::Abstract);
569             use Class::STL::ClassMembers qw(bracket_char);
570             use Class::STL::ClassMembers::Constructor;
571             sub prepare
572             {
573             my $self = shift;
574             $self->owner()->code_text()->append_text($self->bracket_char());
575             return unless ($self->owner()->code_text()->newline_is_on());
576             $self->owner()->code_text()->current_tab($self->owner()->code_text()->current_tab()+1);
577             $self->owner()->code_text()->append_newline();
578             }
579             }
580             # ----------------------------------------------------------------------------------------------------
581             {
582             package Class::CodeStyler::CloseBlock;
583             use base qw(Class::CodeStyler::Element::Abstract);
584             use Class::STL::ClassMembers qw(bracket_char);
585             use Class::STL::ClassMembers::Constructor;
586             sub prepare
587             {
588             my $self = shift;
589             $self->owner()->code_text()->current_tab($self->owner()->code_text()->current_tab()-1) if ($self->owner()->code_text()->newline_is_on());
590             $self->owner()->code_text()->append_text($self->bracket_char());
591             $self->owner()->code_text()->append_newline() if ($self->owner()->code_text()->newline_is_on());
592             }
593             }
594             # ----------------------------------------------------------------------------------------------------
595             {
596             package Class::CodeStyler::Code;
597             use base qw(Class::CodeStyler::Element::Abstract);
598             use Class::STL::ClassMembers qw(code);
599             use Class::STL::ClassMembers::Constructor;
600             sub prepare
601             {
602             my $self = shift;
603             confess "Undefined 'owner' (@{[ $self->code() ]})!" unless (defined($self->owner()));
604             $self->owner()->code_text()->append_text($self->code());
605             $self->owner()->code_text()->append_newline() if ($self->owner()->code_text()->newline_is_on());
606             }
607             }
608             # ----------------------------------------------------------------------------------------------------
609             {
610             package Class::CodeStyler::Comment;
611             use base qw(Class::CodeStyler::Element::Abstract);
612             use Class::STL::ClassMembers;
613             use Class::STL::ClassMembers::Constructor;
614             sub prepare
615             {
616             my $self = shift;
617             #> return if ($self->owner()->code_text()->raw_is_on());
618             $self->owner()->code_text()->append_text($self->owner()->comment_start_char() . $self->data());
619             $self->owner()->code_text()->append_newline() if ($self->owner()->code_text()->newline_is_on());
620             }
621             }
622             # ----------------------------------------------------------------------------------------------------
623             {#TODO:
624             package Class::CodeStyler::CommentBegin;
625             }
626             # ----------------------------------------------------------------------------------------------------
627             {#TODO:
628             package Class::CodeStyler::CommentEnd;
629             }
630             # ----------------------------------------------------------------------------------------------------
631             {
632             package Class::CodeStyler::Divider;
633             use base qw(Class::CodeStyler::Element::Abstract);
634             use Class::STL::ClassMembers;
635             use Class::STL::ClassMembers::Constructor;
636             sub prepare
637             {
638             my $self = shift;
639             $self->owner()->code_text()->append_text($self->owner()->comment_start_char());
640             $self->owner()->code_text()->append_text($self->owner()->divider_char() x $self->owner()->divider_length());
641             $self->owner()->code_text()->append_newline() if ($self->owner()->code_text()->newline_is_on());
642             }
643             }
644             # ----------------------------------------------------------------------------------------------------
645             {
646             package Class::CodeStyler::ToggleNewline;
647             use base qw(Class::CodeStyler::Element::Abstract);
648             use Class::STL::ClassMembers qw(on);
649             use Class::STL::ClassMembers::Constructor;
650             sub prepare
651             {
652             my $self = shift;
653             $self->owner()->code_text()->newline_is_on($self->on());
654             }
655             }
656             # ----------------------------------------------------------------------------------------------------
657             {
658             package Class::CodeStyler::ToggleIndent;
659             use base qw(Class::CodeStyler::Element::Abstract);
660             use Class::STL::ClassMembers qw(on);
661             use Class::STL::ClassMembers::Constructor;
662             sub prepare
663             {
664             my $self = shift;
665             $self->owner()->code_text()->indent_is_on($self->on());
666             }
667             }
668             # ----------------------------------------------------------------------------------------------------
669             {
670             package Class::CodeStyler::Indent;
671             use base qw(Class::CodeStyler::Element::Abstract);
672             use Class::STL::ClassMembers qw(indent);
673             use Class::STL::ClassMembers::Constructor;
674             sub prepare
675             {
676             my $self = shift;
677             $self->owner()->code_text()->current_tab($self->owner()->code_text()->current_tab()+$self->indent());
678             }
679             }
680             # ----------------------------------------------------------------------------------------------------
681             #TODO: User can extend Class::CodeStyler::Element::Abstract to provide specific code-blocks...
682             1;