juneau-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From jamesbog...@apache.org
Subject juneau git commit: Updates to PropertyStore code.
Date Fri, 05 Jan 2018 00:09:30 GMT
Repository: juneau
Updated Branches:
  refs/heads/master 7e0f760a7 -> 9bb2cd718


Updates to PropertyStore code.

Project: http://git-wip-us.apache.org/repos/asf/juneau/repo
Commit: http://git-wip-us.apache.org/repos/asf/juneau/commit/9bb2cd71
Tree: http://git-wip-us.apache.org/repos/asf/juneau/tree/9bb2cd71
Diff: http://git-wip-us.apache.org/repos/asf/juneau/diff/9bb2cd71

Branch: refs/heads/master
Commit: 9bb2cd71827eacb5bb8ad5c8599bfa991b604fff
Parents: 7e0f760
Author: JamesBognar <jamesbognar@apache.org>
Authored: Thu Jan 4 19:09:27 2018 -0500
Committer: JamesBognar <jamesbognar@apache.org>
Committed: Thu Jan 4 19:09:27 2018 -0500

----------------------------------------------------------------------
 .../org/apache/juneau/PropertyStoreTest.java    |  39 ++-
 .../java/org/apache/juneau/ContextBuilder.java  |  25 ++
 .../org/apache/juneau/PropertyConverter.java    |  45 +++-
 .../org/apache/juneau/PropertyStoreBuilder.java | 241 ++++++++-----------
 .../org/apache/juneau/PropertyStoreUtils.java   | 146 +++++++++++
 .../apache/juneau/internal/CollectionUtils.java |  17 ++
 6 files changed, 351 insertions(+), 162 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/juneau/blob/9bb2cd71/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/PropertyStoreTest.java
