Author: britter
Date: Wed Mar 12 19:19:22 2014
New Revision: 1576863

URL: http://svn.apache.org/r1576863
Log:
SANDBOX-463: Implementation of property paths. Thanks to André Diermann

Added:
    
commons/sandbox/beanutils2/trunk/src/main/java/org/apache/commons/beanutils2/PropertyInterpreter.java
    
commons/sandbox/beanutils2/trunk/src/test/java/org/apache/commons/beanutils2/PropertyInterpreterTestCase.java
Modified:
    commons/sandbox/beanutils2/trunk/src/changes/changes.xml

Modified: commons/sandbox/beanutils2/trunk/src/changes/changes.xml
URL: 
http://svn.apache.org/viewvc/commons/sandbox/beanutils2/trunk/src/changes/changes.xml?rev=1576863&r1=1576862&r2=1576863&view=diff
==============================================================================
--- commons/sandbox/beanutils2/trunk/src/changes/changes.xml (original)
+++ commons/sandbox/beanutils2/trunk/src/changes/changes.xml Wed Mar 12 
19:19:22 2014
@@ -23,6 +23,9 @@
   </properties>
   <body>
   <release version="0.1" date="201?-??-??" description="First release.">
+    <action dev="britter" due-to="André Diermann" type="add" 
issue="SANDBOX-463">
+      Implementation of property paths
+    </action>
     <action dev="britter" type="update" issue="SANDBOX-444">
       Add test case for BeanProperties.hasProperty()
     </action>

Added: 
commons/sandbox/beanutils2/trunk/src/main/java/org/apache/commons/beanutils2/PropertyInterpreter.java
URL: 
http://svn.apache.org/viewvc/commons/sandbox/beanutils2/trunk/src/main/java/org/apache/commons/beanutils2/PropertyInterpreter.java?rev=1576863&view=auto
==============================================================================
--- 
commons/sandbox/beanutils2/trunk/src/main/java/org/apache/commons/beanutils2/PropertyInterpreter.java
 (added)
+++ 
commons/sandbox/beanutils2/trunk/src/main/java/org/apache/commons/beanutils2/PropertyInterpreter.java
 Wed Mar 12 19:19:22 2014
