brooklyn-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From rich...@apache.org
Subject [2/4] git commit: expand the methods available for working with functionals and json
Date Tue, 17 Jun 2014 07:15:15 GMT
expand the methods available for working with functionals and json


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

Branch: refs/heads/master
Commit: 9e9f50c6e02fbe35b36395404e53dd9b4ea1f81d
Parents: b651201
Author: Alex Heneveld <alex.heneveld@cloudsoftcorp.com>
Authored: Thu Jun 12 23:25:25 2014 -0700
Committer: Alex Heneveld <alex.heneveld@cloudsoftcorp.com>
Committed: Sat Jun 14 20:03:56 2014 -0700

----------------------------------------------------------------------
 .../event/feed/http/HttpValueFunctions.java     | 23 +++--
 .../brooklyn/event/feed/http/JsonFunctions.java | 98 ++++++++++++++++++--
 .../event/feed/http/JsonFunctionsTest.java      | 88 ++++++++++++++++++
 .../java/brooklyn/util/collections/Jsonya.java  | 49 +++++++++-
 .../java/brooklyn/util/guava/Functionals.java   | 23 +++++
 .../brooklyn/util/guava/MaybeFunctions.java     | 34 +++++++
 .../java/brooklyn/util/guava/TypeTokens.java    |  7 ++
 .../brooklyn/util/collections/JsonyaTest.java   | 41 +++++++-
 8 files changed, 336 insertions(+), 27 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/9e9f50c6/core/src/main/java/brooklyn/event/feed/http/HttpValueFunctions.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/event/feed/http/HttpValueFunctions.java b/core/src/main/java/brooklyn/event/feed/http/HttpValueFunctions.java
index c4d5c8f..b1d0ca4 100644
--- a/core/src/main/java/brooklyn/event/feed/http/HttpValueFunctions.java
+++ b/core/src/main/java/brooklyn/event/feed/http/HttpValueFunctions.java
@@ -2,8 +2,7 @@ package brooklyn.event.feed.http;
 
 import java.util.List;
 
-import javax.annotation.Nullable;
-
+import brooklyn.util.guava.Functionals;
 import brooklyn.util.http.HttpToolResponse;
 
 import com.google.common.base.Function;
@@ -64,19 +63,19 @@ public class HttpValueFunctions {
         };
     }
     
+    /** @deprecated since 0.7.0 use {@link Functionals#chain(Function, Function)} */ @Deprecated
     public static <A,B,C> Function<A,C> chain(final Function<A,? extends B>
f1, final Function<B,C> f2) {
-        return new Function<A,C>() {
-            @Override public C apply(@Nullable A input) {
-                return f2.apply(f1.apply(input));
-            }
-        };
+        return Functionals.chain(f1, f2);
     }
     
+    /** @deprecated since 0.7.0 use {@link Functionals#chain(Function, Function, Function)}
*/ @Deprecated
     public static <A,B,C,D> Function<A,D> chain(final Function<A,? extends
B> f1, final Function<B,? extends C> f2, final Function<C,D> f3) {
-        return new Function<A,D>() {
-            @Override public D apply(@Nullable A input) {
-                return f3.apply(f2.apply(f1.apply(input)));
-            }
-        };
+        return Functionals.chain(f1, f2, f3);
+    }
+
+    /** @deprecated since 0.7.0 use {@link Functionals#chain(Function, Function, Function,
Function)} */ @Deprecated
+    public static <A,B,C,D,E> Function<A,E> chain(final Function<A,? extends
B> f1, final Function<B,? extends C> f2, final Function<C,? extends D> f3,
final Function<D,E> f4) {
+        return Functionals.chain(f1, f2, f3, f4);
     }
+
 }

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/9e9f50c6/core/src/main/java/brooklyn/event/feed/http/JsonFunctions.java
----------------------------------------------------------------------
diff --git a/core/src/main/java/brooklyn/event/feed/http/JsonFunctions.java b/core/src/main/java/brooklyn/event/feed/http/JsonFunctions.java
index 7eee164..46b4635 100644
--- a/core/src/main/java/brooklyn/event/feed/http/JsonFunctions.java
+++ b/core/src/main/java/brooklyn/event/feed/http/JsonFunctions.java
@@ -7,6 +7,12 @@ import java.util.Arrays;
 import java.util.List;
 import java.util.NoSuchElementException;
 
