File Coverage

File:/usr/local/share/automake-1.11/Automake/Struct.pm
Coverage:46.6%

linestmtbrancondsubpodtimecode
1# autoconf -- create `configure' using m4 macros
2# Copyright (C) 2001, 2002, 2006 Free Software Foundation, Inc.
3
4# This program is free software; you can redistribute it and/or modify
5# it under the terms of the GNU General Public License as published by
6# the Free Software Foundation; either version 2, or (at your option)
7# any later version.
8
9# This program is distributed in the hope that it will be useful,
10# but WITHOUT ANY WARRANTY; without even the implied warranty of
11# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12# GNU General Public License for more details.
13
14# You should have received a copy of the GNU General Public License
15# along with this program. If not, see <http://www.gnu.org/licenses/>.
16
17# This file is basically Perl 5.6's Class::Struct, but made compatible
18# with Perl 5.5. If someday this has to be updated, be sure to rename
19# all the occurrences of Class::Struct into Automake::Struct, otherwise
20# if we `use' a Perl module (e.g., File::stat) that uses Class::Struct,
21# we would have two packages defining the same symbols. Boom.
22
23###############################################################
24# The main copy of this file is in Automake's CVS repository. #
25# Updates should be sent to automake-patches@gnu.org. #
26###############################################################
27
28package Automake::Struct;
29
30## See POD after __END__
31
32
1
1
1
34
3
1
use 5.005_03;
33
34
1
1
1
4
2
4
use strict;
35
1
1
1
4
1
4
use vars qw(@ISA @EXPORT $VERSION);
36
37
1
1
1
4
1
4
use Carp;
38
39require Exporter;
40@ISA = qw(Exporter);
41@EXPORT = qw(struct);
42
43$VERSION = '0.58';
44
45## Tested on 5.002 and 5.003 without class membership tests:
46my $CHECK_CLASS_MEMBERSHIP = ($] >= 5.003_95);
47
48my $print = 0;
49sub printem {
50
0
0
0
0
0
    if (@_) { $print = shift }
51
0
0
    else { $print++ }
52}
53
54{
55    package Automake::Struct::Tie_ISA;
56
57    sub TIEARRAY {
58
1
2
        my $class = shift;
59
1
8
        return bless [], $class;
60    }
61
62    sub STORE {
63
0
0
        my ($self, $index, $value) = @_;
64
0
0
        Automake::Struct::_subclass_error();
65    }
66
67    sub FETCH {
68
0
0
        my ($self, $index) = @_;
69
0
0
        $self->[$index];
70    }
71
72    sub FETCHSIZE {
73
0
0
        my $self = shift;
74
0
0
        return scalar(@$self);
75    }
76
77
0
0
    sub DESTROY { }
78}
79
80sub struct {
81
82    # Determine parameter list structure, one of:
83    # struct( class => [ element-list ])
84    # struct( class => { element-list })
85    # struct( element-list )
86    # Latter form assumes current package name as struct name.
87
88
1
1
2
    my ($class, @decls);
89
1
3
    my $base_type = ref $_[1];
90
1
5
    if ( $base_type eq 'HASH' ) {
91
0
0
        $class = shift;
92
0
0
0
0
        @decls = %{shift()};
93
0
0
        _usage_error() if @_;
94    }
95    elsif ( $base_type eq 'ARRAY' ) {
96
0
0
        $class = shift;
97
0
0
0
0
        @decls = @{shift()};
98
0
0
        _usage_error() if @_;
99    }
100    else {
101
1
2
        $base_type = 'ARRAY';
102
1
3
        $class = (caller())[0];
103
1
15
        @decls = @_;
104    }
105
1
6
    _usage_error() if @decls % 2 == 1;
106
107    # Ensure we are not, and will not be, a subclass.
108
109
1
3
    my $isa = do {
110
1
1
1
4
5
2
        no strict 'refs';
111
1
1
1
8
        \@{$class . '::ISA'};
112    };
113
1
4
    _subclass_error() if @$isa;
114
1
1
    tie @$isa, 'Automake::Struct::Tie_ISA';
115
116    # Create constructor.
117
118    croak "function 'new' already defined in package $class"
119
1
1
1
1
1
1
4
1
2
3
1
5
        if do { no strict 'refs'; defined &{$class . "::new"} };
120
121
1
3
    my @methods = ();
122
1
2
    my %refs = ();
123
1
1
    my %arrays = ();
124
1
1
    my %hashes = ();
125
1
2
    my %classes = ();
126
1
1
    my $got_class = 0;
127
1
1
    my $out = '';
128
129
1
3
    $out = "{\n package $class;\n use Carp;\n sub new {\n";
130
1
2
    $out .= " my (\$class, \%init) = \@_;\n";
131
1
2
    $out .= " \$class = __PACKAGE__ unless \@_;\n";
132
133
1
2
    my $cnt = 0;
134
1
2
    my $idx = 0;
135
1
2
    my( $cmt, $name, $type, $elem );
136
137
1
9
    if( $base_type eq 'HASH' ){
138
0
0
        $out .= " my(\$r) = {};\n";
139
0
0
        $cmt = '';
140    }
141    elsif( $base_type eq 'ARRAY' ){
142
1
2
        $out .= " my(\$r) = [];\n";
143    }
144
1
3
    while( $idx < @decls ){
145
23
31
        $name = $decls[$idx];
146
23
36
        $type = $decls[$idx+1];
147
23
27
        push( @methods, $name );
148
23
55
        if( $base_type eq 'HASH' ){
149
0
0
            $elem = "{'${class}::$name'}";
150        }
151        elsif( $base_type eq 'ARRAY' ){
152
23
38
            $elem = "[$cnt]";
153
23
19
            ++$cnt;
154
23
25
            $cmt = " # $name";
155        }
156
23
37
        if( $type =~ /^\*(.)/ ){
157
0
0
            $refs{$name}++;
158
0
0
            $type = $1;
159        }
160
23
45
        my $init = "defined(\$init{'$name'}) ? \$init{'$name'} :";
161
23
63
        if( $type eq '@' ){
162
3
5
            $out .= " croak 'Initializer for $name must be array reference'\n";
163
3
10
            $out .= " if defined(\$init{'$name'}) && ref(\$init{'$name'}) ne 'ARRAY';\n";
164
3
8
            $out .= " \$r->$elem = $init [];$cmt\n";
165
3
6
            $arrays{$name}++;
166        }
167        elsif( $type eq '%' ){
168
0
0
            $out .= " croak 'Initializer for $name must be hash reference'\n";
169
0
0
            $out .= " if defined(\$init{'$name'}) && ref(\$init{'$name'}) ne 'HASH';\n";
170
0
0
            $out .= " \$r->$elem = $init {};$cmt\n";
171
0
0
            $hashes{$name}++;
172        }
173        elsif ( $type eq '$') {
174
20
47
            $out .= " \$r->$elem = $init undef;$cmt\n";
175        }
176        elsif( $type =~ /^\w+(?:::\w+)*$/ ){
177
0
0
            $init = "defined(\$init{'$name'}) ? \%{\$init{'$name'}} : ()";
178
0
0
            $out .= " croak 'Initializer for $name must be hash reference'\n";
179
0
0
            $out .= " if defined(\$init{'$name'}) && ref(\$init{'$name'}) ne 'HASH';\n";
180
0
0
            $out .= " \$r->$elem = '${type}'->new($init);$cmt\n";
181
0
0
            $classes{$name} = $type;
182
0
0
            $got_class = 1;
183        }
184        else{
185
0
0
            croak "'$type' is not a valid struct element type";
186        }
187
23
50
        $idx += 2;
188    }
189
1
18
    $out .= " bless \$r, \$class;\n }\n";
190
191    # Create accessor methods.
192
193
1
2
    my( $pre, $pst, $sel );
194
1
2
    $cnt = 0;
195
1
2
    foreach $name (@methods){
196
1
1
1
23
23
23
4
1
2
21
19
73
        if ( do { no strict 'refs'; defined &{$class . "::$name"} } ) {
197
0
0
            carp "function '$name' already defined, overrides struct accessor method";
198        }
199        else {
200
23
40
            $pre = $pst = $cmt = $sel = '';
201
23
43
            if( defined $refs{$name} ){
202
0
0
                $pre = "\\(";
203
0
0
                $pst = ")";
204
0
0
                $cmt = " # returns ref";
205            }
206
23
48
            $out .= " sub $name {$cmt\n my \$r = shift;\n";
207
23
38
            if( $base_type eq 'ARRAY' ){
208
23
29
                $elem = "[$cnt]";
209
23
26
                ++$cnt;
210            }
211            elsif( $base_type eq 'HASH' ){
212
0
0
                $elem = "{'${class}::$name'}";
213            }
214
23
74
            if( defined $arrays{$name} ){
215
3
4
                $out .= " my \$i;\n";
216
3
4
                $out .= " \@_ ? (\$i = shift) : return \$r->$elem;\n";
217
3
6
                $sel = "->[\$i]";
218            }
219            elsif( defined $hashes{$name} ){
220
0
0
                $out .= " my \$i;\n";
221
0
0
                $out .= " \@_ ? (\$i = shift) : return \$r->$elem;\n";
222
0
0
                $sel = "->{\$i}";
223            }
224            elsif( defined $classes{$name} ){
225
0
0
                if ( $CHECK_CLASS_MEMBERSHIP ) {
226
0
0
                    $out .= " croak '$name argument is wrong class' if \@_ && ! UNIVERSAL::isa(\$_[0], '$classes{$name}');\n";
227                }
228            }
229
23
34
            $out .= " croak 'Too many args to $name' if \@_ > 1;\n";
230
23
87
            $out .= " \@_ ? ($pre\$r->$elem$sel = shift$pst) : $pre\$r->$elem$sel$pst;\n";
231
23
37
            $out .= " }\n";
232        }
233    }
234
1
2
    $out .= "}\n1;\n";
235
236
1
2
    print $out if $print;
237
1
1
1
1
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
54
54
54
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
6
6
6
18
18
18
63
63
63
18
18
18
18
18
18
18
18
18
18
18
18
18
18
18
18
18
18
18
18
18
18
18
18
18
18
18
18
18
18
0
0
0
45
45
45
0
0
0
0
0
0
0
0
0
6
1
7
2
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
69
44
202
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
0
7
13
25
22
33
49
74
125
247
89
31
25
59
41
103
42
39
49
44
51
45
42
89
43
44
85
43
41
37
52
42
47
41
42
41
41
37
44
88
0
0
0
58
88
138
    my $result = eval $out;
238
1
8
    carp $@ if $@;
239}
240
241sub _usage_error {
242
0
    confess "struct usage error";
243}
244
245sub _subclass_error {
246
0
    croak 'struct class cannot be a subclass (@ISA not allowed)';
247}
248
2491; # for require
250
251