----------------------------------------------------------------------
diff --git a/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/PropertyStoreTest.java
b/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/PropertyStoreTest.java
index d7f6e3b..212f0dc 100644
--- a/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/PropertyStoreTest.java
+++ b/juneau-core/juneau-core-test/src/test/java/org/apache/juneau/PropertyStoreTest.java
@@ -161,7 +161,7 @@ public class PropertyStoreTest {
 		testError(b, "A.f1.i/add", "foo", "Cannot add value 'foo' (String) to property 'f1.i' (Integer).");
 		testError(b, "A.f1.i/add.123", "foo", "Cannot add value 'foo' (String) to property 'f1.i'
(Integer).");
 		testError(b, "A.f1.i/remove", "foo", "Cannot remove value 'foo' (String) from property
'f1.i' (Integer).");
-		testError(b, "A.f1.i", "foo", "Value 'foo' (String) cannot be converted to an Integer on
property 'f1.i' (Integer).");
+		testError(b, "A.f1.i", "foo", "Value 'foo' (String) cannot be converted to an Integer.");
 	}
 
 	@Test
@@ -184,7 +184,7 @@ public class PropertyStoreTest {
 		testError(b, "A.f1.c/remove", "foo", "Cannot remove value 'foo' (String) from property
'f1.c' (Class).");
 		
 		// Do not allow this for security reasons.
-		testError(b, "A.f1.c", "java.lang.String", "Value 'java.lang.String' (String) cannot be
converted to a Class on property 'f1.c' (Class).");
+		testError(b, "A.f1.c", "java.lang.String", "Value 'java.lang.String' (String) cannot be
converted to a Class.");
 	}
 
 	@Test
@@ -394,8 +394,8 @@ public class PropertyStoreTest {
 		assertObjectEquals("{A:{'f1.sc':['java.lang.String']}}", b.build());
 		
 		b.clear();
-		testError(b, "A.f1.sc/add", "['java.lang.Integer']", "Cannot add value '[\\'java.lang.Integer\\']'
(java.lang.String) to property 'f1.sc' (Set<Class>).");  
-		testError(b, "A.f1.sc/add", "java.lang.Integer", "Value 'java.lang.Integer' (String) cannot
be converted to a Class on property 'f1.sc' (Set<Class>).");
+		testError(b, "A.f1.sc/add", "['java.lang.Integer']", "Cannot add value '[\\'java.lang.Integer\\']'
(String) to property 'f1.sc' (Set<Class>).");  
+		testError(b, "A.f1.sc/add", "java.lang.Integer", "Value 'java.lang.Integer' (String) cannot
be converted to a Class.");
 
 		b.clear();
 		b.set("A.f1.sc/add", new AList<Class<?>>().appendAll(Integer.class, String.class));
 
@@ -608,7 +608,7 @@ public class PropertyStoreTest {
 		b.set("A.f1.ls/add.1", "['8','9']");  
 		assertObjectEquals("{A:{'f1.ls':['7','8','9','6','1','5','2','3','4']}}", b.build());
 
-		testError(b, "A.f1.li/add.123", "foo", "Value 'foo' (String) cannot be converted to an
Integer on property 'f1.li' (List<Integer>).");
+		testError(b, "A.f1.li/add.123", "foo", "Value 'foo' (String) cannot be converted to an
Integer.");
 		try {
 			b.addTo("A.f1.li", "foo", "bar");
 			fail("Exception expected.");
@@ -637,8 +637,8 @@ public class PropertyStoreTest {
 		assertObjectEquals("{A:{'f1.lc':['java.lang.String']}}", b.build());
 		
 		b.clear();
-		testError(b, "A.f1.lc/add", "['java.lang.Integer']", "Cannot add value '[\\'java.lang.Integer\\']'
(java.lang.String) to property 'f1.lc' (List<Class>).");  
-		testError(b, "A.f1.lc/add", "java.lang.Integer", "Value 'java.lang.Integer' (String) cannot
be converted to a Class on property 'f1.lc' (List<Class>).");
+		testError(b, "A.f1.lc/add", "['java.lang.Integer']", "Cannot add value '[\\'java.lang.Integer\\']'
(String) to property 'f1.lc' (List<Class>).");  
+		testError(b, "A.f1.lc/add", "java.lang.Integer", "Value 'java.lang.Integer' (String) cannot
be converted to a Class.");
 
 		b.clear();
 		b.set("A.f1.lc/add", new AList<Class<?>>().appendAll(Integer.class, String.class));
 
@@ -681,7 +681,7 @@ public class PropertyStoreTest {
 		b.set("A.f1.lc/add.-10", Object.class);
 		assertObjectEquals("{A:{'f1.lc':['java.lang.Object','java.util.List','java.lang.String','java.util.Map','java.lang.Integer']}}",
b.build());
 
-		testError(b, "A.f1.lc/add.123", "foo", "Value 'foo' (String) cannot be converted to a Class
on property 'f1.lc' (List<Class>).");
+		testError(b, "A.f1.lc/add.123", "foo", "Value 'foo' (String) cannot be converted to a Class.");
 		try {
 			b.addTo("A.f1.lc", "foo", "bar");
 			fail("Exception expected.");
@@ -1584,13 +1584,13 @@ public class PropertyStoreTest {
 			b.removeFrom("A.foo.ss", "[xxx]");
 			fail("Exception expected.");
 		} catch (ConfigException e) {
-			assertEquals("Cannot remove value '[xxx]' (java.lang.String) from property 'foo.ss' (Set<String>)
because it's not a valid JSON array.", e.getMessage());
+			assertEquals("Cannot remove value '[xxx]' (String) from property 'foo.ss' (Set<String>)
because it's not a valid JSON array.", e.getMessage());
 		}
 		try {
 			b.removeFrom("A.foo.ls", "[xxx]");
 			fail("Exception expected.");
 		} catch (ConfigException e) {
-			assertEquals("Cannot remove value '[xxx]' (java.lang.String) from property 'foo.ls' (List<String>)
because it's not a valid JSON array.", e.getMessage());
+			assertEquals("Cannot remove value '[xxx]' (String) from property 'foo.ls' (List<String>)
because it's not a valid JSON array.", e.getMessage());
 		}
 	}
 
@@ -1601,19 +1601,19 @@ public class PropertyStoreTest {
 			b.addTo("A.foo.sms", "{xxx}");
 			fail("Exception expected.");
 		} catch (ConfigException e) {
-			assertEquals("Cannot add '{xxx}' (java.lang.String) to property 'foo.sms' (Map<String,String>)
because it's not a valid JSON object.", e.getMessage());
+			assertEquals("Cannot add '{xxx}' (String) to property 'foo.sms' (Map<String,String>)
because it's not a valid JSON object.", e.getMessage());
 		}
 		try {
 			b.addTo("A.foo.sms", "xxx");
 			fail("Exception expected.");
 		} catch (ConfigException e) {
-			assertEquals("Cannot add 'xxx' (java.lang.String) to property 'foo.sms' (Map<String,String>).",
e.getMessage());
+			assertEquals("Cannot add 'xxx' (String) to property 'foo.sms' (Map<String,String>).",
e.getMessage());
 		}
 		try {
 			b.addTo("A.foo.sms", new StringBuilder("foo"));
 			fail("Exception expected.");
 		} catch (ConfigException e) {
-			assertEquals("Cannot add 'foo' (java.lang.StringBuilder) to property 'foo.sms' (Map<String,String>).",
e.getMessage());
+			assertEquals("Cannot add 'foo' (StringBuilder) to property 'foo.sms' (Map<String,String>).",
e.getMessage());
 		}
 	}
 
@@ -1741,6 +1741,19 @@ public class PropertyStoreTest {
 		assertEquals("{A:{'foo.ls':['baz','foo','bar','qux']}}", psb.build().toString());
 	}
 
+	@Test
+	public void testIndexedValuesOnList() {
+		PropertyStoreBuilder psb = PropertyStore.create();
+		
+		psb.set("A.foo.ls", "['foo','bar']");
+		psb.set("A.foo.ls", new String[]{"INHERIT", "[0]:baz"});
+		assertEquals("{A:{'foo.ls':['baz','foo','bar']}}", psb.build().toString());
+		psb.set("A.foo.ls", new String[]{"INHERIT", "[1]:qux"});
+		assertEquals("{A:{'foo.ls':['baz','qux','foo','bar']}}", psb.build().toString());
+		psb.set("A.foo.ls", new String[]{"INHERIT", "[10]:quux"});
+		assertEquals("{A:{'foo.ls':['baz','qux','foo','bar','quux']}}", psb.build().toString());
+	}
+
 	//-------------------------------------------------------------------------------------------------------------------
 	// Utility methods
 	//-------------------------------------------------------------------------------------------------------------------

http://git-wip-us.apache.org/repos/asf/juneau/blob/9bb2cd71/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/ContextBuilder.java
----------------------------------------------------------------------
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/ContextBuilder.java
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/ContextBuilder.java
index aa4e7bc..47bcaa3 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/ContextBuilder.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/ContextBuilder.java
@@ -40,6 +40,31 @@ public abstract class ContextBuilder {
 	}
 
 	/**
+	 * Constructor.
+	 * 
+	 * <p>
+	 * Used in cases where multiple context builder are sharing the same property store builder.
+	 * <br>(e.g. <code>HtlmlDocBuilder</code>)
+	 * 
+	 * @param psb The property store builder to use.
+	 */
+	protected ContextBuilder(PropertyStoreBuilder psb) {
+		this.psb = psb;
+	}
+	
+	/**
+	 * Returns access to the inner property store builder.
+	 * 
+	 * <p>
+	 * Used in conjunction with {@link #ContextBuilder(PropertyStoreBuilder)} when builders
share property store builders.
+	 * 
+	 * @return The inner property store builder.
+	 */
+	protected PropertyStoreBuilder getPropertyStoreBuilder() {
+		return psb;
+	}
+
+	/**
 	 * Build the object.
 	 *
 	 * @return The built object.

http://git-wip-us.apache.org/repos/asf/juneau/blob/9bb2cd71/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/PropertyConverter.java
----------------------------------------------------------------------
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/PropertyConverter.java
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/PropertyConverter.java
index 99cb2d9..e792c30 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/PropertyConverter.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/PropertyConverter.java
@@ -12,60 +12,83 @@
 // ***************************************************************************************************************************
 package org.apache.juneau;
 
-import org.apache.juneau.PropertyStoreBuilder.*;
 import org.apache.juneau.internal.*;
 
 /**
  * Used to convert property values to standardized Boolean/Integer/Class/Object values in
property store builders.
+ * 
+ * @param <T> The normalized form.
  */
-interface PropertyConverter<T> {
-	T convert(Object o, MutableProperty p);
+public interface PropertyConverter<T> {
+	
+	/**
+	 * Convert the value to normalized form.
+	 *  
+	 * @param o The raw value.
+	 * @return The converted value.
+	 */
+	T convert(Object o);
 
+	/**
+	 * Converts objects to strings.
+	 */
 	static final PropertyConverter<String> STRING_CONVERTER = new PropertyConverter<String>()
{
 		@Override
-		public String convert(Object o, MutableProperty p) {
+		public String convert(Object o) {
 			return ClassUtils.toString(o);
 		}
 	};
 	
+	/**
+	 * Converts objects to integers.
+	 */
 	static final PropertyConverter<Integer> INTEGER_CONVERTER = new PropertyConverter<Integer>()
{
 		@Override
-		public Integer convert(Object o, MutableProperty p) {
+		public Integer convert(Object o) {
 			try {
 				if (o instanceof Integer)
 					return (Integer)o;
 				return Integer.valueOf(o.toString());
 			} catch (Exception e) {
-				throw new ConfigException("Value ''{0}'' ({1}) cannot be converted to an Integer on property
''{2}'' ({3}).", o, o.getClass().getSimpleName(), p.name, p.type);
+				throw new ConfigException("Value ''{0}'' ({1}) cannot be converted to an Integer.", o,
o.getClass().getSimpleName());
 			}
 		}
 	};
 		
+	/**
+	 * Converts objects to booleans.
+	 */
 	static final PropertyConverter<Boolean> BOOLEAN_CONVERTER = new PropertyConverter<Boolean>()
{
 		@Override
-		public Boolean convert(Object o, MutableProperty p) {
+		public Boolean convert(Object o) {
 			if (o instanceof Boolean)
 				return (Boolean)o;
 			return Boolean.parseBoolean(o.toString());
 		}
 	};
 		
+	/**
+	 * Converts objects to classes.
+	 */
 	static final PropertyConverter<Class<?>> CLASS_CONVERTER = new PropertyConverter<Class<?>>()
{
 		@Override
-		public Class<?> convert(Object o, MutableProperty p) {
+		public Class<?> convert(Object o) {
 			try {
 				if (o instanceof Class)
 					return (Class<?>)o;
-				throw new ConfigException("Value ''{0}'' ({1}) cannot be converted to a Class on property
''{2}'' ({3}).", o, o.getClass().getSimpleName(), p.name, p.type);
+				throw new ConfigException("Value ''{0}'' ({1}) cannot be converted to a Class.", o, o.getClass().getSimpleName());
 			} catch (Exception e) {
-				throw new ConfigException("Value ''{0}'' ({1}) cannot be converted to a Class on property
''{2}'' ({3}).", o, o.getClass().getSimpleName(), p.name, p.type);
+				throw new ConfigException("Value ''{0}'' ({1}) cannot be converted to a Class.", o, o.getClass().getSimpleName());
 			}
 		}
 	};
 	
+	/**
+	 * Converts objects to objects.
+	 */
 	static final PropertyConverter<Object> OBJECT_CONVERTER = new PropertyConverter<Object>()
{
 		@Override
-		public Object convert(Object o, MutableProperty p) {
+		public Object convert(Object o) {
 			return o;
 		}
 	};

http://git-wip-us.apache.org/repos/asf/juneau/blob/9bb2cd71/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/PropertyStoreBuilder.java
----------------------------------------------------------------------
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/PropertyStoreBuilder.java
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/PropertyStoreBuilder.java
index 7a8bf79..391a201 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/PropertyStoreBuilder.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/PropertyStoreBuilder.java
@@ -12,8 +12,6 @@
 // ***************************************************************************************************************************
 package org.apache.juneau;
 
-import static org.apache.juneau.internal.ClassUtils.*;
-
 import java.lang.reflect.*;
 import java.util.*;
 import java.util.concurrent.*;
@@ -21,6 +19,7 @@ import java.util.concurrent.*;
 import org.apache.juneau.PropertyStore.*;
 import org.apache.juneau.internal.*;
 import org.apache.juneau.json.*;
+import org.apache.juneau.parser.*;
 
 /**
  * A builder for {@link PropertyStore} objects.
@@ -464,17 +463,15 @@ public class PropertyStoreBuilder {
 		abstract Object peek();
 
 		void add(String arg, Object value) {
-			throw new ConfigException("Cannot add value {0} ({1}) to property ''{2}'' ({3}).",
-				JsonSerializer.DEFAULT_LAX.toString(value), value.getClass().getSimpleName(), name, type);
+			throw new ConfigException("Cannot add value {0} ({1}) to property ''{2}'' ({3}).", string(value),
className(value), name, type);
 		}
 
 		void remove(Object value) {
-			throw new ConfigException("Cannot remove value {0} ({1}) from property ''{2}'' ({3}).",
-				JsonSerializer.DEFAULT_LAX.toString(value), value.getClass().getSimpleName(), name, type);
+			throw new ConfigException("Cannot remove value {0} ({1}) from property ''{2}'' ({3}).",
string(value), className(value), name, type);
 		}
 
 		Object convert(Object value) {
-			return value == null ? null : type.converter.convert(value, this);
+			return value == null ? null : type.converter.convert(value);
 		}
 	}
 	
@@ -522,26 +519,28 @@ public class PropertyStoreBuilder {
 	
 	@SuppressWarnings("unchecked")
 	static class MutableSetProperty extends MutableProperty {
-		private final Set<Object> value;
-		private Set<Object> oldValue;
+		private final Set<Object> set;
 
 		MutableSetProperty(String name, PropertyType type, Object value) {
 			super(name, type);
-			this.value = new ConcurrentSkipListSet<>(type.comparator());
+			set = new ConcurrentSkipListSet<>(type.comparator());
 			set(value);
 		}
 		
 		@Override /* MutableProperty */
 		synchronized Property build() {
-			return new Property(name, Collections.unmodifiableSet(new LinkedHashSet<>(value)),
type);
+			return new Property(name, Collections.unmodifiableSet(new LinkedHashSet<>(set)),
type);
 		}
 
 		@Override /* MutableProperty */
 		synchronized void set(Object value) {
-			this.oldValue = this.value.isEmpty() ? Collections.EMPTY_SET : new LinkedHashSet<>(this.value);
-			this.value.clear();
-			add(null, value);
-			this.oldValue = null;
+			try {
+				Set<Object> newSet = PropertyStoreUtils.merge(set, type.converter, value);
+				set.clear();
+				set.addAll(newSet);
+			} catch (ParseException e) {
+				throw new ConfigException("Cannot set value {0} ({1}) on property ''{2}'' ({3}).", string(value),
className(value), name, type).initCause(e);
+			}
 		}
 		
 		@Override /* MutableProperty */
@@ -551,69 +550,57 @@ public class PropertyStoreBuilder {
 		}
 		
 		@Override /* MutableProperty */
-		synchronized void add(String arg, Object value) {
+		synchronized void add(String arg, Object o) {
 			if (arg != null)
 				throw new ConfigException("Cannot use argument ''{0}'' on add command for property ''{1}''
({2})", arg, name, type);
-			if (value != null) {
-				if (value.getClass().isArray()) {
-					for (int i = 0; i < Array.getLength(value); i++)
-						add(null, Array.get(value, i));
-				} else if (value instanceof Collection) {
-					for (Object o : (Collection<Object>)value)
-						add(null, o);
-				} else if (isObjectList(value)) {
+			if (o != null) {
+				if (o.getClass().isArray()) {
+					for (int i = 0; i < Array.getLength(o); i++)
+						add(null, Array.get(o, i));
+				} else if (o instanceof Collection) {
+					for (Object o2 : (Collection<Object>)o)
+						add(null, o2);
+				} else if (isObjectList(o)) {
 					try {
-						add(null, new ObjectList(value.toString()));
+						add(null, new ObjectList(o.toString()));
 					} catch (Exception e) {
-						throw new ConfigException(
-							"Cannot add value {0} ({1}) to property ''{2}'' ({3}).",
-							JsonSerializer.DEFAULT_LAX.toString(value), getReadableClassNameForObject(value),
name, type
-						);
+						throw new ConfigException("Cannot add value {0} ({1}) to property ''{2}'' ({3}).",
string(o), className(o), name, type);
 					}
-				} else if (isNone(value)) {
-					this.value.clear();
-				} else if (isInherit(value) && oldValue != null) {
-					add(null, oldValue);
 				} else {
-					value = convert(value);
-					this.value.add(value);
+					set.add(convert(o));
 				}
 			}
 		}
 		
 		@Override /* MutableProperty */
-		synchronized void remove(Object value) {
-			if (value != null) {
-				if (value.getClass().isArray()) {
-					for (int i = 0; i < Array.getLength(value); i++)
-						remove(Array.get(value, i));
-				} else if (value instanceof Collection) {
-					for (Object o : (Collection<Object>)value)
-						remove(o);
-				} else if (isObjectList(value)) {
+		synchronized void remove(Object o) {
+			if (o != null) {
+				if (o.getClass().isArray()) {
+					for (int i = 0; i < Array.getLength(o); i++)
+						remove(Array.get(o, i));
+				} else if (o instanceof Collection) {
+					for (Object o2 : (Collection<Object>)o)
+						remove(o2);
+				} else if (isObjectList(o)) {
 					try {
-						remove(new ObjectList(value.toString()));
+						remove(new ObjectList(o.toString()));
 					} catch (Exception e) {
-						throw new ConfigException(
-							"Cannot remove value {0} ({1}) from property ''{2}'' ({3}) because it''s not a valid
JSON array.",
-							JsonSerializer.DEFAULT_LAX.toString(value), getReadableClassNameForObject(value),
name, type
-						);
+						throw new ConfigException("Cannot remove value {0} ({1}) from property ''{2}'' ({3})
because it''s not a valid JSON array.", string(o), className(o), name, type);
 					}					
 				} else {
-					value = convert(value);
-					this.value.remove(value);
+					set.remove(convert(o));
 				}
 			}
 		}
 		
 		@Override /* MutableProperty */
 		synchronized boolean isEmpty() {
-			return this.value.isEmpty();
+			return set.isEmpty();
 		}
 
 		@Override /* MutableProperty */
 		synchronized Object peek() {
-			return value;
+			return set;
 		}
 	}
 	
@@ -623,8 +610,7 @@ public class PropertyStoreBuilder {
 	
 	@SuppressWarnings("unchecked")
 	static class MutableListProperty extends MutableProperty {
-		private final List<Object> value = new CopyOnWriteArrayList<>();
-		private List<Object> oldValue;
+		private final List<Object> list = new CopyOnWriteArrayList<>();
 
 		MutableListProperty(String name, PropertyType type, Object value) {
 			super(name, type);
@@ -633,15 +619,20 @@ public class PropertyStoreBuilder {
 		
 		@Override /* MutableProperty */
 		synchronized Property build() {
-			return new Property(name, Collections.unmodifiableList(CollectionUtils.reverse(new ArrayList<>(value))),
type);
+			return new Property(name, Collections.unmodifiableList(CollectionUtils.reverse(new ArrayList<>(list))),
type);
 		}
 
 		@Override /* MutableProperty */
 		synchronized void set(Object value) {
-			this.oldValue = this.value.isEmpty() ? Collections.EMPTY_LIST : new ArrayList<>(this.value);
-			this.value.clear();
-			add(null, value);
-			this.oldValue = null;
+			try {
+				List<Object> oldList = CollectionUtils.reverseCopy(list);
+				List<Object> newList = PropertyStoreUtils.merge(oldList, type.converter, value);
+				Collections.reverse(newList);
+				list.clear();
+				list.addAll(newList);
+			} catch (ParseException e) {
+				throw new ConfigException("Cannot set value {0} ({1}) on property ''{2}'' ({3}).", string(value),
className(value), name, type).initCause(e);
+			}
 		}
 		
 		@Override /* MutableProperty */
@@ -650,22 +641,22 @@ public class PropertyStoreBuilder {
 		}
 
 		@Override /* MutableProperty */
-		synchronized void add(String arg, Object value) {
+		synchronized void add(String arg, Object o) {
 			if (arg != null && ! StringUtils.isNumeric(arg))
 				throw new ConfigException("Invalid argument ''{0}'' on add command for property ''{1}''
({2})", arg, name, type);
 			
 			// Note that since lists are stored in reverse-order, the index should be the compliment.
 			
-			int index = arg == null ? this.value.size() : this.value.size() - Integer.parseInt(arg);
+			int index = arg == null ? list.size() : list.size() - Integer.parseInt(arg);
 			if (index < 0)
 				index = 0;
-			else if (index > this.value.size())
-				index = this.value.size();
-			add(index, value);
+			else if (index > list.size())
+				index = list.size();
+			add(index, o);
 		}
 		
-		private synchronized int add(int index, Object value) {
-			if (value != null) {
+		private synchronized int add(int index, Object o) {
+			if (o != null) {
 				
 				// Important!
 				// Arrays and collections are inserted in REVERSE order.
@@ -675,33 +666,24 @@ public class PropertyStoreBuilder {
 				// subsequent calls to addTo() are added to the end.
 				// What you get is a list ordered in least-to-most important.
 				
-				if (value.getClass().isArray()) {
-					for (int i = Array.getLength(value) - 1; i >= 0; i--)
-						index = add(index, Array.get(value, i));
-				} else if (value instanceof Collection) {
-					for (Object o : CollectionUtils.reverseIterable((Collection<Object>)value))
-						index = add(index, o);
-				} else if (isObjectList(value)) {
+				if (o.getClass().isArray()) {
+					for (int i = Array.getLength(o) - 1; i >= 0; i--)
+						index = add(index, Array.get(o, i));
+				} else if (o instanceof Collection) {
+					for (Object o2 : CollectionUtils.reverseIterable((Collection<Object>)o))
+						index = add(index, o2);
+				} else if (isObjectList(o)) {
 					try {
-						index = add(index, new ObjectList(value.toString()));
+						index = add(index, new ObjectList(o.toString()));
 					} catch (Exception e) {
-						throw new ConfigException(
-							"Cannot add value {0} ({1}) to property ''{2}'' ({3}).",
-							JsonSerializer.DEFAULT_LAX.toString(value), getReadableClassNameForObject(value),
name, type
-						);
+						throw new ConfigException("Cannot add value {0} ({1}) to property ''{2}'' ({3}).",
string(o), className(o), name, type);
 					}
-				} else if (isNone(value)) {
-					this.value.clear();
-					return -1;
-				} else if (isInherit(value) && oldValue != null) {
-					for (Object o : oldValue)
-						index = add(index, o);
 				} else {
-					value = convert(value);
-					boolean replaced = this.value.remove(value);
+					o = convert(o);
+					boolean replaced = list.remove(o);
 					if (replaced)
 						index--;
-					this.value.add(index, value);
+					list.add(index, o);
 					index++;
 				}
 			}
@@ -709,38 +691,34 @@ public class PropertyStoreBuilder {
 		}
 
 		@Override /* MutableProperty */
-		synchronized void remove(Object value) {
-			if (value != null) {
-				if (value.getClass().isArray()) {
-					for (int i = 0; i < Array.getLength(value); i++)
-						remove(Array.get(value, i));
-				} else if (value instanceof Collection) {
-					for (Object o : (Collection<Object>)value)
-						remove(o);
-				} else if (isObjectList(value)) {
+		synchronized void remove(Object o) {
+			if (o != null) {
+				if (o.getClass().isArray()) {
+					for (int i = 0; i < Array.getLength(o); i++)
+						remove(Array.get(o, i));
+				} else if (o instanceof Collection) {
+					for (Object o2 : (Collection<Object>)o)
+						remove(o2);
+				} else if (isObjectList(o)) {
 					try {
-						remove(new ObjectList(value.toString()));
+						remove(new ObjectList(o.toString()));
 					} catch (Exception e) {
-						throw new ConfigException(
-							"Cannot remove value {0} ({1}) from property ''{2}'' ({3}) because it''s not a valid
JSON array.",
-							JsonSerializer.DEFAULT_LAX.toString(value), getReadableClassNameForObject(value),
name, type
-						);
+						throw new ConfigException("Cannot remove value {0} ({1}) from property ''{2}'' ({3})
because it''s not a valid JSON array.", string(o), className(o), name, type);
 					}					
 				} else {
-					value = convert(value);
-					this.value.remove(value);
+					list.remove(convert(o));
 				}
 			}
 		}
 
 		@Override /* MutableProperty */
 		synchronized boolean isEmpty() {
-			return this.value.isEmpty();
+			return list.isEmpty();
 		}
 
 		@Override /* MutableProperty */
 		synchronized Object peek() {
-			return value;
+			return list;
 		}
 	}
 	
@@ -781,34 +759,28 @@ public class PropertyStoreBuilder {
 		}
 
 		@Override /* MutableProperty */
-		synchronized void add(String arg, Object value) {
+		synchronized void add(String arg, Object o) {
 			if (arg != null) {
-				value = convert(value);
-				if (value == null)
+				o = convert(o);
+				if (o == null)
 					this.map.remove(arg);
 				else
-					this.map.put(arg, value);
+					this.map.put(arg, o);
 				
-			} else if (value != null) {
-				if (value instanceof Map) {
-					Map m = (Map)value;
+			} else if (o != null) {
+				if (o instanceof Map) {
+					Map m = (Map)o;
 					for (Map.Entry e : (Set<Map.Entry>)m.entrySet())
 						if (e.getKey() != null)
 							add(e.getKey().toString(), e.getValue());
-				} else if (isObjectMap(value)) {
+				} else if (isObjectMap(o)) {
 					try {
-						add(null, new ObjectMap(value.toString()));
+						add(null, new ObjectMap(o.toString()));
 					} catch (Exception e) {
-						throw new ConfigException(
-							"Cannot add {0} ({1}) to property ''{2}'' ({3}) because it''s not a valid JSON object.",
-							JsonSerializer.DEFAULT_LAX.toString(value), getReadableClassNameForObject(value),
name, type
-						);
+						throw new ConfigException("Cannot add {0} ({1}) to property ''{2}'' ({3}) because it''s
not a valid JSON object.", string(o), className(o), name, type);
 					}
 				} else {
-					throw new ConfigException(
-						"Cannot add {0} ({1}) to property ''{2}'' ({3}).",
-						JsonSerializer.DEFAULT_LAX.toString(value), getReadableClassNameForObject(value), name,
type
-					);
+					throw new ConfigException("Cannot add {0} ({1}) to property ''{2}'' ({3}).", string(o),
className(o), name, type);
 				}
 			}
 		}
@@ -846,6 +818,7 @@ public class PropertyStoreBuilder {
 		}
 	}
 	
+
 	//-------------------------------------------------------------------------------------------------------------------
 	// Utility methods
 	//-------------------------------------------------------------------------------------------------------------------
@@ -857,23 +830,15 @@ public class PropertyStoreBuilder {
 		}
 		return false;
 	}
-
-	static boolean isNone(Object o) {
-		if (o instanceof CharSequence) {
-			String s = o.toString();
-			return "NONE".equals(s);
-		}
-		return false;
+	
+	static String string(Object value) {
+		return JsonSerializer.DEFAULT_LAX.toString(value);
 	}
-
-	static boolean isInherit(Object o) {
-		if (o instanceof CharSequence) {
-			String s = o.toString();
-			return "INHERIT".equals(s);
-		}
-		return false;
+	
+	static String className(Object value) {
+		return value.getClass().getSimpleName();
 	}
-
+	
 	static boolean isObjectMap(Object o) {
 		if (o instanceof CharSequence) {
 			String s = o.toString();

http://git-wip-us.apache.org/repos/asf/juneau/blob/9bb2cd71/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/PropertyStoreUtils.java
----------------------------------------------------------------------
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/PropertyStoreUtils.java
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/PropertyStoreUtils.java
new file mode 100644
index 0000000..d65bca0
--- /dev/null
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/PropertyStoreUtils.java
@@ -0,0 +1,146 @@
+// ***************************************************************************************************************************
+// * 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.juneau;
+
+import java.lang.reflect.*;
+import java.util.*;
+import java.util.regex.*;
+
+import org.apache.juneau.parser.*;
+
+/**
+ * Utility methods for working with property store properties.
+ */
+public class PropertyStoreUtils {
+	
+	/**
+	 * Merges a new object into an existing set.
+	 * 
+	 * @param oldSet The previous set.
+	 * @param pc The property converter to use to convert values in the new value.
+	 * @param o The new value to add.
+	 * @return A new set containing the merged contents.
+	 * @throws ParseException
+	 */
+	public static Set<Object> merge(Set<Object> oldSet, PropertyConverter<?>
pc, Object o) throws ParseException {
+		return merge(oldSet, new LinkedHashSet<>(), pc, o);
+	}
+
+	@SuppressWarnings("unchecked")
+	static Set<Object> merge(Set<Object> oldSet, Set<Object> newSet, PropertyConverter<?>
pc, Object o) throws ParseException {
+		if (o == null) {
+			// Ignore.
+		} else if (o.getClass().isArray()) {
+			for (int i = 0; i < Array.getLength(o); i++)
+				merge(oldSet, newSet, pc, Array.get(o, i));
+		} else if (o instanceof Collection) {
+			for (Object o2 : (Collection<Object>)o)
+				merge(oldSet, newSet, pc, o2);
+		} else if (isObjectList(o)) {
+			merge(oldSet, newSet, pc, new ObjectList(o.toString()));
+		} else if (isNone(o)) {
+			newSet.clear();
+		} else if (isInherit(o)) {
+			if (oldSet != null)
+				for (Object o2 : oldSet)
+					newSet.add(o2);
+		} else {
+			newSet.add(pc == null ? o : pc.convert(o));
+		}
+		return newSet;
+	}
+
+	/**
+	 * Merges a new object into an existing list.
+	 * 
+	 * @param oldList The previous list.
+	 * @param pc The property converter to use to convert values in the new value.
+	 * @param o The new value to add.
+	 * @return A new list containing the merged contents.
+	 * @throws ParseException
+	 */
+	public static List<Object> merge(List<Object> oldList, PropertyConverter<?>
pc, Object o) throws ParseException {
+		return merge(oldList, new ArrayList<>(), pc, o);
+	}
+
+	@SuppressWarnings("unchecked")
+	private static List<Object> merge(List<Object> oldList, List<Object> newList,
PropertyConverter<?> pc, Object o) throws ParseException {
+		if (o == null) {
+			// Ignore
+		} else if (o.getClass().isArray()) {
+			for (int i = 0; i < Array.getLength(o); i++)
+				merge(oldList, newList, pc, Array.get(o, i));
+		} else if (o instanceof Collection) {
+			for (Object o2 : (Collection<Object>)o)
+				merge(oldList, newList, pc, o2);
+		} else if (isIndexed(o)) {
+			Matcher lm = INDEXED_LINK_PATTERN.matcher(o.toString());
+			lm.matches();
+			String key = lm.group(1);
+			int i2 = Math.min(newList.size(), Integer.parseInt(lm.group(2)));
+			String remainder = lm.group(3);
+			newList.add(i2, key.isEmpty() ? remainder : key + ":" + remainder);
+		} else if (isObjectList(o)) {
+			merge(oldList, newList, pc, new ObjectList(o.toString()));
+		} else if (isNone(o)) {
+			newList.clear();
+		} else if (isInherit(o)) {
+			if (oldList != null)
+				for (Object o2 : oldList)
+					newList.add(o2);
+		} else {
+			o = pc == null ? o : pc.convert(o);
+			newList.remove(o);
+			newList.add(o);
+		}
+		return newList;
+	}
+	
+	//-------------------------------------------------------------------------------------------------------------------
+	// Utility methods
+	//-------------------------------------------------------------------------------------------------------------------
+	
+	private static boolean isObjectList(Object o) {
+		if (o instanceof CharSequence) {
+			String s = o.toString();
+			return (s.startsWith("[") && s.endsWith("]") && BeanContext.DEFAULT !=
null);
+		}
+		return false;
+	}
+
+	private static boolean isNone(Object o) {
+		if (o instanceof CharSequence) {
+			String s = o.toString();
+			return "NONE".equals(s);
+		}
+		return false;
+	}
+
+	private static boolean isIndexed(Object o) {
+		if (o instanceof CharSequence) {
+			String s = o.toString();
+			return s.indexOf('[') != -1 && INDEXED_LINK_PATTERN.matcher(s).matches();
+		}
+		return false;
+	}
+
+	private static final Pattern INDEXED_LINK_PATTERN = Pattern.compile("(?s)(\\S*)\\[(\\d+)\\]\\:(.*)");
+	
+	private static boolean isInherit(Object o) {
+		if (o instanceof CharSequence) {
+			String s = o.toString();
+			return "INHERIT".equals(s);
+		}
+		return false;
+	}
+}

http://git-wip-us.apache.org/repos/asf/juneau/blob/9bb2cd71/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/internal/CollectionUtils.java
----------------------------------------------------------------------
diff --git a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/internal/CollectionUtils.java
b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/internal/CollectionUtils.java
index 284619c..e4b5a6c 100644
--- a/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/internal/CollectionUtils.java
+++ b/juneau-core/juneau-marshall/src/main/java/org/apache/juneau/internal/CollectionUtils.java
@@ -116,4 +116,21 @@ public final class CollectionUtils {
 		Collections.reverse(l);
 		return l;
 	}
+	
+	/**
+	 * Creates a new copy of a list in reverse order.
+	 * 
+	 * @param l The old list.
+	 * @return 
+	 * 	A new list with reversed entries.
+	 * 	<br>Returns <jk>null</jk> if the list was <jk>null</jk>.
+	 * 	<br>Returns the same list if the list is empty.
+	 */
+	public static <T> List<T> reverseCopy(List<T> l) {
+		if (l == null || l.isEmpty())
+			return l;
+		List<T> l2 = new ArrayList<>(l);
+		Collections.reverse(l2);
+		return l2;
+	}
 }


Mime
View raw message