You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@wicket.apache.org by sv...@apache.org on 2016/07/14 17:18:59 UTC

wicket git commit: WICKET-5623 let IPropertyLocator return null; added test for custom properties

Repository: wicket
Updated Branches:
  refs/heads/WICKET-5623-extensible-propertyresolver fd72be98a -> 899f824d8


WICKET-5623 let IPropertyLocator return null; added test for custom properties


Project: http://git-wip-us.apache.org/repos/asf/wicket/repo
Commit: http://git-wip-us.apache.org/repos/asf/wicket/commit/899f824d
Tree: http://git-wip-us.apache.org/repos/asf/wicket/tree/899f824d
Diff: http://git-wip-us.apache.org/repos/asf/wicket/diff/899f824d

Branch: refs/heads/WICKET-5623-extensible-propertyresolver
Commit: 899f824d8e4ad9653153033da31d008411ec6205
Parents: fd72be9
Author: Sven Meier <sv...@apache.org>
Authored: Thu Jul 14 19:18:25 2016 +0200
Committer: Sven Meier <sv...@apache.org>
Committed: Thu Jul 14 19:18:25 2016 +0200

----------------------------------------------------------------------
 .../wicket/core/util/lang/PropertyResolver.java | 116 +++++++++++--------
 .../org/apache/wicket/util/lang/Document.java   |  44 +++++++
 .../wicket/util/lang/PropertyResolverTest.java  |  71 +++++++++++-
 3 files changed, 180 insertions(+), 51 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/wicket/blob/899f824d/wicket-core/src/main/java/org/apache/wicket/core/util/lang/PropertyResolver.java
----------------------------------------------------------------------
diff --git a/wicket-core/src/main/java/org/apache/wicket/core/util/lang/PropertyResolver.java b/wicket-core/src/main/java/org/apache/wicket/core/util/lang/PropertyResolver.java
index c3e3025..28023e1 100644
--- a/wicket-core/src/main/java/org/apache/wicket/core/util/lang/PropertyResolver.java
+++ b/wicket-core/src/main/java/org/apache/wicket/core/util/lang/PropertyResolver.java
@@ -35,9 +35,6 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 /**
- * NOTE: THIS CLASS IS NOT PART OF THE WICKET PUBLIC API, DO NOT USE IT UNLESS YOU KNOW WHAT YOU ARE
- * DOING.
- * <p>
  * This class parses expressions to lookup or set a value on the object that is given. <br/>
  * The supported expressions are:
  * <dl>
@@ -88,7 +85,7 @@ public final class PropertyResolver
 	private final static int CREATE_NEW_VALUE = 1;
 	private final static int RESOLVE_CLASS = 2;
 
-	private final static ConcurrentHashMap<Object, IGetAndSetLocator> applicationToLocators = Generics.newConcurrentHashMap(2);
+	private final static ConcurrentHashMap<Object, IPropertyLocator> applicationToLocators = Generics.newConcurrentHashMap(2);
 
 	private static final String GET = "get";
 	private static final String IS = "is";
@@ -389,9 +386,15 @@ public final class PropertyResolver
 
 	private static IGetAndSet getGetAndSet(String exp, final Class<?> clz)
 	{
-		IGetAndSetLocator locator = getLocator();
+		IPropertyLocator locator = getLocator();
+		
+		IGetAndSet getAndSet = locator.get(clz, exp);
+		if (getAndSet == null) {
+			throw new WicketRuntimeException(
+					"Property could not be resolved for class: " + clz + " expression: " + exp);
+		}
 		
-		return locator.getAndSet(clz, exp);
+		return getAndSet;
 	}
 
 	/**
@@ -473,7 +476,7 @@ public final class PropertyResolver
 	/**
 	 * @author jcompagner
 	 */
