intent-details command

- Intended to be used by ONOS-5221, ONOS-5827

Change-Id: I33ab29946907e1963cc66c451d5f1aa77760f4fe
diff --git a/cli/src/main/java/org/onosproject/cli/net/IntentDetailsCommand.java b/cli/src/main/java/org/onosproject/cli/net/IntentDetailsCommand.java
new file mode 100644
index 0000000..1cd0a1b
--- /dev/null
+++ b/cli/src/main/java/org/onosproject/cli/net/IntentDetailsCommand.java
@@ -0,0 +1,132 @@
+/*
+ * Copyright 2017-present Open Networking Laboratory
+ *
+ * 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.onosproject.cli.net;
+
+import java.util.List;
+import java.util.Set;
+import java.util.stream.Collectors;
+import org.apache.karaf.shell.commands.Command;
+import org.apache.karaf.shell.commands.Option;
+import org.onlab.util.Tools;
+import org.onosproject.cli.AbstractShellCommand;
+import org.onosproject.net.Link;
+import org.onosproject.net.LinkKey;
+import org.onosproject.net.intent.Intent;
+import org.onosproject.net.intent.IntentData;
+import org.onosproject.net.intent.IntentId;
+import org.onosproject.net.intent.IntentService;
+
+/**
+ * Displays details about an Intent in the system.
+ */
+@Command(scope = "onos", name = "intent-details",
+         description = "Displays intent details")
+public class IntentDetailsCommand extends AbstractShellCommand {
+
+    @Option(name = "--id",
+            description = "Filter intent by specific Id", multiValued = true)
+    private List<String> idsStr;
+
+    private Set<IntentId> ids = null;
+
+
+    @Override
+    protected void execute() {
+        if (idsStr != null) {
+            ids = idsStr.stream()
+                    .map(IntentId::valueOf)
+                    .collect(Collectors.toSet());
+        }
+
+        IntentService service = get(IntentService.class);
+
+        Tools.stream(service.getIntentData())
+            .filter(this::filter)
+            .forEach(this::printIntentData);
+    }
+
+    private boolean filter(IntentData data) {
+        if (ids != null && !ids.contains(data.intent().id())) {
+            return false;
+        }
+
+        return true;
+    }
+
+    private void printIntentData(IntentData data) {
+        print("Key: %s ID: %s", data.key(), data.intent().id());
+
+        print(" Request: %s Current: %s", data.request(), data.state());
+
+        print(" intent: %s", s(data.intent()));
+
+        data.installables().stream()
+            .forEach(this::printInstallable);
+
+        // empty line
+        print("");
+    }
+
+    private void printInstallable(Intent installable) {
+        print(" installable: %s %s", installable.getClass().getSimpleName(),
+                                     installable.id());
+
+        print("  resources: %s", installable.resources().stream()
+                                    .filter(r -> !(r instanceof Link))
+                                    .map(this::s)
+                                    .collect(Collectors.joining(", ")));
+
+        print("  links: %s", installable.resources().stream()
+                              .filter(Link.class::isInstance)
+                              .map(Link.class::cast)
+                              .map(LinkKey::linkKey)
+                              .map(l -> String.format("%s -> %s", l.src(), l.dst()))
+                              .collect(Collectors.joining(", ")));
+    }
+
+    protected String s(Object o) {
+        return simplify(String.valueOf(o));
+    }
+
+    /**
+     * Simplify toString result for CLI.
+     *
+     * @param input String
+     * @return simplified String
+     */
+    public static String simplify(String input) {
+        String after = input
+                // omit redundant info
+                .replaceAll("treatment=DefaultTrafficTreatment", "treatment=")
+                .replaceAll("selector=DefaultTrafficSelector", "selector=")
+                // shorten AppId
+                .replaceAll("DefaultApplicationId\\{id=(\\d+), name=([.\\w]+)\\}", "$2($1)")
+                // omit empty list/array attribute
+                .replaceAll("(, )?\\w+=\\[\\]", "")
+                // omit empty map attribute
+                .replaceAll("(, )?\\w+=\\{\\}", "")
+                // omit Object which became empty
+                .replaceAll("(, )?\\w+\\{\\}", "\\{\\}")
+                // shorten FilteredConnectPoint
+                .replaceAll("FilteredConnectPoint", "")
+                // trim prefix Default
+                .replaceAll("Default(\\w+)\\{", "$1\\{")
+                .replaceAll(", , ", ", ");
+
+        return after.equals(input) ? input : simplify(after);
+    }
+
+}
diff --git a/cli/src/main/java/org/onosproject/cli/net/IntentsListCommand.java b/cli/src/main/java/org/onosproject/cli/net/IntentsListCommand.java
index 0953291..59b3e47 100644
--- a/cli/src/main/java/org/onosproject/cli/net/IntentsListCommand.java
+++ b/cli/src/main/java/org/onosproject/cli/net/IntentsListCommand.java
@@ -65,7 +65,7 @@
     private boolean pending = false;
 
     @Option(name = "-f", aliases = "--filter",
-            description = "Filter intents by specific key",
+            description = "Filter intents by specific keyword",
             required = false, multiValued = true)
     private List<String> filter = new ArrayList<>();
 
