From 2976eff4932fafe67e76414a32119f84aab64f79 Mon Sep 17 00:00:00 2001 From: Gabriel Belingueres Date: Sat, 9 Jul 2016 17:58:19 -0300 Subject: [PATCH 1/3] Fixed MultiDelimiterStringSearchInterpolator escape String code --- ...MultiDelimiterStringSearchInterpolator.java | 5 ++++- ...iDelimiterStringSearchInterpolatorTest.java | 18 ++++++++++++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/codehaus/plexus/interpolation/multi/MultiDelimiterStringSearchInterpolator.java b/src/main/java/org/codehaus/plexus/interpolation/multi/MultiDelimiterStringSearchInterpolator.java index 69d979d..3351fb1 100644 --- a/src/main/java/org/codehaus/plexus/interpolation/multi/MultiDelimiterStringSearchInterpolator.java +++ b/src/main/java/org/codehaus/plexus/interpolation/multi/MultiDelimiterStringSearchInterpolator.java @@ -201,13 +201,16 @@ private String interpolate( String input, RecursionInterceptor recursionIntercep if ( startIdx >= 0 && escapeString != null && escapeString.length() > 0 ) { - int startEscapeIdx = startIdx == 0 ? 0 : startIdx - escapeString.length(); + int startEscapeIdx = (startIdx == 0) ? 0 : startIdx - escapeString.length(); if ( startEscapeIdx >= 0 ) { String escape = input.substring( startEscapeIdx, startIdx ); if ( escape != null && escapeString.equals( escape ) ) { result.append( wholeExpr ); + if (startEscapeIdx > 0) { + --startEscapeIdx; + } result.replace( startEscapeIdx, startEscapeIdx + escapeString.length(), "" ); continue; } diff --git a/src/test/java/org/codehaus/plexus/interpolation/multi/MultiDelimiterStringSearchInterpolatorTest.java b/src/test/java/org/codehaus/plexus/interpolation/multi/MultiDelimiterStringSearchInterpolatorTest.java index d645785..d3994cc 100644 --- a/src/test/java/org/codehaus/plexus/interpolation/multi/MultiDelimiterStringSearchInterpolatorTest.java +++ b/src/test/java/org/codehaus/plexus/interpolation/multi/MultiDelimiterStringSearchInterpolatorTest.java @@ -85,4 +85,22 @@ public void testInterpolationWithMultipleEscapes() assertEquals( "#${first} and ${last}", result ); } + public void testInterpolationWithMultipleEscapes2() + throws InterpolationException + { + Map ctx = new HashMap(); + ctx.put( "name", "User" ); + ctx.put( "otherName", "#${first} and ##${last}" ); + + String input = "${otherName}"; + + ValueSource vs = new MapBasedValueSource( ctx ); + MultiDelimiterStringSearchInterpolator interpolator = new MultiDelimiterStringSearchInterpolator() + .withValueSource( vs ); + interpolator.setEscapeString("#"); + + String result = interpolator.interpolate( input ); + + assertEquals( "${first} and #${last}", result ); + } } From a3937b2e0c283ec9747ee78eeabe6af5e8bb81e0 Mon Sep 17 00:00:00 2001 From: Gabriel Belingueres Date: Sat, 9 Jul 2016 18:32:35 -0300 Subject: [PATCH 2/3] Modified ReflectionValueExtractor to a more up to date version ReflectionValueExtractor version with capacity to parse expressions with arrays, lists and maps. --- .../reflection/ReflectionValueExtractor.java | 304 +++++++++++++++--- .../StringSearchInterpolatorTest.java | 49 ++- .../FixedStringSearchInterpolatorTest.java | 50 ++- 3 files changed, 349 insertions(+), 54 deletions(-) diff --git a/src/main/java/org/codehaus/plexus/interpolation/reflection/ReflectionValueExtractor.java b/src/main/java/org/codehaus/plexus/interpolation/reflection/ReflectionValueExtractor.java index bca759d..dd3e2b5 100644 --- a/src/main/java/org/codehaus/plexus/interpolation/reflection/ReflectionValueExtractor.java +++ b/src/main/java/org/codehaus/plexus/interpolation/reflection/ReflectionValueExtractor.java @@ -1,5 +1,13 @@ package org.codehaus.plexus.interpolation.reflection; +import java.lang.ref.WeakReference; +import java.lang.reflect.Array; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.List; +import java.util.Map; +import java.util.WeakHashMap; + /* * Copyright 2001-2006 Codehaus Foundation. * @@ -18,13 +26,6 @@ import org.codehaus.plexus.interpolation.util.StringUtils; -import java.lang.ref.SoftReference; -import java.lang.ref.WeakReference; -import java.lang.reflect.Method; -import java.util.Map; -import java.util.StringTokenizer; -import java.util.WeakHashMap; - /** * NOTE: This class was copied from plexus-utils, to allow this library * to stand completely self-contained. @@ -43,32 +44,142 @@ public class ReflectionValueExtractor private static final Object[] OBJECT_ARGS = new Object[0]; /** - * Use a WeakHashMap here, so the keys (Class objects) can be garbage collected. - * This approach prevents permgen space overflows due to retention of discarded - * classloaders. + * Use a WeakHashMap here, so the keys (Class objects) can be garbage collected. This approach prevents permgen + * space overflows due to retention of discarded classloaders. */ - private static final Map, WeakReference> classMaps = new WeakHashMap, WeakReference>(); + private static final Map, WeakReference> classMaps = + new WeakHashMap, WeakReference>(); + + static final int EOF = -1; + + static final char PROPERTY_START = '.'; + + static final char INDEXED_START = '['; + + static final char INDEXED_END = ']'; + + static final char MAPPED_START = '('; + + static final char MAPPED_END = ')'; + + static class Tokenizer + { + final String expression; + + int idx; + + public Tokenizer( String expression ) + { + this.expression = expression; + } + + public int peekChar() + { + return idx < expression.length() ? expression.charAt( idx ) : EOF; + } + + public int skipChar() + { + return idx < expression.length() ? expression.charAt( idx++ ) : EOF; + } + + public String nextToken( char delimiter ) + { + int start = idx; + + while ( idx < expression.length() && delimiter != expression.charAt( idx ) ) + { + idx++; + } + + // delimiter MUST be present + if ( idx <= start || idx >= expression.length() ) + { + return null; + } + + return expression.substring( start, idx++ ); + } + + public String nextPropertyName() + { + final int start = idx; + + while ( idx < expression.length() && Character.isJavaIdentifierPart( expression.charAt( idx ) ) ) + { + idx++; + } + + // property name does not require delimiter + if ( idx <= start || idx > expression.length() ) + { + return null; + } + + return expression.substring( start, idx ); + } + + public int getPosition() + { + return idx < expression.length() ? idx : EOF; + } + + // to make tokenizer look pretty in debugger + @Override + public String toString() + { + return idx < expression.length() ? expression.substring( idx ) : ""; + } + } private ReflectionValueExtractor() { } + /** + *

+ * The implementation supports indexed, nested and mapped properties. + *

+ *
    + *
  • nested properties should be defined by a dot, i.e. "user.address.street"
  • + *
  • indexed properties (java.util.List or array instance) should be contains (\\w+)\\[(\\d+)\\] + * pattern, i.e. "user.addresses[1].street"
  • + *
  • mapped properties should be contains (\\w+)\\((.+)\\) pattern, i.e. + * "user.addresses(myAddress).street"
  • + *
      + * + * @param expression not null expression + * @param root not null object + * @return the object defined by the expression + * @throws Exception if any + */ public static Object evaluate( String expression, Object root ) throws Exception { return evaluate( expression, root, true ); } + /** + *

      + * The implementation supports indexed, nested and mapped properties. + *

      + *
        + *
      • nested properties should be defined by a dot, i.e. "user.address.street"
      • + *
      • indexed properties (java.util.List or array instance) should be contains (\\w+)\\[(\\d+)\\] + * pattern, i.e. "user.addresses[1].street"
      • + *
      • mapped properties should be contains (\\w+)\\((.+)\\) pattern, i.e. + * "user.addresses(myAddress).street"
      • + *
          + * + * @param expression not null expression + * @param root not null object + * @return the object defined by the expression + * @throws Exception if any + */ // TODO: don't throw Exception - public static Object evaluate( String expression, Object root, boolean trimRootToken ) + public static Object evaluate( String expression, final Object root, final boolean trimRootToken ) throws Exception { - // if the root token refers to the supplied root object parameter, remove it. - if ( trimRootToken ) - { - expression = expression.substring( expression.indexOf( '.' ) + 1 ); - } - Object value = root; // ---------------------------------------------------------------------- @@ -76,57 +187,174 @@ public static Object evaluate( String expression, Object root, boolean trimRootT // MavenProject instance. // ---------------------------------------------------------------------- - StringTokenizer parser = new StringTokenizer( expression, "." ); - - while ( parser.hasMoreTokens() ) + if ( expression == null || "".equals(expression.trim()) || !Character.isJavaIdentifierStart( expression.charAt( 0 ) ) ) { - String token = parser.nextToken(); + return null; + } - if ( value == null ) + boolean hasDots = expression.indexOf( PROPERTY_START ) >= 0; + + final Tokenizer tokenizer; + if ( trimRootToken && hasDots ) + { + tokenizer = new Tokenizer( expression ); + tokenizer.nextPropertyName(); + if ( tokenizer.getPosition() == EOF ) { return null; } + } + else + { + tokenizer = new Tokenizer( "." + expression ); + } + + int propertyPosition = tokenizer.getPosition(); + while ( value != null && tokenizer.peekChar() != EOF ) + { + switch ( tokenizer.skipChar() ) + { + case INDEXED_START: + value = + getIndexedValue( expression, propertyPosition, tokenizer.getPosition(), value, + tokenizer.nextToken( INDEXED_END ) ); + break; + case MAPPED_START: + value = + getMappedValue( expression, propertyPosition, tokenizer.getPosition(), value, + tokenizer.nextToken( MAPPED_END ) ); + break; + case PROPERTY_START: + propertyPosition = tokenizer.getPosition(); + value = getPropertyValue( value, tokenizer.nextPropertyName() ); + break; + default: + // could not parse expression + return null; + } + } + + return value; + } + private static Object getMappedValue( final String expression, final int from, final int to, final Object value, + final String key ) + throws Exception + { + if ( value == null || key == null ) + { + return null; + } + + if ( value instanceof Map ) + { + Object[] localParams = new Object[] { key }; ClassMap classMap = getClassMap( value.getClass() ); + Method method = classMap.findMethod( "get", localParams ); + return method.invoke( value, localParams ); + } - String methodBase = StringUtils.capitalizeFirstLetter( token ); + final String message = + String.format( "The token '%s' at position '%d' refers to a java.util.Map, but the value seems is an instance of '%s'", + expression.subSequence( from, to ), from, value.getClass() ); - String methodName = "get" + methodBase; + throw new Exception( message ); + } - Method method = classMap.findMethod( methodName, CLASS_ARGS ); + private static Object getIndexedValue( final String expression, final int from, final int to, final Object value, + final String indexStr ) + throws Exception + { + try + { + int index = Integer.parseInt( indexStr ); - if ( method == null ) + if ( value.getClass().isArray() ) { - // perhaps this is a boolean property?? - methodName = "is" + methodBase; - - method = classMap.findMethod( methodName, CLASS_ARGS ); + return Array.get( value, index ); } - if ( method == null ) + if ( value instanceof List ) + { + ClassMap classMap = getClassMap( value.getClass() ); + // use get method on List interface + Object[] localParams = new Object[] { index }; + Method method = classMap.findMethod( "get", localParams ); + return method.invoke( value, localParams ); + } + } + catch ( NumberFormatException e ) + { + return null; + } + catch ( InvocationTargetException e ) + { + // catch array index issues gracefully, otherwise release + if ( e.getCause() instanceof IndexOutOfBoundsException ) { return null; } - value = method.invoke( value, OBJECT_ARGS ); + throw e; } - return value; + final String message = + String.format( "The token '%s' at position '%d' refers to a java.util.List or an array, but the value seems is an instance of '%s'", + expression.subSequence( from, to ), from, value.getClass() ); + + throw new Exception( message ); + } + + private static Object getPropertyValue( Object value, String property ) + throws Exception + { + if ( value == null || property == null ) + { + return null; + } + + ClassMap classMap = getClassMap( value.getClass() ); + String methodBase = StringUtils.capitalizeFirstLetter( property ); + String methodName = "get" + methodBase; + Method method = classMap.findMethod( methodName, CLASS_ARGS ); + + if ( method == null ) + { + // perhaps this is a boolean property?? + methodName = "is" + methodBase; + + method = classMap.findMethod( methodName, CLASS_ARGS ); + } + + if ( method == null ) + { + return null; + } + + try + { + return method.invoke( value, OBJECT_ARGS ); + } + catch ( InvocationTargetException e ) + { + throw e; + } } private static ClassMap getClassMap( Class clazz ) { - WeakReference ref = classMaps.get( clazz); + + WeakReference softRef = classMaps.get( clazz ); ClassMap classMap; - if ( ref == null || (classMap = ref.get()) == null ) + if ( softRef == null || ( classMap = softRef.get() ) == null ) { classMap = new ClassMap( clazz ); - classMaps.put( clazz, new WeakReference(classMap) ); + classMaps.put( clazz, new WeakReference( classMap ) ); } return classMap; } -} +} \ No newline at end of file diff --git a/src/test/java/org/codehaus/plexus/interpolation/StringSearchInterpolatorTest.java b/src/test/java/org/codehaus/plexus/interpolation/StringSearchInterpolatorTest.java index 79312cf..c4b84a0 100644 --- a/src/test/java/org/codehaus/plexus/interpolation/StringSearchInterpolatorTest.java +++ b/src/test/java/org/codehaus/plexus/interpolation/StringSearchInterpolatorTest.java @@ -17,6 +17,7 @@ */ import java.io.IOException; +import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; @@ -147,15 +148,15 @@ public void testShouldFailOnExpressionCycle() } } - public void testShouldResolveByMy_getVar_Method() - throws InterpolationException - { - StringSearchInterpolator rbi = new StringSearchInterpolator(); - rbi.addValueSource( new ObjectBasedValueSource( this ) ); - String result = rbi.interpolate( "this is a ${var}" ); + public void testShouldResolveByUsingObject_List_Map() + throws InterpolationException + { + StringSearchInterpolator rbi = new StringSearchInterpolator(); + rbi.addValueSource( new ObjectBasedValueSource( this ) ); + String result = rbi.interpolate( "this is a ${var} ${list[1].name} ${anArray[2].name} ${map(Key with spaces).name}" ); - assertEquals( "this is a testVar", result ); - } + assertEquals( "this is a testVar testIndexedWithList testIndexedWithArray testMap", result ); + } public void testShouldResolveByContextValue() throws InterpolationException @@ -429,4 +430,36 @@ public String getVar() return "testVar"; } + public Person[] getAnArray() { + Person[] array = new Person[3]; + array[0] = new Person("Gabriel"); + array[1] = new Person("Daniela"); + array[2] = new Person("testIndexedWithArray"); + return array; + } + + public List getList() { + List list = new ArrayList(); + list.add(new Person("Gabriel")); + list.add(new Person("testIndexedWithList")); + list.add(new Person("Daniela")); + return list; + } + + public Map getMap() { + Map map = new HashMap(); + map.put("Key with spaces", new Person("testMap")); + return map; + } + + public static class Person { + private String name; + public Person(String name) { + this.name = name; + } + public String getName() { + return name; + } + } + } diff --git a/src/test/java/org/codehaus/plexus/interpolation/fixed/FixedStringSearchInterpolatorTest.java b/src/test/java/org/codehaus/plexus/interpolation/fixed/FixedStringSearchInterpolatorTest.java index 074faaf..8bff5d7 100644 --- a/src/test/java/org/codehaus/plexus/interpolation/fixed/FixedStringSearchInterpolatorTest.java +++ b/src/test/java/org/codehaus/plexus/interpolation/fixed/FixedStringSearchInterpolatorTest.java @@ -22,7 +22,9 @@ import org.junit.Test; import java.io.IOException; +import java.util.ArrayList; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.Properties; @@ -148,15 +150,15 @@ public void testShouldFailOnExpressionCycle() } @Test - public void testShouldResolveByMy_getVar_Method() - throws InterpolationException - { - FixedStringSearchInterpolator rbi = create( new ObjectBasedValueSource( this ) ); - String result = rbi.interpolate( "this is a ${var}" ); - - assertEquals( "this is a testVar", result ); - } + public void testShouldResolveByUsingObject_List_Map() + throws InterpolationException + { + FixedStringSearchInterpolator rbi = create( new ObjectBasedValueSource( this ) ); + String result = rbi.interpolate( "this is a ${var} ${list[1].name} ${anArray[2].name} ${map(Key with spaces).name}" ); + assertEquals( "this is a testVar testIndexedWithList testIndexedWithArray testMap", result ); + } + @Test public void testShouldResolveByContextValue() throws InterpolationException @@ -424,6 +426,38 @@ public String getVar() return "testVar"; } + public Person[] getAnArray() { + Person[] array = new Person[3]; + array[0] = new Person("Gabriel"); + array[1] = new Person("Daniela"); + array[2] = new Person("testIndexedWithArray"); + return array; + } + + public List getList() { + List list = new ArrayList(); + list.add(new Person("Gabriel")); + list.add(new Person("testIndexedWithList")); + list.add(new Person("Daniela")); + return list; + } + + public Map getMap() { + Map map = new HashMap(); + map.put("Key with spaces", new Person("testMap")); + return map; + } + + public static class Person { + private String name; + public Person(String name) { + this.name = name; + } + public String getName() { + return name; + } + } + @Test public void testLinkedInterpolators() { From 4d088ded0160cfed01558f3616c16d7b26f33dbc Mon Sep 17 00:00:00 2001 From: belingueres Date: Fri, 12 Aug 2016 19:45:11 -0300 Subject: [PATCH 3/3] Commit to undo the changes I've made since forked the original project. The reason is that I've made a mistake and created a unique PR to solve several issues, which is a bad practice. --- ...ultiDelimiterStringSearchInterpolator.java | 5 +- .../reflection/ReflectionValueExtractor.java | 304 +++--------------- .../StringSearchInterpolatorTest.java | 49 +-- .../FixedStringSearchInterpolatorTest.java | 50 +-- ...DelimiterStringSearchInterpolatorTest.java | 18 -- 5 files changed, 55 insertions(+), 371 deletions(-) diff --git a/src/main/java/org/codehaus/plexus/interpolation/multi/MultiDelimiterStringSearchInterpolator.java b/src/main/java/org/codehaus/plexus/interpolation/multi/MultiDelimiterStringSearchInterpolator.java index 3351fb1..69d979d 100644 --- a/src/main/java/org/codehaus/plexus/interpolation/multi/MultiDelimiterStringSearchInterpolator.java +++ b/src/main/java/org/codehaus/plexus/interpolation/multi/MultiDelimiterStringSearchInterpolator.java @@ -201,16 +201,13 @@ private String interpolate( String input, RecursionInterceptor recursionIntercep if ( startIdx >= 0 && escapeString != null && escapeString.length() > 0 ) { - int startEscapeIdx = (startIdx == 0) ? 0 : startIdx - escapeString.length(); + int startEscapeIdx = startIdx == 0 ? 0 : startIdx - escapeString.length(); if ( startEscapeIdx >= 0 ) { String escape = input.substring( startEscapeIdx, startIdx ); if ( escape != null && escapeString.equals( escape ) ) { result.append( wholeExpr ); - if (startEscapeIdx > 0) { - --startEscapeIdx; - } result.replace( startEscapeIdx, startEscapeIdx + escapeString.length(), "" ); continue; } diff --git a/src/main/java/org/codehaus/plexus/interpolation/reflection/ReflectionValueExtractor.java b/src/main/java/org/codehaus/plexus/interpolation/reflection/ReflectionValueExtractor.java index dd3e2b5..bca759d 100644 --- a/src/main/java/org/codehaus/plexus/interpolation/reflection/ReflectionValueExtractor.java +++ b/src/main/java/org/codehaus/plexus/interpolation/reflection/ReflectionValueExtractor.java @@ -1,13 +1,5 @@ package org.codehaus.plexus.interpolation.reflection; -import java.lang.ref.WeakReference; -import java.lang.reflect.Array; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.util.List; -import java.util.Map; -import java.util.WeakHashMap; - /* * Copyright 2001-2006 Codehaus Foundation. * @@ -26,6 +18,13 @@ import org.codehaus.plexus.interpolation.util.StringUtils; +import java.lang.ref.SoftReference; +import java.lang.ref.WeakReference; +import java.lang.reflect.Method; +import java.util.Map; +import java.util.StringTokenizer; +import java.util.WeakHashMap; + /** * NOTE: This class was copied from plexus-utils, to allow this library * to stand completely self-contained. @@ -44,142 +43,32 @@ public class ReflectionValueExtractor private static final Object[] OBJECT_ARGS = new Object[0]; /** - * Use a WeakHashMap here, so the keys (Class objects) can be garbage collected. This approach prevents permgen - * space overflows due to retention of discarded classloaders. + * Use a WeakHashMap here, so the keys (Class objects) can be garbage collected. + * This approach prevents permgen space overflows due to retention of discarded + * classloaders. */ - private static final Map, WeakReference> classMaps = - new WeakHashMap, WeakReference>(); - - static final int EOF = -1; - - static final char PROPERTY_START = '.'; - - static final char INDEXED_START = '['; - - static final char INDEXED_END = ']'; - - static final char MAPPED_START = '('; - - static final char MAPPED_END = ')'; - - static class Tokenizer - { - final String expression; - - int idx; - - public Tokenizer( String expression ) - { - this.expression = expression; - } - - public int peekChar() - { - return idx < expression.length() ? expression.charAt( idx ) : EOF; - } - - public int skipChar() - { - return idx < expression.length() ? expression.charAt( idx++ ) : EOF; - } - - public String nextToken( char delimiter ) - { - int start = idx; - - while ( idx < expression.length() && delimiter != expression.charAt( idx ) ) - { - idx++; - } - - // delimiter MUST be present - if ( idx <= start || idx >= expression.length() ) - { - return null; - } - - return expression.substring( start, idx++ ); - } - - public String nextPropertyName() - { - final int start = idx; - - while ( idx < expression.length() && Character.isJavaIdentifierPart( expression.charAt( idx ) ) ) - { - idx++; - } - - // property name does not require delimiter - if ( idx <= start || idx > expression.length() ) - { - return null; - } - - return expression.substring( start, idx ); - } - - public int getPosition() - { - return idx < expression.length() ? idx : EOF; - } - - // to make tokenizer look pretty in debugger - @Override - public String toString() - { - return idx < expression.length() ? expression.substring( idx ) : ""; - } - } + private static final Map, WeakReference> classMaps = new WeakHashMap, WeakReference>(); private ReflectionValueExtractor() { } - /** - *

          - * The implementation supports indexed, nested and mapped properties. - *

          - *
            - *
          • nested properties should be defined by a dot, i.e. "user.address.street"
          • - *
          • indexed properties (java.util.List or array instance) should be contains (\\w+)\\[(\\d+)\\] - * pattern, i.e. "user.addresses[1].street"
          • - *
          • mapped properties should be contains (\\w+)\\((.+)\\) pattern, i.e. - * "user.addresses(myAddress).street"
          • - *
              - * - * @param expression not null expression - * @param root not null object - * @return the object defined by the expression - * @throws Exception if any - */ public static Object evaluate( String expression, Object root ) throws Exception { return evaluate( expression, root, true ); } - /** - *

              - * The implementation supports indexed, nested and mapped properties. - *

              - *
                - *
              • nested properties should be defined by a dot, i.e. "user.address.street"
              • - *
              • indexed properties (java.util.List or array instance) should be contains (\\w+)\\[(\\d+)\\] - * pattern, i.e. "user.addresses[1].street"
              • - *
              • mapped properties should be contains (\\w+)\\((.+)\\) pattern, i.e. - * "user.addresses(myAddress).street"
              • - *
                  - * - * @param expression not null expression - * @param root not null object - * @return the object defined by the expression - * @throws Exception if any - */ // TODO: don't throw Exception - public static Object evaluate( String expression, final Object root, final boolean trimRootToken ) + public static Object evaluate( String expression, Object root, boolean trimRootToken ) throws Exception { + // if the root token refers to the supplied root object parameter, remove it. + if ( trimRootToken ) + { + expression = expression.substring( expression.indexOf( '.' ) + 1 ); + } + Object value = root; // ---------------------------------------------------------------------- @@ -187,174 +76,57 @@ public static Object evaluate( String expression, final Object root, final bool // MavenProject instance. // ---------------------------------------------------------------------- - if ( expression == null || "".equals(expression.trim()) || !Character.isJavaIdentifierStart( expression.charAt( 0 ) ) ) - { - return null; - } + StringTokenizer parser = new StringTokenizer( expression, "." ); - boolean hasDots = expression.indexOf( PROPERTY_START ) >= 0; - - final Tokenizer tokenizer; - if ( trimRootToken && hasDots ) + while ( parser.hasMoreTokens() ) { - tokenizer = new Tokenizer( expression ); - tokenizer.nextPropertyName(); - if ( tokenizer.getPosition() == EOF ) - { - return null; - } - } - else - { - tokenizer = new Tokenizer( "." + expression ); - } + String token = parser.nextToken(); - int propertyPosition = tokenizer.getPosition(); - while ( value != null && tokenizer.peekChar() != EOF ) - { - switch ( tokenizer.skipChar() ) + if ( value == null ) { - case INDEXED_START: - value = - getIndexedValue( expression, propertyPosition, tokenizer.getPosition(), value, - tokenizer.nextToken( INDEXED_END ) ); - break; - case MAPPED_START: - value = - getMappedValue( expression, propertyPosition, tokenizer.getPosition(), value, - tokenizer.nextToken( MAPPED_END ) ); - break; - case PROPERTY_START: - propertyPosition = tokenizer.getPosition(); - value = getPropertyValue( value, tokenizer.nextPropertyName() ); - break; - default: - // could not parse expression - return null; + return null; } - } - - return value; - } - private static Object getMappedValue( final String expression, final int from, final int to, final Object value, - final String key ) - throws Exception - { - if ( value == null || key == null ) - { - return null; - } - - if ( value instanceof Map ) - { - Object[] localParams = new Object[] { key }; ClassMap classMap = getClassMap( value.getClass() ); - Method method = classMap.findMethod( "get", localParams ); - return method.invoke( value, localParams ); - } - final String message = - String.format( "The token '%s' at position '%d' refers to a java.util.Map, but the value seems is an instance of '%s'", - expression.subSequence( from, to ), from, value.getClass() ); + String methodBase = StringUtils.capitalizeFirstLetter( token ); - throw new Exception( message ); - } + String methodName = "get" + methodBase; - private static Object getIndexedValue( final String expression, final int from, final int to, final Object value, - final String indexStr ) - throws Exception - { - try - { - int index = Integer.parseInt( indexStr ); + Method method = classMap.findMethod( methodName, CLASS_ARGS ); - if ( value.getClass().isArray() ) + if ( method == null ) { - return Array.get( value, index ); - } + // perhaps this is a boolean property?? + methodName = "is" + methodBase; - if ( value instanceof List ) - { - ClassMap classMap = getClassMap( value.getClass() ); - // use get method on List interface - Object[] localParams = new Object[] { index }; - Method method = classMap.findMethod( "get", localParams ); - return method.invoke( value, localParams ); + method = classMap.findMethod( methodName, CLASS_ARGS ); } - } - catch ( NumberFormatException e ) - { - return null; - } - catch ( InvocationTargetException e ) - { - // catch array index issues gracefully, otherwise release - if ( e.getCause() instanceof IndexOutOfBoundsException ) + + if ( method == null ) { return null; } - throw e; - } - - final String message = - String.format( "The token '%s' at position '%d' refers to a java.util.List or an array, but the value seems is an instance of '%s'", - expression.subSequence( from, to ), from, value.getClass() ); - - throw new Exception( message ); - } - - private static Object getPropertyValue( Object value, String property ) - throws Exception - { - if ( value == null || property == null ) - { - return null; + value = method.invoke( value, OBJECT_ARGS ); } - ClassMap classMap = getClassMap( value.getClass() ); - String methodBase = StringUtils.capitalizeFirstLetter( property ); - String methodName = "get" + methodBase; - Method method = classMap.findMethod( methodName, CLASS_ARGS ); - - if ( method == null ) - { - // perhaps this is a boolean property?? - methodName = "is" + methodBase; - - method = classMap.findMethod( methodName, CLASS_ARGS ); - } - - if ( method == null ) - { - return null; - } - - try - { - return method.invoke( value, OBJECT_ARGS ); - } - catch ( InvocationTargetException e ) - { - throw e; - } + return value; } private static ClassMap getClassMap( Class clazz ) { - - WeakReference softRef = classMaps.get( clazz ); + WeakReference ref = classMaps.get( clazz); ClassMap classMap; - if ( softRef == null || ( classMap = softRef.get() ) == null ) + if ( ref == null || (classMap = ref.get()) == null ) { classMap = new ClassMap( clazz ); - classMaps.put( clazz, new WeakReference( classMap ) ); + classMaps.put( clazz, new WeakReference(classMap) ); } return classMap; } -} \ No newline at end of file +} diff --git a/src/test/java/org/codehaus/plexus/interpolation/StringSearchInterpolatorTest.java b/src/test/java/org/codehaus/plexus/interpolation/StringSearchInterpolatorTest.java index c4b84a0..79312cf 100644 --- a/src/test/java/org/codehaus/plexus/interpolation/StringSearchInterpolatorTest.java +++ b/src/test/java/org/codehaus/plexus/interpolation/StringSearchInterpolatorTest.java @@ -17,7 +17,6 @@ */ import java.io.IOException; -import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.List; @@ -148,15 +147,15 @@ public void testShouldFailOnExpressionCycle() } } - public void testShouldResolveByUsingObject_List_Map() - throws InterpolationException - { - StringSearchInterpolator rbi = new StringSearchInterpolator(); - rbi.addValueSource( new ObjectBasedValueSource( this ) ); - String result = rbi.interpolate( "this is a ${var} ${list[1].name} ${anArray[2].name} ${map(Key with spaces).name}" ); + public void testShouldResolveByMy_getVar_Method() + throws InterpolationException + { + StringSearchInterpolator rbi = new StringSearchInterpolator(); + rbi.addValueSource( new ObjectBasedValueSource( this ) ); + String result = rbi.interpolate( "this is a ${var}" ); - assertEquals( "this is a testVar testIndexedWithList testIndexedWithArray testMap", result ); - } + assertEquals( "this is a testVar", result ); + } public void testShouldResolveByContextValue() throws InterpolationException @@ -430,36 +429,4 @@ public String getVar() return "testVar"; } - public Person[] getAnArray() { - Person[] array = new Person[3]; - array[0] = new Person("Gabriel"); - array[1] = new Person("Daniela"); - array[2] = new Person("testIndexedWithArray"); - return array; - } - - public List getList() { - List list = new ArrayList(); - list.add(new Person("Gabriel")); - list.add(new Person("testIndexedWithList")); - list.add(new Person("Daniela")); - return list; - } - - public Map getMap() { - Map map = new HashMap(); - map.put("Key with spaces", new Person("testMap")); - return map; - } - - public static class Person { - private String name; - public Person(String name) { - this.name = name; - } - public String getName() { - return name; - } - } - } diff --git a/src/test/java/org/codehaus/plexus/interpolation/fixed/FixedStringSearchInterpolatorTest.java b/src/test/java/org/codehaus/plexus/interpolation/fixed/FixedStringSearchInterpolatorTest.java index 8bff5d7..074faaf 100644 --- a/src/test/java/org/codehaus/plexus/interpolation/fixed/FixedStringSearchInterpolatorTest.java +++ b/src/test/java/org/codehaus/plexus/interpolation/fixed/FixedStringSearchInterpolatorTest.java @@ -22,9 +22,7 @@ import org.junit.Test; import java.io.IOException; -import java.util.ArrayList; import java.util.HashMap; -import java.util.List; import java.util.Map; import java.util.Properties; @@ -150,15 +148,15 @@ public void testShouldFailOnExpressionCycle() } @Test - public void testShouldResolveByUsingObject_List_Map() - throws InterpolationException - { - FixedStringSearchInterpolator rbi = create( new ObjectBasedValueSource( this ) ); - String result = rbi.interpolate( "this is a ${var} ${list[1].name} ${anArray[2].name} ${map(Key with spaces).name}" ); + public void testShouldResolveByMy_getVar_Method() + throws InterpolationException + { + FixedStringSearchInterpolator rbi = create( new ObjectBasedValueSource( this ) ); + String result = rbi.interpolate( "this is a ${var}" ); + + assertEquals( "this is a testVar", result ); + } - assertEquals( "this is a testVar testIndexedWithList testIndexedWithArray testMap", result ); - } - @Test public void testShouldResolveByContextValue() throws InterpolationException @@ -426,38 +424,6 @@ public String getVar() return "testVar"; } - public Person[] getAnArray() { - Person[] array = new Person[3]; - array[0] = new Person("Gabriel"); - array[1] = new Person("Daniela"); - array[2] = new Person("testIndexedWithArray"); - return array; - } - - public List getList() { - List list = new ArrayList(); - list.add(new Person("Gabriel")); - list.add(new Person("testIndexedWithList")); - list.add(new Person("Daniela")); - return list; - } - - public Map getMap() { - Map map = new HashMap(); - map.put("Key with spaces", new Person("testMap")); - return map; - } - - public static class Person { - private String name; - public Person(String name) { - this.name = name; - } - public String getName() { - return name; - } - } - @Test public void testLinkedInterpolators() { diff --git a/src/test/java/org/codehaus/plexus/interpolation/multi/MultiDelimiterStringSearchInterpolatorTest.java b/src/test/java/org/codehaus/plexus/interpolation/multi/MultiDelimiterStringSearchInterpolatorTest.java index d3994cc..d645785 100644 --- a/src/test/java/org/codehaus/plexus/interpolation/multi/MultiDelimiterStringSearchInterpolatorTest.java +++ b/src/test/java/org/codehaus/plexus/interpolation/multi/MultiDelimiterStringSearchInterpolatorTest.java @@ -85,22 +85,4 @@ public void testInterpolationWithMultipleEscapes() assertEquals( "#${first} and ${last}", result ); } - public void testInterpolationWithMultipleEscapes2() - throws InterpolationException - { - Map ctx = new HashMap(); - ctx.put( "name", "User" ); - ctx.put( "otherName", "#${first} and ##${last}" ); - - String input = "${otherName}"; - - ValueSource vs = new MapBasedValueSource( ctx ); - MultiDelimiterStringSearchInterpolator interpolator = new MultiDelimiterStringSearchInterpolator() - .withValueSource( vs ); - interpolator.setEscapeString("#"); - - String result = interpolator.interpolate( input ); - - assertEquals( "${first} and #${last}", result ); - } }