@@ -0,0 +1,180 @@
+package org.apache.commons.beanutils2;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import java.util.LinkedList;
+import java.util.Queue;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+final class PropertyInterpreter
+{
+    /**
+     * Interprets a given simple/indexed/mapped/nested property name or any 
combination of it.
+     *
+     * @param beanAccessor A {@link BeanAccessor} to resolve the given {@param 
propertyName}
+     * @param propertyName A simple/indexed/mapped/nested property name or any 
combination of it
+     * @return A {@link BeanAccessor} representing the resolved {@param 
propertyName}
+     */
+    public static BeanAccessor<?> interpret( BeanAccessor<?> beanAccessor, 
String propertyName )
+    {
+        Queue<Expression> expressionQueue = buildExpressionQueue( propertyName 
);
+
+        return processExpressions( beanAccessor, expressionQueue );
+    }
+
+    private static Queue<Expression> buildExpressionQueue( String propertyName 
)
+    {
+        Queue<Expression> expressionQueue = new LinkedList<Expression>();
+
+        String[] properties = obtainProperties( propertyName );
+
+        for ( String property : properties )
+        {
+            if ( Property.isExpression( property ) )
+            {
+                expressionQueue.offer( new Property( property ) );
+            }
+            else if ( IndexedProperty.isExpression( property ) )
+            {
+                expressionQueue.offer( new IndexedProperty( property ) );
+            }
+            else if ( MappedProperty.isExpression( property ) )
+            {
+                expressionQueue.offer( new MappedProperty( property ) );
+            }
+            else
+            {
+                throw new IllegalArgumentException(
+                    String.format( "The specified propertyName '%s' is not 
valid.", propertyName ) );
+            }
+        }
+
+        return expressionQueue;
+    }
+
+    private static BeanAccessor<?> processExpressions( BeanAccessor<?> 
beanAccessor, Queue<Expression> expressionQueue )
+    {
+        Expression expression = null;
+        while ( ( expression = expressionQueue.poll() ) != null )
+        {
+            beanAccessor = expression.interpret( beanAccessor );
+        }
+
+        return beanAccessor;
+    }
+
+    private static String[] obtainProperties( String propertyName )
+    {
+        String[] properties = propertyName.split( "\\." );
+
+        return properties;
+    }
+
+    private static class Property
+        implements Expression
+    {
+        private static final String REGEX = "[a-z]+[A-Za-z]*";
+
+        private final String propertyName;
+
+        private Property( String propertyName )
+        {
+            this.propertyName = propertyName;
+        }
+
+        public BeanAccessor<?> interpret( BeanAccessor<?> beanAccessor )
+        {
+            return beanAccessor.get( propertyName );
+        }
+
+        public static boolean isExpression( String expression )
+        {
+            return expression.matches( REGEX );
+        }
+    }
+
+    private static class IndexedProperty
+        implements Expression
+    {
+        private static final String REGEX = "([a-z]+[A-Za-z]*)\\[([0-9]+)\\]";
+
+        private static final Pattern PATTERN = Pattern.compile( REGEX );
+
+        private final String propertyName;
+
+        private IndexedProperty( String propertyName )
+        {
+            this.propertyName = propertyName;
+        }
+
+        public BeanAccessor<?> interpret( BeanAccessor<?> beanAccessor )
+        {
+            Matcher matcher = PATTERN.matcher( propertyName );
+            matcher.matches();
+
+            String property = matcher.group( 1 );
+            int index = Integer.valueOf( matcher.group( 2 ) );
+
+            return beanAccessor.getIndexed( property ).at( index );
+        }
+
+        public static boolean isExpression( String expression )
+        {
+            return expression.matches( REGEX );
+        }
+    }
+
+    private static class MappedProperty
+        implements Expression
+    {
+        private static final String REGEX = 
"([a-z]+[A-Za-z]*)\\(([A-Za-z0-9\\s]+)\\)";
+
+        private static final Pattern PATTERN = Pattern.compile( REGEX );
+
+        private final String propertyName;
+
+        private MappedProperty( String propertyName )
+        {
+            this.propertyName = propertyName;
+        }
+
+        public BeanAccessor<?> interpret( BeanAccessor<?> beanAccessor )
+        {
+            Matcher matcher = PATTERN.matcher( propertyName );
+            matcher.matches();
+
+            String property = matcher.group( 1 );
+            String key = matcher.group( 2 );
+
+            return beanAccessor.getMapped( property ).of( key );
+        }
+
+        public static boolean isExpression( String expression )
+        {
+            return expression.matches( REGEX );
+        }
+    }
+
+    private interface Expression
+    {
+        BeanAccessor<?> interpret( BeanAccessor<?> beanAccessor );
+    }
+}

Added: 
commons/sandbox/beanutils2/trunk/src/test/java/org/apache/commons/beanutils2/PropertyInterpreterTestCase.java
URL: 
http://svn.apache.org/viewvc/commons/sandbox/beanutils2/trunk/src/test/java/org/apache/commons/beanutils2/PropertyInterpreterTestCase.java?rev=1576863&view=auto
==============================================================================
--- 
commons/sandbox/beanutils2/trunk/src/test/java/org/apache/commons/beanutils2/PropertyInterpreterTestCase.java
 (added)
+++ 
commons/sandbox/beanutils2/trunk/src/test/java/org/apache/commons/beanutils2/PropertyInterpreterTestCase.java
 Wed Mar 12 19:19:22 2014
@@ -0,0 +1,169 @@
+package org.apache.commons.beanutils2;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import org.apache.commons.beanutils2.testbeans.TestBean;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import static org.apache.commons.beanutils2.BeanUtils.on;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+public class PropertyInterpreterTestCase
+{
+    private TestBean target;
+
+    @Before
+    public void setUp()
+    {
+        target = new TestBean();
+    }
+
+    @After
+    public void tearDown()
+    {
+        target = null;
+    }
+
+    @Test
+    public void getProperty()
+    {
+        BeanAccessor<TestBean> beanAccessor = on( target );
+
+        Object value = PropertyInterpreter.interpret( beanAccessor, 
"intProperty" ).get();
+
+        assertNotNull( "Got no value", value );
+        assertTrue( "Got a value of the wrong type", ( value instanceof 
Integer ) );
+        assertTrue( "Got an incorrect value", ( (Integer) value ).intValue() 
== target.getIntProperty() );
+    }
+
+    @Test( expected = IllegalArgumentException.class )
+    public void getMisspelledProperty()
+    {
+        BeanAccessor<TestBean> beanAccessor = on( target );
+
+        PropertyInterpreter.interpret( beanAccessor, "IntProperty" ).get();
+    }
+
+    @Test
+    public void getIndexedProperty()
+    {
+        BeanAccessor<TestBean> beanAccessor = on( target );
+
+        Object value = PropertyInterpreter.interpret( beanAccessor, 
"intIndexed[4]" ).get();
+
+        assertNotNull( "Got no value", value );
+        assertTrue( "Got a value of the wrong type", ( value instanceof 
Integer ) );
+        assertTrue( "Got an incorrect value", ( (Integer) value ).intValue() 
== target.getIntArray()[4] );
+    }
+
+
+    @Test( expected = IllegalArgumentException.class )
+    public void getNegativeIndexedProperty()
+    {
+        BeanAccessor<TestBean> beanAccessor = on( target );
+
+        Object value = PropertyInterpreter.interpret( beanAccessor, 
"intIndexed[-4]" ).get();
+    }
+
+    @Test( expected = IllegalArgumentException.class )
+    public void getInvalidIndexedProperty()
+    {
+        BeanAccessor<TestBean> beanAccessor = on( target );
+
+        Object value = PropertyInterpreter.interpret( beanAccessor, 
"intIndexed[a]" ).get();
+    }
+
+    @Test( expected = IllegalArgumentException.class )
+    public void getInvalidBraceIndexedProperty()
+    {
+        BeanAccessor<TestBean> beanAccessor = on( target );
+
+        Object value = PropertyInterpreter.interpret( beanAccessor, 
"intIndexed[4)" ).get();
+    }
+
+    @Test
+    public void getMappedProperty()
+    {
+        BeanAccessor<TestBean> beanAccessor = on( target );
+
+        Object value = PropertyInterpreter.interpret( beanAccessor, 
"mappedProperty(First Key)" ).get();
+
+        assertNotNull( "Got no value", value );
+        assertTrue( "Got a value of the wrong type", ( value instanceof String 
) );
+        assertTrue( "Got an incorrect value", ( (String) value ).equals( 
target.getMappedProperty( "First Key" ) ) );
+    }
+
+
+    @Test( expected = IllegalArgumentException.class )
+    public void getInvalidKeyMappedProperty()
+    {
+        BeanAccessor<TestBean> beanAccessor = on( target );
+
+        Object value = PropertyInterpreter.interpret( beanAccessor, 
"mappedProperty(First_Key)" ).get();
+    }
+
+    @Test( expected = IllegalArgumentException.class )
+    public void getInvalidBraceMappedProperty()
+    {
+        BeanAccessor<TestBean> beanAccessor = on( target );
+
+        Object value = PropertyInterpreter.interpret( beanAccessor, 
"mappedProperty(FirstKey]" ).get();
+    }
+
+    @Test
+    public void getNestedProperty()
+    {
+        BeanAccessor<TestBean> beanAccessor = on( target );
+
+        Object value = PropertyInterpreter.interpret( beanAccessor, 
"nested.nested.mappedNested.value(key)" ).get();
+
+        assertNotNull( "Got no value", value );
+        assertTrue( "Got a value of the wrong type", ( value instanceof String 
) );
+        assertTrue( "Got an incorrect value",
+                    target.getNested().getNested().getMappedNested().getValue( 
"key" ).equals( (String) value ) );
+    }
+
+    @Test( expected = IllegalArgumentException.class )
+    public void getInvalidNestedProperty()
+    {
+        BeanAccessor<TestBean> beanAccessor = on( target );
+
+        Object value = PropertyInterpreter.interpret( beanAccessor, 
"nested..nested.mappedNested.value(key)" ).get();
+    }
+
+    @Test( expected = IllegalArgumentException.class )
+    public void getAnotherInvalidNestedProperty()
+    {
+        BeanAccessor<TestBean> beanAccessor = on( target );
+
+        Object value = PropertyInterpreter.interpret( beanAccessor, 
".nested.mappedNested.value(key)" ).get();
+    }
+
+    @Test( expected = IllegalArgumentException.class )
+    public void getYetAnotherInvalidNestedProperty()
+    {
+        BeanAccessor<TestBean> beanAccessor = on( target );
+
+        Object value = PropertyInterpreter.interpret( beanAccessor, 
"nested.MappedNested.value(key)" ).get();
+    }
+}


Reply via email to