Improve DSL toString methods to produce re-parseable output

Adding a simpler way to test the DSL, and adding support for  entity("ID")  
where ID is the internal ID
(also allowing more complex items  entity(config("targetEntity")).


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

Branch: refs/heads/master
Commit: 503ecbaddfcf2343436db6ca964e0cd2f75556a5
Parents: df62c17
Author: Alex Heneveld <alex.henev...@cloudsoftcorp.com>
Authored: Tue Jan 17 16:52:39 2017 +0000
Committer: Alex Heneveld <alex.henev...@cloudsoftcorp.com>
Committed: Tue Jan 17 17:01:57 2017 +0000

----------------------------------------------------------------------
 .../spi/dsl/BrooklynDslInterpreter.java         |   3 +-
 .../spi/dsl/DslDeferredFunctionCall.java        | 101 +++++++-----
 .../spi/dsl/methods/BrooklynDslCommon.java      |  21 +--
 .../brooklyn/spi/dsl/methods/DslComponent.java  |  57 ++++---
 .../spi/dsl/methods/DslToStringHelpers.java     |  79 +++++++++
 .../spi/dsl/DslParseComponentsTest.java         | 162 +++++++++++++++++++
 .../brooklyn/camp/brooklyn/spi/dsl/DslTest.java |   1 +
 .../brooklyn/test/lite/CampYamlLiteTest.java    |   1 +
 8 files changed, 355 insertions(+), 70 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/503ecbad/camp/camp-brooklyn/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/dsl/BrooklynDslInterpreter.java
----------------------------------------------------------------------
diff --git 
a/camp/camp-brooklyn/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/dsl/BrooklynDslInterpreter.java
 
b/camp/camp-brooklyn/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/dsl/BrooklynDslInterpreter.java
index ab20c3b..4f252b6 100644
--- 
a/camp/camp-brooklyn/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/dsl/BrooklynDslInterpreter.java
+++ 
b/camp/camp-brooklyn/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/dsl/BrooklynDslInterpreter.java
@@ -32,7 +32,6 @@ import 
org.apache.brooklyn.camp.spi.resolve.PlanInterpreter.PlanInterpreterAdapt
 import org.apache.brooklyn.camp.spi.resolve.interpret.PlanInterpretationNode;
 import 
org.apache.brooklyn.camp.spi.resolve.interpret.PlanInterpretationNode.Role;
 import org.apache.brooklyn.util.exceptions.Exceptions;
-import org.apache.brooklyn.util.guava.Maybe;
 import org.apache.brooklyn.util.text.Strings;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -156,7 +155,7 @@ public class BrooklynDslInterpreter extends 
PlanInterpreterAdapter {
             throw new IllegalStateException("Invalid function-only expression 
'"+f.getFunction()+"'");
 
         String fn = f.getFunction();
-        fn = Strings.removeFromStart(fn, "$brooklyn:");
+        fn = Strings.removeFromStart(fn, BrooklynDslCommon.PREFIX);
         if (fn.startsWith("function.")) {
             // If the function name starts with 'function.', then we look for 
the function in BrooklynDslCommon.Functions
             // As all functions in BrooklynDslCommon.Functions are static, we 
don't need to worry whether a class

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/503ecbad/camp/camp-brooklyn/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/dsl/DslDeferredFunctionCall.java
----------------------------------------------------------------------
diff --git 
a/camp/camp-brooklyn/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/dsl/DslDeferredFunctionCall.java
 
b/camp/camp-brooklyn/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/dsl/DslDeferredFunctionCall.java
index 3353151..1d547ab 100644
--- 
a/camp/camp-brooklyn/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/dsl/DslDeferredFunctionCall.java
+++ 
b/camp/camp-brooklyn/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/dsl/DslDeferredFunctionCall.java
@@ -25,6 +25,7 @@ import java.util.concurrent.ConcurrentHashMap;
 
 import org.apache.brooklyn.api.mgmt.Task;
 import org.apache.brooklyn.camp.brooklyn.spi.dsl.methods.BrooklynDslCommon;
+import org.apache.brooklyn.camp.brooklyn.spi.dsl.methods.DslToStringHelpers;
 import org.apache.brooklyn.core.entity.EntityInternal;
 import org.apache.brooklyn.core.mgmt.BrooklynTaskTags;
 import org.apache.brooklyn.util.core.task.Tasks;
@@ -34,7 +35,6 @@ import org.apache.brooklyn.util.javalang.Reflections;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
-import com.google.common.base.Joiner;
 import com.google.common.base.Objects;
 import com.google.common.collect.ImmutableList;
 
@@ -62,7 +62,7 @@ public class DslDeferredFunctionCall extends 
BrooklynDslDeferredSupplier<Object>
     @Override
     public Task<Object> newTask() {
         return Tasks.builder()
-                .displayName("Deferred function call " + object + "." + fnName 
+ "(" + toString(args) + ")")
+                .displayName("Deferred function call " + object + "." + fnName 
+ args + ")")
                 .tag(BrooklynTaskTags.TRANSIENT_TASK_TAG)
                 .dynamic(false)
                 .body(new Callable<Object>() {
@@ -80,14 +80,14 @@ public class DslDeferredFunctionCall extends 
BrooklynDslDeferredSupplier<Object>
             Object instance = resolvedMaybe.get();
 
             if (instance == null) {
-                throw new IllegalArgumentException("Deferred function call, " 
+ object + 
-                        " evaluates to null (when calling " + fnName + "(" + 
toString(args) + "))");
+                throw new IllegalArgumentException("Deferred function call not 
found: " + 
+                        object + " evaluates to null (wanting to call " + 
fnName + args + "))");
             }
 
             return invokeOn(instance);
         } else {
             if (immediate) {
-                return Maybe.absent("Could not evaluate immediately " + obj);
+                return Maybe.absent("Could not evaluate immediately: " + obj);
             } else {
                 return Maybe.absent(Maybe.getException(resolvedMaybe));
             }
@@ -99,48 +99,81 @@ public class DslDeferredFunctionCall extends 
BrooklynDslDeferredSupplier<Object>
     }
 
     protected static Maybe<Object> invokeOn(Object obj, String fnName, List<?> 
args) {
-        Object instance = obj;
-        List<?> instanceArgs = args;
-        Maybe<Method> method = Reflections.getMethodFromArgs(instance, fnName, 
instanceArgs);
-
-        if (method.isAbsent()) {
+        return new Invoker(obj, fnName, args).invoke();
+    }
+    
+    protected static class Invoker {
+        final Object obj;
+        final String fnName;
+        final List<?> args;
+        
+        Maybe<Method> method;
+        Object instance;
+        List<?> instanceArgs;
+        
+        protected Invoker(Object obj, String fnName, List<?> args) {
+            this.fnName = fnName;
+            this.obj = obj;
+            this.args = args;
+        }
+        
+        protected Maybe<Object> invoke() {
+            findMethod();
+            
+            if (method.isPresent()) {
+                Method m = method.get();
+    
+                checkCallAllowed(m);
+    
+                try {
+                    // Value is most likely another 
BrooklynDslDeferredSupplier - let the caller handle it,
+                    return Maybe.of(Reflections.invokeMethodFromArgs(instance, 
m, instanceArgs));
+                } catch (IllegalArgumentException | IllegalAccessException | 
InvocationTargetException e) {
+                    // If the method is there but not executable for whatever 
reason fail with a fatal error, don't return an absent.
+                    throw Exceptions.propagate(new 
InvocationTargetException(e, "Error invoking '"+fnName+instanceArgs+"' on 
'"+instance+"'"));
+                }
+            } else {
+                // could do deferred execution if an argument is a deferred 
supplier:
+                // if we get a present from:
+                // new Invoker(obj, fnName, 
replaceSuppliersWithNull(args)).findMethod()
+                // then return a
+                // new DslDeferredFunctionCall(...)
+                
+                return Maybe.absent(new IllegalArgumentException("No such 
function '"+fnName+"' taking args "+args+" (on "+obj+")"));
+            }
+        }
+    
+        protected void findMethod() {
+            method = Reflections.getMethodFromArgs(obj, fnName, args);
+            if (method.isPresent()) {
+                this.instance = obj;
+                this.instanceArgs = args;
+                return ;
+            }
+                
             instance = BrooklynDslCommon.class;
             instanceArgs = 
ImmutableList.builder().add(obj).addAll(args).build();
             method = Reflections.getMethodFromArgs(instance, fnName, 
instanceArgs);
-        }
-
-        if (method.isAbsent()) {
+            if (method.isPresent()) return ;
+    
             Maybe<?> facade;
             try {
                 facade = 
Reflections.invokeMethodFromArgs(BrooklynDslCommon.DslFacades.class, "wrap", 
ImmutableList.of(obj));
             } catch (IllegalArgumentException | IllegalAccessException | 
InvocationTargetException e) {
                 facade = Maybe.absent();
             }
-
+    
             if (facade.isPresent()) {
                 instance = facade.get();
                 instanceArgs = args;
                 method = Reflections.getMethodFromArgs(instance, fnName, 
instanceArgs);
+                if (method.isPresent()) return ;
             }
-        }
-
-        if (method.isPresent()) {
-            Method m = method.get();
-
-            checkCallAllowed(m);
-
-            try {
-                // Value is most likely another BrooklynDslDeferredSupplier - 
let the caller handle it,
-                return Maybe.of(Reflections.invokeMethodFromArgs(instance, m, 
instanceArgs));
-            } catch (IllegalArgumentException | IllegalAccessException | 
InvocationTargetException e) {
-                // If the method is there but not executable for whatever 
reason fail with a fatal error, don't return an absent.
-                throw Exceptions.propagate(new InvocationTargetException(e, 
"Error invoking '"+fnName+"("+toString(instanceArgs)+")' on '"+instance+"'"));
-            }
-        } else {
-            return Maybe.absent(new IllegalArgumentException("No such function 
'"+fnName+"("+toString(args)+")' on "+obj));
+            
+            method = Maybe.absent();
         }
     }
-
+    
     protected Maybe<?> resolve(Object object, boolean immediate) {
         if (object instanceof DslFunctionSource || object == null) {
             return Maybe.of(object);
@@ -205,11 +238,7 @@ public class DslDeferredFunctionCall extends 
BrooklynDslDeferredSupplier<Object>
 
     @Override
     public String toString() {
-        return object + "." + fnName + "(" + toString(args) + ")";
+        return DslToStringHelpers.fn(object + "." + fnName, args);
     }
 
-    private static String toString(List<?> args) {
-        if (args == null) return "";
-        return Joiner.on(", ").join(args);
-    }
 }

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/503ecbad/camp/camp-brooklyn/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/dsl/methods/BrooklynDslCommon.java
----------------------------------------------------------------------
diff --git 
a/camp/camp-brooklyn/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/dsl/methods/BrooklynDslCommon.java
 
b/camp/camp-brooklyn/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/dsl/methods/BrooklynDslCommon.java
index 8b28c74..a6774d4 100644
--- 
a/camp/camp-brooklyn/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/dsl/methods/BrooklynDslCommon.java
+++ 
b/camp/camp-brooklyn/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/dsl/methods/BrooklynDslCommon.java
@@ -62,8 +62,6 @@ import org.apache.brooklyn.util.core.task.Tasks;
 import org.apache.brooklyn.util.exceptions.Exceptions;
 import org.apache.brooklyn.util.guava.Maybe;
 import org.apache.brooklyn.util.javalang.Reflections;
-import org.apache.brooklyn.util.text.StringEscapes.JavaStringEscapes;
-import org.apache.brooklyn.util.text.Strings;
 import org.apache.commons.beanutils.BeanUtils;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -82,6 +80,8 @@ public class BrooklynDslCommon {
 
     private static final Logger LOG = 
LoggerFactory.getLogger(BrooklynDslCommon.class);
 
+    public static final String PREFIX = "$brooklyn:";
+    
     // Access specific entities
 
     @DslAccessible
@@ -203,8 +203,7 @@ public class BrooklynDslCommon {
 
         @Override
         public String toString() {
-            return (obj.toString()+".") +
-                "config("+JavaStringEscapes.wrapJavaString(keyName)+")";
+            return DslToStringHelpers.concat(DslToStringHelpers.internal(obj), 
".", DslToStringHelpers.fn("config", keyName));
         }
     }
 
@@ -376,14 +375,10 @@ public class BrooklynDslCommon {
 
         @Override
         public String toString() {
-            return "$brooklyn:formatString("+
-                JavaStringEscapes.wrapJavaString(pattern)+
-                (args==null || args.length==0 ? "" : ","+Strings.join(args, 
","))+")";
+            return DslToStringHelpers.fn("formatString", 
MutableList.<Object>builder().add(pattern).addAll(args).build());
         }
     }
 
-
-
     protected static class DslRegexReplacement extends 
BrooklynDslDeferredSupplier<String> {
 
         private static final long serialVersionUID = 737189899361183341L;
@@ -425,7 +420,7 @@ public class BrooklynDslCommon {
 
         @Override
         public String toString() {
-            return String.format("$brooklyn:regexReplace(%s:%s:%s)",source, 
pattern, replacement);
+            return DslToStringHelpers.fn("regexReplace", source, pattern, 
replacement);
         }
     }
 
@@ -656,7 +651,7 @@ public class BrooklynDslCommon {
 
         @Override
         public String toString() {
-            return "$brooklyn:object(\""+(type != null ? type.getName() : 
typeName)+"\")";
+            return DslToStringHelpers.fn("object", type != null ? 
type.getName() : typeName);
         }
     }
 
@@ -717,7 +712,7 @@ public class BrooklynDslCommon {
 
         @Override
         public String toString() {
-            return "$brooklyn:external("+providerName+", "+key+")";
+            return DslToStringHelpers.fn("external", providerName, key);
         }
     }
 
@@ -777,7 +772,7 @@ public class BrooklynDslCommon {
 
             @Override
             public String toString() {
-                return String.format("$brooklyn:regexReplace(%s:%s)", pattern, 
replacement);
+                return DslToStringHelpers.fn("function.regexReplace", pattern, 
replacement);
             }
         }
     }

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/503ecbad/camp/camp-brooklyn/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/dsl/methods/DslComponent.java
----------------------------------------------------------------------
diff --git 
a/camp/camp-brooklyn/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/dsl/methods/DslComponent.java
 
b/camp/camp-brooklyn/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/dsl/methods/DslComponent.java
index a947fee..7484c3f 100644
--- 
a/camp/camp-brooklyn/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/dsl/methods/DslComponent.java
+++ 
b/camp/camp-brooklyn/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/dsl/methods/DslComponent.java
@@ -27,6 +27,7 @@ import java.util.concurrent.ExecutionException;
 import org.apache.brooklyn.api.entity.Entity;
 import org.apache.brooklyn.api.mgmt.ExecutionContext;
 import org.apache.brooklyn.api.mgmt.Task;
+import org.apache.brooklyn.api.objs.BrooklynObject;
 import org.apache.brooklyn.api.sensor.AttributeSensor;
 import org.apache.brooklyn.api.sensor.Sensor;
 import org.apache.brooklyn.camp.brooklyn.BrooklynCampConstants;
@@ -51,7 +52,6 @@ import org.apache.brooklyn.util.core.task.Tasks;
 import org.apache.brooklyn.util.exceptions.Exceptions;
 import org.apache.brooklyn.util.groovy.GroovyJavaMethods;
 import org.apache.brooklyn.util.guava.Maybe;
-import org.apache.brooklyn.util.text.StringEscapes.JavaStringEscapes;
 import org.apache.brooklyn.util.text.Strings;
 
 import com.google.common.base.CaseFormat;
@@ -166,6 +166,10 @@ public class DslComponent extends 
BrooklynDslDeferredSupplier<Entity> implements
 
     // ---------------------------
 
+    public Scope getScope() {
+        return scope;
+    }
+    
     @Override
     public final Maybe<Entity> getImmediately() {
         return new EntityInScopeFinder(scopeComponent, scope, componentId, 
componentIdSupplier).getImmediately();
@@ -279,7 +283,7 @@ public class DslComponent extends 
BrooklynDslDeferredSupplier<Entity> implements
                 }
                 
                 // Support being passed an explicit entity via the DSL
-                if (maybeComponentId.get() instanceof Entity) {
+                if (maybeComponentId.get() instanceof BrooklynObject) {
                     if (Iterables.contains(entitiesToSearch, 
maybeComponentId.get())) {
                         return Maybe.of((Entity)maybeComponentId.get());
                     } else {
@@ -298,7 +302,11 @@ public class DslComponent extends 
BrooklynDslDeferredSupplier<Entity> implements
             }
             
             Optional<Entity> result = Iterables.tryFind(entitiesToSearch, 
EntityPredicates.configEqualTo(BrooklynCampConstants.PLAN_ID, 
desiredComponentId));
+            if (result.isPresent()) {
+                return Maybe.of(result.get());
+            }
             
+            result = Iterables.tryFind(entitiesToSearch, 
EntityPredicates.idEqualTo(desiredComponentId));
             if (result.isPresent()) {
                 return Maybe.of(result.get());
             }
@@ -420,7 +428,7 @@ public class DslComponent extends 
BrooklynDslDeferredSupplier<Entity> implements
         }
         @Override
         public String toString() {
-            return (component.scope==Scope.THIS ? "" : 
component.toString()+".") + "entityId()";
+            return DslToStringHelpers.component(component, 
DslToStringHelpers.fn("entityId"));
         }
     }
 
@@ -477,8 +485,7 @@ public class DslComponent extends 
BrooklynDslDeferredSupplier<Entity> implements
         }
         @Override
         public String toString() {
-            return (component.scope==Scope.THIS ? "" : 
component.toString()+".") +
-                
"attributeWhenReady("+JavaStringEscapes.wrapJavaString(sensorName)+")";
+            return DslToStringHelpers.component(component, 
DslToStringHelpers.fn("attributeWhenReady", sensorName));
         }
     }
 
@@ -539,8 +546,7 @@ public class DslComponent extends 
BrooklynDslDeferredSupplier<Entity> implements
 
         @Override
         public String toString() {
-            return (component.scope==Scope.THIS ? "" : 
component.toString()+".") + 
-                "config("+JavaStringEscapes.wrapJavaString(keyName)+")";
+            return DslToStringHelpers.component(component, 
DslToStringHelpers.fn("config", keyName));
         }
     }
 
@@ -639,11 +645,8 @@ public class DslComponent extends 
BrooklynDslDeferredSupplier<Entity> implements
 
         @Override
         public String toString() {
-            return (component.scope==Scope.THIS ? "" : 
component.toString()+".") + 
-                "sensor("+
-                    (sensorName instanceof String ? 
JavaStringEscapes.wrapJavaString((String)sensorName) :
-                        sensorName instanceof Sensor ? 
JavaStringEscapes.wrapJavaString(((Sensor<?>)sensorName).getName()) :
-                        sensorName)+")";
+            return DslToStringHelpers.component(component, 
DslToStringHelpers.fn("sensorName", 
+                sensorName instanceof Sensor ? 
((Sensor<?>)sensorName).getName() : sensorName));
         }
     }
 
@@ -702,11 +705,27 @@ public class DslComponent extends 
BrooklynDslDeferredSupplier<Entity> implements
 
     @Override
     public String toString() {
-        return "$brooklyn:entity("+
-            (scopeComponent==null ? "" : 
JavaStringEscapes.wrapJavaString(scopeComponent.toString())+", ")+
-            (scope==Scope.GLOBAL ? "" : 
JavaStringEscapes.wrapJavaString(scope.toString())+", ")+
-            (componentId != null ? 
JavaStringEscapes.wrapJavaString(componentId) : componentIdSupplier)+
-            ")";
+        Object component = componentId != null ? componentId : 
componentIdSupplier;
+        
+        if (scope==Scope.GLOBAL) {
+            return DslToStringHelpers.fn("entity", component);
+        }
+        
+        if (scope==Scope.THIS) {
+            if (scopeComponent!=null) {
+                return scopeComponent.toString();
+            }
+            return DslToStringHelpers.fn("entity", "this", "");
+        }
+        
+        String remainder;
+        if (component==null || "".equals(component)) {
+            remainder = DslToStringHelpers.fn(scope.toString());
+        } else {
+            remainder = DslToStringHelpers.fn(scope.toString(), component);
+        }
+               
+        return DslToStringHelpers.component(scopeComponent, remainder);
     }
-
-}
\ No newline at end of file
+    
+}

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/503ecbad/camp/camp-brooklyn/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/dsl/methods/DslToStringHelpers.java
----------------------------------------------------------------------
diff --git 
a/camp/camp-brooklyn/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/dsl/methods/DslToStringHelpers.java
 
b/camp/camp-brooklyn/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/dsl/methods/DslToStringHelpers.java
new file mode 100644
index 0000000..03b1a33
--- /dev/null
+++ 
b/camp/camp-brooklyn/src/main/java/org/apache/brooklyn/camp/brooklyn/spi/dsl/methods/DslToStringHelpers.java
@@ -0,0 +1,79 @@
+/*
+ * 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.brooklyn.camp.brooklyn.spi.dsl.methods;
+
+import java.util.Arrays;
+
+import org.apache.brooklyn.api.objs.BrooklynObject;
+import org.apache.brooklyn.camp.brooklyn.spi.dsl.methods.DslComponent.Scope;
+import org.apache.brooklyn.util.collections.MutableList;
+import org.apache.brooklyn.util.text.Strings;
+import org.apache.brooklyn.util.text.StringEscapes.JavaStringEscapes;
+
+/** Helper methods for producing toString outputs for DSL components, with the 
$brooklyn: prefix in the right place.
+ * In general, component's toString methods should produce re-parseable 
output, normally using these methods. */
+public class DslToStringHelpers {
+    
+    /** concatenates the arguments, each appropriately dsl-tostringed with 
prefixes removed,
+     * and then prefixes the result */
+    public static String concat(String ...x) {
+        StringBuilder r = new StringBuilder();
+        for (String xi: x) {
+            r.append(Strings.removeFromStart(xi, BrooklynDslCommon.PREFIX));
+        }
+        if (r.length()==0) {
+            return "";
+        }
+        return BrooklynDslCommon.PREFIX+r.toString();
+    }
+
+    /** convenience for functions, inserting parentheses and commas */
+    public static String fn(String functionName, Iterable<Object> args) {
+        StringBuilder out = new StringBuilder();
+        out.append(BrooklynDslCommon.PREFIX);
+        out.append(functionName);
+        out.append("(");
+        if (args!=null) {
+            boolean nonFirst = false;
+            for (Object s: args) {
+                if (nonFirst) out.append(", ");
+                out.append(internal(s));
+                nonFirst = true;
+            }
+        }
+        out.append(")");
+        return out.toString();
+    }
+
+    public static String fn(String functionName, Object ...args) {
+        return fn(functionName, Arrays.asList(args));
+    }
+    
+    /** convenience for internal arguments, removing any prefix, and applying 
wrap/escapes and other conversions */
+    public static String internal(Object x) {
+        if (x==null) return "null";
+        if (x instanceof String) return 
JavaStringEscapes.wrapJavaString((String)x);
+        if (x instanceof BrooklynObject) return fn("entity", MutableList.of( 
((BrooklynObject)x).getId() ));
+        return Strings.removeFromStart(x.toString(), BrooklynDslCommon.PREFIX);
+    }
+
+    public static String component(DslComponent component, String remainder) {
+        return component==null || component.getScope()==Scope.THIS ? remainder 
: concat(internal(component), ".", remainder);
+    }
+}

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/503ecbad/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/spi/dsl/DslParseComponentsTest.java
----------------------------------------------------------------------
diff --git 
a/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/spi/dsl/DslParseComponentsTest.java
 
b/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/spi/dsl/DslParseComponentsTest.java
new file mode 100644
index 0000000..1e8ccb8
--- /dev/null
+++ 
b/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/spi/dsl/DslParseComponentsTest.java
@@ -0,0 +1,162 @@
+/*
+ * Copyright 2016 The Apache Software Foundation.
+ *
+ * Licensed 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.brooklyn.camp.brooklyn.spi.dsl;
+
+import org.apache.brooklyn.api.entity.Entity;
+import org.apache.brooklyn.camp.BasicCampPlatform;
+import org.apache.brooklyn.camp.brooklyn.AbstractYamlTest;
+import org.apache.brooklyn.camp.brooklyn.BrooklynCampConstants;
+import org.apache.brooklyn.config.ConfigKey;
+import org.apache.brooklyn.core.config.ConfigKeys;
+import org.apache.brooklyn.core.entity.Attributes;
+import org.apache.brooklyn.core.entity.EntityInternal;
+import org.apache.brooklyn.core.entity.EntityPredicates;
+import org.apache.brooklyn.entity.stock.BasicApplication;
+import org.apache.brooklyn.entity.stock.BasicEntity;
+import org.apache.brooklyn.test.Asserts;
+import org.apache.brooklyn.util.collections.MutableMap;
+import org.apache.brooklyn.util.core.task.Tasks;
+import org.apache.brooklyn.util.stream.Streams;
+import org.apache.brooklyn.util.yaml.Yamls;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.testng.Assert;
+import org.testng.annotations.AfterMethod;
+import org.testng.annotations.Test;
+
+import com.google.common.collect.Iterables;
+
+public class DslParseComponentsTest extends AbstractYamlTest {
+    
+    private static final ConfigKey<Object> DEST = 
ConfigKeys.newConfigKey(Object.class, "dest");
+    private static final ConfigKey<Object> DEST2 = 
ConfigKeys.newConfigKey(Object.class, "dest2");
+
+    private static final Logger log = 
LoggerFactory.getLogger(DslParseComponentsTest.class);
+    
+    Entity app = null;
+    
+    protected Entity app() throws Exception {
+        if (app==null) {
+            app = createAndStartApplication(
+                    "services:",
+                    "- type: " + BasicApplication.class.getName(),
+                    "  id: one",
+                    "  brooklyn.config:",
+                    "    dest: 1",
+                    "    dest2: 1",
+                    "  brooklyn.children:",
+                    "  - type: "+BasicEntity.class.getName(),
+                    "    id: two",
+                    "    brooklyn.config:",
+                    "      dest2: 2"
+                    );
+        }
+        return app;
+    }
+
+    @AfterMethod(alwaysRun = true)
+    @Override
+    public void tearDown() throws Exception {
+        app = null;
+        super.tearDown();
+    }
+    
+    private Entity find(String desiredComponentId) {
+        return Iterables.tryFind(mgmt().getEntityManager().getEntities(), 
EntityPredicates.configEqualTo(BrooklynCampConstants.PLAN_ID, 
desiredComponentId)).orNull();
+    }
+    
+    public Object parseDslExpression(String input) {
+        String s1 = Yamls.getAs( Yamls.parseAll( 
Streams.reader(Streams.newInputStreamWithContents(input)) ), String.class );
+        BasicCampPlatform p = new BasicCampPlatform();
+        p.pdp().addInterpreter(new BrooklynDslInterpreter());
+        Object out = p.pdp().applyInterpreters(MutableMap.of("key", 
s1)).get("key");
+        log.debug("parsed "+input+" as "+out+" ("+(out==null ? "null" : 
out.getClass())+")");
+        return out;
+    }
+    
+    @Test
+    public void testTestSetup() throws Exception {
+        app();
+        Assert.assertEquals(find("two").getConfig(DEST), 1);
+        Assert.assertEquals(find("two").getConfig(DEST2), 2);
+    }
+    
+    @Test
+    public void testDslParsingAndFormatStringEvaluation() throws Exception {
+        // format string evaluates immediately
+        Assert.assertEquals(parseDslExpression("$brooklyn:formatString(\"hello 
%s\", \"world\")"), "hello world");
+    }
+    
+    @Test
+    public void testConfigParsingAndToString() throws Exception {
+        String x1 = "$brooklyn:config(\"dest\")";
+        Object y1 = parseDslExpression(x1);
+        // however config is a deferred supplier, with a toString in canonical 
form
+        Asserts.assertInstanceOf(y1, BrooklynDslDeferredSupplier.class);
+        Assert.assertEquals(y1.toString(), x1);
+    }
+
+    @Test
+    public void testConfigEvaluation() throws Exception {
+        app();
+        
+        Object y1 = parseDslExpression("$brooklyn:config(\"dest\")");
+        String y2 = Tasks.resolveValue(y1, String.class, ((EntityInternal) 
find("two")).getExecutionContext());
+        Assert.assertEquals(y2.toString(), "1");
+    }
+
+    @Test
+    public void testFormatStringWithConfig() throws Exception {
+        app();
+        
+        Object y1 = parseDslExpression("$brooklyn:formatString(\"%s-%s\", 
config(\"dest\"), $brooklyn:config(\"dest2\"))");
+        Assert.assertEquals(y1.toString(), "$brooklyn:formatString(\"%s-%s\", 
config(\"dest\"), config(\"dest2\"))");
+        
+        String y2 = Tasks.resolveValue(y1, String.class, ((EntityInternal) 
find("two")).getExecutionContext());
+        Assert.assertEquals(y2.toString(), "1-2");
+        
+        String y3 = Tasks.resolveValue(y1, String.class, ((EntityInternal) 
find("one")).getExecutionContext());
+        Assert.assertEquals(y3.toString(), "1-1");
+    }
+
+    @Test
+    public void testEntityReferenceAndAttributeWhenReady() throws Exception {
+        app();
+        find("one").sensors().set(Attributes.ADDRESS, "1");
+        find("two").sensors().set(Attributes.ADDRESS, "2");
+        
+        Object y1 = parseDslExpression("$brooklyn:formatString(\"%s-%s\", "
+            + "parent().attributeWhenReady(\"host.address\"), "
+            + "$brooklyn:attributeWhenReady(\"host.address\"))");
+        Assert.assertEquals(y1.toString(), "$brooklyn:formatString(\"%s-%s\", "
+            + "parent().attributeWhenReady(\"host.address\"), "
+            + "attributeWhenReady(\"host.address\"))");
+        
+        String y2 = Tasks.resolveValue(y1, String.class, ((EntityInternal) 
find("two")).getExecutionContext());
+        Assert.assertEquals(y2.toString(), "1-2");
+        
+        Object z1 = parseDslExpression("$brooklyn:formatString(\"%s-%s\", "
+            + 
"entity(\"one\").descendant(\"two\").attributeWhenReady(\"host.address\"), "
+            + 
"component(\"two\").entity(entityId()).attributeWhenReady(\"host.address\"))");
+        Assert.assertEquals(z1.toString(), "$brooklyn:formatString(\"%s-%s\", "
+            + 
"entity(\"one\").descendant(\"two\").attributeWhenReady(\"host.address\"), "
+            + "entity(entityId()).attributeWhenReady(\"host.address\"))");
+        
+        String z2 = Tasks.resolveValue(z1, String.class, ((EntityInternal) 
find("one")).getExecutionContext());
+        Assert.assertEquals(z2.toString(), "2-1");
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/503ecbad/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/spi/dsl/DslTest.java
----------------------------------------------------------------------
diff --git 
a/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/spi/dsl/DslTest.java
 
b/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/spi/dsl/DslTest.java
index 4292dbe..0a2c6eb 100644
--- 
a/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/spi/dsl/DslTest.java
+++ 
b/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/spi/dsl/DslTest.java
@@ -327,6 +327,7 @@ public class DslTest extends BrooklynAppUnitTestSupport {
             return this;
         }
         
+        @SuppressWarnings("unused")  // included for completeness?
         public DslTestWorker wrapInTaskForImmediately(boolean val) {
             wrapInTaskForImmediately = val;
             return this;

http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/503ecbad/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/test/lite/CampYamlLiteTest.java
----------------------------------------------------------------------
diff --git 
a/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/test/lite/CampYamlLiteTest.java
 
b/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/test/lite/CampYamlLiteTest.java
index 1f259a2..1257b3e 100644
--- 
a/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/test/lite/CampYamlLiteTest.java
+++ 
b/camp/camp-brooklyn/src/test/java/org/apache/brooklyn/camp/brooklyn/test/lite/CampYamlLiteTest.java
@@ -191,6 +191,7 @@ public class CampYamlLiteTest {
         assertMgmtHasSampleMyCatalogApp(symbolicName, bundleUrl);
     }
 
+    @SuppressWarnings("deprecation")
     @Test
     public void testResetXmlWithCustomEntity() throws IOException {
         
TestResourceUnavailableException.throwIfResourceUnavailable(getClass(), 
OsgiStandaloneTest.BROOKLYN_TEST_OSGI_ENTITIES_PATH);

Reply via email to