Jsonya allows primitives and lists at the root now

bringing it in line with json which has always allowed lists and now allows 
primitives, as root objects


Project: http://git-wip-us.apache.org/repos/asf/brooklyn-server/repo
Commit: http://git-wip-us.apache.org/repos/asf/brooklyn-server/commit/86440030
Tree: http://git-wip-us.apache.org/repos/asf/brooklyn-server/tree/86440030
Diff: http://git-wip-us.apache.org/repos/asf/brooklyn-server/diff/86440030

Branch: refs/heads/master
Commit: 864400306d0433e26a51e96639de2d2b08b9bc4f
Parents: 785342a
Author: Alex Heneveld <[email protected]>
Authored: Fri Jun 24 11:00:14 2016 +0100
Committer: Alex Heneveld <[email protected]>
Committed: Fri Jun 24 22:52:28 2016 +0100

----------------------------------------------------------------------
 .../brooklyn/util/collections/Jsonya.java       |  68 ++++++++--
 .../brooklyn/util/collections/JsonyaTest.java   | 130 ++++++++++++++++++-
 2 files changed, 179 insertions(+), 19 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/86440030/utils/common/src/main/java/org/apache/brooklyn/util/collections/Jsonya.java
----------------------------------------------------------------------
diff --git 
a/utils/common/src/main/java/org/apache/brooklyn/util/collections/Jsonya.java 
b/utils/common/src/main/java/org/apache/brooklyn/util/collections/Jsonya.java
index ef7f451..6a13dbf 100644
--- 
a/utils/common/src/main/java/org/apache/brooklyn/util/collections/Jsonya.java
+++ 
b/utils/common/src/main/java/org/apache/brooklyn/util/collections/Jsonya.java
@@ -66,7 +66,7 @@ public class Jsonya {
     /** creates a {@link Navigator} backed by a newly created map;
      * the map can be accessed by {@link Navigator#getMap()} */
     public static Navigator<MutableMap<Object,Object>> newInstance() {
-        return new Navigator<MutableMap<Object,Object>>(new 
MutableMap<Object,Object>(), MutableMap.class);
+        return new Navigator<MutableMap<Object,Object>>(MutableMap.class);
     }
     /** convenience for {@link Navigator#at(Object, Object...)} on a {@link 
#newInstance()} */
     public static Navigator<MutableMap<Object,Object>> at(Object 
...pathSegments) {
@@ -113,7 +113,7 @@ public class Jsonya {
     @SuppressWarnings({"rawtypes","unchecked"})
     public static class Navigator<T extends Map<?,?>> {
 
-        protected final Object root;
+        protected Object root;
         protected final Class<? extends Map> mapType;
         protected Object focus;
         protected Stack<Object> focusStack = new Stack<Object>();
@@ -125,6 +125,19 @@ public class Jsonya {
             this.focus = backingStore;
             this.mapType = mapType;
         }
+
+        public Navigator(Class<? extends Map> mapType) {
+            this.root = null;
+            this.focus = null;
+            this.mapType = mapType;
+            this.creationInPreviousFocus = new Function<Object, Void>() {
+                @Override
+                public Void apply(Object o) {
+                    root = o;
+                    return null;
+                }
+            }; 
+        }
         
         // -------------- access and configuration
         
@@ -227,16 +240,21 @@ public class Jsonya {
             return this;
         }
         
-        /** returns the navigator moved to focus at the indicated key sequence 
in the given map */
+        /** returns the navigator moved to focus at the indicated key sequence 
in the given map, creating the path needed */
         public Navigator<T> at(Object pathSegment, Object 
...furtherPathSegments) {
-            down(pathSegment);
+            down(pathSegment, false);
             return atArray(furtherPathSegments);
         }
         public Navigator<T> atArray(Object[] furtherPathSegments) {
             for (Object p: furtherPathSegments)
-                down(p);
+                down(p, false);
             return this;
         }
+        /** returns the navigator moved to focus at the indicated key sequence 
in the given map, failing if not available */
+        public Navigator<T> atExisting(Object pathSegment, Object 
...furtherPathSegments) {
+            down(pathSegment, true);
+            return atArray(furtherPathSegments);
+        }
         
         /** ensures the given focus is a map, creating if needed (and creating 
inside the list if it is in a list) */
         public Navigator<T> map() {
@@ -297,17 +315,17 @@ public class Jsonya {
         }
 
         /** utility for {@link #at(Object, Object...)}, taking one argument at 
a time */
-        protected Navigator<T> down(final Object pathSegment) {
+        protected Navigator<T> down(final Object pathSegment, boolean 
requireExisting) {
             if (focus instanceof List) {
-                return downList(pathSegment);
+                return downList(pathSegment, requireExisting);
             }
             if ((focus instanceof Map) || focus==null) {
-                return downMap(pathSegment);
+                return downMap(pathSegment, requireExisting);
             }
             throw new IllegalStateException("focus here is "+focus+"; cannot 
descend to '"+pathSegment+"'");
         }
 
-        protected Navigator<T> downMap(Object pathSegmentO) {
+        protected Navigator<T> downMap(Object pathSegmentO, boolean 
requireExisting) {
             final Object pathSegment = translateKey(pathSegmentO);
             final Map givenParentMap = (Map)focus;
             if (givenParentMap!=null) {
@@ -315,6 +333,9 @@ public class Jsonya {
                 focus = givenParentMap.get(pathSegment);
             }
             if (focus==null) {
+                if (requireExisting) {
+                    throw new IllegalStateException("No key '"+pathSegmentO+"' 
found to descend");
+                }
                 final Function<Object, Void> previousCreation = 
creationInPreviousFocus;
                 creationInPreviousFocus = new Function<Object, Void>() {
                     public Void apply(Object input) {
@@ -332,7 +353,7 @@ public class Jsonya {
             return this;
         }
 
-        protected Navigator<T> downList(final Object pathSegment) {
+        protected Navigator<T> downList(final Object pathSegment, boolean 
requireExisting) {
             if (!(pathSegment instanceof Integer))
                 throw new IllegalStateException("focus here is a list 
("+focus+"); cannot descend to '"+pathSegment+"'");
             final List givenParentList = (List)focus;
@@ -340,6 +361,10 @@ public class Jsonya {
             creationInPreviousFocus = null;
             focus = givenParentList.get((Integer)pathSegment);
             if (focus==null) {
+                if (requireExisting) {
+                    throw new IllegalStateException("No index 
'"+pathSegment+"' found to descend");
+                }
+
                 // don't need to worry about creation here; we don't create 
list entries simply by navigating
                 // TODO a nicer architecture would create a new object with 
focus for each traversal
                 // in that case we could create, filling other positions with 
null; but is there a need?
@@ -370,7 +395,9 @@ public class Jsonya {
         }
         
         /** adds the given items to the focus, whether a list or a map,
-         * creating the focus as a map if it doesn't already exist.
+         * creating the focus if it doesn't already exist.
+         * if there is just one argument being added and the focus doesn't 
exist, that item is set as the focus.
+         * if there are more than one argument the focus is made as a map (and 
an even number of arguments is required).  
          * to add items to a list which might not exist, precede by a call to 
{@link #list()}.
          * <p>
          * when adding items to a list, iterable and array arguments are 
flattened because 
@@ -387,7 +414,19 @@ public class Jsonya {
          * auto-conversion to a list may be added in a future version
          * */
         public Navigator<T> add(Object o1, Object ...others) {
-            if (focus==null) map();
+            if (focus==null) {
+                if (others.length>0) {
+                    // default to map, but only if multiple args given
+                    map();
+                } else {
+                    // if single arg and no focus, focus becomes the arg, and 
no need to add
+                    focus = o1;
+                    if (creationInPreviousFocus!=null) {
+                        creationInPreviousFocus.apply(o1);
+                    }
+                    return this;
+                }
+            }
             addInternal(focus, focus, o1, others);
             return this;
         }
@@ -404,12 +443,12 @@ public class Jsonya {
                 Map target = (Map)currentFocus;
                 Map source;
                 if (others.length==0) {
-                    // add as a map
                     if (o1==null)
                         // ignore if null
                         return ;
-                    if (!(o1 instanceof Map))
+                    if (!(o1 instanceof Map)) {
                         throw new IllegalStateException("cannot add: focus 
here is "+currentFocus+" (in "+initialFocus+"); expected a collection, or a map 
(with a map being added, not "+o1+")");
+                    }
                     source = (Map)translate(o1);
                 } else {
                     // build a source map from the arguments as key-value pairs
@@ -487,6 +526,7 @@ public class Jsonya {
             for (Object entry: (Collection<?>)focus) {
                 if (!first) sb.append(",");
                 else first = false;
+                sb.append( " " );
                 sb.append( render(entry) );
             }
             sb.append(" ]");

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/86440030/utils/common/src/test/java/org/apache/brooklyn/util/collections/JsonyaTest.java
----------------------------------------------------------------------
diff --git 
a/utils/common/src/test/java/org/apache/brooklyn/util/collections/JsonyaTest.java
 
b/utils/common/src/test/java/org/apache/brooklyn/util/collections/JsonyaTest.java
index 9ce77d2..4bf94a6 100644
--- 
a/utils/common/src/test/java/org/apache/brooklyn/util/collections/JsonyaTest.java
+++ 
b/utils/common/src/test/java/org/apache/brooklyn/util/collections/JsonyaTest.java
@@ -22,9 +22,7 @@ import java.util.Arrays;
 import java.util.List;
 import java.util.Map;
 
-import org.apache.brooklyn.util.collections.Jsonya;
-import org.apache.brooklyn.util.collections.MutableMap;
-import org.apache.brooklyn.util.collections.MutableSet;
+import org.apache.brooklyn.test.Asserts;
 import org.apache.brooklyn.util.collections.Jsonya.Navigator;
 import org.testng.Assert;
 import org.testng.annotations.Test;
@@ -110,9 +108,14 @@ public class JsonyaTest {
         Assert.assertEquals( n.root().at("europe").getFocusMap().size(), 3 );
     }
     
-    @Test(expectedExceptions=Exception.class)
+    @Test
     public void testJsonyaDeepSimpleFailure() {
-        Jsonya.of(europeMap()).at("euroope").add("spain");
+        try {
+            Jsonya.of(europeMap()).atExisting("euroope");
+            Asserts.shouldHaveFailedPreviously();
+        } catch (Exception e) {
+            Asserts.expectedFailureContainsIgnoreCase(e, "euroope", "no", 
"found");
+        }
     }
 
     @Test
@@ -190,4 +193,121 @@ public class JsonyaTest {
         Assert.assertFalse(m.getFocusMap().containsKey("edinburgh"));
     }
 
+    @Test
+    public void testAddMapAddsReference() {
+        MutableMap<Object, Object> map = MutableMap.<Object,Object>of("a", 1, 
"b", 2); 
+        Navigator<MutableMap<Object, Object>> j = 
Jsonya.newInstance().add("root", map);
+        Assert.assertEquals( j.toString(), "{ \"root\": { \"a\": 1, \"b\": 2 } 
}");
+        map.put("b", 1);
+        Assert.assertEquals( j.toString(), "{ \"root\": { \"a\": 1, \"b\": 1 } 
}");
+    }
+
+    @Test
+    public void testAddListAddsReference() {
+        MutableList<Object> list = MutableList.<Object>of("a", "b"); 
+        Navigator<MutableMap<Object, Object>> j = 
Jsonya.newInstance().add("root", list);
+        Assert.assertEquals( j.toString(), "{ \"root\": [ \"a\", \"b\" ] }");
+        list.append("c");
+        Assert.assertEquals( j.toString(), "{ \"root\": [ \"a\", \"b\", \"c\" 
] }");
+    }
+
+    @Test
+    public void testAddListToExistingAddsCopy() {
+        MutableList<Object> list = MutableList.<Object>of("a", "b"); 
+        Navigator<MutableMap<Object, Object>> j = 
Jsonya.newInstance().at("root").list().add(list).root();
+        Assert.assertEquals( j.toString(), "{ \"root\": [ \"a\", \"b\" ] }");
+        list.append("c");
+        Assert.assertEquals( j.toString(), "{ \"root\": [ \"a\", \"b\" ] }");
+    }
+
+    @Test
+    public void testAddMapToExistingAddsCopy() {
+        MutableMap<Object, Object> map = MutableMap.<Object,Object>of("a", 1, 
"b", 2); 
+        Navigator<MutableMap<Object, Object>> j = 
Jsonya.newInstance().at("root").map().add(map).root();
+        Assert.assertEquals( j.toString(), "{ \"root\": { \"a\": 1, \"b\": 2 } 
}");
+        map.put("b", 1);
+        Assert.assertEquals( j.toString(), "{ \"root\": { \"a\": 1, \"b\": 2 } 
}");
+    }
+
+    @Test
+    public void testAddMapToExistingRootAddsCopy() {
+        MutableMap<Object, Object> map = MutableMap.<Object,Object>of("a", 1, 
"b", 2); 
+        Navigator<MutableMap<Object, Object>> j = 
Jsonya.newInstance().map().add(map);
+        Assert.assertEquals( j.toString(), "{ \"a\": 1, \"b\": 2 }");
+        map.put("b", 1);
+        Assert.assertEquals( j.toString(), "{ \"a\": 1, \"b\": 2 }");
+    }
+
+    @Test
+    public void testAddListToExistingRootAddsCopy() {
+        MutableList<Object> list = MutableList.<Object>of("a", "b"); 
+        Navigator<MutableMap<Object, Object>> j = 
Jsonya.newInstance().list().add(list);
+        Assert.assertEquals( j.toString(), "[ \"a\", \"b\" ]");
+        list.append("c");
+        Assert.assertEquals( j.toString(), "[ \"a\", \"b\" ]");
+    }
+
+    @Test
+    public void testAddMapAtRootAddsReference() {
+        MutableMap<Object, Object> map = MutableMap.<Object,Object>of("a", 1, 
"b", 2); 
+        Navigator<MutableMap<Object, Object>> j = 
Jsonya.newInstance().add(map);
+        Assert.assertEquals( j.toString(), "{ \"a\": 1, \"b\": 2 }");
+        map.put("b", 1);
+        Assert.assertEquals( j.toString(), "{ \"a\": 1, \"b\": 1 }");
+    }
+
+    @Test
+    public void testAddListAtRootAddsReference() {
+        MutableList<Object> list = MutableList.<Object>of("a", "b"); 
+        Navigator<MutableMap<Object, Object>> j = 
Jsonya.newInstance().add(list);
+        Assert.assertEquals( j.toString(), "[ \"a\", \"b\" ]");
+        list.append("c");
+        Assert.assertEquals( j.toString(), "[ \"a\", \"b\", \"c\" ]");
+    }
+
+    @Test
+    public void testAddStringToList() {
+        Navigator<MutableMap<Object, Object>> j = 
Jsonya.newInstance().at("root").list().add("a", "b").root();
+        Assert.assertEquals( j.toString(), "{ \"root\": [ \"a\", \"b\" ] }");
+    }
+
+    @Test
+    public void testAddStringToListAtRoot() {
+        Navigator<MutableMap<Object, Object>> j = 
Jsonya.newInstance().list().add("a", "b").root();
+        Assert.assertEquals( j.toString(), "[ \"a\", \"b\" ]" );
+        Assert.assertEquals( j.get(), MutableList.of("a", "b") );
+    }
+
+    @Test
+    public void testAddStringToRoot() {
+        Navigator<MutableMap<Object, Object>> j = 
Jsonya.newInstance().add("a");
+        Assert.assertEquals( j.toString(), "\"a\"");
+        Assert.assertEquals( j.get(), "a");
+    }
+
+    @Test
+    public void testAddStringsAtRootDefaultsToMap() {
+        Navigator<MutableMap<Object, Object>> j = 
Jsonya.newInstance().add("a", 1);
+        Assert.assertEquals( j.toString(), "{ \"a\": 1 }");
+    }
+
+    @Test
+    public void testAddOddStringsAtRootIsError() {
+        try {
+            Jsonya.newInstance().add("a", 1, "b");
+            Asserts.shouldHaveFailedPreviously();
+        } catch (Exception e) {
+            Asserts.expectedFailureContainsIgnoreCase(e, "odd");
+        }
+    }
+
+    @Test
+    public void testAddStringAtKey() {
+        Navigator<MutableMap<Object, Object>> j = 
Jsonya.newInstance().at("root").add("value").root();
+        Assert.assertEquals( j.get(), MutableMap.of("root", "value"));
+    }
+    public void testAddStringAtKeySequence() {
+        Navigator<MutableMap<Object, Object>> j = Jsonya.newInstance().at("1", 
"2").add("value");
+        Assert.assertEquals( j.get(), MutableMap.of("1", MutableMap.of("2", 
"value")));
+    }
 }

Reply via email to