Merge pull request #124 from ahgittin/fix-dsl-literal

DSL Viewer ignored literals; this fixes that and tidies the code a bit
diff --git a/ui-modules/blueprint-composer/app/components/dsl-editor/dsl-editor.js b/ui-modules/blueprint-composer/app/components/dsl-editor/dsl-editor.js
index cf1e695..63d687a 100644
--- a/ui-modules/blueprint-composer/app/components/dsl-editor/dsl-editor.js
+++ b/ui-modules/blueprint-composer/app/components/dsl-editor/dsl-editor.js
@@ -18,7 +18,7 @@
  */
 import angular from 'angular';
 import angularSanitize from 'angular-sanitize';
-import {Dsl, KIND} from '../util/model/dsl.model';
+import {Dsl, DslParser, KIND} from '../util/model/dsl.model';
 import template from './dsl-editor.template.html';
 import brAutoFocus from 'brooklyn-ui-utils/autofocus/autofocus';
 import brUtils from 'brooklyn-ui-utils/utils/general';
@@ -166,7 +166,7 @@
             newValue.forEach((argument, index) => {
                 let dsl;
                 try {
-                    dsl = dslService.parse(argument, scope.entity, blueprintService.get());
+                    dsl = new DslParser().parseString(argument, scope.entity, blueprintService.get());
                     scope.dsl.params.splice(index + 1, 1, dsl);
                 } catch (ex) {
                     $log.debug(`Argument ${index} is not a DSL. Defaulting to string`, ex);
@@ -271,7 +271,7 @@
             }
 
             try {
-                dsl = dslService.parse(dsl.toString(), scope.entity, blueprintService.get());
+                dsl = new DslParser().parseString(dsl.toString(), scope.entity, blueprintService.get());
             } catch (ex) {
                 $log.debug(`Cannot get DSL relationship for DSL "${dsl}`, ex);
             }
diff --git a/ui-modules/blueprint-composer/app/components/dsl-viewer/dsl-viewer.js b/ui-modules/blueprint-composer/app/components/dsl-viewer/dsl-viewer.js
index e751638..75343e3 100644
--- a/ui-modules/blueprint-composer/app/components/dsl-viewer/dsl-viewer.js
+++ b/ui-modules/blueprint-composer/app/components/dsl-viewer/dsl-viewer.js
@@ -18,18 +18,18 @@
  */
 import angular from 'angular';
 import template from './dsl-viewer.template.html';
-import {KIND} from '../util/model/dsl.model';
+import {KIND, FAMILY} from '../util/model/dsl.model';
 
 const MODULE_NAME = 'brooklyn.components.dsl-viewer';
 const TEMPLATE_URL = 'blueprint-composer/component/dsl-viewer/index.html';
 
 angular.module(MODULE_NAME, [])
-    .directive('dslViewer', dslViewerDirective)
+    .directive('dslViewer', ['$log', dslViewerDirective])
     .run(['$templateCache', templateCache]);
 
 export default MODULE_NAME;
 
-export function dslViewerDirective() {
+export function dslViewerDirective($log) {
     return {
         restrict: 'E',
         templateUrl: function (tElement, tAttrs) {
@@ -42,30 +42,77 @@
     };
 
     function link(scope) {
-        scope.isTargetDsl = (dsl) => {
-            return dsl.kind === KIND.TARGET;
+        
+        function getIconForFunction(dsl) {
+            if (dsl.name === 'config') return 'fa-cog';
+            if (dsl.name === 'sensor') return 'fa-rss';
+            if (dsl.name === 'attributeWhenReady') return 'fa-pause';
+            if (dsl.name === 'literal') return 'fa-clone';
+            if (dsl.name === 'formatString') return 'fa-qrcode';
+            // catch-all
+            $log.warn("unexpected DSL function, using default icon", dsl, dsl.name); 
+            return 'fa-bolt';
         };
-        scope.isMethodDsl = (dsl) => {
-            return dsl.kind === KIND.METHOD;
+        
+        function updateModeAndIcon(dsl) {
+            var fam = dsl.kind && dsl.kind.family;
+            if (fam === FAMILY.FUNCTION) {
+                switch (dsl.kind) {
+                    case KIND.METHOD: {
+                        // "method" -- eg config, attrWhenReady -- shows param inline if one param
+                        // (if more than one, which shouldn't happen currently, shows all in a list)
+                        scope.mode = "method"; 
+                        scope.icon = getIconForFunction(dsl); 
+                        return;
+                    }
+                    case KIND.UTILITY: {
+                        // "utility" -- eg format string -- shows first param inline, then other params in list
+                        scope.mode = "utility"; 
+                        scope.icon = getIconForFunction(dsl); 
+                        return;
+                    }
+                    case KIND.TARGET: {
+                        scope.mode = "target"; 
+                        scope.icon = null;
+                        scope.relatedEntity = getRelatedEntity(dsl);
+                        return;
+                    }
+                }      
+            }
+            if (fam === FAMILY.CONSTANT) {
+                scope.mode = "constant"; 
+                scope.icon = null; 
+                return;
+            }
+            if (fam === FAMILY.REFERENCE) {
+                scope.mode = "reference"; 
+                scope.icon = null; 
+                return;
+            }
+            // catch-all
+            $log.warn("unexpected DSL family, using default icon", dsl, dsl.kind);
+            scope.icon = "fa-bolt";
+            scope.mode = "DSL";
+            return;
         };
-        scope.isFormatStringDsl = (dsl) => {
-            return dsl.kind === KIND.UTILITY && dsl.name === 'formatString';
-        };
-        scope.isLiteralDsl = (dsl) => {
-            return [KIND.STRING, KIND.NUMBER].includes(dsl.kind);
-        };
-        scope.getRelatedEntity = () => {
-            if (scope.dsl.params.length > 0) {
+        
+        function getRelatedEntity(dsl) {
+            if (dsl.params.length > 0) {
                 // If the DSL is looking at an entity ID
-                return scope.dsl.getRoot().relationships.find(entity => entity.id === scope.dsl.params[0].name)
-            } else if (scope.dsl.getRoot().relationships.length > 0) {
+                return dsl.getRoot().relationships.find(entity => entity.id === dsl.params[0].name)
+            } else if (dsl.getRoot().relationships.length > 0) {
                 // If the DSL is of the form $brooklyn:self() or $brooklyn:parent()
-                return scope.dsl.getRoot().relationships[0];
+                return dsl.getRoot().relationships[0];
             } else {
                 // Otherwise, there is no related entity
                 return null;
             }
         }
+        
+        scope.$watch('dsl', () => {
+            updateModeAndIcon(scope.dsl);
+        }, true);
+        
     }
 }
 
diff --git a/ui-modules/blueprint-composer/app/components/dsl-viewer/dsl-viewer.less b/ui-modules/blueprint-composer/app/components/dsl-viewer/dsl-viewer.less
new file mode 100644
index 0000000..efb26d3
--- /dev/null
+++ b/ui-modules/blueprint-composer/app/components/dsl-viewer/dsl-viewer.less
@@ -0,0 +1,23 @@
+/*
+ * 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.
+ */
+dsl-viewer {
+    .dsl-constant {
+        font-size: 80%;  // same as label
+    }
+}
diff --git a/ui-modules/blueprint-composer/app/components/dsl-viewer/dsl-viewer.template.html b/ui-modules/blueprint-composer/app/components/dsl-viewer/dsl-viewer.template.html
index 49d8f9c..3574ecf 100644
--- a/ui-modules/blueprint-composer/app/components/dsl-viewer/dsl-viewer.template.html
+++ b/ui-modules/blueprint-composer/app/components/dsl-viewer/dsl-viewer.template.html
@@ -16,36 +16,75 @@
   specific language governing permissions and limitations
   under the License.
 -->
-<span ng-if="isTargetDsl(dsl)">
-    <span ng-if="dsl.getRoot().relationships.length === 0" class="label label-primary">{{dsl.params[0].name}}</span>
-    <a ng-if="dsl.getRoot().relationships.length > 0" ui-sref="main.graphical.edit.entity({entityId: getRelatedEntity()._id})"><span class="label label-primary">{{(getRelatedEntity() | entityName) || 'New application'}}</span></a>
+<span ng-switch="mode">
+
+  <span ng-switch-when="target">
+    <span ng-if="dsl.getRoot().relationships.length === 0" class="label label-primary">
+        <span ng-if="!dsl.params[0] || !dsl.params[0].name || (dsl.name!='component' && dsl.name!='entity')">
+            {{dsl.name}}
+        </span>
+        {{dsl.params[0].name}}
+    </span>
+    <a ng-if="dsl.getRoot().relationships.length > 0" ui-sref="main.graphical.edit.entity({entityId: relatedEntity._id})"><span class="label label-primary">{{relatedEntity ? (relatedEntity | entityName) || 'Application' : dsl.name}}</span></a>
     <span ng-if="dsl.next">

         <dsl-viewer dsl="dsl.next"></dsl-viewer>
     </span>
-</span>
-
-<span ng-if="isMethodDsl(dsl) && !isFormatStringDsl(dsl)">
+  </span>
+  
+  <span ng-switch-when="method">
     <span class="label label-default">
-        <i class="fa" ng-class="{'fa-cog': dsl.name === 'config', 'fa-rss': dsl.name === 'sensor', 'fa-pause': dsl.name === 'attributeWhenReady'}"></i>
+        <i class="fa" ng-class="icon"></i>
         {{dsl.name}}
     </span>
-    →
-    <dsl-viewer dsl="dsl.params[0]"></dsl-viewer>
-</span>
-
-<span ng-if="isFormatStringDsl(dsl)">
-    <span class="label label-info">
-        <i class="fa fa-qrcode"></i>
-        pattern
+    
+    <span ng-if="dsl.params.length == 1">
+        →
+        <dsl-viewer dsl="dsl.params[0]"></dsl-viewer>
     </span>
-    →
-    {{dsl.params[0].name}}
-    <ol ng-if="dsl.params.length > 0">
+    <ul ng-if="dsl.params.length > 1">
+        <li ng-repeat="argument in dsl.params | limitTo:dsl.params.length">
+            <dsl-viewer dsl="argument"></dsl-viewer>
+        </li>
+    </ul>
+    
+    <span ng-if="dsl.next">
+        ⇒
+        <dsl-viewer dsl="dsl.next"></dsl-viewer>
+    </span>
+  </span>
+  
+  <span ng-switch-when="utility">
+    <span class="label label-info">
+        <i class="fa" ng-class="icon"></i>
+        {{dsl.name}}
+    </span>
+    
+    <span ng-if="dsl.params.length >= 1">
+        →
+        <dsl-viewer dsl="dsl.params[0]"></dsl-viewer>
+    </span>
+    <ul ng-if="dsl.params.length > 1">
         <li ng-repeat="argument in dsl.params | limitTo:dsl.params.length:1">
             <dsl-viewer dsl="argument"></dsl-viewer>
         </li>
-    </ol>
-</span>
+    </ul>
+  </span>
 
-<span ng-if="isLiteralDsl(dsl)">{{dsl.name}}</span>
\ No newline at end of file
+  <span ng-switch-when="constant">
+    <span class="dsl-constant">
+        <samp>{{dsl.name}}</samp>
+    </span>
+  </span>
+
+  <span ng-switch-when="reference">
+    <span class="label label-primary">
+        {{dsl.name}}
+    </span>
+  </span>
+
+  <span ng-switch-default>
+    {{ !mode || mode==='unknown' ? '' : mode }}: {{ dsl.name }}
+  </span>
+  
+</span>
diff --git a/ui-modules/blueprint-composer/app/index.less b/ui-modules/blueprint-composer/app/index.less
index 0ab3f4d..e42474c 100644
--- a/ui-modules/blueprint-composer/app/index.less
+++ b/ui-modules/blueprint-composer/app/index.less
@@ -35,6 +35,7 @@
 @import "components/blueprint-data-manager/blueprint-data-manager.style.less";
 @import "components/catalog-saver/catalog-saver.less";
 @import "components/dsl-editor/dsl-editor";
+@import "components/dsl-viewer/dsl-viewer";
 
 .make-icon(@size) {
   width: auto;