Filename | /var/www/foswikidev/core/lib/Foswiki/Attrs.pm |
Statements | Executed 48994 statements in 53.9ms |
Calls | P | F | Exclusive Time |
Inclusive Time |
Subroutine |
---|---|---|---|---|---|
1786 | 7 | 4 | 21.3ms | 35.3ms | new | Foswiki::Attrs::
1593 | 1 | 1 | 13.1ms | 13.1ms | _unfriendly | Foswiki::Attrs::
3755 | 8 | 2 | 7.16ms | 7.16ms | remove | Foswiki::Attrs::
43 | 1 | 1 | 912µs | 912µs | _friendly | Foswiki::Attrs::
10 | 1 | 1 | 118µs | 118µs | stringify | Foswiki::Attrs::
1 | 1 | 1 | 12µs | 25µs | BEGIN@47 | Foswiki::Attrs::
1 | 1 | 1 | 8µs | 33µs | BEGIN@49 | Foswiki::Attrs::
1 | 1 | 1 | 8µs | 12µs | BEGIN@48 | Foswiki::Attrs::
1 | 1 | 1 | 4µs | 4µs | BEGIN@51 | Foswiki::Attrs::
0 | 0 | 0 | 0s | 0s | TO_JSON | Foswiki::Attrs::
0 | 0 | 0 | 0s | 0s | extractValue | Foswiki::Attrs::
0 | 0 | 0 | 0s | 0s | findFirstOccurenceAttrs | Foswiki::Attrs::
0 | 0 | 0 | 0s | 0s | get | Foswiki::Attrs::
0 | 0 | 0 | 0s | 0s | isEmpty | Foswiki::Attrs::
Line | State ments |
Time on line |
Calls | Time in subs |
Code |
---|---|---|---|---|---|
1 | # See bottom of file for license and copyright information | ||||
2 | |||||
3 | =begin TML | ||||
4 | |||||
5 | ---+ package Foswiki::Attrs | ||||
6 | |||||
7 | Class of attribute sets, designed for parsing and storing attribute values | ||||
8 | from a macro e.g. =%<nop>MACRO{"joe" fred="bad" joe="mad"}%= | ||||
9 | |||||
10 | An attribute set is a hash containing an entry for each parameter. The | ||||
11 | default parameter (unnamed quoted string) is named <code>_<nop>DEFAULT</code> in the hash. | ||||
12 | |||||
13 | Attributes declared later in the string will override those of the same | ||||
14 | name defined earlier. The one exception to this is the _DEFAULT key, where | ||||
15 | the _first_ instance is always taken. | ||||
16 | |||||
17 | As well as the default Foswiki syntax (parameter values double-quoted) | ||||
18 | this class also parses single-quoted values, unquoted spaceless | ||||
19 | values, spaces around the =, and commas as well as spaces separating values. | ||||
20 | The extended syntax has to be enabled by passing the =$friendly= parameter | ||||
21 | to =new=. | ||||
22 | |||||
23 | *Since* _date_ indicates where functions or parameters have been added since | ||||
24 | the baseline of the API (TWiki release 4.2.3). The _date_ indicates the | ||||
25 | earliest date of a Foswiki release that will support that function or | ||||
26 | parameter. | ||||
27 | |||||
28 | *Deprecated* _date_ indicates where a function or parameters has been | ||||
29 | [[http://en.wikipedia.org/wiki/Deprecation][deprecated]]. Deprecated | ||||
30 | functions will still work, though they should | ||||
31 | _not_ be called in new plugins and should be replaced in older plugins | ||||
32 | as soon as possible. Deprecated parameters are simply ignored in Foswiki | ||||
33 | releases after _date_. | ||||
34 | |||||
35 | *Until* _date_ indicates where a function or parameter has been removed. | ||||
36 | The _date_ indicates the latest date at which Foswiki releases still supported | ||||
37 | the function or parameter. | ||||
38 | |||||
39 | =cut | ||||
40 | |||||
41 | # THIS PACKAGE IS PART OF THE PUBLISHED API USED BY EXTENSION AUTHORS. | ||||
42 | # DO NOT CHANGE THE EXISTING APIS (well thought out extensions are OK) | ||||
43 | # AND ENSURE ALL POD DOCUMENTATION IS COMPLETE AND ACCURATE. | ||||
44 | |||||
45 | package Foswiki::Attrs; | ||||
46 | |||||
47 | 2 | 25µs | 2 | 38µs | # spent 25µs (12+13) within Foswiki::Attrs::BEGIN@47 which was called:
# once (12µs+13µs) by Foswiki::PageCache::BEGIN@61 at line 47 # spent 25µs making 1 call to Foswiki::Attrs::BEGIN@47
# spent 13µs making 1 call to strict::import |
48 | 2 | 22µs | 2 | 16µs | # spent 12µs (8+4) within Foswiki::Attrs::BEGIN@48 which was called:
# once (8µs+4µs) by Foswiki::PageCache::BEGIN@61 at line 48 # spent 12µs making 1 call to Foswiki::Attrs::BEGIN@48
# spent 4µs making 1 call to warnings::import |
49 | 2 | 43µs | 2 | 57µs | # spent 33µs (8+24) within Foswiki::Attrs::BEGIN@49 which was called:
# once (8µs+24µs) by Foswiki::PageCache::BEGIN@61 at line 49 # spent 33µs making 1 call to Foswiki::Attrs::BEGIN@49
# spent 24µs making 1 call to Exporter::import |
50 | |||||
51 | # spent 4µs within Foswiki::Attrs::BEGIN@51 which was called:
# once (4µs+0s) by Foswiki::PageCache::BEGIN@61 at line 56 | ||||
52 | 1 | 8µs | if ( $Foswiki::cfg{UseLocale} ) { | ||
53 | require locale; | ||||
54 | import locale(); | ||||
55 | } | ||||
56 | 1 | 1.13ms | 1 | 4µs | } # spent 4µs making 1 call to Foswiki::Attrs::BEGIN@51 |
57 | |||||
58 | # Used in interpolation an regexes, so constant not appropriate | ||||
59 | 1 | 500ns | our $MARKER = "\0"; | ||
60 | |||||
61 | =begin TML | ||||
62 | |||||
63 | ---++ ClassMethod new ($string) => \%attrsObjectRef | ||||
64 | |||||
65 | * =$string= - String containing attribute specification | ||||
66 | |||||
67 | Parse a standard attribute string containing name=value pairs and create a new | ||||
68 | attributes object. The value may be a word or a quoted string. If there is an | ||||
69 | error during parsing, the parse will complete but $attrs->{_ERROR} will be | ||||
70 | set in the new object. $attrs->{_RAW} will always contain the full unprocessed | ||||
71 | $string. | ||||
72 | |||||
73 | =cut | ||||
74 | |||||
75 | # spent 35.3ms (21.3+14.0) within Foswiki::Attrs::new which was called 1786 times, avg 20µs/call:
# 1075 times (14.8ms+7.46ms) by Foswiki::Templates::readTemplate at line 320 of /var/www/foswikidev/core/lib/Foswiki/Templates.pm, avg 21µs/call
# 397 times (3.72ms+4.01ms) by Foswiki::_expandMacroOnTopicRendering at line 3434 of /var/www/foswikidev/core/lib/Foswiki.pm, avg 19µs/call
# 308 times (2.77ms+2.43ms) by Foswiki::Templates::expandTemplate at line 135 of /var/www/foswikidev/core/lib/Foswiki/Templates.pm, avg 17µs/call
# 2 times (31µs+43µs) by Foswiki::Store::Rcs::Handler::_getTOPICINFO at line 274 of /var/www/foswikidev/core/lib/Foswiki/Store/Rcs/Handler.pm, avg 37µs/call
# 2 times (18µs+32µs) by Foswiki::Func::extractParameters at line 3141 of /var/www/foswikidev/core/lib/Foswiki/Func.pm, avg 25µs/call
# once (14µs+46µs) by Foswiki::_expandMacroOnTopicRendering at line 3441 of /var/www/foswikidev/core/lib/Foswiki.pm
# once (10µs+6µs) by Foswiki::Templates::_expandTrivialTemplate at line 111 of /var/www/foswikidev/core/lib/Foswiki/Templates.pm | ||||
76 | 1786 | 1.40ms | my ( $class, $string, $friendly ) = @_; | ||
77 | 1786 | 1.68ms | my $this = bless( {}, $class ); | ||
78 | |||||
79 | 1786 | 1.23ms | $this->{_RAW} = $string; | ||
80 | |||||
81 | 1786 | 555µs | return $this unless defined($string); | ||
82 | |||||
83 | # Escapes | ||||
84 | 1636 | 847µs | $string =~ s/\\'/\x01/g; | ||
85 | 1636 | 411µs | $string =~ s/\\"/\x02/g; | ||
86 | |||||
87 | 1636 | 574µs | 43 | 912µs | if ($friendly) { # spent 912µs making 43 calls to Foswiki::Attrs::_friendly, avg 21µs/call |
88 | _friendly( $this, $string ); | ||||
89 | } | ||||
90 | else { | ||||
91 | 1593 | 1.70ms | 1593 | 13.1ms | _unfriendly( $this, $string ); # spent 13.1ms making 1593 calls to Foswiki::Attrs::_unfriendly, avg 8µs/call |
92 | } | ||||
93 | 1636 | 2.02ms | for ( values %$this ) { | ||
94 | 3660 | 1.02ms | s/\x01/'/g; | ||
95 | 3660 | 1.84ms | s/\x02/"/g; | ||
96 | } | ||||
97 | 1636 | 3.37ms | return $this; | ||
98 | } | ||||
99 | |||||
100 | # spent 13.1ms within Foswiki::Attrs::_unfriendly which was called 1593 times, avg 8µs/call:
# 1593 times (13.1ms+0s) by Foswiki::Attrs::new at line 91, avg 8µs/call | ||||
101 | 1593 | 683µs | my ( $this, $string ) = @_; | ||
102 | |||||
103 | 1593 | 327µs | my $first = 1; | ||
104 | |||||
105 | 1593 | 4.64ms | if ( $string =~ s/^\s*\"(.*?)\"\s*(?=[a-z0-9_]+\s*=\s*\"|$)//si ) { | ||
106 | $this->{_DEFAULT} = $1; | ||||
107 | } | ||||
108 | 1593 | 7.52ms | while ( $string =~ m/\S/s ) { | ||
109 | |||||
110 | # name="value" pairs | ||||
111 | 923 | 3.59ms | if ( $string =~ s/^\s*([a-z0-9_]+)\s*=\s*\"(.*?)\"//is ) { | ||
112 | 356 | 481µs | $this->{$1} = $2; | ||
113 | 356 | 82µs | $first = 0; | ||
114 | } | ||||
115 | |||||
116 | # simple double-quoted value with no name, sets the default | ||||
117 | elsif ( $string =~ s/^\s*\"(.*?)\"//s ) { | ||||
118 | 2 | 3µs | $this->{_DEFAULT} = $1 | ||
119 | unless defined( $this->{_DEFAULT} ); | ||||
120 | 2 | 800ns | $first = 0; | ||
121 | } | ||||
122 | |||||
123 | # Unquoted string not matching any recognised structure | ||||
124 | # SMELL: unchecked implicit untaint? | ||||
125 | elsif ( $string =~ m/^\s*(.*?)\s*$/s ) { | ||||
126 | 565 | 634µs | $this->{_DEFAULT} = $1 if ($first); | ||
127 | 565 | 1.49ms | last; | ||
128 | } | ||||
129 | } | ||||
130 | } | ||||
131 | |||||
132 | # spent 912µs within Foswiki::Attrs::_friendly which was called 43 times, avg 21µs/call:
# 43 times (912µs+0s) by Foswiki::Attrs::new at line 87, avg 21µs/call | ||||
133 | 43 | 23µs | my ( $this, $string ) = @_; | ||
134 | |||||
135 | 43 | 8µs | my $first = 1; | ||
136 | |||||
137 | 43 | 66µs | while ( $string =~ m/\S/s ) { | ||
138 | |||||
139 | # name="value" pairs | ||||
140 | 104 | 599µs | if ( $string =~ s/^[\s,]*([a-z0-9_]+)\s*=\s*\"(.*?)\"//is ) { | ||
141 | 56 | 79µs | $this->{$1} = $2; | ||
142 | 56 | 13µs | $first = 0; | ||
143 | } | ||||
144 | |||||
145 | # simple double-quoted value with no name, sets the default | ||||
146 | elsif ( $string =~ s/^[\s,]*\"(.*?)\"//s ) { | ||||
147 | 42 | 58µs | $this->{_DEFAULT} = $1 | ||
148 | unless defined( $this->{_DEFAULT} ); | ||||
149 | 42 | 13µs | $first = 0; | ||
150 | } | ||||
151 | |||||
152 | # name='value' pairs | ||||
153 | elsif ( $string =~ s/^[\s,]*([a-z0-9_]+)\s*=\s*'(.*?)'//is ) { | ||||
154 | $this->{$1} = $2; | ||||
155 | } | ||||
156 | |||||
157 | # name=value pairs | ||||
158 | elsif ( $string =~ s/^[\s,]*([a-z0-9_]+)\s*=\s*([^\s,\}\'\"]*)//is ) { | ||||
159 | $this->{$1} = $2; | ||||
160 | } | ||||
161 | |||||
162 | # simple single-quoted value with no name, sets the default | ||||
163 | elsif ( $string =~ s/^[\s,]*'(.*?)'//s ) { | ||||
164 | $this->{_DEFAULT} = $1 | ||||
165 | unless defined( $this->{_DEFAULT} ); | ||||
166 | } | ||||
167 | |||||
168 | # simple name with no value (boolean, or _DEFAULT) | ||||
169 | elsif ( $string =~ s/^[\s,]*([a-z][a-z0-9_]*)\b//is ) { | ||||
170 | my $key = $1; | ||||
171 | $this->{$key} = 1; | ||||
172 | } | ||||
173 | |||||
174 | # otherwise the whole string - sans padding - is the default | ||||
175 | else { | ||||
176 | |||||
177 | # SMELL: unchecked implicit untaint? | ||||
178 | if ( $string =~ m/^\s*(.*?)\s*$/s | ||||
179 | && !defined( $this->{_DEFAULT} ) ) | ||||
180 | { | ||||
181 | $this->{_DEFAULT} = $1; | ||||
182 | } | ||||
183 | last; | ||||
184 | } | ||||
185 | } | ||||
186 | 43 | 113µs | return $this; | ||
187 | } | ||||
188 | |||||
189 | =begin TML | ||||
190 | |||||
191 | ---++ ObjectMethod isEmpty() -> boolean | ||||
192 | |||||
193 | Return false if attribute set is not empty. | ||||
194 | |||||
195 | =cut | ||||
196 | |||||
197 | sub isEmpty { | ||||
198 | my $this = shift; | ||||
199 | |||||
200 | foreach my $k ( keys %$this ) { | ||||
201 | return 0 if $k ne '_RAW'; | ||||
202 | } | ||||
203 | return 1; | ||||
204 | } | ||||
205 | |||||
206 | =begin TML | ||||
207 | |||||
208 | ---++ ObjectMethod remove($key) -> $value | ||||
209 | |||||
210 | * =$key= - Attribute to remove | ||||
211 | Remove an attr value from the map, return old value. After a call to | ||||
212 | =remove= the attribute is no longer defined. | ||||
213 | |||||
214 | =cut | ||||
215 | |||||
216 | # spent 7.16ms within Foswiki::Attrs::remove which was called 3755 times, avg 2µs/call:
# 1075 times (2.51ms+0s) by Foswiki::Templates::readTemplate at line 324 of /var/www/foswikidev/core/lib/Foswiki/Templates.pm, avg 2µs/call
# 1075 times (1.92ms+0s) by Foswiki::Templates::readTemplate at line 325 of /var/www/foswikidev/core/lib/Foswiki/Templates.pm, avg 2µs/call
# 309 times (631µs+0s) by Foswiki::Templates::tmplP at line 163 of /var/www/foswikidev/core/lib/Foswiki/Templates.pm, avg 2µs/call
# 309 times (601µs+0s) by Foswiki::Templates::tmplP at line 162 of /var/www/foswikidev/core/lib/Foswiki/Templates.pm, avg 2µs/call
# 309 times (495µs+0s) by Foswiki::Templates::tmplP at line 164 of /var/www/foswikidev/core/lib/Foswiki/Templates.pm, avg 2µs/call
# 309 times (455µs+0s) by Foswiki::Templates::tmplP at line 165 of /var/www/foswikidev/core/lib/Foswiki/Templates.pm, avg 1µs/call
# 309 times (440µs+0s) by Foswiki::Templates::tmplP at line 166 of /var/www/foswikidev/core/lib/Foswiki/Templates.pm, avg 1µs/call
# 60 times (114µs+0s) by Foswiki::INCLUDE at line 374 of /var/www/foswikidev/core/lib/Foswiki/Macros/INCLUDE.pm, avg 2µs/call | ||||
217 | 3755 | 1.35ms | my ( $this, $attr ) = @_; | ||
218 | 3755 | 1.10ms | my $val = $this->{$attr}; | ||
219 | 3755 | 1.74ms | delete( $this->{$attr} ) if ( exists $this->{$attr} ); | ||
220 | 3755 | 11.3ms | return $val; | ||
221 | } | ||||
222 | |||||
223 | =begin TML | ||||
224 | |||||
225 | ---++ ObjectMethod stringify() -> $string | ||||
226 | |||||
227 | Generate a printed form for the map, using strict | ||||
228 | attribute syntax, with only the single-quote extension | ||||
229 | syntax observed (no {} brackets, though). | ||||
230 | |||||
231 | =cut | ||||
232 | |||||
233 | # spent 118µs within Foswiki::Attrs::stringify which was called 10 times, avg 12µs/call:
# 10 times (118µs+0s) by Foswiki::INCLUDE at line 369 of /var/www/foswikidev/core/lib/Foswiki/Macros/INCLUDE.pm, avg 12µs/call | ||||
234 | 10 | 3µs | my $this = shift; | ||
235 | 10 | 1µs | my $key; | ||
236 | 10 | 2µs | my @ss; | ||
237 | 10 | 35µs | foreach $key ( sort keys %$this ) { | ||
238 | 21 | 17µs | if ( $key ne '_ERROR' && $key ne '_RAW' ) { | ||
239 | 11 | 7µs | my $es = ( $key eq '_DEFAULT' ) ? '' : $key . '='; | ||
240 | 11 | 7µs | my $val = $this->{$key}; | ||
241 | 11 | 6µs | $val =~ s/"/\\"/g; | ||
242 | 11 | 17µs | push( @ss, $es . '"' . $val . '"' ); | ||
243 | } | ||||
244 | } | ||||
245 | 10 | 38µs | return join( ' ', @ss ); | ||
246 | } | ||||
247 | |||||
248 | =begin TML | ||||
249 | |||||
250 | ---++ ObjectMethod TO_JSON() -> \%map | ||||
251 | |||||
252 | Support for the JSON cpan module | ||||
253 | |||||
254 | =cut | ||||
255 | |||||
256 | sub TO_JSON { | ||||
257 | my $this = shift; | ||||
258 | my %res; | ||||
259 | while ( my ( $k, $v ) = each %$this ) { | ||||
260 | $res{$k} = $v; | ||||
261 | } | ||||
262 | return \%res; | ||||
263 | } | ||||
264 | |||||
265 | =begin TML | ||||
266 | |||||
267 | ---++ StaticMethod findFirstOccurenceAttrs($macro, $text) -> $args | ||||
268 | |||||
269 | Extract the first occurence of a macro from the text, taking into | ||||
270 | account balancing %{}%'s. For example, given $macro=X and $text="X%Y{%X{%X{}%}%}%Y" | ||||
271 | it will return "%X{}%". Given $text="%X%" it will return ''. Given "YYY" it | ||||
272 | will return undef, because neither "%X%" nor "%X{}%" occur. | ||||
273 | |||||
274 | =cut | ||||
275 | |||||
276 | sub findFirstOccurenceAttrs { | ||||
277 | my ( $macro, $text ) = @_; | ||||
278 | return undef unless $text =~ m/\%${macro}[%{]/s; | ||||
279 | my @queue = split( /(%[A-Za-z0-9_]*{|}%|\%${macro}\%)/, $text ); | ||||
280 | my $eat = 0; | ||||
281 | my $eaten = ''; | ||||
282 | while ( scalar(@queue) ) { | ||||
283 | my $token = shift @queue; | ||||
284 | if ($eat) { | ||||
285 | if ( $token =~ m/^%[A-Za-z0-9_]*{$/ ) { | ||||
286 | $eat++; | ||||
287 | } | ||||
288 | elsif ( $eat && $token eq '}%' ) { | ||||
289 | $eat--; | ||||
290 | return $eaten if ( !$eat ); | ||||
291 | } | ||||
292 | $eaten .= $token; | ||||
293 | } | ||||
294 | else { | ||||
295 | if ( $token eq "\%${macro}%" ) { | ||||
296 | return ''; | ||||
297 | } | ||||
298 | elsif ( $token eq "\%${macro}\{" ) { | ||||
299 | $eat = 1; | ||||
300 | } | ||||
301 | } | ||||
302 | } | ||||
303 | return ''; | ||||
304 | } | ||||
305 | |||||
306 | # ---++ StaticMethod extractValue() -> $string | ||||
307 | # | ||||
308 | # Legacy support, formerly known as extractNameValuePair. This | ||||
309 | # static method uses context information to determine how a value | ||||
310 | # string is to be parsed. For example, if you have an attribute string | ||||
311 | # like this: | ||||
312 | # | ||||
313 | # "abc def="ghi" jkl" def="qqq" | ||||
314 | # | ||||
315 | # then call extractValue( "def" ), it will return "ghi". | ||||
316 | |||||
317 | sub extractValue { | ||||
318 | my ( $str, $name ) = @_; | ||||
319 | |||||
320 | my $value = ''; | ||||
321 | return $value unless ($str); | ||||
322 | $str =~ s/\\\"/\\$MARKER/g; # escape \" | ||||
323 | |||||
324 | if ($name) { | ||||
325 | |||||
326 | # format is: %VAR{ ... name = "value" }% | ||||
327 | if ( $str =~ m/(^|[^\S])$name\s*=\s*\"([^\"]*)\"/ ) { | ||||
328 | $value = $2 if defined $2; # distinguish between '' and "0" | ||||
329 | } | ||||
330 | |||||
331 | } | ||||
332 | else { | ||||
333 | |||||
334 | # test if format: { "value" ... } | ||||
335 | # SMELL: unchecked implicit untaint? | ||||
336 | if ( $str =~ | ||||
337 | m/(^|\=\s*\"[^\"]*\")\s*\"(.*?)\"\s*([a-z0-9_]+\s*=\s*\"|$)/ ) | ||||
338 | { | ||||
339 | |||||
340 | # is: %VAR{ "value" }% | ||||
341 | # or: %VAR{ "value" param="etc" ... }% | ||||
342 | # or: %VAR{ ... = "..." "value" ... }% | ||||
343 | # Note: "value" may contain embedded double quotes | ||||
344 | $value = $2 if defined $2; # distinguish between '' and "0"; | ||||
345 | |||||
346 | } | ||||
347 | elsif ( ( $str =~ m/^\s*[a-z0-9_]+\s*=\s*\"([^\"]*)/ ) && ($1) ) { | ||||
348 | |||||
349 | # is: %VAR{ name = "value" }% | ||||
350 | # do nothing, is not a standalone var | ||||
351 | |||||
352 | } | ||||
353 | else { | ||||
354 | |||||
355 | # format is: %VAR{ value }% | ||||
356 | $value = $str; | ||||
357 | } | ||||
358 | } | ||||
359 | $value =~ s/\\$MARKER/\"/g; # resolve \" | ||||
360 | return $value; | ||||
361 | } | ||||
362 | |||||
363 | # ---++ ObjectMethod get($key) -> $value | ||||
364 | # | ||||
365 | # | $key | Attribute to get | | ||||
366 | # Get an attr value from the map. | ||||
367 | # | ||||
368 | # Synonymous with $attrs->{$key}. Retained mainly for compatibility with | ||||
369 | # the old AttrsContrib. | ||||
370 | sub get { | ||||
371 | my ( $this, $field ) = @_; | ||||
372 | return $this->{$field}; | ||||
373 | } | ||||
374 | |||||
375 | 1 | 3µs | 1; | ||
376 | __END__ |