You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@karaf.apache.org by jb...@apache.org on 2013/04/12 15:58:21 UTC

svn commit: r1467272 - in /karaf/trunk/shell: console/src/main/java/org/apache/karaf/shell/console/completer/ help/src/main/java/org/apache/karaf/shell/help/impl/

Author: jbonofre
Date: Fri Apr 12 13:58:21 2013
New Revision: 1467272

URL: http://svn.apache.org/r1467272
Log:
[KARAF-2121] Add support for displaying gogo commands in Karaf shell help

Modified:
    karaf/trunk/shell/console/src/main/java/org/apache/karaf/shell/console/completer/CommandNamesCompleter.java
    karaf/trunk/shell/console/src/main/java/org/apache/karaf/shell/console/completer/CommandsCompleter.java
    karaf/trunk/shell/help/src/main/java/org/apache/karaf/shell/help/impl/CommandListHelpProvider.java
    karaf/trunk/shell/help/src/main/java/org/apache/karaf/shell/help/impl/HelpSystem.java
    karaf/trunk/shell/help/src/main/java/org/apache/karaf/shell/help/impl/SingleCommandHelpProvider.java

Modified: karaf/trunk/shell/console/src/main/java/org/apache/karaf/shell/console/completer/CommandNamesCompleter.java
URL: http://svn.apache.org/viewvc/karaf/trunk/shell/console/src/main/java/org/apache/karaf/shell/console/completer/CommandNamesCompleter.java?rev=1467272&r1=1467271&r2=1467272&view=diff
==============================================================================
--- karaf/trunk/shell/console/src/main/java/org/apache/karaf/shell/console/completer/CommandNamesCompleter.java (original)
+++ karaf/trunk/shell/console/src/main/java/org/apache/karaf/shell/console/completer/CommandNamesCompleter.java Fri Apr 12 13:58:21 2013
@@ -75,6 +75,7 @@ public class CommandNamesCompleter imple
                 commands.add(name);
                 if (name.indexOf(':') > 0) {
                     commands.add(name.substring(0, name.indexOf(':')));
+                    commands.add(name.substring(name.indexOf(':') + 1));
                 }
             }
         }

Modified: karaf/trunk/shell/console/src/main/java/org/apache/karaf/shell/console/completer/CommandsCompleter.java
URL: http://svn.apache.org/viewvc/karaf/trunk/shell/console/src/main/java/org/apache/karaf/shell/console/completer/CommandsCompleter.java?rev=1467272&r1=1467271&r2=1467272&view=diff
==============================================================================
--- karaf/trunk/shell/console/src/main/java/org/apache/karaf/shell/console/completer/CommandsCompleter.java (original)
+++ karaf/trunk/shell/console/src/main/java/org/apache/karaf/shell/console/completer/CommandsCompleter.java Fri Apr 12 13:58:21 2013
@@ -102,6 +102,7 @@ public class CommandsCompleter implement
                         }
                         try {
                             completers.add(new ArgumentCompleter(session, (CommandWithAction) function, command));
+                            completers.add(new CommandNamesCompleter(session));
                         } catch (Throwable t) {
                             LOGGER.debug("Unable to create completers for command '" + command + "'", t);
                         }

Modified: karaf/trunk/shell/help/src/main/java/org/apache/karaf/shell/help/impl/CommandListHelpProvider.java
URL: http://svn.apache.org/viewvc/karaf/trunk/shell/help/src/main/java/org/apache/karaf/shell/help/impl/CommandListHelpProvider.java?rev=1467272&r1=1467271&r2=1467272&view=diff
==============================================================================
--- karaf/trunk/shell/help/src/main/java/org/apache/karaf/shell/help/impl/CommandListHelpProvider.java (original)
+++ karaf/trunk/shell/help/src/main/java/org/apache/karaf/shell/help/impl/CommandListHelpProvider.java Fri Apr 12 13:58:21 2013
@@ -20,13 +20,11 @@ package org.apache.karaf.shell.help.impl
 import java.io.ByteArrayOutputStream;
 import java.io.PrintStream;
 import java.lang.reflect.Field;
