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();
+ }
+}