diff --git a/cli/src/main/java/org/onosproject/cli/net/completer/IntentIdCompleter.java b/cli/src/main/java/org/onosproject/cli/net/completer/IntentIdCompleter.java
new file mode 100644
index 0000000..2d4662e
--- /dev/null
+++ b/cli/src/main/java/org/onosproject/cli/net/completer/IntentIdCompleter.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2017-present Open Networking Laboratory
+ *
+ * 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.onosproject.cli.net.completer;
+
+import java.util.List;
+import java.util.stream.Collectors;
+
+import org.onlab.util.Tools;
+import org.onosproject.cli.AbstractChoicesCompleter;
+import org.onosproject.cli.AbstractShellCommand;
+import org.onosproject.net.intent.Intent;
+import org.onosproject.net.intent.IntentId;
+import org.onosproject.net.intent.IntentService;
+
+/**
+ * IntentId Completer.
+ */
+public class IntentIdCompleter extends AbstractChoicesCompleter {
+
+    @Override
+    protected List<String> choices() {
+        IntentService service = AbstractShellCommand.get(IntentService.class);
+
+        return Tools.stream(service.getIntents())
+            .map(Intent::id)
+            .map(IntentId::toString)
+            .collect(Collectors.toList());
+    }
+
+}
diff --git a/cli/src/main/resources/OSGI-INF/blueprint/shell-config.xml b/cli/src/main/resources/OSGI-INF/blueprint/shell-config.xml
index 8b60699..92271f2 100644
--- a/cli/src/main/resources/OSGI-INF/blueprint/shell-config.xml
+++ b/cli/src/main/resources/OSGI-INF/blueprint/shell-config.xml
@@ -729,6 +729,14 @@
         <command>
             <action class="org.onosproject.cli.net.DpisListCommand"/>
         </command>
+
+        <command>
+            <action class="org.onosproject.cli.net.IntentDetailsCommand"/>
+            <optional-completers>
+                <entry key="--id" value-ref="intentIdCompleter"/>
+            </optional-completers>
+        </command>
+
     </command-bundle>
 
     <bean id="reviewAppNameCompleter" class="org.onosproject.cli.security.ReviewApplicationNameCompleter"/>
@@ -747,6 +755,7 @@
     <bean id="roleCompleter" class="org.onosproject.cli.net.RoleCompleter"/>
     <bean id="hostIdCompleter" class="org.onosproject.cli.net.HostIdCompleter"/>
     <bean id="intentKeyCompleter" class="org.onosproject.cli.net.IntentKeyCompleter"/>
+    <bean id="intentIdCompleter" class="org.onosproject.cli.net.completer.IntentIdCompleter"/>
     <bean id="flowRuleStatusCompleter" class="org.onosproject.cli.net.FlowRuleStatusCompleter"/>
     <bean id="groupStatusCompleter" class="org.onosproject.cli.net.GroupStatusCompleter" />
     <bean id="connectPointCompleter" class="org.onosproject.cli.net.ConnectPointCompleter"/>