-import java.util.Map;
-import java.util.Set;
-import java.util.SortedMap;
-import java.util.TreeMap;
+import java.util.*;
 
 import jline.Terminal;
 
+import org.apache.felix.gogo.runtime.CommandProxy;
 import org.apache.felix.gogo.runtime.CommandSessionImpl;
 import org.apache.felix.service.command.CommandSession;
 import org.apache.felix.service.command.Function;
@@ -79,6 +77,14 @@ public class CommandListHelpProvider imp
                     name = name.substring(2);
                 }
                 commands.put(name, description);
+            } else if (function instanceof CommandProxy) {
+                Hashtable<String, HelpSystem.GogoCommandHelper> helpers = (Hashtable<String, HelpSystem.GogoCommandHelper>) session.get(HelpSystem.GOGO_COMMAND_HELPERS);
+                if (helpers != null) {
+                    HelpSystem.GogoCommandHelper helper = helpers.get(name);
+                    if (helper != null) {
+                        commands.put(name, helper.getDescription());
+                    }
+                }
             }
         }
         return commands;

Modified: karaf/trunk/shell/help/src/main/java/org/apache/karaf/shell/help/impl/HelpSystem.java
URL: http://svn.apache.org/viewvc/karaf/trunk/shell/help/src/main/java/org/apache/karaf/shell/help/impl/HelpSystem.java?rev=1467272&r1=1467271&r2=1467272&view=diff
==============================================================================
--- karaf/trunk/shell/help/src/main/java/org/apache/karaf/shell/help/impl/HelpSystem.java (original)
+++ karaf/trunk/shell/help/src/main/java/org/apache/karaf/shell/help/impl/HelpSystem.java Fri Apr 12 13:58:21 2013
@@ -17,25 +17,39 @@
  */
 package org.apache.karaf.shell.help.impl;
 
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
+import java.io.PrintStream;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Method;
+import java.util.*;
 
+import jline.Terminal;
+import org.apache.felix.service.command.CommandProcessor;
 import org.apache.felix.service.command.CommandSession;
+import org.apache.felix.service.command.Descriptor;
 import org.apache.karaf.shell.console.HelpProvider;
+import org.apache.karaf.shell.console.NameScoping;
+import org.apache.karaf.shell.util.IndentFormatter;
 import org.apache.karaf.util.InterpolationHelper;
+import org.fusesource.jansi.Ansi;
 import org.osgi.framework.BundleContext;
+import org.osgi.framework.Filter;
 import org.osgi.framework.InvalidSyntaxException;
 import org.osgi.framework.ServiceReference;
+import org.osgi.util.tracker.ServiceTracker;
 
 public class HelpSystem implements HelpProvider {
 
     private BundleContext context;
+    Hashtable<String, GogoCommandHelper> helpers = new Hashtable<String, GogoCommandHelper>();
 
     public HelpSystem(BundleContext context) {
         this.context = context;
+        try {
+            ServiceTracker commandTracker = trackOSGiCommands(context);
+            commandTracker.open();
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
     }
 
     @SuppressWarnings("unchecked")
@@ -55,6 +69,8 @@ public class HelpSystem implements HelpP
     }
     
     public String getHelp(final CommandSession session, String path) {
+        session.put(GOGO_COMMAND_HELPERS, helpers);
+
         if (path == null) {
             path = "%root%";
         }
@@ -81,5 +97,115 @@ public class HelpSystem implements HelpP
         }
         return help;
     }