+import javax.annotation.Nullable;
+
+import brooklyn.util.guava.Functionals;
+import brooklyn.util.guava.Maybe;
+import brooklyn.util.guava.MaybeFunctions;
+
 import com.google.common.base.Function;
 import com.google.common.base.Splitter;
 import com.google.common.collect.Lists;
@@ -40,17 +46,23 @@ public class JsonFunctions {
         };
     }
 
-    public static Function<JsonElement, JsonElement> walk(String elements) {
-        Iterable<String> iterable = Splitter.on('.').split(elements);
-        return walk(iterable);
+    
+    /** as {@link #walkM(Iterable)} taking a single string consisting of a dot separated
path */
+    public static Function<JsonElement, JsonElement> walk(String elementOrDotSeparatedElements)
{
+        return walk( Splitter.on('.').split(elementOrDotSeparatedElements) );
     }
 
-    public static Function<JsonElement, JsonElement> walk(Iterable<String> elements)
{
-        String[] array = Lists.newArrayList(elements).toArray(new String[0]);
-        return walk(array);
+    /** as {@link #walkM(Iterable)} taking a series of strings (dot separators not respected
here) */
+    public static Function<JsonElement, JsonElement> walk(final String... elements)
{
+        return walk(Arrays.asList(elements));
     }
 
-    public static Function<JsonElement, JsonElement> walk(final String... elements)
{
+    /** returns a function which traverses the supplied path of entries in a json object
(maps of maps of maps...), 
+     * @throws NoSuchElementException if any path is not present as a key in that map */
+    public static Function<JsonElement, JsonElement> walk(final Iterable<String>
elements) {
+        // could do this instead, pointing at Maybe for this, and for walkN, but it's slightly
less efficient
+//      return Functionals.chain(MaybeFunctions.<JsonElement>wrap(), walkM(elements),
MaybeFunctions.<JsonElement>get());
+        
         return new Function<JsonElement, JsonElement>() {
             @Override public JsonElement apply(JsonElement input) {
                 JsonElement curr = input;
@@ -58,13 +70,68 @@ public class JsonFunctions {
                     JsonObject jo = curr.getAsJsonObject();
                     curr = jo.get(element);
                     if (curr==null) 
-                        throw new NoSuchElementException("No element '"+element+" in JSON,
when walking "+Arrays.asList(elements));
+                        throw new NoSuchElementException("No element '"+element+" in JSON,
when walking "+elements);
                 }
                 return curr;
             }
         };
     }
+
     
+    /** as {@link #walk(String)} but if any element is not found it simply returns null */
+    public static Function<JsonElement, JsonElement> walkN(@Nullable String elements)
{
+        return walkN( Splitter.on('.').split(elements) );
+    }
+
+    /** as {@link #walk(String...))} but if any element is not found it simply returns null
*/
+    public static Function<JsonElement, JsonElement> walkN(final String... elements)
{
+        return walkN(Arrays.asList(elements));
+    }
+
+    /** as {@link #walk(Iterable))} but if any element is not found it simply returns null
*/
+    public static Function<JsonElement, JsonElement> walkN(final Iterable<String>
elements) {
+        return new Function<JsonElement, JsonElement>() {
+            @Override public JsonElement apply(JsonElement input) {
+                JsonElement curr = input;
+                for (String element : elements) {
+                    if (curr==null) return null;
+                    JsonObject jo = curr.getAsJsonObject();
+                    curr = jo.get(element);
+                }
+                return curr;
+            }
+        };
+    }
+
+    /** as {@link #walk(String))} and {@link #walk(Iterable)} */
+    public static Function<Maybe<JsonElement>, Maybe<JsonElement>> walkM(@Nullable
String elements) {
+        return walkM( Splitter.on('.').split(elements) );
+    }
+
+    /** as {@link #walk(String...))} and {@link #walk(Iterable)} */
+    public static Function<Maybe<JsonElement>, Maybe<JsonElement>> walkM(final
String... elements) {
+        return walkM(Arrays.asList(elements));
+    }
+
+    /** as {@link #walk(Iterable))} but working with objects which {@link Maybe} contain
{@link JsonElement},
+     * simply preserving a {@link Maybe#absent()} object if additional walks are requested
upon it
+     * (cf jquery) */
+    public static Function<Maybe<JsonElement>, Maybe<JsonElement>> walkM(final
Iterable<String> elements) {
+        return new Function<Maybe<JsonElement>, Maybe<JsonElement>>() {
+            @Override public Maybe<JsonElement> apply(Maybe<JsonElement> input)
{
+                Maybe<JsonElement> curr = input;
+                for (String element : elements) {
+                    if (curr.isAbsent()) return curr;
+                    JsonObject jo = curr.get().getAsJsonObject();
+                    JsonElement currO = jo.get(element);
+                    if (currO==null) return Maybe.absent("No element '"+element+" in JSON,
when walking "+elements);
+                    curr = Maybe.of(currO);
+                }
+                return curr;
+            }
+        };
+    }
+
     @SuppressWarnings("unchecked")
     public static <T> Function<JsonElement, T> cast(final Class<T> expected)
{
         return new Function<JsonElement, T>() {
@@ -120,4 +187,19 @@ public class JsonFunctions {
             }
         };
     }
+    
+    public static <T> Function<Maybe<JsonElement>, T> castM(final Class<T>
expected) {
+        return Functionals.chain(MaybeFunctions.<JsonElement>get(), cast(expected));
+    }
+    
+    public static <T> Function<Maybe<JsonElement>, T> castM(final Class<T>
expected, final T defaultValue) {
+        return new Function<Maybe<JsonElement>, T>() {
+            @Override
+            public T apply(Maybe<JsonElement> input) {
+                if (input.isAbsent()) return defaultValue;
+                return cast(expected).apply(input.get());
+            }
+        };
+    }
+
 }

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/9e9f50c6/core/src/test/java/brooklyn/event/feed/http/JsonFunctionsTest.java
----------------------------------------------------------------------
diff --git a/core/src/test/java/brooklyn/event/feed/http/JsonFunctionsTest.java b/core/src/test/java/brooklyn/event/feed/http/JsonFunctionsTest.java
new file mode 100644
index 0000000..d555b37
--- /dev/null
+++ b/core/src/test/java/brooklyn/event/feed/http/JsonFunctionsTest.java
@@ -0,0 +1,88 @@
+package brooklyn.event.feed.http;
+
+import java.util.NoSuchElementException;
+
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+import brooklyn.util.collections.Jsonya;
+import brooklyn.util.collections.Jsonya.Navigator;
+import brooklyn.util.collections.MutableMap;
+import brooklyn.util.guava.Functionals;
+import brooklyn.util.guava.Maybe;
+
+import com.google.gson.JsonElement;
+import com.google.gson.JsonParser;
+
+public class JsonFunctionsTest {
+
+    public static JsonElement europeMap() {
+        Navigator<MutableMap<Object, Object>> europe = Jsonya.newInstance().at("europe",
"uk", "edinburgh")
+                .put("population", 500*1000)
+                .put("weather", "wet", "lighting", "dark")
+                .root().at("europe").at("france").put("population", 80*1000*1000)
+                .root();
+        return new JsonParser().parse( europe.toString() );
+    }
+
+    @Test
+    public void testWalk1() {
+        JsonElement pop = JsonFunctions.walk("europe", "france", "population").apply(europeMap());
+        Assert.assertEquals( (int)JsonFunctions.cast(Integer.class).apply(pop), 80*1000*1000
);
+    }
+
+    @Test
+    public void testWalk2() {
+        String weather = Functionals.chain(
+            JsonFunctions.walk("europe.uk.edinburgh.weather"),
+            JsonFunctions.cast(String.class) ).apply(europeMap());
+        Assert.assertEquals(weather, "wet");
+    }
+
+    @Test(expectedExceptions=NoSuchElementException.class)
+    public void testWalkWrong() {
+        Functionals.chain(
+            JsonFunctions.walk("europe", "spain", "barcelona"),
+            JsonFunctions.cast(String.class) ).apply(europeMap());
+    }
+
+
+    @Test
+    public void testWalkM() {
+        Maybe<JsonElement> pop = JsonFunctions.walkM("europe", "france", "population").apply(
Maybe.of(europeMap()) );
+        Assert.assertEquals( (int)JsonFunctions.castM(Integer.class).apply(pop), 80*1000*1000
);
+    }
+
+    @Test
+    public void testWalkMWrong1() {
+        Maybe<JsonElement> m = JsonFunctions.walkM("europe", "spain", "barcelona").apply(
Maybe.of( europeMap()) );
+        Assert.assertTrue(m.isAbsent());
+    }
+
+    @Test(expectedExceptions=Exception.class)
+    public void testWalkMWrong2() {
+        Maybe<JsonElement> m = JsonFunctions.walkM("europe", "spain", "barcelona").apply(
Maybe.of( europeMap()) );
+        JsonFunctions.castM(String.class).apply(m);
+    }
+
+    
+    @Test
+    public void testWalkN() {
+        JsonElement pop = JsonFunctions.walkN("europe", "france", "population").apply( europeMap()
);
+        Assert.assertEquals( (int)JsonFunctions.cast(Integer.class).apply(pop), 80*1000*1000
);
+    }
+
+    @Test
+    public void testWalkNWrong1() {
+        JsonElement m = JsonFunctions.walkN("europe", "spain", "barcelona").apply( europeMap()
);
+        Assert.assertNull(m);
+    }
+
+    public void testWalkNWrong2() {
+        JsonElement m = JsonFunctions.walkN("europe", "spain", "barcelona").apply( europeMap()
);
+        String n = JsonFunctions.cast(String.class).apply(m);
+        Assert.assertNull(n);
+    }
+
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/9e9f50c6/utils/common/src/main/java/brooklyn/util/collections/Jsonya.java
----------------------------------------------------------------------
diff --git a/utils/common/src/main/java/brooklyn/util/collections/Jsonya.java b/utils/common/src/main/java/brooklyn/util/collections/Jsonya.java
index c4bac40..ef39165 100644
--- a/utils/common/src/main/java/brooklyn/util/collections/Jsonya.java
+++ b/utils/common/src/main/java/brooklyn/util/collections/Jsonya.java
@@ -7,7 +7,11 @@ import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
+import java.util.Stack;
 
+import javax.annotation.Nonnull;
+
+import brooklyn.util.guava.Maybe;
 import brooklyn.util.text.StringEscapes.JavaStringEscapes;
 
 import com.google.common.annotations.Beta;
@@ -94,6 +98,7 @@ public class Jsonya {
         protected final Object root;
         protected final Class<? extends Map> mapType;
         protected Object focus;
+        protected Stack<Object> focusStack = new Stack<Object>();
         protected Function<Object,Void> creationInPreviousFocus;
         protected Function<Object,Object> translator;
 
@@ -109,15 +114,33 @@ public class Jsonya {
         public Object get() {
             return focus;
         }
+
+        /** as {@link #get()} but always wrapped in a {@link Maybe}, absent if null */
+        public @Nonnull Maybe<Object> getMaybe() {
+            return Maybe.fromNullable(focus);
+        }
         
-        /** returns the object at the focus, casted to the given type, null if none */
+        /** returns the object at the focus, casted to the given type, null if none
+         * @throws ClassCastException if object exists here but of the wrong type  */
         public <V> V get(Class<V> type) {
             return (V)focus;
         }
-        
+
+        /** as {@link #get(Class)} but always wrapped in a {@link Maybe}, absent if null
+         * @throws ClassCastException if object exists here but of the wrong type  */
+        public @Nonnull <V> Maybe<V> getMaybe(Class<V> type) {
+            return Maybe.fromNullable(get(type));
+        }
+
+        /** gets the object at the indicated path from the current focus
+         * (without changing the path to that focus; use {@link #at(Object, Object...)} to
change focus) */
+        // Jun 2014, semantics changed so that focus does not change, which is more natural
         public Object get(Object pathSegment, Object ...furtherPathSegments) {
+            push();
             at(pathSegment, furtherPathSegments);
-            return get();
+            Object result = get();
+            pop();
+            return result;
         }
         
         public Navigator<T> root() {
@@ -141,6 +164,12 @@ public class Jsonya {
             map();
             return (T)focus;
         }
+        
+        /** as {@link #getFocusMap()} but always wrapped in a {@link Maybe}, absent if null
+         * @throws ClassCastException if object exists here but of the wrong type  */
+        public @Nonnull Maybe<T> getFocusMapMaybe() {
+            return Maybe.fromNullable(getFocusMap());
+        }
 
         /** specifies a translator function to use when new data is added;
          * by default everything is added as a literal (ie {@link Functions#identity()}),

@@ -168,7 +197,19 @@ public class Jsonya {
 
         // ------------- navigation (map mainly)
 
-        /** returns the navigator focussed at the indicated key sequence in the given map
*/
+        /** pushes the current focus to a stack, so that this location will be restored on
the corresponding {@link #pop()} */
+        public Navigator<T> push() {
+            focusStack.push(focus);
+            return this;
+        }
+        
+        /** pops the most recently pushed focus, so that it returns to the last location
{@link #push()}ed */
+        public Navigator<T> pop() {
+            focus = focusStack.pop();
+            return this;
+        }
+        
+        /** returns the navigator moved to focus at the indicated key sequence in the given
map */
         public Navigator<T> at(Object pathSegment, Object ...furtherPathSegments) {
             down(pathSegment);
             return atArray(furtherPathSegments);

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/9e9f50c6/utils/common/src/main/java/brooklyn/util/guava/Functionals.java
----------------------------------------------------------------------
diff --git a/utils/common/src/main/java/brooklyn/util/guava/Functionals.java b/utils/common/src/main/java/brooklyn/util/guava/Functionals.java
new file mode 100644
index 0000000..d026710
--- /dev/null
+++ b/utils/common/src/main/java/brooklyn/util/guava/Functionals.java
@@ -0,0 +1,23 @@
+package brooklyn.util.guava;
+
+import com.google.common.base.Function;
+import com.google.common.base.Functions;
+
+public class Functionals {
+
+    /** applies f1 to the input, then the result of that is passed to f2 (note opposite semantics
to {@link Functions#compose(Function, Function)} */ 
+    public static <A,B,C> Function<A,C> chain(final Function<A,? extends B>
f1, final Function<B,C> f2) {
+        return Functions.compose(f2, f1);
+    }
+    
+    /** applies f1 to the input, then f2 to that result, then f3 to that result */
+    public static <A,B,C,D> Function<A,D> chain(final Function<A,? extends
B> f1, final Function<B,? extends C> f2, final Function<C,D> f3) {
+        return chain(f1, chain(f2, f3));
+    }
+    
+    /** applies f1 to the input, then f2 to that result, then f3 to that result, then f4
to that result */
+    public static <A,B,C,D,E> Function<A,E> chain(final Function<A,? extends
B> f1, final Function<B,? extends C> f2, final Function<C,? extends D> f3,
final Function<D,E> f4) {
+        return chain(f1, chain(f2, chain(f3, f4)));
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/9e9f50c6/utils/common/src/main/java/brooklyn/util/guava/MaybeFunctions.java
----------------------------------------------------------------------
diff --git a/utils/common/src/main/java/brooklyn/util/guava/MaybeFunctions.java b/utils/common/src/main/java/brooklyn/util/guava/MaybeFunctions.java
new file mode 100644
index 0000000..8836e73
--- /dev/null
+++ b/utils/common/src/main/java/brooklyn/util/guava/MaybeFunctions.java
@@ -0,0 +1,34 @@
+package brooklyn.util.guava;
+
+import com.google.common.base.Function;
+
+public class MaybeFunctions {
+
+    public static <T> Function<T, Maybe<T>> wrap() {
+        return new Function<T, Maybe<T>>() {
+            @Override
+            public Maybe<T> apply(T input) {
+                return Maybe.fromNullable(input);
+            }
+        };
+    }
+
+    public static <T> Function<Maybe<T>, T> get() {
+        return new Function<Maybe<T>, T>() {
+            @Override
+            public T apply(Maybe<T> input) {
+                return input.get();
+            }
+        };
+    }
+
+    public static <T> Function<Maybe<T>, T> or(final T value) {
+        return new Function<Maybe<T>, T>() {
+            @Override
+            public T apply(Maybe<T> input) {
+                return input.or(value);
+            }
+        };
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/9e9f50c6/utils/common/src/main/java/brooklyn/util/guava/TypeTokens.java
----------------------------------------------------------------------
diff --git a/utils/common/src/main/java/brooklyn/util/guava/TypeTokens.java b/utils/common/src/main/java/brooklyn/util/guava/TypeTokens.java
index ecd2772..26105f4 100644
--- a/utils/common/src/main/java/brooklyn/util/guava/TypeTokens.java
+++ b/utils/common/src/main/java/brooklyn/util/guava/TypeTokens.java
@@ -44,4 +44,11 @@ public class TypeTokens {
         throw new IllegalStateException("Both indicators of type are null");
     }
 
+    /** gets the Class<T> object from a token; normal methods return Class<? super
T> which may technically be correct 
+     * with generics but this sloppily but handily gives you Class<T> which is usually
what you have anyway */
+    @SuppressWarnings({ "rawtypes", "unchecked" })
+    public static <T> Class<T> getRawRawType(TypeToken<T> token) {
+        return (Class)token.getRawType();
+    }
+    
 }

http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/9e9f50c6/utils/common/src/test/java/brooklyn/util/collections/JsonyaTest.java
----------------------------------------------------------------------
diff --git a/utils/common/src/test/java/brooklyn/util/collections/JsonyaTest.java b/utils/common/src/test/java/brooklyn/util/collections/JsonyaTest.java
index 14306e5..1650c9c 100644
--- a/utils/common/src/test/java/brooklyn/util/collections/JsonyaTest.java
+++ b/utils/common/src/test/java/brooklyn/util/collections/JsonyaTest.java
@@ -13,7 +13,7 @@ import com.google.common.collect.ImmutableSet;
 
 public class JsonyaTest {
     
-    protected Navigator<MutableMap<Object,Object>> europeMap() {
+    public static Navigator<MutableMap<Object,Object>> europeMap() {
         return Jsonya.newInstance().at("europe", "uk", "edinburgh")
                 .put("population", 500*1000)
                 .put("weather", "wet", "lighting", "dark")
@@ -50,10 +50,10 @@ public class JsonyaTest {
         n.at("europe", "uk", "neighbours").list().add("ireland")
             .root().at("europe", "france", "neighbours").list().add("spain", "germany").add("switzerland")
             .root().at("europe", "france", "neighbours").add("lux");
-        Object l = n.root().get("europe", "france", "neighbours");
+        Object l = n.root().at("europe", "france", "neighbours").get();
         Assert.assertTrue(l instanceof List);
         Assert.assertEquals(((List)l).size(), 4);
-        // currently remembers last position; not sure that behaviour will continue however...
+        // this wants a map, so it creates a map in the list
         n.put("east", "germany", "south", "spain");
         Assert.assertEquals(((List)l).size(), 5);
         Map nd = (Map) ((List)l).get(4);
@@ -135,4 +135,39 @@ public class JsonyaTest {
         Assert.assertEquals(MutableMap.copyOf(mapP).add('C', null), MutableMap.copyOf(map).add('C',
null));
     }
 
+    @Test
+    public void testJsonyaBadPathNull() {
+        Navigator<MutableMap<Object, Object>> m = europeMap();
+        // does not create (but if we 'pushed' it would)
+        Assert.assertNull( m.at("europe",  "spain", "barcelona").get() );
+        Assert.assertNull( m.root().at("europe").at("spain").at("barcelona").get() );
+    }
+    @Test
+    public void testJsonyaMaybe() {
+        Navigator<MutableMap<Object, Object>> m = europeMap();
+        Assert.assertEquals( m.at("europe",  "spain", "barcelona").getMaybe().or("norealabc"),
"norealabc" );
+        Assert.assertEquals(m.root().at("europe").getFocusMap().keySet(), MutableSet.of("uk",
"france"));
+    }
+    
+    @Test
+    public void testJsonyaPushPop() {
+        Navigator<MutableMap<Object, Object>> m = europeMap();
+        Assert.assertTrue(m.getFocusMap().containsKey("europe"));
+        Assert.assertFalse(m.getFocusMap().containsKey("edinburgh"));
+        m.push();
+        
+        m.at("europe", "uk");
+        Assert.assertTrue(m.getFocusMap().containsKey("edinburgh"));
+        Assert.assertFalse(m.getFocusMap().containsKey("europe"));
+        
+        m.pop();
+        Assert.assertTrue(m.getFocusMap().containsKey("europe"));
+        Assert.assertFalse(m.getFocusMap().containsKey("edinburgh"));
+
+        // also check 'get' does not change focus
+        m.get("europe", "uk");
+        Assert.assertTrue(m.getFocusMap().containsKey("europe"));
+        Assert.assertFalse(m.getFocusMap().containsKey("edinburgh"));
+    }
+
 }


Mime
View raw message