You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@karaf.apache.org by gn...@apache.org on 2014/05/14 10:31:19 UTC

git commit: [KARAF-413] Tab completion on path when installing bundles

Repository: karaf
Updated Branches:
  refs/heads/master a0596c3f4 -> 06dee189a


[KARAF-413] Tab completion on path when installing bundles

The maven completion isn’t perfect.  It’s quite difficult to know all the groupIds in advance, unless we completely scan the local repo which can be very time consuming.

Project: http://git-wip-us.apache.org/repos/asf/karaf/repo
Commit: http://git-wip-us.apache.org/repos/asf/karaf/commit/06dee189
Tree: http://git-wip-us.apache.org/repos/asf/karaf/tree/06dee189
Diff: http://git-wip-us.apache.org/repos/asf/karaf/diff/06dee189

Branch: refs/heads/master
Commit: 06dee189ab733e3ad0eb54087e20102bbeaa09d6
Parents: a0596c3
Author: Guillaume Nodet <gn...@gmail.com>
Authored: Wed May 14 10:30:43 2014 +0200
Committer: Guillaume Nodet <gn...@gmail.com>
Committed: Wed May 14 10:30:43 2014 +0200

----------------------------------------------------------------------
 .../karaf/bundle/command/BundleCommand.java     |   4 -
 .../apache/karaf/bundle/command/Install.java    |   9 +-
 .../org/apache/karaf/bundle/command/Update.java |   6 +-
 .../impl/action/command/ArgumentCompleter.java  |  53 +++---
 .../shell/impl/console/ConsoleSessionImpl.java  |   4 +
 .../shell/support/completers/FileCompleter.java |   8 +-
 .../shell/support/completers/UriCompleter.java  | 172 +++++++++++++++++++
 .../org/apache/karaf/util/maven/Parser.java     |   2 +-
 8 files changed, 211 insertions(+), 47 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/karaf/blob/06dee189/bundle/core/src/main/java/org/apache/karaf/bundle/command/BundleCommand.java
----------------------------------------------------------------------
diff --git a/bundle/core/src/main/java/org/apache/karaf/bundle/command/BundleCommand.java b/bundle/core/src/main/java/org/apache/karaf/bundle/command/BundleCommand.java
index 99582fa..acca105 100644
--- a/bundle/core/src/main/java/org/apache/karaf/bundle/command/BundleCommand.java
+++ b/bundle/core/src/main/java/org/apache/karaf/bundle/command/BundleCommand.java
@@ -16,15 +16,11 @@
  */
 package org.apache.karaf.bundle.command;
 
-import java.util.Collections;
-import java.util.List;
-
 import org.apache.karaf.bundle.core.BundleService;
 import org.apache.karaf.shell.api.action.Action;
 import org.apache.karaf.shell.api.action.Argument;
 import org.apache.karaf.shell.api.action.Option;
 import org.apache.karaf.shell.api.action.lifecycle.Reference;
-import org.apache.karaf.shell.support.ShellUtil;
 import org.osgi.framework.Bundle;
 import org.osgi.framework.BundleContext;
 

http://git-wip-us.apache.org/repos/asf/karaf/blob/06dee189/bundle/core/src/main/java/org/apache/karaf/bundle/command/Install.java
----------------------------------------------------------------------
diff --git a/bundle/core/src/main/java/org/apache/karaf/bundle/command/Install.java b/bundle/core/src/main/java/org/apache/karaf/bundle/command/Install.java
index 017e780..cdf2691 100644
--- a/bundle/core/src/main/java/org/apache/karaf/bundle/command/Install.java
+++ b/bundle/core/src/main/java/org/apache/karaf/bundle/command/Install.java
@@ -16,6 +16,7 @@
  */
 package org.apache.karaf.bundle.command;
 
+import java.net.URI;
 import java.util.ArrayList;
 import java.util.List;
 
