Debugger update
[p5sagit/p5-mst-13.2.git] / lib / Class / Template.pm
CommitLineData
36477c24 1package Class::Template;
2require 5.000;
3require Exporter;
4
5@ISA = qw(Exporter);
6@EXPORT = qw(members struct);
7use strict;
8
9# Template.pm --- struct/member template builder
10# 12mar95
11# Dean Roehrich
12#
13# changes/bugs fixed since 28nov94 version:
14# - podified
15# changes/bugs fixed since 21nov94 version:
16# - Fixed examples.
17# changes/bugs fixed since 02sep94 version:
18# - Moved to Class::Template.
19# changes/bugs fixed since 20feb94 version:
20# - Updated to be a more proper module.
21# - Added "use strict".
22# - Bug in build_methods, was using @var when @$var needed.
23# - Now using my() rather than local().
24#
25# Uses perl5 classes to create nested data types.
26# This is offered as one implementation of Tom Christiansen's "structs.pl"
27# idea.
28
29=head1 NAME
30
31Class::Template - struct/member template builder
32
b133f4ec 33=head1 SYNOPSIS
34
35 use Class::Template;
36 struct(name => { key1 => type1, key2 => type2 });
37
38 package Myobj;
39 use Class::Template;
40 members Myobj { key1 => type1, key2 => type2 };
41
42=head1 DESCRIPTION
43
44This module uses perl5 classes to create nested data types.
45
36477c24 46=head1 EXAMPLES
47
48=item * Example 1
49
50 use Class::Template;
51
52 struct( rusage => {
53 ru_utime => timeval,
54 ru_stime => timeval,
55 });
56
57 struct( timeval => [
58 tv_secs => '$',
59 tv_usecs => '$',
60 ]);
61
62 my $s = new rusage;
63
64=item * Example 2
65
66 package OBJ;
67 use Class::Template;
68
69 members OBJ {
70 'a' => '$',
71 'b' => '$',
72 };
73
74 members OBJ2 {
75 'd' => '@',
76 'c' => '$',
77 };
78
79 package OBJ2; @ISA = (OBJ);
80
81 sub new {
82 my $r = InitMembers( &OBJ::InitMembers() );
83 bless $r;
84 }
85
86=head1 NOTES
a6006777 87
36477c24 88Use '%' if the member should point to an anonymous hash. Use '@' if the
89member should point to an anonymous array.
90
91When using % and @ the method requires one argument for the key or index
92into the hash or array.
93
94Prefix the %, @, or $ with '*' to indicate you want to retrieve pointers to
95the values rather than the values themselves.
96
97=cut
98
99Var: {
100 $Class::Template::print = 0;
101 sub printem { $Class::Template::print++ }
102}
103
104
105sub struct {
106 my( $struct, $ref ) = @_;
107 my @methods = ();
108 my %refs = ();
109 my %arrays = ();
110 my %hashes = ();
111 my $out = '';
112
113 $out = "{\n package $struct;\n sub new {\n";
114 parse_fields( $ref, \$out, \@methods, \%refs, \%arrays, \%hashes, 0 );
115 $out .= " bless \$r;\n }\n";
116 build_methods( $ref, \$out, \@methods, \%refs, \%arrays, \%hashes );
117 $out .= "}\n1;\n";
118
119 ( $Class::Template::print ) ? print( $out ) : eval $out;
120}
121
122sub members {
123 my( $pkg, $ref ) = @_;
124 my @methods = ();
125 my %refs = ();
126 my %arrays = ();
127 my %hashes = ();
128 my $out = '';
129
130 $out = "{\n package $pkg;\n sub InitMembers {\n";
131 parse_fields( $ref, \$out, \@methods, \%refs, \%arrays, \%hashes, 1 );
132 $out .= " bless \$r;\n }\n";
133 build_methods( $ref, \$out, \@methods, \%refs, \%arrays, \%hashes );
134 $out .= "}\n1;\n";
135
136 ( $Class::Template::print ) ? print( $out ) : eval $out;
137}
138
139
140sub parse_fields {
141 my( $ref, $out, $methods, $refs, $arrays, $hashes, $member ) = @_;
142 my $type = ref $ref;
143 my @keys;
144 my $val;
145 my $cnt = 0;
146 my $idx = 0;
147 my( $cmt, $n );
148
149 if( $type eq 'HASH' ){
150 if( $member ){
151 $$out .= " my(\$r) = \@_ ? shift : {};\n";
152 }
153 else{
154 $$out .= " my(\$r) = {};\n";
155 }
156 @keys = keys %$ref;
157 foreach (@keys){
158 $val = $ref->{$_};
159 if( $val =~ /^\*(.)/ ){
160 $refs->{$_}++;
161 $val = $1;
162 }
163 if( $val eq '@' ){
164 $$out .= " \$r->{'$_'} = [];\n";
165 $arrays->{$_}++;
166 }
167 elsif( $val eq '%' ){
168 $$out .= " \$r->{'$_'} = {};\n";
169 $hashes->{$_}++;
170 }
171 elsif( $val ne '$' ){
172 $$out .= " \$r->{'$_'} = \&${val}::new();\n";
173 }
174 else{
175 $$out .= " \$r->{'$_'} = undef;\n";
176 }
177 push( @$methods, $_ );
178 }
179 }
180 elsif( $type eq 'ARRAY' ){
181 if( $member ){
182 $$out .= " my(\$r) = \@_ ? shift : [];\n";
183 }
184 else{
185 $$out .= " my(\$r) = [];\n";
186 }
187 while( $idx < @$ref ){
188 $n = $ref->[$idx];
189 push( @$methods, $n );
190 $val = $ref->[$idx+1];
191 $cmt = "# $n";
192 if( $val =~ /^\*(.)/ ){
193 $refs->{$n}++;
194 $val = $1;
195 }
196 if( $val eq '@' ){
197 $$out .= " \$r->[$cnt] = []; $cmt\n";
198 $arrays->{$n}++;
199 }
200 elsif( $val eq '%' ){
201 $$out .= " \$r->[$cnt] = {}; $cmt\n";
202 $hashes->{$n}++;
203 }
204 elsif( $val ne '$' ){
205 $$out .= " \$r->[$cnt] = \&${val}::new();\n";
206 }
207 else{
208 $$out .= " \$r->[$cnt] = undef; $cmt\n";
209 }
210 ++$cnt;
211 $idx += 2;
212 }
213 }
214}
215
216
217sub build_methods {
218 my( $ref, $out, $methods, $refs, $arrays, $hashes ) = @_;
219 my $type = ref $ref;
220 my $elem = '';
221 my $cnt = 0;
222 my( $pre, $pst, $cmt, $idx );
223
224 foreach (@$methods){
225 $pre = $pst = $cmt = $idx = '';
226 if( defined $refs->{$_} ){
227 $pre = "\\(";
228 $pst = ")";
229 $cmt = " # returns ref";
230 }
231 $$out .= " sub $_ {$cmt\n my \$r = shift;\n";
232 if( $type eq 'ARRAY' ){
233 $elem = "[$cnt]";
234 ++$cnt;
235 }
236 elsif( $type eq 'HASH' ){
237 $elem = "{'$_'}";
238 }
239 if( defined $arrays->{$_} ){
240 $$out .= " my \$i;\n";
241 $$out .= " \@_ ? (\$i = shift) : return \$r->$elem;\n";
242 $idx = "->[\$i]";
243 }
244 elsif( defined $hashes->{$_} ){
245 $$out .= " my \$i;\n";
246 $$out .= " \@_ ? (\$i = shift) : return \$r->$elem;\n";
247 $idx = "->{\$i}";
248 }
249 $$out .= " \@_ ? (\$r->$elem$idx = shift) : $pre\$r->$elem$idx$pst;\n";
250 $$out .= " }\n";
251 }
252}
253
2541;