1 # Module of TWiki Enterprise Collaboration Platform, http://TWiki.org/
3 # Copyright (C) 1999-2007 TWiki Contributors. All Rights Reserved.
4 # TWiki Contributors are listed in the AUTHORS file in the root of
5 # this distribution. NOTE: Please extend that file, not this notice.
7 # Additional copyrights apply to some or all of the code in this
9 # Derived from Contrib::Attrs, which is
10 # Copyright (C) 2001 Motorola - All rights reserved
12 # This program is free software; you can redistribute it and/or
13 # modify it under the terms of the GNU General Public License
14 # as published by the Free Software Foundation; either version 2
15 # of the License, or (at your option) any later version. For
16 # more details read LICENSE in the root of this distribution.
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
22 # As per the GPL, removal of this notice is prohibited.
26 ---+ package TWiki::Attrs
28 Class of attribute sets, designed for parsing and storing attribute values
29 from a TWiki tag e.g. =%<nop>TAG{"joe" fred="bad" joe="mad"}%=
31 An attribute set is a hash containing an entry for each parameter. The
32 default parameter (unnamed quoted string) is named <code>_<nop>DEFAULT</code> in the hash.
34 Attributes declared later in the string will override those of the same
35 name defined earlier. The one exception to this is the _DEFAULT key, where
36 the _first_ instance is always taken.
38 As well as the default TWiki syntax (parameter values double-quoted)
39 this class also parses single-quoted values, unquoted spaceless
40 values, spaces around the =, and commas as well as spaces separating values.
41 The extended syntax has to be enabled by passing the =$friendly= parameter
51 use vars qw( $ERRORKEY $DEFAULTKEY $RAWKEY );
54 $DEFAULTKEY = '_DEFAULT';
59 ---++ ClassMethod new ($string, $friendly) => \%attrsObjectRef
61 * =$string= - String containing attribute specification
62 * =$friendly= - if true, the parse will be according to the extended syntax pioneered by the original Contrib::Attrs. Otherwise it will be strict as per traditional TWiki syntax.
64 Parse a standard attribute string containing name=value pairs and create a new
65 attributes object. The value may be a word or a quoted string. If there is an
66 error during parsing, the parse will complete but $attrs->{_ERROR} will be
67 set in the new object. $attrs->{_RAW} will always contain the full unprocessed
70 Extended syntax example:
72 my $attrs = new TWiki::Attrs('the="time \\"has come", "the walrus" said to=speak of=\'many \\'things\', 1);
75 * =the= will be =time "has come=
76 * <code>_<nop>_<nop>default__</code> will be =the walrus=
78 * =to= will be =speak=
79 * =of= will be =many 'things=
81 Only " and ' are escaped.
83 Traditional syntax is as old TWiki, except that the whole string is parsed
84 (the old parser would only recognise default values in position 1, nowhere
90 my ( $class, $string, $friendly ) = @_;
91 my $this = bless( {}, $class );
93 $this->{$RAWKEY} = $string;
95 return $this unless defined( $string );
97 $string =~ s/\\(["'])/$TWiki::TranslationToken.sprintf("%.2u", ord($1))/ge; # escapes
99 my $sep = ( $friendly ? "[\\s,]" : "\\s" );
102 if( !$friendly && $string =~ s/^\s*\"(.*?)\"\s*(\w+\s*=\s*\"|$)/$2/s ) {
103 $this->{$DEFAULTKEY} = $1;
105 while ( $string =~ m/\S/s ) {
107 if ( $string =~ s/^$sep*(\w+)\s*=\s*\"(.*?)\"//is ) {
111 # simple double-quoted value with no name, sets the default
112 elsif ( $string =~ s/^$sep*\"(.*?)\"//os ) {
113 $this->{$DEFAULTKEY} = $1
114 unless defined( $this->{$DEFAULTKEY} );
117 elsif ( $friendly ) {
119 if ( $string =~ s/^$sep*(\w+)\s*=\s*'(.*?)'//is ) {
123 elsif ( $string =~ s/^$sep*(\w+)\s*=\s*([^\s,\}\'\"]*)//is ) {
126 # simple single-quoted value with no name, sets the default
127 elsif ( $string =~ s/^$sep*'(.*?)'//os ) {
128 $this->{$DEFAULTKEY} = $1
129 unless defined( $this->{$DEFAULTKEY} );
131 # simple name with no value (boolean, or _DEFAULT)
132 elsif ( $string =~ s/^$sep*([a-z]\w*)\b//is ) {
136 # otherwise the whole string - sans padding - is the default
138 if( $string =~ m/^\s*(.*?)\s*$/s &&
139 !defined($this->{$DEFAULTKEY})) {
140 $this->{$DEFAULTKEY} = $1;
144 } elsif( $string =~ m/^\s*(.*?)\s*$/s ) {
145 $this->{$DEFAULTKEY} = $1 if( $first );
149 foreach my $k ( keys %$this ) {
150 $this->{$k} =~ s/$TWiki::TranslationToken(\d\d)/chr($1)/geo; # escapes
157 ---++ ObjectMethod isEmpty() -> boolean
159 Return false if attribute set is not empty.
167 foreach my $k ( keys %$this ) {
168 return 0 if $k ne $RAWKEY;
175 ---++ ObjectMethod remove($key) -> $value
177 * =$key= - Attribute to remove
178 Remove an attr value from the map, return old value. After a call to
179 =remove= the attribute is no longer defined.
184 my ( $this, $attr ) = @_;
185 my $val = $this->{$attr};
186 delete( $this->{$attr} ) if ( exists $this->{$attr} );
192 ---++ ObjectMethod stringify() -> $string
194 Generate a printed form for the map, using strict
195 attribute syntax, with only the single-quote extension
196 syntax observed (no {} brackets, though).
204 foreach $key ( sort keys %$this ) {
205 if ( $key ne $ERRORKEY && $key ne $RAWKEY ) {
206 my $es = ( $key eq $DEFAULTKEY ) ? '' : $key.'=';
207 my $val = $this->{$key};
209 push( @ss, $es.'"'.$val.'"' );
212 return join( ' ', @ss );
216 # ---++ StaticMethod extractValue() -> $string
218 # Legacy support, formerly known as extractNameValuePair. This
219 # static method uses context information to determine how a value
220 # string is to be parsed. For example, if you have an attribute string
223 # "abc def="ghi" jkl" def="qqq"
225 # then call extractValue( "def" ), it will return "ghi".
228 my( $str, $name ) = @_;
231 return $value unless( $str );
232 $str =~ s/\\\"/\\$TWiki::TranslationToken/g; # escape \"
235 # format is: %VAR{ ... name = "value" }%
236 if( $str =~ /(^|[^\S])$name\s*=\s*\"([^\"]*)\"/ ) {
237 $value = $2 if defined $2; # distinguish between '' and "0"
241 # test if format: { "value" ... }
242 if( $str =~ /(^|\=\s*\"[^\"]*\")\s*\"(.*?)\"\s*(\w+\s*=\s*\"|$)/ ) {
243 # is: %VAR{ "value" }%
244 # or: %VAR{ "value" param="etc" ... }%
245 # or: %VAR{ ... = "..." "value" ... }%
246 # Note: "value" may contain embedded double quotes
247 $value = $2 if defined $2; # distinguish between '' and "0";
249 } elsif( ( $str =~ /^\s*\w+\s*=\s*\"([^\"]*)/ ) && ( $1 ) ) {
250 # is: %VAR{ name = "value" }%
251 # do nothing, is not a standalone var
254 # format is: %VAR{ value }%
258 $value =~ s/\\$TWiki::TranslationToken/\"/go; # resolve \"
262 # ---++ ObjectMethod get($key) -> $value
264 # | $key | Attribute to get |
265 # Get an attr value from the map.
267 # Synonymous with $attrs->{$key}. Retained mainly for compatibility with
268 # the old AttrsContrib.
270 my( $this, $field) = @_;
271 return $this->{$field};