@@ -38,7 +39,7 @@ import org.osgi.framework.startlevel.BundleStartLevel;
 public class Install implements Action {
 
     @Argument(index = 0, name = "urls", description = "Bundle URLs separated by whitespaces", required = true, multiValued = true)
-    List<String> urls;
+    List<URI> urls;
 
     @Option(name = "-s", aliases={"--start"}, description="Starts the bundles after installation", required = false, multiValued = false)
     boolean start;
@@ -64,16 +65,16 @@ public class Install implements Action {
             int sbsl = bundleService.getSystemBundleThreshold();
             if (level < sbsl) {
                 if (!JaasHelper.currentUserHasRole(BundleService.SYSTEM_BUNDLES_ROLE)) {
-                    throw new IllegalArgumentException("Insufficient priviledges");
+                    throw new IllegalArgumentException("Insufficient privileges");
                 }
             }
         }
         // install the bundles
         List<Exception> exceptions = new ArrayList<>();
         List<Bundle> bundles = new ArrayList<>();
-        for (String url : urls) {
+        for (URI url : urls) {
             try {
-                bundles.add(bundleContext.installBundle(url, null));
+                bundles.add(bundleContext.installBundle(url.toString(), null));
             } catch (Exception e) {
                 exceptions.add(new Exception("Unable to install bundle " + url, e));
             }

http://git-wip-us.apache.org/repos/asf/karaf/blob/06dee189/bundle/core/src/main/java/org/apache/karaf/bundle/command/Update.java
----------------------------------------------------------------------
diff --git a/bundle/core/src/main/java/org/apache/karaf/bundle/command/Update.java b/bundle/core/src/main/java/org/apache/karaf/bundle/command/Update.java
index b918bca..5222822 100644
--- a/bundle/core/src/main/java/org/apache/karaf/bundle/command/Update.java
+++ b/bundle/core/src/main/java/org/apache/karaf/bundle/command/Update.java
@@ -17,7 +17,7 @@
 package org.apache.karaf.bundle.command;
 
 import java.io.InputStream;
-import java.net.URL;
+import java.net.URI;
 
 import org.apache.karaf.shell.api.action.Argument;
 import org.apache.karaf.shell.api.action.Command;
@@ -29,12 +29,12 @@ import org.osgi.framework.Bundle;
 public class Update extends BundleCommand {
 
     @Argument(index = 1, name = "location", description = "The bundles update location", required = false, multiValued = false)
-    String location;
+    URI location;
 
     protected Object doExecute(Bundle bundle) throws Exception {
         if (location != null) {
             try (
-                InputStream is = new URL(location).openStream()
+                InputStream is = location.toURL().openStream()
             ) {
                 bundle.update(is);
             }

http://git-wip-us.apache.org/repos/asf/karaf/blob/06dee189/shell/core/src/main/java/org/apache/karaf/shell/impl/action/command/ArgumentCompleter.java
----------------------------------------------------------------------
diff --git a/shell/core/src/main/java/org/apache/karaf/shell/impl/action/command/ArgumentCompleter.java b/shell/core/src/main/java/org/apache/karaf/shell/impl/action/command/ArgumentCompleter.java
index 2e8fca3..1bd04cc 100644
--- a/shell/core/src/main/java/org/apache/karaf/shell/impl/action/command/ArgumentCompleter.java
+++ b/shell/core/src/main/java/org/apache/karaf/shell/impl/action/command/ArgumentCompleter.java
@@ -20,7 +20,9 @@ package org.apache.karaf.shell.impl.action.command;
 
 import java.io.File;
 import java.lang.reflect.Field;
+import java.net.URI;
 import java.util.ArrayList;
+import java.util.Collection;
 import java.util.EnumSet;
 import java.util.HashMap;
 import java.util.HashSet;
@@ -39,6 +41,8 @@ import org.apache.karaf.shell.support.completers.ArgumentCommandLine;
 import org.apache.karaf.shell.support.completers.FileCompleter;
 import org.apache.karaf.shell.support.completers.NullCompleter;
 import org.apache.karaf.shell.support.completers.StringsCompleter;
+import org.apache.karaf.shell.support.completers.UriCompleter;
+import org.apache.karaf.shell.support.converter.GenericType;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -51,10 +55,9 @@ public class ArgumentCompleter implements Completer {
     final Completer optionsCompleter;
     final List<Completer> argsCompleters;
     final Map<String, Completer> optionalCompleters;
-    final Map<Option, Field> fields = new HashMap<Option, Field>();
-    final Map<String, Option> options = new HashMap<String, Option>();
-    final Map<Integer, Field> arguments = new HashMap<Integer, Field>();
-    boolean strict = true;
+    final Map<Option, Field> fields = new HashMap<>();
+    final Map<String, Option> options = new HashMap<>();
+    final Map<Integer, Field> arguments = new HashMap<>();
 
     @SuppressWarnings({ "unchecked", "rawtypes" })
     public ArgumentCompleter(ActionCommand command, boolean scoped) {
@@ -91,7 +94,7 @@ public class ArgumentCompleter implements Completer {
         }
         options.put(HelpOption.HELP.name(), HelpOption.HELP);
 
-        argsCompleters = new ArrayList<Completer>();
+        argsCompleters = new ArrayList<>();
         optionsCompleter = new StringsCompleter(options.keySet());
 
         boolean multi = false;
@@ -113,7 +116,7 @@ public class ArgumentCompleter implements Completer {
                         }
                     }
                 } else {
-                    completer = getDefaultCompleter(field);
+                    completer = getDefaultCompleter(field, multi);
                 }
             }
             if (completer == null) {
@@ -124,7 +127,7 @@ public class ArgumentCompleter implements Completer {
         if (argsCompleters.isEmpty() || !multi) {
             argsCompleters.add(NullCompleter.INSTANCE);
         }
-        optionalCompleters = new HashMap<String, Completer>();
+        optionalCompleters = new HashMap<>();
         for (Option option : fields.keySet()) {
             Completer completer = null;
             Field field = fields.get(option);
@@ -140,6 +143,8 @@ public class ArgumentCompleter implements Completer {
                             completer = command.getCompleter(clazz);
                         }
                     }
+                } else {
+                    completer = getDefaultCompleter(field, option.multiValued());
                 }
             }
             if (completer == null) {
@@ -154,43 +159,31 @@ public class ArgumentCompleter implements Completer {
         }
     }
 
-    private Completer getDefaultCompleter(Field field) {
+    @SuppressWarnings("unchecked")
+    private Completer getDefaultCompleter(Field field, boolean multi) {
         Completer completer = null;
         Class<?> type = field.getType();
-        if (type.isAssignableFrom(File.class)) {
+        GenericType genericType = new GenericType(field.getGenericType());
+        if (Collection.class.isAssignableFrom(genericType.getRawClass()) && multi) {
+            type = genericType.getActualTypeArgument(0).getRawClass();
+        }
+        if (type.isAssignableFrom(URI.class)) {
+            completer = new UriCompleter();
+        } else if (type.isAssignableFrom(File.class)) {
             completer = new FileCompleter();
         } else if (type.isAssignableFrom(Boolean.class) || type.isAssignableFrom(boolean.class)) {
             completer = new StringsCompleter(new String[] {"false", "true"}, false);
         } else if (type.isAssignableFrom(Enum.class)) {
-            Set<String> values = new HashSet<String>();
+            Set<String> values = new HashSet<>();
             for (Object o : EnumSet.allOf((Class<Enum>) type)) {
                 values.add(o.toString());
             }
             completer = new StringsCompleter(values, false);
-        } else {
-            // TODO any other completers we can add?
         }
         return completer;
     }
 
-    /**
-     *  If true, a completion at argument index N will only succeed
-     *  if all the completions from 0-(N-1) also succeed.
-     */
-    public void setStrict(final boolean strict) {
-        this.strict = strict;
-    }
-
-    /**
-     *  Returns whether a completion at argument index N will succees
-     *  if all the completions from arguments 0-(N-1) also succeed.
-     */
-    public boolean getStrict() {
-        return this.strict;
-    }
-
     public int complete(Session session, final CommandLine list, final List<String> candidates) {
-        int argpos = list.getArgumentPosition();
         int argIndex = list.getCursorArgumentIndex();
 
         Completer comp = null;
@@ -312,7 +305,7 @@ public class ArgumentCompleter implements Completer {
     }
 
     protected boolean verifyCompleter(Session session, Completer completer, String argument) {
-        List<String> candidates = new ArrayList<String>();
+        List<String> candidates = new ArrayList<>();
         return completer.complete(session, new ArgumentCommandLine(argument, argument.length()), candidates) != -1 && !candidates.isEmpty();
     }
 

http://git-wip-us.apache.org/repos/asf/karaf/blob/06dee189/shell/core/src/main/java/org/apache/karaf/shell/impl/console/ConsoleSessionImpl.java
----------------------------------------------------------------------
diff --git a/shell/core/src/main/java/org/apache/karaf/shell/impl/console/ConsoleSessionImpl.java b/shell/core/src/main/java/org/apache/karaf/shell/impl/console/ConsoleSessionImpl.java
index a284091..1673f37 100644
--- a/shell/core/src/main/java/org/apache/karaf/shell/impl/console/ConsoleSessionImpl.java
+++ b/shell/core/src/main/java/org/apache/karaf/shell/impl/console/ConsoleSessionImpl.java
@@ -53,6 +53,8 @@ import org.apache.karaf.shell.api.console.Session;
 import org.apache.karaf.shell.api.console.SessionFactory;
 import org.apache.karaf.shell.api.console.Terminal;
 import org.apache.karaf.shell.support.ShellUtil;
+import org.apache.karaf.shell.support.completers.FileCompleter;
+import org.apache.karaf.shell.support.completers.UriCompleter;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -149,6 +151,8 @@ public class ConsoleSessionImpl implements Session {
         reader.addCompleter(new CompleterAsCompletor(this, completer));
         registry.register(completer);
         registry.register(new CommandNamesCompleter());
+        registry.register(new FileCompleter());
+        registry.register(new UriCompleter());
 
         // Session
         session = processor.createSession(in != null ? console : null, out, err);

http://git-wip-us.apache.org/repos/asf/karaf/blob/06dee189/shell/core/src/main/java/org/apache/karaf/shell/support/completers/FileCompleter.java
----------------------------------------------------------------------
diff --git a/shell/core/src/main/java/org/apache/karaf/shell/support/completers/FileCompleter.java b/shell/core/src/main/java/org/apache/karaf/shell/support/completers/FileCompleter.java
index 20a812f..c200f4c 100644
--- a/shell/core/src/main/java/org/apache/karaf/shell/support/completers/FileCompleter.java
+++ b/shell/core/src/main/java/org/apache/karaf/shell/support/completers/FileCompleter.java
@@ -64,10 +64,8 @@ public class FileCompleter implements Completer
             return 0;
         }
 
-        String buffer = commandLine.getBuffer();
-        if (buffer == null) {
-            buffer = "";
-        }
+        String buffer = commandLine.getCursorArgument();
+        buffer = buffer != null ? buffer.substring(0, commandLine.getArgumentPosition()) : "";
 
         if (OS_IS_WINDOWS) {
             buffer = buffer.replace('/', '\\');
@@ -101,7 +99,7 @@ public class FileCompleter implements Completer
 
         File[] entries = dir == null ? new File[0] : dir.listFiles();
 
-        return matchFiles(buffer, translated, entries, candidates);
+        return matchFiles(buffer, translated, entries, candidates) + commandLine.getBufferPosition() - commandLine.getArgumentPosition();
     }
 
     protected String separator() {

http://git-wip-us.apache.org/repos/asf/karaf/blob/06dee189/shell/core/src/main/java/org/apache/karaf/shell/support/completers/UriCompleter.java
----------------------------------------------------------------------
diff --git a/shell/core/src/main/java/org/apache/karaf/shell/support/completers/UriCompleter.java b/shell/core/src/main/java/org/apache/karaf/shell/support/completers/UriCompleter.java
new file mode 100644
index 0000000..dacba12
--- /dev/null
+++ b/shell/core/src/main/java/org/apache/karaf/shell/support/completers/UriCompleter.java
@@ -0,0 +1,172 @@
+/*
+ * 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.karaf.shell.support.completers;
+
+import java.io.File;
+import java.nio.file.DirectoryStream;
+import java.nio.file.FileSystems;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.List;
+
+import org.apache.karaf.shell.api.console.CommandLine;
+import org.apache.karaf.shell.api.console.Completer;
+import org.apache.karaf.shell.api.console.Session;
+
+public class UriCompleter implements Completer {
+
+    @Override
+    public int complete(Session session, CommandLine commandLine, List<String> candidates) {
+        String arg = commandLine.getCursorArgument();
+        if (arg != null) {
+            if (arg.startsWith("mvn:")) {
+                return maven(session, commandLine, candidates);
+            } else if (arg.startsWith("file:")) {
+                return file(session, commandLine, candidates);
+            }
+        }
+        return 0;
+    }
+
+    private int file(Session session, CommandLine commandLine, List<String> candidates) {
+        String buffer = commandLine.getCursorArgument();
+        String path = buffer.substring("file:".length(), commandLine.getArgumentPosition());
+
+        String rem = "";
+        try {
+
+            Path dir;
+            if (path.length() == 0) {
+                for (Path root : FileSystems.getDefault().getRootDirectories()) {
+                    candidates.add(root.toString());
+                }
+                dir = Paths.get(".");
+            } else {
+                dir = Paths.get(decode(path));
+                if (!path.endsWith("/")) {
+                    rem = dir.getFileName().toString();
+                    dir = dir.getParent();
+                    if (dir == null) {
+                        dir = Paths.get(".");
+                    }
+                }
+            }
+            if (Files.isDirectory(dir)) {
+                try (DirectoryStream<Path> paths = Files.newDirectoryStream(dir, rem + "*")) {
+                    for (Path child : paths) {
+                        String name = encode(child.getFileName().toString());
+                        if (Files.isDirectory(child)) {
+                            name += "/";
+                        }
+                        candidates.add(name);
+                    }
+                }
+            }
+        } catch (Exception e) {
+            // Ignore
+        }
+
+        if (candidates.size() == 1) {
+            String cand = candidates.get(0);
+            if (!cand.endsWith("/")) {
+                candidates.set(0, cand + " ");
+            }
+        }
+        return candidates.isEmpty() ? -1 : commandLine.getBufferPosition() - rem.length();
+    }
+
+    private String encode(String s) {
+        return s.replaceAll(" ", "%20");
+    }
+
+    private String decode(String s) {
+        return s.replaceAll("%20", " ");
+    }
+
+    private int maven(Session session, CommandLine commandLine, List<String> candidates) {
+        String repo = System.getProperty("user.home") + "/.m2/repository";
+        String buffer = commandLine.getCursorArgument();
+        String mvn = buffer.substring("mvn:".length(), commandLine.getArgumentPosition());
+
+        String rem = "";
+
+        try {
+            String[] parts = mvn.split("/");
+            if (parts.length == 0 || parts.length == 1 && !mvn.endsWith("/")) {
+                String known = "";
+                String group = "";
+                String[] dirs = parts.length > 0 ? parts[0].split("\\.") : new String[] { "" };
+                if (parts.length > 0 && parts[0].endsWith(".")) {
+                    for (int i = 0; i < dirs.length; i++) {
+                        known += dirs[i] + File.separator;
+                        group += dirs[i] + ".";
+                    }
+                } else {
+                    for (int i = 0; i < dirs.length - 1; i++) {
+                        known += dirs[i] + File.separator;
+                        group += dirs[i] + ".";
+                    }
+                    rem = dirs[dirs.length - 1];
+                }
+                Path dir = Paths.get(repo + File.separator + known);
+                try (DirectoryStream<Path> paths = Files.newDirectoryStream(dir, rem + "*")) {
+                    for (Path path : paths) {
+                        if (Files.isDirectory(path)) {
+                            String name = path.getFileName().toString();
+                            candidates.add(group + name);
+                        }
+                    }
+                }
+                rem = group + rem;
+            } else if (parts.length == 1 || parts.length == 2 && !mvn.endsWith("/")) {
+                rem = parts.length > 1 ? parts[1] : "";
+                Path dir = Paths.get(repo + File.separator + parts[0].replace(".", "/"));
+                try (DirectoryStream<Path> paths = Files.newDirectoryStream(dir, rem + "*")) {
+                    for (Path path : paths) {
+                        if (Files.isDirectory(path)) {
+                            String name = path.getFileName().toString();
+                            candidates.add(name);
+                        }
+                    }
+                }
+            } else if (parts.length == 2 || parts.length == 3 && !mvn.endsWith("/")) {
+                rem = parts.length > 2 ? parts[2] : "";
+                Path dir = Paths.get(repo + File.separator + parts[0].replace(".", "/") + File.separator + parts[1]);
+                try (DirectoryStream<Path> paths = Files.newDirectoryStream(dir, rem + "*")) {
+                    for (Path path : paths) {
+                        if (Files.isDirectory(path)) {
+                            String name = path.getFileName().toString();
+                            candidates.add(name);
+                        }
+                    }
+                }
+            }
+        } catch (Exception e) {
+            // Ignore
+        }
+
+        if (candidates.size() == 1) {
+            String cand = candidates.get(0);
+            if (cand.split("/").length == 3) {
+                candidates.set(0, candidates.get(0) + " ");
+            }
+        }
+        return candidates.isEmpty() ? -1 : commandLine.getBufferPosition() - rem.length();
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/karaf/blob/06dee189/util/src/main/java/org/apache/karaf/util/maven/Parser.java
----------------------------------------------------------------------
diff --git a/util/src/main/java/org/apache/karaf/util/maven/Parser.java b/util/src/main/java/org/apache/karaf/util/maven/Parser.java
index 94f0478..232942e 100644
--- a/util/src/main/java/org/apache/karaf/util/maven/Parser.java
+++ b/util/src/main/java/org/apache/karaf/util/maven/Parser.java
@@ -188,7 +188,7 @@ public class Parser
         {
             m_type = segments[ 3 ];
         }
-        // classifier is optional (if not pressent or empty we will have a null classsifier
+        // classifier is optional (if not present or empty we will have a null classifier
         m_fullClassifier = "";
         if( segments.length >= 5 && segments[ 4 ].trim().length() > 0 )
         {