Skip to content

Commit

Permalink
Item13594: Implement param+="Stuff" +"more stuff"
Browse files Browse the repository at this point in the history
  • Loading branch information
Jlevens committed Jul 31, 2015
1 parent 5bfbfd7 commit 9545209
Show file tree
Hide file tree
Showing 2 changed files with 192 additions and 24 deletions.
100 changes: 100 additions & 0 deletions UnitTestContrib/test/unit/AttrsTests.pm
Expand Up @@ -313,4 +313,104 @@ sub test_endsWithEscapedQuote {
$this->assert( $attrs->isEmpty() );
}

sub test_concat_friendly {
my $this = shift;

my $attrs =
Foswiki::Attrs->new( "var1=\"val1\" var2=\"val2\" var3 = \"3\" c=\"One\" +\" and \" d=\"interloper\" c+=two c+=\" and three\" ", 1 );

$this->assert_str_equals( "val1", $attrs->remove("var1") );
$this->assert_str_equals( "val2", $attrs->remove("var2") );
$this->assert_str_equals( "3", $attrs->remove("var3") );
$this->assert_str_equals( "One and two and three", $attrs->remove("c") );
$this->assert_str_equals( "interloper", $attrs->remove("d") );
$this->assert( $attrs->isEmpty() );
}


# Many _unfriendly test cases added here
# VVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV

sub test_isEmpty_unfriendly {
my $this = shift;

my $attrs = Foswiki::Attrs->new( undef );
$this->assert( $attrs->isEmpty() );
$attrs = Foswiki::Attrs->new( "" );
$this->assert( $attrs->isEmpty() );
$attrs = Foswiki::Attrs->new( " \t \n\t" );
$this->assert( $attrs->isEmpty() );
}

sub test_1st_is_default_unfriendly {
my $this = shift;

my $attrs = Foswiki::Attrs->new( "\"wibble\"" );
$this->assert( !$attrs->isEmpty() );
$this->assert_str_equals( "wibble", $attrs->remove("_DEFAULT") );
$this->assert_null( $attrs->{"_DEFAULT"} );
$this->assert( $attrs->isEmpty() );

# Note difference with the _friendly version of this
$attrs = Foswiki::Attrs->new( "\"wibble\" \"fleegle\"" );
$this->assert_str_equals( "wibble\" \"fleegle", $attrs->remove("_DEFAULT") );
$this->assert( $attrs->isEmpty() );
}

sub test_1st_is_default_assign_follows_unfriendly {
my $this = shift;

my $attrs = Foswiki::Attrs->new( "\"wibble\" a=\"fleegle\"" );
$this->assert_str_equals( "wibble", $attrs->remove("_DEFAULT") );
$this->assert_str_equals( "fleegle", $attrs->remove("a") );
$this->assert( $attrs->isEmpty() );
}

sub test_1st_is_default_concat_follows_unfriendly {
my $this = shift;

my $attrs = Foswiki::Attrs->new( "\"wibble\" a+=\"fleegle\"" );
$this->assert_str_equals( "wibble", $attrs->remove("_DEFAULT") );
$this->assert_str_equals( "fleegle", $attrs->remove("a") );
$this->assert( $attrs->isEmpty() );
}

sub test_concat_unfriendly {
my $this = shift;

my $attrs =
Foswiki::Attrs->new( "var1=\"val1\" var2=\"val2\" var3 = \"3\" c=\"One\" +\" and \" d=\"interloper\" c+=\"two\" c+=\" and three\" " );

$this->assert_str_equals( "val1", $attrs->remove("var1") );
$this->assert_str_equals( "val2", $attrs->remove("var2") );
$this->assert_str_equals( "3", $attrs->remove("var3") );
$this->assert_str_equals( "One and two and three", $attrs->remove("c") );
$this->assert_str_equals( "interloper", $attrs->remove("d") );
$this->assert( $attrs->isEmpty() );
}

sub test_doubleQuoted_unfriendly {
my $this = shift;

my $attrs = Foswiki::Attrs->new(
"var1 =\"val 1\" var2= \"val 2\" \" default \" var3 = \" val 3 \"" );
$this->assert_str_equals( "val 1", $attrs->remove("var1") );
$this->assert_str_equals( "val 2", $attrs->remove("var2") );
$this->assert_str_equals( " val 3 ", $attrs->remove("var3") );
$this->assert_str_equals( " default ", $attrs->remove("_DEFAULT") );
$this->assert( $attrs->isEmpty() );
}

sub test_toString_unfriendly {
my $this = shift;

my $attrs = Foswiki::Attrs->new( "a =\"\\\"\" b=\"'\" \"'\" " );
my $s = $attrs->stringify();
$attrs = Foswiki::Attrs->new( $attrs->stringify() );
$this->assert_str_equals( "\"", $attrs->remove("a") );
$this->assert_str_equals( "'", $attrs->remove("b") );
$this->assert_str_equals( "'", $attrs->remove("_DEFAULT") );
$this->assert( $attrs->isEmpty() );
}

1;
116 changes: 92 additions & 24 deletions core/lib/Foswiki/Attrs.pm
Expand Up @@ -97,26 +97,76 @@ sub new {
return $this;
}

sub _assign { ${$_[0]} = $_[1]; }
sub _append { ${$_[0]} .= $_[1]; }

# Kept as potential new FeatureProposal:
# sub _prepend { ${$_[0]} = $_[1] . ${$_[0]}; }

# Perl warned about undefined $_[1] (and the sub was buggy)
# hence 'my ($sr, $v) = @_;' which fixed it
sub _multi { my ($sr, $v) = @_; ${$sr} x= $v if $v =~ /[0-9]+/; }