-    
+
+    public static String GOGO_COMMAND_HELPERS = "gogocommand_helpers";
+
+    private ServiceTracker trackOSGiCommands(final BundleContext context) throws InvalidSyntaxException {
+        Filter filter = context.createFilter(String.format("(&(%s=*)(%s=*))", CommandProcessor.COMMAND_SCOPE, CommandProcessor.COMMAND_FUNCTION));
+
+        return new ServiceTracker(context, filter, null) {
+
+            @Override
+            public Object addingService(ServiceReference reference) {
+                Object scope = reference.getProperty(CommandProcessor.COMMAND_SCOPE);
+                Object function = reference.getProperty(CommandProcessor.COMMAND_FUNCTION);
+                List<Object> commands = new ArrayList<Object>();
+
+                Object commandObject = context.getService(reference);
+
+                if (scope != null && function != null) {
+                    if (function.getClass().isArray()) {
+                        for (Object f : ((Object[]) function)) {
+                            GogoCommandHelper gogoCommandHelper = new GogoCommandHelper(commandObject, (String) scope, f.toString());
+                            helpers.put(scope + ":" + f.toString(), gogoCommandHelper);
+                        }
+                    } else {
+                        GogoCommandHelper gogoCommandHelper = new GogoCommandHelper(commandObject, (String) scope, function.toString());
+                        helpers.put(scope + ":" + function.toString(), gogoCommandHelper);
+                    }
+                    return commands;
+                }
+                return null;
+            }
+
+            @Override
+            public void removedService(ServiceReference reference, Object service) {
+                super.removedService(reference, service);
+            }
+        };
+    }
+
+    class GogoCommandHelper {
+
+        private Object commandObject;
+        private String scope;
+        private String function;
+        private String description = "";
+
+        public GogoCommandHelper(Object commandObject, String scope, String function) {
+            this.commandObject = commandObject;
+            this.scope = scope;
+            this.function = function;
+
+            for (Method m : commandObject.getClass().getMethods()) {
+                if (m.getName().equals(function)) {
+                    Descriptor descriptor = m.getAnnotation(Descriptor.class);
+                    if (descriptor != null) {
+                        description = descriptor.value();
+                    }
+                }
+            }
+        }
+
+        public String getDescription() {
+            return description;
+        }
+
+        public void printUsage(CommandSession session, PrintStream out) {
+            Terminal term = session != null ? (Terminal) session.get(".jline.terminal") : null;
+            int termWidth = term != null ? term.getWidth() : 80;
+            boolean globalScope = NameScoping.isGlobalScope(session, scope);
+
+            Hashtable<String, String> arguments = new Hashtable<String, String>();
+            for (Method m : commandObject.getClass().getMethods()) {
+                if (m.getName().equals(function)) {
+                    Annotation[][] annotations = m.getParameterAnnotations();
+                    int i = 0;
+                    for (Class<?> paramClass : m.getParameterTypes()) {
+                        String argumentDescription = "";
+                        for (Annotation annotation : annotations[i++]) {
+                            if (annotation.annotationType().equals(Descriptor.class)) {
+                                argumentDescription = ((Descriptor) annotation).value();
+                                break;
+                            }
+                        }
+                        arguments.put(paramClass.getSimpleName(), argumentDescription);
+                    }
+                }
+            }
+            out.println(Ansi.ansi().a(Ansi.Attribute.INTENSITY_BOLD).a("DESCRIPTION").a(Ansi.Attribute.RESET));
+            out.print("        ");
+            if (globalScope) {
+                out.println(Ansi.ansi().a(Ansi.Attribute.INTENSITY_BOLD).a(function).a(Ansi.Attribute.RESET));
+            } else {
+                out.println(Ansi.ansi().a(scope).a(":").a(Ansi.Attribute.INTENSITY_BOLD).a(function).a(Ansi.Attribute.RESET));
+            }
+            out.println();
+            out.print("\t");
+            out.println(getDescription());
+            out.println();
+
+            if (arguments.size() > 0) {
+                out.println(Ansi.ansi().a(Ansi.Attribute.INTENSITY_BOLD).a("ARGUMENTS").a(Ansi.Attribute.RESET));
+                for (String argumentName : arguments.keySet()) {
+                    out.print("        ");
+                    out.println(Ansi.ansi().a(Ansi.Attribute.INTENSITY_BOLD).a(argumentName).a(Ansi.Attribute.RESET));
+                    IndentFormatter.printFormatted("                ", arguments.get(argumentName), termWidth, out);
+                }
+                out.println();
+            }
+        }
+
+    }
+
 }

