Add unique suffixes to app name completer candidates

- Add appname suffix acceptable as appname fragment
  app activate [appname fragment]
   e.g., app activate conf[tab]
    => config configsync configsync-netconf

Change-Id: Ibea0a3d3995f54001fbfd5015db8f3785c353e3d
diff --git a/cli/src/main/java/org/onosproject/cli/app/ApplicationNameCompleter.java b/cli/src/main/java/org/onosproject/cli/app/ApplicationNameCompleter.java
index 1848bf3..3f93ced 100644
--- a/cli/src/main/java/org/onosproject/cli/app/ApplicationNameCompleter.java
+++ b/cli/src/main/java/org/onosproject/cli/app/ApplicationNameCompleter.java
@@ -22,10 +22,19 @@
 import org.onosproject.cli.AbstractCompleter;
 import org.onosproject.core.Application;
 
+import com.google.common.base.Strings;
+import com.google.common.collect.Lists;
+
+import java.util.ArrayDeque;
+import java.util.ArrayList;
+import java.util.Deque;
 import java.util.Iterator;
 import java.util.List;
+import java.util.Map.Entry;
 import java.util.SortedSet;
+import java.util.stream.Collectors;
 
+import static java.util.Arrays.asList;
 import static org.onosproject.app.ApplicationState.ACTIVE;
 import static org.onosproject.app.ApplicationState.INSTALLED;
 import static org.onosproject.cli.AbstractShellCommand.get;
@@ -70,6 +79,33 @@
             }
         }
 
+        // add unique suffix to candidates, if user has something in buffer
+        if (!Strings.isNullOrEmpty(buffer)) {
+            List<String> suffixCandidates = strings.stream()
+                    // remove onos common prefix
+                    .map(full -> full.replaceFirst("org\\.onosproject\\.", ""))
+                    // a.b.c -> [c, b.c, a.b.c]
+                    .flatMap(appName -> {
+                        List<String> suffixes = new ArrayList<>();
+                        Deque<String> frags = new ArrayDeque<>();
+                        // a.b.c -> [c, b, a] -> [c, b.c, a.b.c]
+                        Lists.reverse(asList(appName.split("\\."))).forEach(frag -> {
+                            frags.addFirst(frag);
+                            suffixes.add(frags.stream().collect(Collectors.joining(".")));
+                        });
+                        return suffixes.stream();
+                    })
+                    // convert to occurrence map
+                    .collect(Collectors.groupingBy(e -> e, Collectors.counting()))
+                    .entrySet().stream()
+                    // only accept unique suffix
+                    .filter(e -> e.getValue() == 1L)
+                    .map(Entry::getKey)
+                    .collect(Collectors.toList());
+
+            delegate.getStrings().addAll(suffixCandidates);
+        }
+
         // Now let the completer do the work for figuring out what to offer.
         return delegate.complete(buffer, cursor, candidates);
     }