-	public static interface IGetAndSet
+	public interface IGetAndSet
 	{
 		/**
 		 * @param object
@@ -520,7 +523,7 @@ public final class PropertyResolver
 		public Method getSetter();
 	}
 
-	private static abstract class AbstractGetAndSet implements IGetAndSet
+	public static abstract class AbstractGetAndSet implements IGetAndSet
 	{
 		/**
 		 * {@inheritDoc}
@@ -1238,10 +1241,10 @@ public final class PropertyResolver
 	}
 
 	/**
-	 * Sets the {@link IGetAndSetLocator} for the given application.
+	 * Sets the {@link IPropertyLocator} for the given application.
 	 *
 	 * If the Application is null then it will be the default if no application is found. So if you
-	 * want to be sure that your {@link IGetAndSetLocator} is handled in all situations then call this
+	 * want to be sure that your {@link IPropertyLocator} is handled in all situations then call this
 	 * method twice with your implementations. One time for the application and the second time with
 	 * null.
 	 *
@@ -1252,12 +1255,12 @@ public final class PropertyResolver
 	@Deprecated
 	public static void setClassCache(final Application application, final IClassCache classCache)
 	{
-		setLocator(application, new IGetAndSetLocator() {
+		setLocator(application, new IPropertyLocator() {
 			
 			private DefaultGetAndSetLocator locator = new DefaultGetAndSetLocator();
 			
 			@Override
-			public IGetAndSet getAndSet(Class<?> clz, String name) {
+			public IGetAndSet get(Class<?> clz, String name) {
 				Map<String, IGetAndSet> map = classCache.get(clz);
 				if (map == null) {
 					map = new ConcurrentHashMap<String, IGetAndSet>(8);
@@ -1266,7 +1269,7 @@ public final class PropertyResolver
 				
 				IGetAndSet getAndSetter = map.get(name);
 				if (getAndSetter == null) {
-					getAndSetter = locator.getAndSet(clz, name);
+					getAndSetter = locator.get(clz, name);
 					map.put(name, getAndSetter);
 				}
 				
@@ -1276,12 +1279,12 @@ public final class PropertyResolver
 	}
 
 	/**
-	 * Get the current {@link IGetAndSetLocator}.
+	 * Get the current {@link IPropertyLocator}.
 	 * 
 	 * @return locator for the current {@link Application} or a general one if no current application is present
 	 * @see Application#get()
 	 */
-	public static IGetAndSetLocator getLocator()
+	public static IPropertyLocator getLocator()
 	{
 		Object key;
 		if (Application.exists())
@@ -1292,10 +1295,10 @@ public final class PropertyResolver
 		{
 			key = PropertyResolver.class;
 		}
-		IGetAndSetLocator result = applicationToLocators.get(key);
+		IPropertyLocator result = applicationToLocators.get(key);
 		if (result == null)
 		{
-			IGetAndSetLocator tmpResult = applicationToLocators.putIfAbsent(key, result = new CachingGetAndSetLocator(new DefaultGetAndSetLocator()));
+			IPropertyLocator tmpResult = applicationToLocators.putIfAbsent(key, result = new CachingGetAndSetLocator(new DefaultGetAndSetLocator()));
 			if (tmpResult != null)
 			{
 				result = tmpResult;
@@ -1310,7 +1313,7 @@ public final class PropertyResolver
 	 * @param application application, may be {@code null}
 	 * @param locator locator
 	 */
-	public static void setLocator(final Application application, final IGetAndSetLocator locator)
+	public static void setLocator(final Application application, final IPropertyLocator locator)
 	{
 		if (application == null)
 		{
@@ -1323,7 +1326,7 @@ public final class PropertyResolver
 	}
 
 	/**
-	 * Specify an {@link IGetAndSetLocator} instead.
+	 * Specify an {@link IPropertyLocator} instead.
 	 */
 	@Deprecated
 	public static interface IClassCache
@@ -1346,58 +1349,81 @@ public final class PropertyResolver
 	}
 
 	/**
-	 * A locator of {@link IGetAndSet}s.
+	 * A locator of properties.
 	 * 
-	 * @param clz owning class
-	 * @param exp identifying expression
-	 *  
 	 * @see https://issues.apache.org/jira/browse/WICKET-5623
 	 */
-	public static interface IGetAndSetLocator
+	public static interface IPropertyLocator
 	{
 		/**
-		 * Get {@link IGetAndSet}.
+		 * Get {@link IGetAndSet} for a property.
 		 * 
 		 * @param clz owning class
-		 * @param exp identifying expression
-		 * @return get and set
+		 * @param exp identifying the property
+		 * @return getAndSet or {@code null} if non located
 		 */
-		IGetAndSet getAndSet(Class<?> clz, String exp);
+		IGetAndSet get(Class<?> clz, String exp);
 	}
 
-	public static class CachingGetAndSetLocator implements IGetAndSetLocator
+	public static class CachingGetAndSetLocator implements IPropertyLocator
 	{
 		private final ConcurrentHashMap<String, IGetAndSet> map = Generics.newConcurrentHashMap(16);
 		
-		private IGetAndSetLocator locator;
+		/**
+		 * Special token to put into the cache representing no located {@link IGetAndSet}. 
+		 */
+		private IGetAndSet NONE = new AbstractGetAndSet() {
+
+			@Override
+			public Object getValue(Object object) {
+				return null;
+			}
+
+			@Override
+			public Object newValue(Object object) {
+				return null;
+			}
 
-		public CachingGetAndSetLocator(IGetAndSetLocator locator) {
+			@Override
+			public void setValue(Object object, Object value, PropertyResolverConverter converter) {
+			}
+		};
+
+		private IPropertyLocator locator;
+
+		public CachingGetAndSetLocator(IPropertyLocator locator) {
 			this.locator = locator;
 		}
 
 		@Override
-		public IGetAndSet getAndSet(Class<?> clz, String exp) {
+		public IGetAndSet get(Class<?> clz, String exp) {
 			String key = clz.getName() + "#" + exp;
 			
-			IGetAndSet accessor = map.get(key);
-			if (accessor == null) {
-				accessor = locator.getAndSet(clz, exp);
-
-				map.put(key, accessor);
+			IGetAndSet located = map.get(key);
+			if (located == null) {
+				located = locator.get(clz, exp);
+				if (located == null) {
+					located = NONE;
+				}
+				map.put(key, located);
 			}
 			
-			return accessor;
+			if (located == NONE) {
+				located = null;
+			}
+			
+			return located;
 		}
 	}
 
 	/**
 	 * Default implementation supporting <em>Java Beans</em> properties, maps, lists and method invocations.
 	 */
-	public static class DefaultGetAndSetLocator implements IGetAndSetLocator
+	public static class DefaultGetAndSetLocator implements IPropertyLocator
 	{
 		@Override
-		public IGetAndSet getAndSet(Class<?> clz, String exp) {
-			IGetAndSet getAndSet;
+		public IGetAndSet get(Class<?> clz, String exp) {
+			IGetAndSet getAndSet = null;
 			
 			Method method = null;
 			Field field;
@@ -1508,14 +1534,6 @@ public final class PropertyResolver
 											" expression: " + propertyName);
 								}
 							}
-							else
-							{
-								// We do not look for a public FIELD because
-								// that is not good programming with beans patterns
-								throw new WicketRuntimeException(
-									"No get method defined for class: " + clz + " expression: " +
-										exp);
-							}
 						}
 						else
 						{

http://git-wip-us.apache.org/repos/asf/wicket/blob/899f824d/wicket-core/src/test/java/org/apache/wicket/util/lang/Document.java
----------------------------------------------------------------------
diff --git a/wicket-core/src/test/java/org/apache/wicket/util/lang/Document.java b/wicket-core/src/test/java/org/apache/wicket/util/lang/Document.java
new file mode 100644
index 0000000..11c05fd
--- /dev/null
+++ b/wicket-core/src/test/java/org/apache/wicket/util/lang/Document.java
@@ -0,0 +1,44 @@
+/*
+ * 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.
+ */
+package org.apache.wicket.util.lang;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public class Document {
+	
+	private Map<String, Object> values = new HashMap<>();
+	
+	private String type;
+	
+	public String getType() {
+		return type;
+	}
+	
+	public void setType(String type) {
+		this.type = type;
+	}
+	
+	@SuppressWarnings("unchecked")
+	public <T> T getProperty(String name) {
+		return (T) values.get(name);
+	}
+	
+	public <T> void setProperty(String name, T t) {
+		values.put(name, t);
+	}
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/wicket/blob/899f824d/wicket-core/src/test/java/org/apache/wicket/util/lang/PropertyResolverTest.java
----------------------------------------------------------------------
diff --git a/wicket-core/src/test/java/org/apache/wicket/util/lang/PropertyResolverTest.java b/wicket-core/src/test/java/org/apache/wicket/util/lang/PropertyResolverTest.java
index 5265354..e76e1d6 100644
--- a/wicket-core/src/test/java/org/apache/wicket/util/lang/PropertyResolverTest.java
+++ b/wicket-core/src/test/java/org/apache/wicket/util/lang/PropertyResolverTest.java
@@ -31,6 +31,11 @@ import org.apache.wicket.ConverterLocator;
 import org.apache.wicket.IConverterLocator;
 import org.apache.wicket.WicketRuntimeException;
 import org.apache.wicket.core.util.lang.PropertyResolver;
+import org.apache.wicket.core.util.lang.PropertyResolver.AbstractGetAndSet;
+import org.apache.wicket.core.util.lang.PropertyResolver.CachingGetAndSetLocator;
+import org.apache.wicket.core.util.lang.PropertyResolver.DefaultGetAndSetLocator;
+import org.apache.wicket.core.util.lang.PropertyResolver.IGetAndSet;
+import org.apache.wicket.core.util.lang.PropertyResolver.IPropertyLocator;
 import org.apache.wicket.core.util.lang.PropertyResolverConverter;
 import org.apache.wicket.util.convert.ConversionException;
 import org.apache.wicket.util.convert.IConverter;
@@ -46,6 +51,7 @@ import org.junit.Test;
  */
 public class PropertyResolverTest extends WicketTestCase
 {
+
 	private static final PropertyResolverConverter CONVERTER = new PropertyResolverConverter(
 		new ConverterLocator(), Locale.US);
 
@@ -599,8 +605,8 @@ public class PropertyResolverTest extends WicketTestCase
 		private static final long serialVersionUID = 1L;
 
 		/**
-		 * 
 		 */
+		@SuppressWarnings("unused")
 		public String testValue = "vector";
 	}
 
@@ -629,6 +635,7 @@ public class PropertyResolverTest extends WicketTestCase
 	{
 		private int value;
 
+		@SuppressWarnings("unused")
 		public String getValue()
 		{
 			return String.valueOf(value);
@@ -739,4 +746,64 @@ public class PropertyResolverTest extends WicketTestCase
 		Object actual = converter.convert(date, Long.class);
 		assertEquals(date.getTime(), actual);
 	}
-}
+	
+	/**
+	 * WICKET-5623 custom properties
+	 */
+	@Test
+	public void custom() {
+		Document document = new Document();
+		document.setType("type");
+		document.setProperty("string", "string");
+		
+		Document nestedCustom = new Document();
+		nestedCustom.setProperty("string", "string2");
+		document.setProperty("nested", nestedCustom);
+		
+		PropertyResolver.setLocator(tester.getApplication(), new CachingGetAndSetLocator(new CustomGetAndSetLocator()));
+		
+		assertEquals("type", PropertyResolver.getValue("type", document));
+		assertEquals("string", PropertyResolver.getValue("string", document));
+		assertEquals("string2", PropertyResolver.getValue("nested.string", document));
+	}
+	
+	class CustomGetAndSetLocator implements IPropertyLocator {
+
+		private IPropertyLocator locator = new DefaultGetAndSetLocator();
+		
+		@Override
+		public IGetAndSet get(Class<?> clz, String exp) {
+			// first try default properties
+			IGetAndSet getAndSet = locator.get(clz, exp);
+			if (getAndSet == null && Document.class.isAssignableFrom(clz)) {
+				// fall back to document properties
+				getAndSet = new DocumentPropertyGetAndSet(exp);
+			}
+			return getAndSet;
+		}
+		
+		public class DocumentPropertyGetAndSet extends AbstractGetAndSet {
+
+			private String name;
+
+			public DocumentPropertyGetAndSet(String name) {
+				this.name = name;
+			}
+
+			@Override
+			public Object getValue(Object object) {
+				return ((Document) object).getProperty(name);
+			}
+
+			@Override
+			public Object newValue(Object object) {
+				return new Document();
+			}
+
+			@Override
+			public void setValue(Object object, Object value, PropertyResolverConverter converter) {
+				((Document) object).setProperty(name, value);
+			}
+		}
+	}
+}
\ No newline at end of file