You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@brooklyn.apache.org by he...@apache.org on 2016/07/07 09:18:37 UTC

[03/10] brooklyn-server git commit: Jsonya allows primitives and lists at the root now

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 <al...@cloudsoftcorp.com>
Authored: Fri Jun 24 11:00:14 2016 +0100
Committer: Alex Heneveld <al...@cloudsoftcorp.com>
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")));
+    }
 }