Merge remote-tracking branch 'origin/master'
diff --git a/cli/src/main/java/org/onlab/onos/cli/net/IntentsListCommand.java b/cli/src/main/java/org/onlab/onos/cli/net/IntentsListCommand.java
index 7cfe2c6..ab9fb83 100644
--- a/cli/src/main/java/org/onlab/onos/cli/net/IntentsListCommand.java
+++ b/cli/src/main/java/org/onlab/onos/cli/net/IntentsListCommand.java
@@ -19,6 +19,7 @@
 import org.onlab.onos.net.intent.PointToPointIntent;
 import org.onlab.onos.net.intent.SinglePointToMultiPointIntent;
 
+import java.util.List;
 import java.util.Set;
 
 /**
@@ -39,19 +40,23 @@
                 print("id=%s, state=%s, type=%s, appId=%s",
                       intent.id(), state, intent.getClass().getSimpleName(),
                       intent.appId().name());
-                printDetails(intent);
+                printDetails(service, intent);
             }
         }
     }
 
-    private void printDetails(Intent intent) {
+    private void printDetails(IntentService service, Intent intent) {
         if (intent.resources() != null && !intent.resources().isEmpty()) {
             print("    resources=%s", intent.resources());
         }
         if (intent instanceof ConnectivityIntent) {
             ConnectivityIntent ci = (ConnectivityIntent) intent;
-            print("    selector=%s", ci.selector().criteria());
-            print("    treatment=%s", ci.treatment().instructions());
+            if (!ci.selector().criteria().isEmpty()) {
+                print("    selector=%s", ci.selector().criteria());
+            }
+            if (!ci.treatment().instructions().isEmpty()) {
+                print("    treatment=%s", ci.treatment().instructions());
+            }
         }
 
         if (intent instanceof PointToPointIntent) {
@@ -71,6 +76,11 @@
             print("    links=%s", li.links());
             print("    egress=%s", li.egressPoint());
         }
+
+        List<Intent> installable = service.getInstallableIntents(intent.id());
+        if (installable != null && !installable.isEmpty()) {
+            print("    installable=%s", installable);
+        }
     }
 
     // Produces JSON array of the specified intents.
@@ -86,10 +96,14 @@
     private JsonNode json(IntentService service, ObjectMapper mapper, Intent intent) {
         ObjectNode result = mapper.createObjectNode()
                 .put("id", intent.id().toString())
-                .put("state", service.getIntentState(intent.id()).toString())
                 .put("type", intent.getClass().getSimpleName())
                 .put("appId", intent.appId().name());
 
+        IntentState state = service.getIntentState(intent.id());
+        if (state != null) {
+            result.put("state", state.toString());
+        }
+
         if (intent.resources() != null && !intent.resources().isEmpty()) {
             ArrayNode rnode = mapper.createArrayNode();
             for (NetworkResource resource : intent.resources()) {
@@ -136,6 +150,10 @@
             result.set("links", LinksListCommand.json(li.links()));
         }
 
+        List<Intent> installable = service.getInstallableIntents(intent.id());
+        if (installable != null && !installable.isEmpty()) {
+            result.set("installable", json(service, installable));
+        }
         return result;
     }
 
diff --git a/core/api/src/main/java/org/onlab/onos/net/intent/IntentService.java b/core/api/src/main/java/org/onlab/onos/net/intent/IntentService.java
index c3aae54..700066d 100644
--- a/core/api/src/main/java/org/onlab/onos/net/intent/IntentService.java
+++ b/core/api/src/main/java/org/onlab/onos/net/intent/IntentService.java
@@ -1,6 +1,8 @@
 package org.onlab.onos.net.intent;
 
 
+import java.util.List;
+
 /**
  * Service for application submitting or withdrawing their intents.
  */
@@ -68,6 +70,15 @@
     IntentState getIntentState(IntentId id);
 
     /**
+     * Returns the list of the installable events associated with the specified
+     * top-level intent.
+     *
+     * @param intentId top-level intent identifier
+     * @return compiled installable intents
+     */
+    List<Intent> getInstallableIntents(IntentId intentId);
+
+    /**
      * Adds the specified listener for intent events.
      *
      * @param listener listener to be added
diff --git a/core/api/src/test/java/org/onlab/onos/net/intent/FakeIntentManager.java b/core/api/src/test/java/org/onlab/onos/net/intent/FakeIntentManager.java
index 4b3fd37..5020459 100644
--- a/core/api/src/test/java/org/onlab/onos/net/intent/FakeIntentManager.java
+++ b/core/api/src/test/java/org/onlab/onos/net/intent/FakeIntentManager.java
@@ -196,6 +196,11 @@
     }
 
     @Override
+    public List<Intent> getInstallableIntents(IntentId intentId) {
+        return installables.get(intentId);
+    }
+
+    @Override
     public void addListener(IntentListener listener) {
         listeners.add(listener);
     }
diff --git a/core/net/src/main/java/org/onlab/onos/net/intent/impl/IntentManager.java b/core/net/src/main/java/org/onlab/onos/net/intent/impl/IntentManager.java
index 03d5ce8..bfdc57e 100644
--- a/core/net/src/main/java/org/onlab/onos/net/intent/impl/IntentManager.java
+++ b/core/net/src/main/java/org/onlab/onos/net/intent/impl/IntentManager.java
@@ -153,6 +153,12 @@
     }
 
     @Override
+    public List<Intent> getInstallableIntents(IntentId intentId) {
+        checkNotNull(intentId, INTENT_ID_NULL);
+        return store.getInstallableIntents(intentId);
+    }
+
+    @Override
     public void addListener(IntentListener listener) {
         listenerRegistry.addListener(listener);
     }