coercing from string to list can now handle objects

now have two methods for parsing a list as a string, depending whether it wants 
object or string


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

Branch: refs/heads/master
Commit: 89c692e4951deb17cbca05567dea74ce2dd78860
Parents: ddc6c71
Author: Alex Heneveld <alex.henev...@cloudsoftcorp.com>
Authored: Fri Aug 31 00:24:32 2018 +0100
Committer: Alex Heneveld <alex.henev...@cloudsoftcorp.com>
Committed: Fri Aug 31 00:24:32 2018 +0100

----------------------------------------------------------------------
 .../core/location/BasicLocationRegistry.java    |   2 +-
 .../brooklyn/core/location/PortRanges.java      |   2 +-
 .../location/multi/MultiLocationResolver.java   |   2 +-
 .../LocalhostLocationResolverTest.java          |   4 +-
 .../main/java/org/apache/brooklyn/cli/Main.java |   2 +-
 .../coerce/CommonAdaptorTypeCoercions.java      |  10 +-
 .../brooklyn/util/text/StringEscapes.java       | 118 ++++++++++++++++++-
 .../brooklyn/util/text/StringEscapesTest.java   |  80 ++++++++++++-
 8 files changed, 206 insertions(+), 14 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/89c692e4/core/src/main/java/org/apache/brooklyn/core/location/BasicLocationRegistry.java
----------------------------------------------------------------------
diff --git 
a/core/src/main/java/org/apache/brooklyn/core/location/BasicLocationRegistry.java
 
