File Coverage

blib/lib/Class/Visitor.pm
Criterion Covered Total %
statement 7 9 77.7
branch n/a
condition n/a
subroutine 3 3 100.0
pod n/a
total 10 12 83.3


line stmt bran cond sub pod time code
1             #
2             # Copyright (C) 1997 Ken MacLeod
3             # Class::Visitor is free software; you can redistribute it and/or
4             # modify it under the same terms as Perl itself.
5             #
6             # $Id: Visitor.pm,v 1.3 1997/11/03 17:38:10 ken Exp $
7             #
8              
9             package Class::Visitor;
10              
11             require 5.000;
12             require Exporter;
13              
14             @ISA = qw(Exporter);
15             @EXPORT = qw(visitor_class);
16 1     1   679 use strict;
  1         2  
  1         40  
17 1     1   5 use vars qw($VERSION);
  1         2  
  1         64  
18              
19             $VERSION = '0.02';
20              
21 1     1   1428 use Class::Template;
  0            
  0            
22             use Class::Iter;
23              
24             sub visitor_class {
25             my( $pkg, $super, $ref ) = @_;
26             my @methods = ();
27             my %refs = ();
28             my %arrays = ();
29             my %hashes = ();
30             my $out = '';
31              
32             members ($pkg, $ref);
33              
34             # XXX this is redundant, but saves hacking Class::Template
35             Class::Template::parse_fields( $ref, \$out, \@methods, \%refs, \%arrays, \%hashes );
36              
37             eval "\@${pkg}::ISA = qw{$super}";
38             if ($super eq 'Class::Visitor::Base') {
39             eval "\@${pkg}::Iter::ISA = qw{Class::Iter}";
40             } else {
41             eval "\@${pkg}::Iter::ISA = qw{${super}::Iter}";
42             }
43              
44             $out = <
45             {
46             package $pkg;
47             EOF
48             build_push_methods_( $ref, \$out, \@methods, \%refs, \%arrays, \%hashes );
49              
50              
51             my $str = <<'EOF';
52             sub accept {
53             my $self = shift; my $visitor = shift;
54             $visitor->visit_!visit_method! ($self, @_);
55             }
56             # [self, parent, array, index]
57             sub iter {
58             my $iter = [@_];
59             bless $iter, '!package!::Iter';
60             }
61              
62             sub new {
63             my ($type) = shift;
64              
65             my ($self) = !type!;
66             bless ($self, $type);
67              
68             return ($self);
69             }
70             }
71              
72             {
73             package !package!::Iter;
74              
75             sub accept {
76             my $self = shift; my $visitor = shift;
77             $visitor->visit_!visit_method! ($self, @_);
78             }
79             EOF
80             my $visit_method = $pkg;
81             $visit_method =~ s/::/_/g;
82             $str =~ s/!package!/$pkg/g;
83             $str =~ s/!visit_method!/$visit_method/g;
84             my $type = (ref ($ref) eq 'HASH') ? '{@_}' : '[@_]';
85             $str =~ s/!type!/$type/g;
86             $out .= $str;
87              
88             build_iter_methods_( $ref, \$out, \@methods, \%refs, \%arrays, \%hashes );
89              
90             $out .= "}\n1;\n";
91              
92             # XXX we ``inherit'' `print' from Class::Template
93             ( $Class::Template::print ) ? print( $out ) : eval $out;
94             }
95              
96             sub build_iter_methods_ {
97             my( $ref, $out, $methods, $refs, $arrays, $hashes ) = @_;
98             my $type = ref $ref;
99              
100             my $method;
101             foreach $method (@$methods) {
102             $$out .= <
103             sub $method {
104             my \$self = shift;
105             return (\$self->[0]->$method (\@_));
106             }
107             EOF
108             if (defined $arrays->{$method}) {
109             if ($method eq 'contents') {
110             $$out .= <
111             sub children_accept {
112             children_accept_contents (\@_);
113             }
114             sub as_string {
115             contents_as_string (\@_);
116             }
117             EOF
118             }
119             my $str = <<'EOF';
120             sub push_!member! {
121             my $self = shift;
122             return ($self->[0]->push_!member! (@_));
123             }
124             sub pop_!member! {
125             my $self = shift;
126             return ($self->[0]->pop_!member! (@_));
127             }
128             sub !member!_as_string {
129             my $self = shift;
130             return ($self->[0]->!member!_as_string (@_));
131             }
132             sub children_accept_!member! {
133             my $self = shift; my $visitor = shift;
134             my $array = $self->[0]->!member!();
135             my $ii;
136             for ($ii = 0; $ii <= $#$array; $ii ++) {
137             my ($child) = $array->[$ii];
138             if (!ref ($child)) {
139             my $iter = bless ([$child, $self, $array, $ii],
140             'Class::Scalar::Iter');
141             $visitor->visit_scalar ($iter, @_);
142             } else {
143             my $iter = $child->iter ($self, $array, $ii);
144             $iter->accept ($visitor, @_);
145             }
146             }
147             }
148             EOF
149             $str =~ s/!member!/$method/g;
150             $$out .= $str;
151             }
152             }
153             }
154              
155             sub build_push_methods_ {
156             my( $ref, $out, $methods, $refs, $arrays, $hashes ) = @_;
157             my $type = ref $ref;
158              
159             my $method;
160             my $cnt = 0; # count used for array classes
161             foreach $method (@$methods) {
162             if (defined $arrays->{$method}) {
163             if ($method eq 'contents') {
164             $$out .= <
165             sub push {
166             return (push_contents (\@_));
167             }
168             sub pop {
169             return (pop_contents (\@_));
170             }
171             sub as_string {
172             return (contents_as_string (\@_));
173             }
174             sub children_accept {
175             return (children_accept_contents (\@_));
176             }
177             EOF
178             }
179             my $str = <<'EOF';
180             sub push_!member! {
181             my $self = shift;
182             push (@{$self->!member_ref!}, @_);
183             }
184             sub pop_!member! {
185             my $self = shift;
186             return (pop (@{$self->!member_ref!}));
187             }
188             sub !member!_as_string {
189             my $self = shift;
190             my $array = $self->!member_ref!;
191             my @string;
192             my $ii;
193             for ($ii = 0; $ii <= $#$array; $ii ++) {
194             my ($child) = $array->[$ii];
195             if (!ref ($child)) {
196             # XXX should use context for a CDATA mapper
197             push (@string, $child);
198             } else {
199             # note, not passing as iterator
200             push (@string, $child->as_string(@_));
201             }
202             }
203             return (join ("", @string));
204             }
205             sub children_accept_!member! {
206             my $self = shift; my $visitor = shift;
207             my $array = $self->!member_ref!;
208             my $ii;
209             for ($ii = 0; $ii <= $#$array; $ii ++) {
210             my ($child) = $array->[$ii];
211             if (!ref ($child)) {
212             $visitor->visit_scalar ($child, @_);
213             } else {
214             $child->accept ($visitor, @_);
215             }
216             }
217             }
218             EOF
219             $str =~ s/!member!/$method/g;
220             my $member_ref = ($type eq 'HASH') ? "{'$method'}" : "[$cnt]";
221             $str =~ s/!member_ref!/$member_ref/g;
222             $$out .= $str;
223             }
224              
225             $cnt ++;
226             }
227             }
228              
229             package Class::Visitor::Base;
230              
231             sub is_iter {
232             return 0;
233             }
234              
235             sub delegate {
236             return $_[0];
237             }
238              
239             1;
240             __END__