my %Ops = (
# Op => [ WithName , Without Name ]
'=' => [ \&_assign , undef ] ,

'+=' => [ \&_append , undef ] ,
'+' => [ undef , \&_append ] ,

# Kept as potential FeatureProposal
# The community will also need to agree operators to use
# '-=' => [ \&_prepend , undef ] ,
# '-' => [ undef , \&_prepend ] ,

);
sub OpRegex {
my ($opType) = @_;
return join( '|', map { quotemeta($_); }
grep { $Ops{$_}->[ $opType ]; }

# We need to reverse sort to ensure that in cases when we have two
# synonymous ops (e.g. '+=' & '=') then we always handle += first
# (remember random hash order otherwise)
# This ensures that in the _friendly case parm=56 +=78 always give
# parm as '5678' and not '56=78'
reverse sort { length($a) <=> length($b) }
keys %Ops);
}

my $nameOp = OpRegex(0);
my $nonmOp = OpRegex(1);

sub _unfriendly {
my ( $this, $string ) = @_;

my $key = '_DEFAULT';
my $first = 1;

if ( $string =~ s/^\s*\"(.*?)\"\s*(?=[a-z0-9_]+\s*=\s*\"|$)//si ) {
if ( $string =~ s/^\s*\"(.*?)\"\s*(?=([a-z0-9_]+\s*(?:$nameOp)|[a-z0-9_]*\s*(?:$nonmOp))\s*\"|$)//is ) {
$this->{_DEFAULT} = $1;
}

while ( $string =~ m/\S/s ) {
# name $op "value" pairs
if ( $string =~ s/^\s*([a-z0-9_]+)\s*($nameOp)\s*\"(.*?)\"//is ) {
$key = $1;
$Ops{$2}[0](\$this->{$key}, $3);
$first = 0;
}

# name="value" pairs
if ( $string =~ s/^\s*([a-z0-9_]+)\s*=\s*\"(.*?)\"//is ) {
$this->{$1} = $2;
# $op "value" (the name (or key) is the most recent one seen)
elsif( $string =~ s/^\s*($nonmOp)\s*\"(.*?)\"//is ) {
$Ops{$1}[1](\$this->{$key}, $2);
$first = 0;
}

# simple double-quoted value with no name, sets the default
elsif ( $string =~ s/^\s*\"(.*?)\"//s ) {
$this->{_DEFAULT} = $1
unless defined( $this->{_DEFAULT} );
unless( defined( $this->{_DEFAULT} ) ) {
$key = '_DEFAULT';
$this->{_DEFAULT} = $1;
}
$first = 0;
}

Expand All @@ -132,43 +182,61 @@ sub _unfriendly {
sub _friendly {
my ( $this, $string ) = @_;

my $first = 1;
my $key = "_DEFAULT";

while ( $string =~ m/\S/s ) {

# name="value" pairs
if ( $string =~ s/^[\s,]*([a-z0-9_]+)\s*=\s*\"(.*?)\"//is ) {
$this->{$1} = $2;
$first = 0;
# name $op "value" pairs
if ( $string =~ s/^[\s,]*([a-z0-9_]+)\s*($nameOp)\s*\"(.*?)\"//is ) {
$key = $1;
$Ops{$2}[0](\$this->{$key}, $3);
}

# simple double-quoted value with no name, sets the default
elsif ( $string =~ s/^[\s,]*\"(.*?)\"//s ) {
$this->{_DEFAULT} = $1
unless defined( $this->{_DEFAULT} );
$first = 0;
unless( defined( $this->{_DEFAULT} ) ) {
$key = '_DEFAULT';
$this->{_DEFAULT} = $1;
}
}

# name $op 'value' pairs
elsif ( $string =~ s/^[\s,]*([a-z0-9_]+)\s*($nameOp)\s*'(.*?)'//is ) {
$key = $1;
$Ops{$2}[0](\$this->{$key}, $3);
}

# name='value' pairs
elsif ( $string =~ s/^[\s,]*([a-z0-9_]+)\s*=\s*'(.*?)'//is ) {
$this->{$1} = $2;
# name $op value pairs
elsif ( $string =~ s/^[\s,]*([a-z0-9_]+)\s*($nameOp)\s*([^\s,\}\'\"]*)//is ) {
$key = $1;
$Ops{$2}[0](\$this->{$key}, $3);
}

# name=value pairs
elsif ( $string =~ s/^[\s,]*([a-z0-9_]+)\s*=\s*([^\s,\}\'\"]*)//is ) {
$this->{$1} = $2;
# $op "value"
elsif ( $string =~ s/^[\s,]*($nonmOp)\s*\"(.*?)\"//is ) {
$Ops{$1}[1](\$this->{$key}, $2);
}
# $op 'value'
elsif ( $string =~ s/^[\s,]*($nonmOp)\s*'(.*?)'//is ) {
$Ops{$1}[1](\$this->{$key}, $2);
}
# $op value
elsif ( $string =~ s/^[\s,]*($nonmOp)\s*([^\s,\}\'\"]*)//is ) {
$Ops{$1}[1](\$this->{$key}, $2);
}


# simple single-quoted value with no name, sets the default
elsif ( $string =~ s/^[\s,]*'(.*?)'//s ) {
$this->{_DEFAULT} = $1
unless defined( $this->{_DEFAULT} );
unless( defined( $this->{_DEFAULT} ) ) {
$key = '_DEFAULT';
$this->{_DEFAULT} = $1;
}
}

# simple name with no value (boolean, or _DEFAULT)
elsif ( $string =~ s/^[\s,]*([a-z][a-z0-9_]*)\b//is ) {
my $key = $1;
$this->{$key} = 1;
$this->{$1} = 1;
}

# otherwise the whole string - sans padding - is the default
Expand Down

0 comments on commit 9545209

Please sign in to comment.