b/core/src/main/java/org/apache/brooklyn/core/location/BasicLocationRegistry.java
index b28e560..ae77d90 100644
--- 
a/core/src/main/java/org/apache/brooklyn/core/location/BasicLocationRegistry.java
+++ 
b/core/src/main/java/org/apache/brooklyn/core/location/BasicLocationRegistry.java
@@ -513,7 +513,7 @@ public class BasicLocationRegistry implements 
LocationRegistry {
     @Override 
     public List<Location> getListOfLocationsManaged(Object l) {
         if (l==null) l = Collections.emptyList();
-        if (l instanceof String) l = 
JavaStringEscapes.unwrapJsonishListIfPossible((String)l);
+        if (l instanceof String) l = 
JavaStringEscapes.unwrapJsonishListStringIfPossible((String)l);
         if (l instanceof Iterable) return 
getFromIterableListOfLocationsManaged((Iterable<?>)l);
         throw new IllegalArgumentException("Location list must be supplied as 
a collection or a string, not "+
             JavaClassNames.simpleClassName(l)+"/"+l);

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/89c692e4/core/src/main/java/org/apache/brooklyn/core/location/PortRanges.java
----------------------------------------------------------------------
diff --git 
a/core/src/main/java/org/apache/brooklyn/core/location/PortRanges.java 
b/core/src/main/java/org/apache/brooklyn/core/location/PortRanges.java
index 22a037c..b3368e0 100644
--- a/core/src/main/java/org/apache/brooklyn/core/location/PortRanges.java
+++ b/core/src/main/java/org/apache/brooklyn/core/location/PortRanges.java
@@ -196,7 +196,7 @@ public class PortRanges {
         for (Object o: c) {
             if (o instanceof Integer) l.add(fromInteger((Integer)o));
             else if (o instanceof String)
-                for (String string : 
JavaStringEscapes.unwrapJsonishListIfPossible((String)o))
+                for (String string : 
JavaStringEscapes.unwrapJsonishListStringIfPossible((String)o))
                     l.add(fromString(string));
             else if (o instanceof Iterable) 
l.add(fromIterable((Iterable<?>)o));
             else if (o instanceof int[]) 
l.add(fromIterable(Ints.asList((int[])o)));

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/89c692e4/core/src/main/java/org/apache/brooklyn/location/multi/MultiLocationResolver.java
----------------------------------------------------------------------
diff --git 
a/core/src/main/java/org/apache/brooklyn/location/multi/MultiLocationResolver.java
 
b/core/src/main/java/org/apache/brooklyn/location/multi/MultiLocationResolver.java
index 765be5f..8ab1b98 100644
--- 
a/core/src/main/java/org/apache/brooklyn/location/multi/MultiLocationResolver.java
+++ 
b/core/src/main/java/org/apache/brooklyn/location/multi/MultiLocationResolver.java
@@ -99,7 +99,7 @@ public class MultiLocationResolver implements 
LocationResolver {
         Object targetSpecs = locationArgs.remove("targets");
 
         if (targetSpecs instanceof String) {
-            for (String targetSpec : 
JavaStringEscapes.unwrapJsonishListIfPossible((String)targetSpecs)) {
+            for (String targetSpec : 
JavaStringEscapes.unwrapJsonishListStringIfPossible((String)targetSpecs)) {
                 
targets.add(managementContext.getLocationRegistry().getLocationSpec(targetSpec, 
ImmutableMap.of()).get());
             }
         } else if (targetSpecs instanceof Iterable) {

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/89c692e4/core/src/test/java/org/apache/brooklyn/location/localhost/LocalhostLocationResolverTest.java
----------------------------------------------------------------------
diff --git 
a/core/src/test/java/org/apache/brooklyn/location/localhost/LocalhostLocationResolverTest.java
 
b/core/src/test/java/org/apache/brooklyn/location/localhost/LocalhostLocationResolverTest.java
index 3e17883..1dc9a81 100644
--- 
a/core/src/test/java/org/apache/brooklyn/location/localhost/LocalhostLocationResolverTest.java
+++ 
b/core/src/test/java/org/apache/brooklyn/location/localhost/LocalhostLocationResolverTest.java
@@ -159,14 +159,14 @@ public class LocalhostLocationResolverTest {
     @Test
     public void testRegistryCommaResolution() throws 
NoMachinesAvailableException {
         List<Location> l;
-        l = 
getLocationResolver().getListOfLocationsManaged(JavaStringEscapes.unwrapJsonishListIfPossible("localhost,localhost,localhost"));
+        l = 
getLocationResolver().getListOfLocationsManaged(JavaStringEscapes.unwrapJsonishListStringIfPossible("localhost,localhost,localhost"));
         assertEquals(l.size(), 3, "l="+l);
         assertTrue(l.get(0) instanceof LocalhostMachineProvisioningLocation, 
"l="+l);
         assertTrue(l.get(1) instanceof LocalhostMachineProvisioningLocation, 
"l="+l);
         assertTrue(l.get(2) instanceof LocalhostMachineProvisioningLocation, 
"l="+l);
 
         // And check works if comma in brackets
-        l = 
getLocationResolver().getListOfLocationsManaged(JavaStringEscapes.unwrapJsonishListIfPossible(
+        l = 
getLocationResolver().getListOfLocationsManaged(JavaStringEscapes.unwrapJsonishListStringIfPossible(
             "[ \"byon:(hosts=\\\"192.168.0.1\\\",user=bob)\", 
\"byon:(hosts=\\\"192.168.0.2\\\",user=bob2)\" ]"));
         assertEquals(l.size(), 2, "l="+l);
         assertTrue(l.get(0) instanceof FixedListMachineProvisioningLocation, 
"l="+l);

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/89c692e4/server-cli/src/main/java/org/apache/brooklyn/cli/Main.java
----------------------------------------------------------------------
diff --git a/server-cli/src/main/java/org/apache/brooklyn/cli/Main.java 
b/server-cli/src/main/java/org/apache/brooklyn/cli/Main.java
index cc473de..4711b9f 100644
--- a/server-cli/src/main/java/org/apache/brooklyn/cli/Main.java
+++ b/server-cli/src/main/java/org/apache/brooklyn/cli/Main.java
@@ -552,7 +552,7 @@ public class Main extends AbstractMain {
                     .ignoreCatalogErrors(!startupFailOnCatalogErrors)
                     .ignoreWebErrors(startupContinueOnWebErrors)
                     .ignoreAppErrors(!startupFailOnManagedAppsErrors)
-                    .locations(Strings.isBlank(locations) ? 
ImmutableList.<String>of() : 
JavaStringEscapes.unwrapJsonishListIfPossible(locations));
+                    .locations(Strings.isBlank(locations) ? 
ImmutableList.<String>of() : 
JavaStringEscapes.unwrapJsonishListStringIfPossible(locations));
             
             launcher.restServer(!noConsole);
             if (useHttps) {

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/89c692e4/utils/common/src/main/java/org/apache/brooklyn/util/javalang/coerce/CommonAdaptorTypeCoercions.java
----------------------------------------------------------------------
diff --git 
a/utils/common/src/main/java/org/apache/brooklyn/util/javalang/coerce/CommonAdaptorTypeCoercions.java
 
b/utils/common/src/main/java/org/apache/brooklyn/util/javalang/coerce/CommonAdaptorTypeCoercions.java
index 4072bc1..3c31bbc 100644
--- 
a/utils/common/src/main/java/org/apache/brooklyn/util/javalang/coerce/CommonAdaptorTypeCoercions.java
+++ 
b/utils/common/src/main/java/org/apache/brooklyn/util/javalang/coerce/CommonAdaptorTypeCoercions.java
@@ -367,14 +367,16 @@ public class CommonAdaptorTypeCoercions {
     public void registerCollectionJsonAdapters() {
         registerAdapter(String.class, List.class, new Function<String,List>() {
             @Override
-            public List<String> apply(final String input) {
-                return JavaStringEscapes.unwrapJsonishListIfPossible(input);
+            public List<Object> apply(final String input) {
+                return JavaStringEscapes.tryUnwrapJsonishList(input).orNull();
             }
         });
         registerAdapter(String.class, Set.class, new Function<String,Set>() {
             @Override
-            public Set<String> apply(final String input) {
-                return 
MutableSet.copyOf(JavaStringEscapes.unwrapJsonishListIfPossible(input)).asUnmodifiable();
+            public Set<Object> apply(final String input) {
+                List<Object> l = 
JavaStringEscapes.tryUnwrapJsonishList(input).orNull();
+                if (l==null) return null;
+                return MutableSet.copyOf(l).asUnmodifiable();
             }
         });
         registerAdapter(String.class, Map.class, new Function<String,Map>() {

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/89c692e4/utils/common/src/main/java/org/apache/brooklyn/util/text/StringEscapes.java
----------------------------------------------------------------------
diff --git 
a/utils/common/src/main/java/org/apache/brooklyn/util/text/StringEscapes.java 
b/utils/common/src/main/java/org/apache/brooklyn/util/text/StringEscapes.java
index f713ff6..f3df2b2 100644
--- 
a/utils/common/src/main/java/org/apache/brooklyn/util/text/StringEscapes.java
+++ 
b/utils/common/src/main/java/org/apache/brooklyn/util/text/StringEscapes.java
@@ -23,16 +23,21 @@ import java.io.UnsupportedEncodingException;
 import java.net.URLEncoder;
 import java.util.ArrayList;
 import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
 
 import javax.annotation.Nonnull;
 
 import org.apache.brooklyn.util.collections.MutableList;
 import org.apache.brooklyn.util.exceptions.Exceptions;
+import org.apache.brooklyn.util.guava.Maybe;
 import org.apache.brooklyn.util.net.URLParamEncoder;
+import org.apache.brooklyn.util.yaml.Yamls;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 import com.google.common.base.Function;
+import com.google.common.collect.Iterables;
 
 public class StringEscapes {
 
@@ -315,12 +320,121 @@ public class StringEscapes {
             throw new IllegalArgumentException("String '"+s+"' is not a valid 
Java string (unterminated string)");
         }
         
+        /** @deprecated since 1.0.0, use {@link 
#unwrapJsonishListStringIfPossible(String)} (old semantics)
+         * or {@link #unwrapJsonishListStringIfPossible(String)} (improved) */
+        public static List<String> unwrapJsonishListIfPossible(String input) {
+            return unwrapJsonishListStringIfPossible(input);
+        }
+
+        /** converts a comma separated list in a single string to a list of 
json primitives or maps, 
+         * falling back to returning the input.
+         * <p>
+         * specifically:
+         * <li> 1) if of form <code>[ X ]</code> (in brackets after trim), 
parse as YAML;
+         *         if parse succeeds return the result, or if parse fails, 
return {@link Maybe#absent()}.
+         * <ll> 2) if not of form <code>[ X ]</code>, wrap in brackets and 
parse as YAML, 
+         *         and if that succeeds and is a list, return the result.
+         * <li> 3) otherwise, expect comma-separated tokens which after 
trimming are of the form "A" or B,
+         *         where "A" is a valid Java string or C is any string not 
starting with ' 
+         *         and not containing " or ,.  return the list of those 
tokens, where A and B
+         *         are their string value, and C as a primitive if it is a 
number or boolean or null, 
+         *         or else a string, including the empty string if empty.
+         * <li> 4) if such tokens are not found, return {@link Maybe#absent()}.
+         * <p>
+         * @see #unwrapOptionallyQuotedJavaStringList(String)
+         **/
+        public static Maybe<List<Object>> tryUnwrapJsonishList(String input) {
+            if (input==null) return Maybe.absent("null input cannot unwrap to 
a list");
+            String inputT = input.trim();
+            
+            String inputYaml = null;
+            if (!inputT.startsWith("[") && !inputT.endsWith("]")) {
+                inputYaml = "[" + inputT + "]";
+            }
+            if (inputT.startsWith("[") && inputT.endsWith("]")) {
+                inputYaml = inputT;
+            }
+            if (inputYaml!=null) {
+                try {
+                    Object r = Iterables.getOnlyElement( 
Yamls.parseAll(inputYaml) );
+                    if (r instanceof List) {
+                        @SuppressWarnings("unchecked")
+                        List<Object> result = (List<Object>)r;
+                        return Maybe.of(result);
+                    }
+                } catch (Exception e) {}
+                if (inputT.startsWith("[")) {
+                    // if supplied as yaml, don't allow failures
+                    return Maybe.absent("Supplied format looked like YAML but 
could not parse as YAML");
+                }
+            }
+            
+            List<Object> result = MutableList.of();
+            
+            // double quote:  ^ \s* " ([not quote or backslash] or [backslash 
any-char])* " \s* (, or $)
+            Pattern dq = 
Pattern.compile("^\\s*(\"([^\"\\\\]|[\\\\.])*\")\\s*(,|$)");
+            // could also support this, but we need new unescape routines
+//            // single quote:  ^ \s* ' ([not quote or backslash] or 
[backslash any-char])* ' \s* (, or $)
+//            Pattern sq = 
Pattern.compile("^\\s*'([^\'\\\\]|[\\\\.])'*\\s*(,|$)");
+            // no quote:  ^ \s* (empty, or [not ' or " or space] ([not , or 
"]* [not , or " or space])?) \s* (, or $)
+            Pattern nq = 
Pattern.compile("^\\s*(|[^,\"\\s]([^,\"]*[^,\"\\s])?)\\s*(,|$)");
+            
+            int removedChars = 0;
+            while (true) {
+                Object ri;
+                Matcher m = dq.matcher(input);
+                if (m.find()) {
+                    try {
+                        ri = unwrapJavaString(m.group(1));
+                    } catch (Exception e) {
+                        Exceptions.propagateIfFatal(e);
+                        return Maybe.absent("Could not match valid quote 
pattern" +
+                            (removedChars>0 ? " at position "+removedChars : 
"")
+                            + ": "+ Exceptions.collapseText(e));
+                    }
+                } else {
+                    m = nq.matcher(input);
+                    if (m.find()) {
+                        String w = m.group(1);
+                        
+                        ri = w;
+                        if (w.matches("[0-9]*.[0-9]+")) {
+                            try {
+                                ri = Double.parseDouble(w);
+                            } catch (Exception e) {}
+                        } else if (w.matches("[0-9]+")) {
+                            try {
+                                ri = Integer.parseInt(w);
+                            } catch (Exception e) {}
+                        } else if (Boolean.TRUE.toString().equals(w)) {
+                            ri = true;
+                        } else if (Boolean.FALSE.toString().equals(w)) {
+                            ri = false;
+                        } else if ("null".equals(w)) {
+                            ri = null;
+                        }
+                        
+                    } else {
+                        return Maybe.absent("Could not match valid quote 
pattern" +
+                            (removedChars>0 ? " at position "+removedChars : 
""));
+                    }
+                }
+                
+                result.add(ri);
+                
+                input = input.substring(m.end());
+                removedChars += m.end();
+                if (!m.group(0).endsWith(",")) break;
+            }
+            return Maybe.of(result);
+        }
+
         /** converts a comma separated list in a single string to a list of 
strings, 
          * doing what would be expected if given java or json style string as 
input,
          * and falling back to returning the input.
          * <p>
          * this method does <b>not</b> throw exceptions on invalid input,
-         * but just returns that input
+         * but just returns that input wrapped in a list
          * <p>
          * specifically, uses the following rules (executed once in sequence:
          * <li> 1) if of form <code>[ X ]</code> (in brackets after trim), 
@@ -337,7 +451,7 @@ public class StringEscapes {
          * <p>
          * @see #unwrapOptionallyQuotedJavaStringList(String)
          **/
-        public static List<String> unwrapJsonishListIfPossible(String input) {
+        public static List<String> unwrapJsonishListStringIfPossible(String 
input) {
             try {
                 return unwrapOptionallyQuotedJavaStringList(input);
             } catch (Exception e) {

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/89c692e4/utils/common/src/test/java/org/apache/brooklyn/util/text/StringEscapesTest.java
----------------------------------------------------------------------
diff --git 
a/utils/common/src/test/java/org/apache/brooklyn/util/text/StringEscapesTest.java
 
b/utils/common/src/test/java/org/apache/brooklyn/util/text/StringEscapesTest.java
index b864240..b23a2a7 100644
--- 
a/utils/common/src/test/java/org/apache/brooklyn/util/text/StringEscapesTest.java
+++ 
b/utils/common/src/test/java/org/apache/brooklyn/util/text/StringEscapesTest.java
@@ -18,8 +18,9 @@
  */
 package org.apache.brooklyn.util.text;
 
+import org.apache.brooklyn.test.Asserts;
 import org.apache.brooklyn.util.collections.MutableList;
-import org.apache.brooklyn.util.text.StringEscapes;
+import org.apache.brooklyn.util.collections.MutableMap;
 import org.apache.brooklyn.util.text.StringEscapes.BashStringEscapes;
 import org.apache.brooklyn.util.text.StringEscapes.JavaStringEscapes;
 import org.testng.Assert;
@@ -83,8 +84,9 @@ public class StringEscapesTest {
         Assert.assertEquals(JavaStringEscapes.wrapJavaString("Hello 
\"World\""), "\"Hello \\\"World\\\"\"");
     }
     
+    @SuppressWarnings("deprecation")
     @Test
-    public void testJavaLists() {
+    public void testJavaListDeprecated() {
         Assert.assertEquals(MutableList.of("hello", "world"),
             JavaStringEscapes.unwrapQuotedJavaStringList("\"hello\", 
\"world\"", ","));
         try {
@@ -115,4 +117,78 @@ public class StringEscapesTest {
             JavaStringEscapes.unwrapJsonishListIfPossible("\"\",,x,\"\""));
     }
 
+    @Test
+    public void testJavaListString() {
+        Assert.assertEquals(MutableList.of("hello", "world"),
+            JavaStringEscapes.unwrapQuotedJavaStringList("\"hello\", 
\"world\"", ","));
+        try {
+            JavaStringEscapes.unwrapQuotedJavaStringList("\"hello\", world", 
",");
+            Assert.fail("Should have thrown");
+        } catch (Exception e) { /* expected */ }
+        
+        Assert.assertEquals(MutableList.of("hello", "world"),
+            JavaStringEscapes.unwrapJsonishListStringIfPossible("\"hello\", 
\"world\""));
+        Assert.assertEquals(MutableList.of("hello"),
+            JavaStringEscapes.unwrapJsonishListStringIfPossible("hello"));
+        Assert.assertEquals(MutableList.of("hello", "world"),
+            JavaStringEscapes.unwrapJsonishListStringIfPossible("hello, 
world"));
+        Assert.assertEquals(MutableList.of("hello", "world"),
+            JavaStringEscapes.unwrapJsonishListStringIfPossible("\"hello\", 
world"));
+        Assert.assertEquals(MutableList.of("hello", "world"),
+            JavaStringEscapes.unwrapJsonishListStringIfPossible("[ \"hello\", 
world ]"));
+        // if can't parse e.g. because contains double quote, then returns 
original string as single element list
+        Assert.assertEquals(MutableList.of("hello\", \"world\""),
+            JavaStringEscapes.unwrapJsonishListStringIfPossible("hello\", 
\"world\""));
+        Assert.assertEquals(MutableList.of(),
+            JavaStringEscapes.unwrapJsonishListStringIfPossible(" "));
+        Assert.assertEquals(MutableList.of(""),
+            JavaStringEscapes.unwrapJsonishListStringIfPossible("\"\""));
+        Assert.assertEquals(MutableList.of("x"),
+            JavaStringEscapes.unwrapJsonishListStringIfPossible(",,x,"));
+        Assert.assertEquals(MutableList.of("","x",""),
+            
JavaStringEscapes.unwrapJsonishListStringIfPossible("\"\",,x,\"\""));
+    }
+
+    @Test
+    public void testJavaListObject() {
+        Assert.assertEquals(MutableList.of("hello", "world"),
+            JavaStringEscapes.tryUnwrapJsonishList("\"hello\", 
\"world\"").get());
+        Assert.assertEquals(MutableList.of("hello"),
+            JavaStringEscapes.tryUnwrapJsonishList("hello").get());
+        Assert.assertEquals(MutableList.of("hello", "world"),
+            JavaStringEscapes.tryUnwrapJsonishList("hello, world").get());
+        Assert.assertEquals(MutableList.of("hello", "world"),
+            JavaStringEscapes.tryUnwrapJsonishList("\"hello\", world").get());
+        Assert.assertEquals(MutableList.of("hello", "world"),
+            JavaStringEscapes.tryUnwrapJsonishList("[ \"hello\", world 
]").get());
+        Assert.assertEquals(MutableList.of(""),
+            JavaStringEscapes.tryUnwrapJsonishList(" ").get());
+        Assert.assertEquals(MutableList.of(""),
+            JavaStringEscapes.tryUnwrapJsonishList("\"\"").get());
+        Assert.assertEquals(MutableList.of("","","x",""),
+            JavaStringEscapes.tryUnwrapJsonishList(",,x,").get());
+        Assert.assertEquals(MutableList.of("","","x",""),
+            JavaStringEscapes.tryUnwrapJsonishList("\"\",,x,\"\"").get());
+
+        Assert.assertEquals(MutableList.<Object>of(1, 2, "buckle my shoe", 
true, "true", null, "null", ","),
+            JavaStringEscapes.tryUnwrapJsonishList("1, 2, buckle my shoe, 
true, \"true\", null, \"null\", \",\"").get());
+
+        try {
+            JavaStringEscapes.tryUnwrapJsonishList("\"hello").get();
+            Asserts.shouldHaveFailedPreviously();
+        } catch (Exception e) { Asserts.expectedFailureDoesNotContain(e, 
"position"); }
+        try {
+            JavaStringEscapes.tryUnwrapJsonishList(", \"hello").get();
+            Asserts.shouldHaveFailedPreviously();
+        } catch (Exception e) { 
+            Asserts.expectedFailureContains(e, "position 1"); 
+        }
+        
+        Assert.assertEquals(MutableList.of(MutableMap.of("a", "b"), "world"),
+            JavaStringEscapes.tryUnwrapJsonishList("[ { a: b }, world 
]").get());
+        
+        Assert.assertEquals(MutableList.of(MutableMap.of("a", 
MutableList.<Object>of("b", 2)), "world"),
+            JavaStringEscapes.tryUnwrapJsonishList("{ a: [ b, 2 ] }, 
world").get());
+    }
+
 }

Reply via email to