Modified: karaf/trunk/shell/help/src/main/java/org/apache/karaf/shell/help/impl/SingleCommandHelpProvider.java
URL: http://svn.apache.org/viewvc/karaf/trunk/shell/help/src/main/java/org/apache/karaf/shell/help/impl/SingleCommandHelpProvider.java?rev=1467272&r1=1467271&r2=1467272&view=diff
==============================================================================
--- karaf/trunk/shell/help/src/main/java/org/apache/karaf/shell/help/impl/SingleCommandHelpProvider.java (original)
+++ karaf/trunk/shell/help/src/main/java/org/apache/karaf/shell/help/impl/SingleCommandHelpProvider.java Fri Apr 12 13:58:21 2013
@@ -20,12 +20,19 @@ package org.apache.karaf.shell.help.impl
 import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
 import java.io.PrintStream;
+import java.lang.reflect.Field;
+import java.util.Hashtable;
 import java.util.Set;
 
+import org.apache.felix.gogo.runtime.CommandProxy;
 import org.apache.felix.gogo.runtime.CommandSessionImpl;
 import org.apache.felix.service.command.CommandSession;
+import org.apache.felix.service.command.Function;
 import org.apache.felix.service.threadio.ThreadIO;
+import org.apache.karaf.shell.console.BundleContextAware;
 import org.apache.karaf.shell.console.HelpProvider;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceReference;
 
 public class SingleCommandHelpProvider implements HelpProvider {
 
@@ -49,7 +56,19 @@ public class SingleCommandHelpProvider i
             ByteArrayOutputStream baos = new ByteArrayOutputStream();
             io.setStreams(new ByteArrayInputStream(new byte[0]), new PrintStream(baos, true), new PrintStream(baos, true));
             try {
-                session.execute(path + " --help");
+                Function function = (Function) session.get(path);
+                function = unproxy(function);
+                if (function != null && function instanceof CommandProxy) {
+                    Hashtable<String, HelpSystem.GogoCommandHelper> helpers = (Hashtable<String, HelpSystem.GogoCommandHelper>) session.get(HelpSystem.GOGO_COMMAND_HELPERS);
+                    if (helpers != null) {
+                        HelpSystem.GogoCommandHelper gogoCommandHelper = helpers.get(path);
+                        if (gogoCommandHelper != null) {
+                            gogoCommandHelper.printUsage(session, session.getConsole());
+                        }
+                    }
+                } else {
+                    session.execute(path + " --help");
+                }
             } catch (Throwable t) {
                 t.printStackTrace();
             } finally {
@@ -59,4 +78,29 @@ public class SingleCommandHelpProvider i
         }
         return null;
     }
+
+    protected Function unproxy(Function function) {
+        try {
+            if (function.getClass().getName().contains("CommandProxy")) {
+                Field contextField = function.getClass().getDeclaredField("context");
+                Field referenceField = function.getClass().getDeclaredField("reference");
+                contextField.setAccessible(true);
+                referenceField.setAccessible(true);
+                BundleContext context = (BundleContext) contextField.get(function);
+                ServiceReference reference = (ServiceReference) referenceField.get(function);
+                Object target = context.getService(reference);
+                try {
+                    if (target instanceof Function) {
+                        function = (Function) target;
+                    }
+                } finally {
+                    context.ungetService(reference);
+                }
+            }
+        } catch (Throwable t) {
+            // nothing to do
+        }
+        return function;
+    }
+
 }