File Coverage

blib/lib/ExtUtils/XSpp/Node/Class.pm
Criterion Covered Total %
statement 74 82 90.2
branch 20 32 62.5
condition 6 8 75.0
subroutine 13 14 92.8
pod 5 9 55.5
total 118 145 81.3


line stmt bran cond sub pod time code
1             package ExtUtils::XSpp::Node::Class;
2 21     21   241 use strict;
  21         38  
  21         829  
3 21     21   137 use warnings;
  21         45  
  21         600  
4 21     21   108 use base 'ExtUtils::XSpp::Node::Package';
  21         39  
  21         1597  
5              
6             =head1 NAME
7              
8             ExtUtils::XSpp::Node::Class - A class (inherits from Package).
9              
10             =head1 DESCRIPTION
11              
12             An L sub-class representing a class
13             declaration such as
14              
15             %name{PerlClassName} class MyClass : public BaseClass
16             {
17             ...
18             }
19              
20             The Perl-level class name and the C++ class name attributes
21             are inherited from the L class.
22              
23             =head1 METHODS
24              
25             =head2 new
26              
27             Creates a new C object.
28              
29             Optional named parameters:
30             C can be a reference to an array of methods
31             (L) of the class,
32             and C, a reference to an array of
33             base classes (C objects).
34             C may be a list of exception names that all
35             methods in the class handle.
36              
37             =cut
38              
39             # internal list of all the non-empty class objects, either defined by the
40             # parser or created by plugins; does not include dummy base class objects
41             my %all_classes;
42              
43             sub init {
44 54     54 1 101 my $this = shift;
45 54         269 my %args = @_;
46              
47 54         402 $this->SUPER::init( @_ );
48 54         161 $this->{METHODS} = [];
49 54   100     368 $this->{BASE_CLASSES} = $args{base_classes} || [];
50 54 50       177 $this->add_methods( @{$args{methods}} ) if $args{methods};
  0         0  
51 54         144 $this->{CATCH} = $args{catch};
52 54         135 $this->{CONDITION} = $args{condition};
53 54         120 $this->{EMIT_CONDITION} = $args{emit_condition};
54 54         231 $this->{GETTER_STYLE} = $this->{SETTER_STYLE} = 'underscore';
55              
56 54 50       229 $all_classes{$this->cpp_name} = $this unless $this->empty;
57              
58             # TODO check the Perl name of the base class?
59 54         139 foreach my $base ( @{$this->base_classes} ) {
  54         185  
60 3 50       15 $base = $all_classes{$base->cpp_name}
61             if $all_classes{$base->cpp_name};
62             }
63             }
64              
65             =head2 add_methods
66              
67             Adds new methods to the class. By default, their
68             scope is C. Takes arbitrary number of arguments
69             which are processed in order.
70              
71             If an argument is an L,
72             the current method scope is changed accordingly for
73             all following methods.
74              
75             If an argument is an L
76             it is added to the list of methods of the class.
77             The method's class name is set to the current class
78             and its scope is set to the current method scope.
79              
80             =cut
81              
82             sub add_methods {
83 107     107 1 166 my $this = shift;
84 107         174 my $access = 'public'; # good enough for now
85 107         224 foreach my $meth ( @_ ) {
86 71 100       496 if( $meth->isa( 'ExtUtils::XSpp::Node::Function' ) ) {
    100          
    50          
87 51         102 $meth->{CLASS} = $this;
88 51         130 $meth->{ACCESS} = $access;
89 51 50       83 $meth->add_exception_handlers( @{$this->{CATCH} || []} );
  51         417  
90 51         282 $meth->resolve_typemaps;
91 51         338 $meth->resolve_exceptions;
92             } elsif( $meth->isa( 'ExtUtils::XSpp::Node::Member' ) ) {
93 12         20 $meth->{CLASS} = $this;
94 12         57 $meth->{ACCESS} = $access;
95 12         50 $meth->resolve_typemaps;
96             } elsif( $meth->isa( 'ExtUtils::XSpp::Node::Access' ) ) {
97 0         0 $access = $meth->access;
98 0         0 next;
99             }
100             # FIXME: Should there be else{croak}?
101 71         136 push @{$this->{METHODS}}, $meth;
  71         275  
102             }
103              
104 107 100       298 $all_classes{$this->cpp_name} = $this unless $this->empty;
105             }
106              
107             sub delete_methods {
108 0     0 0 0 my( $this, @methods ) = @_;
109 0         0 my %methods = map { $_ => 1 } @methods;
  0         0  
110              
111 0         0 $this->{METHODS} = [ grep !$methods{$_}, @{$this->{METHODS}} ];
  0         0  
112             }
113              
114             sub print {
115 50     50 1 102 my $this = shift;
116 50         95 my $state = shift;
117 50         271 my $out = $this->SUPER::print( $state );
118              
119 50 50       265 $out .= '#if ' . $this->emit_condition . "\n" if $this->emit_condition;
120              
121 50         105 foreach my $m ( @{$this->methods} ) {
  50         143  
122 67 50 66     708 next if $m->can( 'access' ) && $m->access ne 'public';
123 67         344 $out .= $m->print( $state );
124             }
125              
126             # add a BOOT block for base classes
127 50 100       104 if( @{$this->base_classes} ) {
  50         154  
128 2         6 my $class = $this->perl_name;
129              
130 2         5 $out .= <
131             BOOT:
132             {
133             EOT
134              
135 2 50       11 $out .= '#ifdef ' . $this->condition . "\n" if $this->condition;
136 2         5 $out .= <
137             AV* isa = get_av( "${class}::ISA", 1 );
138             EOT
139              
140 2         4 foreach my $b ( @{$this->base_classes} ) {
  2         4  
141 3         13 my $base = $b->perl_name;
142              
143 3         11 $out .= <
144             av_store( isa, 0, newSVpv( "$base", 0 ) );
145             EOT
146             }
147              
148             # close block in BOOT
149 2 50       6 $out .= '#endif // ' . $this->condition . "\n" if $this->condition;
150 2         5 $out .= <
151             } // blank line here is important
152              
153             EOT
154             }
155              
156 50 50       175 $out .= '#endif // ' . $this->emit_condition . "\n" if $this->emit_condition;
157              
158 50         340 return $out;
159             }
160              
161             my %getter_maker =
162             ( no_prefix => sub { $_[0] },
163             underscore => sub { 'get_' . $_[0] },
164             camelcase => sub { 'get' . ucfirst $_[0] },
165             uppercase => sub { 'Get' . ucfirst $_[0] },
166             );
167              
168             my %setter_maker =
169             ( no_prefix => sub { $_[0] },
170             underscore => sub { 'set_' . $_[0] },
171             camelcase => sub { 'set' . ucfirst $_[0] },
172             uppercase => sub { 'Set' . ucfirst $_[0] },
173             );
174              
175             sub _getter_name {
176 8     8   17 my( $this, $base ) = @_;
177              
178 8         32 return $getter_maker{$this->{GETTER_STYLE}}->( $base );
179             }
180              
181             sub _setter_name {
182 8     8   16 my( $this, $base ) = @_;
183              
184 8         31 return $setter_maker{$this->{SETTER_STYLE}}->( $base );
185             }
186              
187             sub set_getter_style {
188 6     6 0 10 my( $this, $style ) = @_;
189              
190 6 50       17 die "Invalid accessor style '$style'" unless exists $getter_maker{$style};
191 6         29 $this->{GETTER_STYLE} = $style;
192             }
193              
194             sub set_setter_style {
195 6     6 0 9 my( $this, $style ) = @_;
196              
197 6 50       19 die "Invalid accessor style '$style'" unless exists $setter_maker{$style};
198 6         19 $this->{SETTER_STYLE} = $style;
199             }
200              
201             =head1 ACCESSORS
202              
203             =head2 methods
204              
205             Returns the internal reference to the array of methods in this class.
206             Each of the methods is an C
207              
208             =head2 base_classes
209              
210             Returns the internal reference to the array of base classes of
211             this class.
212              
213             If the base classes have been defined in the same file, these are the
214             complete class objects including method definitions, otherwise only
215             the C++ and Perl name of the class are available as attributes.
216              
217             =cut
218              
219 373     373 1 1872 sub methods { $_[0]->{METHODS} }
220 106     106 1 542 sub base_classes { $_[0]->{BASE_CLASSES} }
221 161   66 161 0 447 sub empty { !$_[0]->methods || !@{$_[0]->methods} }
222              
223             1;