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/12/10 16:08:57 UTC

svn commit: r1549857 - in /karaf/trunk/shell/console: ./ src/main/java/org/apache/felix/gogo/commands/ src/main/java/org/apache/felix/gogo/commands/basic/ src/main/java/org/apache/felix/gogo/commands/converter/

Author: jbonofre
Date: Tue Dec 10 15:08:56 2013
New Revision: 1549857

URL: http://svn.apache.org/r1549857
Log:
[KARAF-2307] Fix the ActiveMQ command (I gonna add it in the completer)
[KARAF-2606] Re-add felix.gogo.commands package/impl for commands that directly using felix.gogo.AbstractCommandSupport

Added:
    karaf/trunk/shell/console/src/main/java/org/apache/felix/gogo/commands/CommandException.java
    karaf/trunk/shell/console/src/main/java/org/apache/felix/gogo/commands/converter/
    karaf/trunk/shell/console/src/main/java/org/apache/felix/gogo/commands/converter/DefaultConverter.java
    karaf/trunk/shell/console/src/main/java/org/apache/felix/gogo/commands/converter/GenericType.java
    karaf/trunk/shell/console/src/main/java/org/apache/felix/gogo/commands/converter/ReifiedType.java
Modified:
    karaf/trunk/shell/console/pom.xml
    karaf/trunk/shell/console/src/main/java/org/apache/felix/gogo/commands/Action.java
    karaf/trunk/shell/console/src/main/java/org/apache/felix/gogo/commands/Option.java
    karaf/trunk/shell/console/src/main/java/org/apache/felix/gogo/commands/SubShell.java
    karaf/trunk/shell/console/src/main/java/org/apache/felix/gogo/commands/basic/AbstractCommand.java
    karaf/trunk/shell/console/src/main/java/org/apache/felix/gogo/commands/basic/ActionPreparator.java
    karaf/trunk/shell/console/src/main/java/org/apache/felix/gogo/commands/basic/DefaultActionPreparator.java

Modified: karaf/trunk/shell/console/pom.xml
URL: http://svn.apache.org/viewvc/karaf/trunk/shell/console/pom.xml?rev=1549857&r1=1549856&r2=1549857&view=diff
==============================================================================
--- karaf/trunk/shell/console/pom.xml (original)
+++ karaf/trunk/shell/console/pom.xml Tue Dec 10 15:08:56 2013
@@ -135,7 +135,6 @@
                             *
                         </Import-Package>
                         <Export-Package>
-                        	org.apache.felix.gogo.commands;version=${felix.gogo.version},
                         	org.apache.karaf.shell.commands*,
                         	org.apache.karaf.shell.console,
                         	org.apache.karaf.shell.console.commands,

Modified: karaf/trunk/shell/console/src/main/java/org/apache/felix/gogo/commands/Action.java
URL: http://svn.apache.org/viewvc/karaf/trunk/shell/console/src/main/java/org/apache/felix/gogo/commands/Action.java?rev=1549857&r1=1549856&r2=1549857&view=diff
==============================================================================
--- karaf/trunk/shell/console/src/main/java/org/apache/felix/gogo/commands/Action.java (original)
+++ karaf/trunk/shell/console/src/main/java/org/apache/felix/gogo/commands/Action.java Tue Dec 10 15:08:56 2013
@@ -18,18 +18,12 @@
  */
 package org.apache.felix.gogo.commands;
 
+import org.apache.felix.service.command.CommandSession;
+
+public interface Action
+{
+
+    Object execute(CommandSession session) throws Exception;
 
-/**
- * An action allows to easily execute commands in karaf.
- * It can be assumed that each action is only accessed by a single thread at a time.
- * 
- * An Action is always part of an AbstractCommand. The AbstractCommand makes sure
- * the single threaded assumption above is true. Before the call to the execute method
- * the action is checked for annotated fields (@Argument, @Option). These fields
- * are populated from the command arguments before the action is called.
- * 
- * @deprecated use org.apache.karaf.shell.commands.Action instead
- */
-@Deprecated
-public interface Action extends org.apache.karaf.shell.commands.Action {
 }
+

Added: karaf/trunk/shell/console/src/main/java/org/apache/felix/gogo/commands/CommandException.java
URL: http://svn.apache.org/viewvc/karaf/trunk/shell/console/src/main/java/org/apache/felix/gogo/commands/CommandException.java?rev=1549857&view=auto
==============================================================================
--- karaf/trunk/shell/console/src/main/java/org/apache/felix/gogo/commands/CommandException.java (added)
+++ karaf/trunk/shell/console/src/main/java/org/apache/felix/gogo/commands/CommandException.java Tue Dec 10 15:08:56 2013
@@ -0,0 +1,64 @@
+/*
+ * 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.felix.gogo.commands;
+
+import org.fusesource.jansi.Ansi;
+
+/**
+ * Base class for exceptions thrown when executing commands.
+ */
+public class CommandException extends Exception {
+
+    private String help;
+
+    public CommandException() {
+    }
+
+    public CommandException(String message) {
+        super(message);
+    }
+
+    public CommandException(String message, Throwable cause) {
+        super(message, cause);
+    }
+
+    public CommandException(Throwable cause) {
+        super(cause);
+    }
+
+    public CommandException(String help, String message) {
+        super(message);
+        this.help = help;
+    }
+
+    public CommandException(String help, String message, Throwable cause) {
+        super(message, cause);
+        this.help = help;
+    }
+
+    public String getNiceHelp() {
+        return  help != null ? help
+                : Ansi.ansi()
+                .fg(Ansi.Color.RED)
+                .a("Error executing command: ")
+                .a(getMessage() != null ? getMessage() : getClass().getName())
+                .fg(Ansi.Color.DEFAULT).toString();
+    }
+
+}

Modified: karaf/trunk/shell/console/src/main/java/org/apache/felix/gogo/commands/Option.java
URL: http://svn.apache.org/viewvc/karaf/trunk/shell/console/src/main/java/org/apache/felix/gogo/commands/Option.java?rev=1549857&r1=1549856&r2=1549857&view=diff
==============================================================================
--- karaf/trunk/shell/console/src/main/java/org/apache/felix/gogo/commands/Option.java (original)
+++ karaf/trunk/shell/console/src/main/java/org/apache/felix/gogo/commands/Option.java Tue Dec 10 15:08:56 2013
@@ -18,10 +18,10 @@
  */
 package org.apache.felix.gogo.commands;
 
-import java.lang.annotation.ElementType;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.lang.annotation.Target;
+import java.lang.annotation.ElementType;
 
 /**
  * Used to mark an optional named command line option who's name typically starts with "--"

Modified: karaf/trunk/shell/console/src/main/java/org/apache/felix/gogo/commands/SubShell.java
URL: http://svn.apache.org/viewvc/karaf/trunk/shell/console/src/main/java/org/apache/felix/gogo/commands/SubShell.java?rev=1549857&r1=1549856&r2=1549857&view=diff
==============================================================================
--- karaf/trunk/shell/console/src/main/java/org/apache/felix/gogo/commands/SubShell.java (original)
+++ karaf/trunk/shell/console/src/main/java/org/apache/felix/gogo/commands/SubShell.java Tue Dec 10 15:08:56 2013
@@ -25,6 +25,7 @@ import java.lang.annotation.Target;
 
 @Retention(RetentionPolicy.RUNTIME)
 @Target({ElementType.TYPE})
+@Deprecated
 public @interface SubShell {
 
     /**

Modified: karaf/trunk/shell/console/src/main/java/org/apache/felix/gogo/commands/basic/AbstractCommand.java
URL: http://svn.apache.org/viewvc/karaf/trunk/shell/console/src/main/java/org/apache/felix/gogo/commands/basic/AbstractCommand.java?rev=1549857&r1=1549856&r2=1549857&view=diff
==============================================================================
--- karaf/trunk/shell/console/src/main/java/org/apache/felix/gogo/commands/basic/AbstractCommand.java (original)
+++ karaf/trunk/shell/console/src/main/java/org/apache/felix/gogo/commands/basic/AbstractCommand.java Tue Dec 10 15:08:56 2013
@@ -18,7 +18,46 @@
  */
 package org.apache.felix.gogo.commands.basic;
 
+import java.util.List;
+
+import org.apache.felix.gogo.commands.Action;
+import org.apache.felix.service.command.CommandSession;
+import org.apache.karaf.shell.commands.CompatibleCommandWithAction;
+
 @Deprecated
-public abstract class AbstractCommand extends org.apache.karaf.shell.commands.basic.AbstractCommand {
+public abstract class AbstractCommand implements CompatibleCommandWithAction {
+
+    public Object execute(CommandSession session, List<Object> arguments) throws Exception {
+        Action action = createNewAction();
+        try {
+            if (getPreparator().prepare(action, session, arguments)) {
+                return action.execute(session);
+            } else {
+                return null;
+            }
+        } finally {
+            releaseAction(action);
+        }
+    }
+
+    public Class<? extends Action> getActionClass() {
+        return createNewAction().getClass();
+    }
+
+    public abstract Action createNewAction();
+
+    /**
+     * Release the used Action.
+     * This method has to be overridden for pool based Actions.
+     * @param action Action that was executed
+     * @throws Exception if something went wrong during the Action release
+     */
+    public void releaseAction(Action action) throws Exception {
+        // Do nothing by default (stateful)
+    }
+
+    protected ActionPreparator getPreparator() throws Exception {
+        return new DefaultActionPreparator();
+    }
 
 }

Modified: karaf/trunk/shell/console/src/main/java/org/apache/felix/gogo/commands/basic/ActionPreparator.java
URL: http://svn.apache.org/viewvc/karaf/trunk/shell/console/src/main/java/org/apache/felix/gogo/commands/basic/ActionPreparator.java?rev=1549857&r1=1549856&r2=1549857&view=diff
==============================================================================
--- karaf/trunk/shell/console/src/main/java/org/apache/felix/gogo/commands/basic/ActionPreparator.java (original)
+++ karaf/trunk/shell/console/src/main/java/org/apache/felix/gogo/commands/basic/ActionPreparator.java Tue Dec 10 15:08:56 2013
@@ -18,7 +18,14 @@
  */
 package org.apache.felix.gogo.commands.basic;
 
+import java.util.List;
+
+import org.apache.felix.gogo.commands.Action;
+import org.apache.felix.service.command.CommandSession;
+
 @Deprecated
-public interface ActionPreparator extends org.apache.karaf.shell.commands.basic.ActionPreparator {
+public interface ActionPreparator {
+
+    boolean prepare(Action action, CommandSession session, List<Object> arguments) throws Exception;
 
 }

Modified: karaf/trunk/shell/console/src/main/java/org/apache/felix/gogo/commands/basic/DefaultActionPreparator.java
URL: http://svn.apache.org/viewvc/karaf/trunk/shell/console/src/main/java/org/apache/felix/gogo/commands/basic/DefaultActionPreparator.java?rev=1549857&r1=1549856&r2=1549857&view=diff
==============================================================================
--- karaf/trunk/shell/console/src/main/java/org/apache/felix/gogo/commands/basic/DefaultActionPreparator.java (original)
+++ karaf/trunk/shell/console/src/main/java/org/apache/felix/gogo/commands/basic/DefaultActionPreparator.java Tue Dec 10 15:08:56 2013
@@ -18,7 +18,598 @@
  */
 package org.apache.felix.gogo.commands.basic;
 
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.io.StringWriter;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Field;
+import java.lang.reflect.Type;
+import java.util.*;
+import java.io.PrintStream;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import jline.Terminal;
+import org.apache.felix.gogo.commands.CommandException;
+import org.apache.felix.gogo.commands.Option;
+import org.apache.felix.gogo.commands.Action;
+import org.apache.felix.gogo.commands.Argument;
+import org.apache.felix.gogo.commands.Command;
+import org.apache.felix.gogo.commands.converter.DefaultConverter;
+import org.apache.felix.gogo.commands.converter.GenericType;
+import org.apache.felix.service.command.CommandSession;
+import org.apache.karaf.shell.console.NameScoping;
+import org.fusesource.jansi.Ansi;
+
 @Deprecated
-public abstract class DefaultActionPreparator extends org.apache.karaf.shell.commands.basic.DefaultActionPreparator {
+public class DefaultActionPreparator implements ActionPreparator {
+
+    public static final Option HELP = new Option() {
+        public String name() {
+            return "--help";
+        }
+
+        public String[] aliases() {
+            return new String[]{};
+        }
+
+        public String description() {
+            return "Display this help message";
+        }
+
+        public boolean required() {
+            return false;
+        }
+
+        public boolean multiValued() {
+            return false;
+        }
+
+        public String valueToShowInHelp() {
+            return Option.DEFAULT_STRING;
+        }
+
+        public Class<? extends Annotation> annotationType() {
+            return Option.class;
+        }
+    };
+
+    public boolean prepare(Action action, CommandSession session, List<Object> params) throws Exception {
+        Map<Option, Field> options = new HashMap<Option, Field>();
+        Map<Argument, Field> arguments = new HashMap<Argument, Field>();
+        List<Argument> orderedArguments = new ArrayList<Argument>();
+        // Introspect
+        for (Class type = action.getClass(); type != null; type = type.getSuperclass()) {
+            for (Field field : type.getDeclaredFields()) {
+                Option option = field.getAnnotation(Option.class);
+                if (option != null) {
+                    options.put(option, field);
+                }
+                Argument argument = field.getAnnotation(Argument.class);
+                if (argument != null) {
+                    if (Argument.DEFAULT.equals(argument.name())) {
+                        final Argument delegate = argument;
+                        final String name = field.getName();
+                        argument = new Argument() {
+                            public String name() {
+                                return name;
+                            }
+
+                            public String description() {
+                                return delegate.description();
+                            }
+
+                            public boolean required() {
+                                return delegate.required();
+                            }
+
+                            public int index() {
+                                return delegate.index();
+                            }
+
+                            public boolean multiValued() {
+                                return delegate.multiValued();
+                            }
+
+                            public String valueToShowInHelp() {
+                                return delegate.valueToShowInHelp();
+                            }
+
+                            public Class<? extends Annotation> annotationType() {
+                                return delegate.annotationType();
+                            }
+                        };
+                    }
+                    arguments.put(argument, field);
+                    int index = argument.index();
+                    while (orderedArguments.size() <= index) {
+                        orderedArguments.add(null);
+                    }
+                    if (orderedArguments.get(index) != null) {
+                        throw new IllegalArgumentException("Duplicate argument index: " + index);
+                    }
+                    orderedArguments.set(index, argument);
+                }
+            }
+        }
+        // Check indexes are correct
+        for (int i = 0; i < orderedArguments.size(); i++) {
+            if (orderedArguments.get(i) == null) {
+                throw new IllegalArgumentException("Missing argument for index: " + i);
+            }
+        }
+        // Populate
+        Map<Option, Object> optionValues = new HashMap<Option, Object>();
+        Map<Argument, Object> argumentValues = new HashMap<Argument, Object>();
+        boolean processOptions = true;
+        int argIndex = 0;
+        for (Iterator<Object> it = params.iterator(); it.hasNext(); ) {
+            Object param = it.next();
+            // Check for help
+            if (HELP.name().equals(param) || Arrays.asList(HELP.aliases()).contains(param)) {
+                printUsage(session, action, options, arguments, System.out);
+                return false;
+            }
+            if (processOptions && param instanceof String && ((String) param).startsWith("-")) {
+                boolean isKeyValuePair = ((String) param).indexOf('=') != -1;
+                String name;
+                Object value = null;
+                if (isKeyValuePair) {
+                    name = ((String) param).substring(0, ((String) param).indexOf('='));
+                    value = ((String) param).substring(((String) param).indexOf('=') + 1);
+                } else {
+                    name = (String) param;
+                }
+                Option option = null;
+                for (Option opt : options.keySet()) {
+                    if (name.equals(opt.name()) || Arrays.asList(opt.aliases()).contains(name)) {
+                        option = opt;
+                        break;
+                    }
+                }
+                if (option == null) {
+                    Command command = action.getClass().getAnnotation(Command.class);
+                    if (command != null) {
+                        throw new CommandException(
+                                Ansi.ansi()
+                                        .fg(Ansi.Color.RED)
+                                        .a("Error executing command ")
+                                        .a(command.scope())
+                                        .a(":")
+                                        .a(Ansi.Attribute.INTENSITY_BOLD)
+                                        .a(command.name())
+                                        .a(Ansi.Attribute.INTENSITY_BOLD_OFF)
+                                        .a(" undefined option ")
+                                        .a(Ansi.Attribute.INTENSITY_BOLD)
+                                        .a(param)
+                                        .a(Ansi.Attribute.INTENSITY_BOLD_OFF)
+                                        .fg(Ansi.Color.DEFAULT)
+                                        .toString(),
+                                "Undefined option: " + param
+                        );
+                    } else {
+                        throw new CommandException("Undefined option: " + param);
+                    }
+                }
+                Field field = options.get(option);
+                if (value == null && (field.getType() == boolean.class || field.getType() == Boolean.class)) {
+                    value = Boolean.TRUE;
+                }
+                if (value == null && it.hasNext()) {
+                    value = it.next();
+                }
+                if (value == null) {
+                    Command command = action.getClass().getAnnotation(Command.class);
+                    if (command != null) {
+                        throw new CommandException(
+                                Ansi.ansi()
+                                        .fg(Ansi.Color.RED)
+                                        .a("Error executing command ")
+                                        .a(command.scope())
+                                        .a(":")
+                                        .a(Ansi.Attribute.INTENSITY_BOLD)
+                                        .a(command.name())
+                                        .a(Ansi.Attribute.INTENSITY_BOLD_OFF)
+                                        .a(" missing value for option ")
+                                        .a(Ansi.Attribute.INTENSITY_BOLD)
+                                        .a(param)
+                                        .a(Ansi.Attribute.INTENSITY_BOLD_OFF)
+                                        .fg(Ansi.Color.DEFAULT)
+                                        .toString(),
+                                "Missing value for option: " + param
+                        );
+                    } else {
+                        throw new CommandException("Missing value for option: " + param);
+                    }
+                }
+                if (option.multiValued()) {
+                    List<Object> l = (List<Object>) optionValues.get(option);
+                    if (l == null) {
+                        l = new ArrayList<Object>();
+                        optionValues.put(option, l);
+                    }
+                    l.add(value);
+                } else {
+                    optionValues.put(option, value);
+                }
+            } else {
+                processOptions = false;
+                if (argIndex >= orderedArguments.size()) {
+                    Command command = action.getClass().getAnnotation(Command.class);
+                    if (command != null) {
+                        throw new CommandException(
+                                Ansi.ansi()
+                                        .fg(Ansi.Color.RED)
+                                        .a("Error executing command ")
+                                        .a(command.scope())
+                                        .a(":")
+                                        .a(Ansi.Attribute.INTENSITY_BOLD)
+                                        .a(command.name())
+                                        .a(Ansi.Attribute.INTENSITY_BOLD_OFF)
+                                        .a(": too many arguments specified")
+                                        .fg(Ansi.Color.DEFAULT)
+                                        .toString(),
+                                "Too many arguments specified"
+                        );
+                    } else {
+                        throw new CommandException("Too many arguments specified");
+                    }
+                }
+                Argument argument = orderedArguments.get(argIndex);
+                if (!argument.multiValued()) {
+                    argIndex++;
+                }
+                if (argument.multiValued()) {
+                    List<Object> l = (List<Object>) argumentValues.get(argument);
+                    if (l == null) {
+                        l = new ArrayList<Object>();
+                        argumentValues.put(argument, l);
+                    }
+                    l.add(param);
+                } else {
+                    argumentValues.put(argument, param);
+                }
+            }
+        }
+        // Check required arguments / options
+        for (Option option : options.keySet()) {
+            if (option.required() && optionValues.get(option) == null) {
+                Command command = action.getClass().getAnnotation(Command.class);
+                if (command != null) {
+                    throw new CommandException(
+                            Ansi.ansi()
+                                    .fg(Ansi.Color.RED)
+                                    .a("Error executing command ")
+                                    .a(command.scope())
+                                    .a(":")
+                                    .a(Ansi.Attribute.INTENSITY_BOLD)
+                                    .a(command.name())
+                                    .a(Ansi.Attribute.INTENSITY_BOLD_OFF)
+                                    .a(": option ")
+                                    .a(Ansi.Attribute.INTENSITY_BOLD)
+                                    .a(option.name())
+                                    .a(Ansi.Attribute.INTENSITY_BOLD_OFF)
+                                    .a(" is required")
+                                    .fg(Ansi.Color.DEFAULT)
+                                    .toString(),
+                            "Option " + option.name() + " is required"
+                    );
+                } else {
+                    throw new CommandException("Option " + option.name() + " is required");
+                }
+            }
+        }
+        for (Argument argument : orderedArguments) {
+            if (argument.required() && argumentValues.get(argument) == null) {
+                Command command = action.getClass().getAnnotation(Command.class);
+                if (command != null) {
+                    throw new CommandException(
+                            Ansi.ansi()
+                                    .fg(Ansi.Color.RED)
+                                    .a("Error executing command ")
+                                    .a(command.scope())
+                                    .a(":")
+                                    .a(Ansi.Attribute.INTENSITY_BOLD)
+                                    .a(command.name())
+                                    .a(Ansi.Attribute.INTENSITY_BOLD_OFF)
+                                    .a(": argument ")
+                                    .a(Ansi.Attribute.INTENSITY_BOLD)
+                                    .a(argument.name())
+                                    .a(Ansi.Attribute.INTENSITY_BOLD_OFF)
+                                    .a(" is required")
+                                    .fg(Ansi.Color.DEFAULT)
+                                    .toString(),
+                            "Argument " + argument.name() + " is required"
+                    );
+                } else {
+                    throw new CommandException("Argument " + argument.name() + " is required");
+                }
+            }
+        }
+        // Convert and inject values
+        for (Map.Entry<Option, Object> entry : optionValues.entrySet()) {
+            Field field = options.get(entry.getKey());
+            Object value;
+            try {
+                value = convert(action, session, entry.getValue(), field.getGenericType());
+            } catch (Exception e) {
+                Command command = action.getClass().getAnnotation(Command.class);
+                if (command != null) {
+                    throw new CommandException(
+                            Ansi.ansi()
+                                    .fg(Ansi.Color.RED)
+                                    .a("Error executing command ")
+                                    .a(command.scope())
+                                    .a(":")
+                                    .a(Ansi.Attribute.INTENSITY_BOLD)
+                                    .a(command.name())
+                                    .a(Ansi.Attribute.INTENSITY_BOLD_OFF)
+                                    .a(": unable to convert option ")
+                                    .a(Ansi.Attribute.INTENSITY_BOLD)
+                                    .a(entry.getKey().name())
+                                    .a(Ansi.Attribute.INTENSITY_BOLD_OFF)
+                                    .a(" with value '")
+                                    .a(entry.getValue())
+                                    .a("' to type ")
+                                    .a(new GenericType(field.getGenericType()).toString())
+                                    .fg(Ansi.Color.DEFAULT)
+                                    .toString(),
+                            "Unable to convert option " + entry.getKey().name() + " with value '"
+                                    + entry.getValue() + "' to type " + new GenericType(field.getGenericType()).toString(),
+                            e
+                    );
+                } else {
+                    throw new CommandException("Unable to convert option " + entry.getKey().name() + " with value '"
+                            + entry.getValue() + "' to type " + new GenericType(field.getGenericType()).toString(),
+                            e);
+                }
+            }
+            field.setAccessible(true);
+            field.set(action, value);
+        }
+        for (Map.Entry<Argument, Object> entry : argumentValues.entrySet()) {
+            Field field = arguments.get(entry.getKey());
+            Object value;
+            try {
+                value = convert(action, session, entry.getValue(), field.getGenericType());
+            } catch (Exception e) {
+                Command command = action.getClass().getAnnotation(Command.class);
+                if (command != null) {
+                    throw new CommandException(
+                            Ansi.ansi()
+                                    .fg(Ansi.Color.RED)
+                                    .a("Error executing command ")
+                                    .a(command.scope())
+                                    .a(":")
+                                    .a(Ansi.Attribute.INTENSITY_BOLD)
+                                    .a(command.name())
+                                    .a(Ansi.Attribute.INTENSITY_BOLD_OFF)
+                                    .a(": unable to convert argument ")
+                                    .a(Ansi.Attribute.INTENSITY_BOLD)
+                                    .a(entry.getKey().name())
+                                    .a(Ansi.Attribute.INTENSITY_BOLD_OFF)
+                                    .a(" with value '")
+                                    .a(entry.getValue())
+                                    .a("' to type ")
+                                    .a(new GenericType(field.getGenericType()).toString())
+                                    .fg(Ansi.Color.DEFAULT)
+                                    .toString(),
+                            "Unable to convert argument " + entry.getKey().name() + " with value '"
+                                    + entry.getValue() + "' to type " + new GenericType(field.getGenericType()).toString(),
+                            e
+                    );
+                } else {
+                    throw new CommandException("Unable to convert argument " + entry.getKey().name() + " with value '"
+                            + entry.getValue() + "' to type " + new GenericType(field.getGenericType()).toString(),
+                            e);
+                }
+            }
+            field.setAccessible(true);
+            field.set(action, value);
+        }
+        return true;
+    }
+
+    protected void printUsage(CommandSession session, Action action, Map<Option, Field> optionsMap, Map<Argument, Field> argsMap, PrintStream out) {
+        Command command = action.getClass().getAnnotation(Command.class);
+        if (command != null) {
+            Terminal term = session != null ? (Terminal) session.get(".jline.terminal") : null;
+            List<Argument> arguments = new ArrayList<Argument>(argsMap.keySet());
+            Collections.sort(arguments, new Comparator<Argument>() {
+                public int compare(Argument o1, Argument o2) {
+                    return Integer.valueOf(o1.index()).compareTo(Integer.valueOf(o2.index()));
+                }
+            });
+            Set<Option> options = new HashSet<Option>(optionsMap.keySet());
+            options.add(HELP);
+            boolean globalScope = NameScoping.isGlobalScope(session, command.scope());
+            if (command != null && (command.description() != null || command.name() != null)) {
+                out.println(Ansi.ansi().a(Ansi.Attribute.INTENSITY_BOLD).a("DESCRIPTION").a(Ansi.Attribute.RESET));
+                out.print("        ");
+                if (command.name() != null) {
+                    if (globalScope) {
+                        out.println(Ansi.ansi().a(Ansi.Attribute.INTENSITY_BOLD).a(command.name()).a(Ansi.Attribute.RESET));
+                    } else {
+                        out.println(Ansi.ansi().a(command.scope()).a(":").a(Ansi.Attribute.INTENSITY_BOLD).a(command.name()).a(Ansi.Attribute.RESET));
+                    }
+                    out.println();
+                }
+                out.print("\t");
+                out.println(command.description());
+                out.println();
+            }
+            StringBuffer syntax = new StringBuffer();
+            if (command != null) {
+                if (globalScope) {
+                    syntax.append(command.name());
+                } else {
+                    syntax.append(String.format("%s:%s", command.scope(), command.name()));
+                }
+            }
+            if (options.size() > 0) {
+                syntax.append(" [options]");
+            }
+            if (arguments.size() > 0) {
+                syntax.append(' ');
+                for (Argument argument : arguments) {
+                    if (!argument.required()) {
+                        syntax.append(String.format("[%s] ", argument.name()));
+                    } else {
+                        syntax.append(String.format("%s ", argument.name()));
+                    }
+                }
+            }
+
+            out.println(Ansi.ansi().a(Ansi.Attribute.INTENSITY_BOLD).a("SYNTAX").a(Ansi.Attribute.RESET));
+            out.print("        ");
+            out.println(syntax.toString());
+            out.println();
+            if (arguments.size() > 0) {
+                out.println(Ansi.ansi().a(Ansi.Attribute.INTENSITY_BOLD).a("ARGUMENTS").a(Ansi.Attribute.RESET));
+                for (Argument argument : arguments) {
+                    out.print("        ");
+                    out.println(Ansi.ansi().a(Ansi.Attribute.INTENSITY_BOLD).a(argument.name()).a(Ansi.Attribute.RESET));
+                    printFormatted("                ", argument.description(), term != null ? term.getWidth() : 80, out);
+                    if (!argument.required()) {
+                        if (argument.valueToShowInHelp() != null && argument.valueToShowInHelp().length() != 0) {
+                            try {
+                                if (Argument.DEFAULT_STRING.equals(argument.valueToShowInHelp())) {
+                                    argsMap.get(argument).setAccessible(true);
+                                    Object o = argsMap.get(argument).get(action);
+                                    printObjectDefaultsTo(out, o);
+                                } else {
+                                    printDefaultsTo(out, argument.valueToShowInHelp());
+                                }
+                            } catch (Throwable t) {
+                                // Ignore
+                            }
+                        }
+                    }
+                }
+                out.println();
+            }
+            if (options.size() > 0) {
+                out.println(Ansi.ansi().a(Ansi.Attribute.INTENSITY_BOLD).a("OPTIONS").a(Ansi.Attribute.RESET));
+                for (Option option : options) {
+                    String opt = option.name();
+                    for (String alias : option.aliases()) {
+                        opt += ", " + alias;
+                    }
+                    out.print("        ");
+                    out.println(Ansi.ansi().a(Ansi.Attribute.INTENSITY_BOLD).a(opt).a(Ansi.Attribute.RESET));
+                    printFormatted("                ", option.description(), term != null ? term.getWidth() : 80, out);
+                    if (option.valueToShowInHelp() != null && option.valueToShowInHelp().length() != 0) {
+                        try {
+                            if (Option.DEFAULT_STRING.equals(option.valueToShowInHelp())) {
+                                optionsMap.get(option).setAccessible(true);
+                                Object o = optionsMap.get(option).get(action);
+                                printObjectDefaultsTo(out, o);
+                            } else {
+                                printDefaultsTo(out, option.valueToShowInHelp());
+                            }
+                        } catch (Throwable t) {
+                            // Ignore
+                        }
+                    }
+                }
+                out.println();
+            }
+            if (command.detailedDescription().length() > 0) {
+                out.println(Ansi.ansi().a(Ansi.Attribute.INTENSITY_BOLD).a("DETAILS").a(Ansi.Attribute.RESET));
+                String desc = loadDescription(action.getClass(), command.detailedDescription());
+                printFormatted("        ", desc, term != null ? term.getWidth() : 80, out);
+            }
+        }
+    }
+
+    private void printObjectDefaultsTo(PrintStream out, Object o) {
+        if (o != null
+                && (!(o instanceof Boolean) || ((Boolean) o))
+                && (!(o instanceof Number) || ((Number) o).doubleValue() != 0.0)) {
+            printDefaultsTo(out, o.toString());
+        }
+    }
+
+    private void printDefaultsTo(PrintStream out, String value) {
+        out.print("                (defaults to ");
+        out.print(value);
+        out.println(")");
+    }
+
+    protected String loadDescription(Class clazz, String desc) {
+        if (desc.startsWith("classpath:")) {
+            InputStream is = clazz.getResourceAsStream(desc.substring("classpath:".length()));
+            if (is == null) {
+                is = clazz.getClassLoader().getResourceAsStream(desc.substring("classpath:".length()));
+            }
+            if (is == null) {
+                desc = "Unable to load description from " + desc;
+            } else {
+                try {
+                    Reader r = new InputStreamReader(is);
+                    StringWriter sw = new StringWriter();
+                    int c;
+                    while ((c = r.read()) != -1) {
+                        sw.append((char) c);
+                    }
+                    desc = sw.toString();
+                } catch (IOException e) {
+                    desc = "Unable to load description from " + desc;
+                } finally {
+                    try {
+                        is.close();
+                    } catch (IOException e) {
+                        // Ignore
+                    }
+                }
+            }
+        }
+        return desc;
+    }
+
+    // TODO move this to a helper class?
+    public static void printFormatted(String prefix, String str, int termWidth, PrintStream out) {
+        printFormatted(prefix, str, termWidth, out, true);
+    }
+
+    public static void printFormatted(String prefix, String str, int termWidth, PrintStream out, boolean prefixFirstLine) {
+        int pfxLen = length(prefix);
+        int maxwidth = termWidth - pfxLen;
+        Pattern wrap = Pattern.compile("(\\S\\S{" + maxwidth + ",}|.{1," + maxwidth + "})(\\s+|$)");
+        int cur = 0;
+        while (cur >= 0) {
+            int lst = str.indexOf('\n', cur);
+            String s = (lst >= 0) ? str.substring(cur, lst) : str.substring(cur);
+            if (s.length() == 0) {
+                out.println();
+            } else {
+                Matcher m = wrap.matcher(s);
+                while (m.find()) {
+                    if (cur > 0 || prefixFirstLine) {
+                        out.print(prefix);
+                    }
+                    out.println(m.group());
+                }
+            }
+            if (lst >= 0) {
+                cur = lst + 1;
+            } else {
+                break;
+            }
+        }
+    }
+
+    public static int length(String str) {
+        return str.length();
+    }
+
+    protected Object convert(Action action, CommandSession session, Object value, Type toType) throws Exception {
+        if (toType == String.class) {
+            return value != null ? value.toString() : null;
+        }
+        return new DefaultConverter(action.getClass().getClassLoader()).convert(value, toType);
+    }
 
 }

Added: karaf/trunk/shell/console/src/main/java/org/apache/felix/gogo/commands/converter/DefaultConverter.java
URL: http://svn.apache.org/viewvc/karaf/trunk/shell/console/src/main/java/org/apache/felix/gogo/commands/converter/DefaultConverter.java?rev=1549857&view=auto
==============================================================================
--- karaf/trunk/shell/console/src/main/java/org/apache/felix/gogo/commands/converter/DefaultConverter.java (added)
+++ karaf/trunk/shell/console/src/main/java/org/apache/felix/gogo/commands/converter/DefaultConverter.java Tue Dec 10 15:08:56 2013
@@ -0,0 +1,401 @@
+/*
+ * 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.felix.gogo.commands.converter;
+
+import java.util.Collection;
+import java.util.Map;
+import java.util.Dictionary;
+import java.util.Locale;
+import java.util.Properties;
+import java.util.Hashtable;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.SortedMap;
+import java.util.TreeMap;
+import java.util.LinkedHashMap;
+import java.util.SortedSet;
+import java.util.TreeSet;
+import java.util.Set;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.ArrayList;
+import java.util.Queue;
+import java.util.LinkedList;
+import java.util.regex.Pattern;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicLong;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.ConcurrentHashMap;
+import java.math.BigInteger;
+import java.math.BigDecimal;
+import java.io.ByteArrayInputStream;
+import java.lang.reflect.Modifier;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Array;
+import java.lang.reflect.Type;
+import java.lang.reflect.InvocationTargetException;
+
+public class DefaultConverter
+{
+
+    private Object loader;
+
+    public DefaultConverter(Object loader) {
+        this.loader = loader;
+    }
+
+    public Object convert(Object source, Type target) throws Exception {
+        return convert( source, new GenericType(target));
+    }
+
+    public Object convert(Object fromValue, ReifiedType type) throws Exception {
+        // Discard null values
+        if (fromValue == null) {
+            return null;
+        }
+        // If the object is an instance of the type, just return it
+        if (isAssignable(fromValue, type)) {
+            return fromValue;
+        }
+        Object value = convertWithConverters(fromValue, type);
+        if (value == null) {
+            if (fromValue instanceof Number && Number.class.isAssignableFrom(unwrap(toClass(type)))) {
+                return convertToNumber((Number) fromValue, toClass(type));
+            } else if (fromValue instanceof String) {
+                return convertFromString((String) fromValue, toClass(type), loader);
+            } else if (toClass(type).isArray() && (fromValue instanceof Collection || fromValue.getClass().isArray())) {
+                return convertToArray(fromValue, type);
+            } else if (Map.class.isAssignableFrom(toClass(type)) && (fromValue instanceof Map || fromValue instanceof Dictionary)) {
+                return convertToMap(fromValue, type);
+            } else if (Dictionary.class.isAssignableFrom(toClass(type)) && (fromValue instanceof Map || fromValue instanceof Dictionary)) {
+                return convertToDictionary(fromValue, type);
+            } else if (Collection.class.isAssignableFrom(toClass(type)) && (fromValue instanceof Collection || fromValue.getClass().isArray())) {
+                return convertToCollection(fromValue, type);
+            } else {
+                throw new Exception("Unable to convert value " + fromValue + " to type " + type);
+            }
+        }
+        return value;
+    }
+
+    private Object convertWithConverters(Object source, ReifiedType type) throws Exception {
+        Object value = null;
+//        for (Converter converter : converters) {
+//            if (converter.canConvert(source, type)) {
+//                value = converter.convert(source, type);
+//                if (value != null) {
+//                    return value;
+//                }
+//            }
+//        }
+        return value;
+    }
+
+    public Object convertToNumber(Number value, Class toType) throws Exception {
+        toType = unwrap(toType);
+        if (AtomicInteger.class == toType) {
+            return new AtomicInteger((Integer) convertToNumber(value, Integer.class));
+        } else if (AtomicLong.class == toType) {
+            return new AtomicLong((Long) convertToNumber(value, Long.class));
+        } else if (Integer.class == toType) {
+            return value.intValue();
+        } else if (Short.class == toType) {
+            return value.shortValue();
+        } else if (Long.class == toType) {
+            return value.longValue();
+        } else if (Float.class == toType) {
+            return value.floatValue();
+        } else if (Double.class == toType) {
+            return value.doubleValue();
+        } else if (Byte.class == toType) {
+            return value.byteValue();
+        } else if (BigInteger.class == toType) {
+            return new BigInteger(value.toString());
+        } else if (BigDecimal.class == toType) {
+            return new BigDecimal(value.toString());
+        } else {
+            throw new Exception("Unable to convert number " + value + " to " + toType);
+        }
+    }
+
+    public Object convertFromString(String value, Class toType, Object loader) throws Exception {
+        toType = unwrap(toType);
+        if (ReifiedType.class == toType) {
+            try {
+                return GenericType.parse(value, loader);
+            } catch (ClassNotFoundException e) {
+                throw new Exception("Unable to convert", e);
+            }
+        } else if (Class.class == toType) {
+            try {
+                return GenericType.parse(value, loader).getRawClass();
+            } catch (ClassNotFoundException e) {
+                throw new Exception("Unable to convert", e);
+            }
+        } else if (Locale.class == toType) {
+            String[] tokens = value.split("_");
+            if (tokens.length == 1) {
+                return new Locale(tokens[0]);
+            } else if (tokens.length == 2) {
+                return new Locale(tokens[0], tokens[1]);
+            } else if (tokens.length == 3) {
+                return new Locale(tokens[0], tokens[1], tokens[2]);
+            } else {
+                throw new Exception("Invalid locale string:" + value);
+            }
+        } else if (Pattern.class == toType) {
+            return Pattern.compile(value);
+        } else if (Properties.class == toType) {
+            Properties props = new Properties();
+            ByteArrayInputStream in = new ByteArrayInputStream(value.getBytes("UTF8"));
+            props.load(in);
+            return props;
+        } else if (Boolean.class == toType) {
+            if ("yes".equalsIgnoreCase(value) || "true".equalsIgnoreCase(value) || "on".equalsIgnoreCase(value)) {
+                return Boolean.TRUE;
+            } else if ("no".equalsIgnoreCase(value) || "false".equalsIgnoreCase(value) || "off".equalsIgnoreCase(value)) {
+                return Boolean.FALSE;
+            } else {
+                throw new RuntimeException("Invalid boolean value: " + value);
+            }
+        } else if (Integer.class == toType) {
+            return Integer.valueOf(value);
+        } else if (Short.class == toType) {
+            return Short.valueOf(value);
+        } else if (Long.class == toType) {
+            return Long.valueOf(value);
+        } else if (Float.class == toType) {
+            return Float.valueOf(value);
+        } else if (Double.class == toType) {
+            return Double.valueOf(value);
+        } else if (Character.class == toType) {
+            if (value.length() == 6 && value.startsWith("\\u")) {
+                int code = Integer.parseInt(value.substring(2), 16);
+                return (char)code;
+            } else if (value.length() == 1) {
+                return value.charAt(0);
+            } else {
+                throw new Exception("Invalid value for character type: " + value);
+            }
+        } else if (Byte.class == toType) {
+            return Byte.valueOf(value);
+        } else if (Enum.class.isAssignableFrom(toType)) {
+            return Enum.valueOf((Class<Enum>) toType, value);
+        } else {
+            return createObject(value, toType);
+        }
+    }
+
+    private static Object createObject(String value, Class type) throws Exception {
+        if (type.isInterface() || Modifier.isAbstract(type.getModifiers())) {
+            throw new Exception("Unable to convert value " + value + " to type " + type + ". Type " + type + " is an interface or an abstract class");
+        }
+        Constructor constructor = null;
+        try {
+            constructor = type.getConstructor(String.class);
+        } catch (NoSuchMethodException e) {
+            throw new RuntimeException("Unable to convert to " + type);
+        }
+        try {
+            return constructor.newInstance(value);
+        } catch (Exception e) {
+            throw new Exception("Unable to convert ", getRealCause(e));
+        }
+    }
+
+    private static Throwable getRealCause(Throwable t) {
+        if (t instanceof InvocationTargetException && t.getCause() != null) {
+            return t.getCause();
+        }
+        return t;
+    }
+
+    private Object convertToCollection(Object obj, ReifiedType type) throws Exception {
+        ReifiedType valueType = type.getActualTypeArgument(0);
+        Collection newCol = (Collection) getCollection(toClass(type)).newInstance();
+        if (obj.getClass().isArray()) {
+            for (int i = 0; i < Array.getLength(obj); i++) {
+                try {
+                    newCol.add(convert(Array.get(obj, i), valueType));
+                } catch (Exception t) {
+                    throw new Exception("Unable to convert from " + obj + " to " + type + "(error converting array element)", t);
+                }
+            }
+        } else {
+            for (Object item : (Collection) obj) {
+                try {
+                    newCol.add(convert(item, valueType));
+                } catch (Exception t) {
+                    throw new Exception("Unable to convert from " + obj + " to " + type + "(error converting collection entry)", t);
+                }
+            }
+        }
+        return newCol;
+    }
+
+    private Object convertToDictionary(Object obj, ReifiedType type) throws Exception {
+        ReifiedType keyType = type.getActualTypeArgument(0);
+        ReifiedType valueType = type.getActualTypeArgument(1);
+        Dictionary newDic = new Hashtable();
+        if (obj instanceof Dictionary) {
+            Dictionary dic = (Dictionary) obj;
+            for (Enumeration keyEnum = dic.keys(); keyEnum.hasMoreElements();) {
+                Object key = keyEnum.nextElement();
+                try {
+                    newDic.put(convert(key, keyType), convert(dic.get(key), valueType));
+                } catch (Exception t) {
+                    throw new Exception("Unable to convert from " + obj + " to " + type + "(error converting map entry)", t);
+                }
+            }
+        } else {
+            for (Map.Entry e : ((Map<Object,Object>) obj).entrySet()) {
+                try {
+                    newDic.put(convert(e.getKey(), keyType), convert(e.getValue(), valueType));
+                } catch (Exception t) {
+                    throw new Exception("Unable to convert from " + obj + " to " + type + "(error converting map entry)", t);
+                }
+            }
+        }
+        return newDic;
+    }
+
+    private Object convertToMap(Object obj, ReifiedType type) throws Exception {
+        ReifiedType keyType = type.getActualTypeArgument(0);
+        ReifiedType valueType = type.getActualTypeArgument(1);
+        Map newMap = (Map) getMap(toClass(type)).newInstance();
+        if (obj instanceof Dictionary) {
+            Dictionary dic = (Dictionary) obj;
+            for (Enumeration keyEnum = dic.keys(); keyEnum.hasMoreElements();) {
+                Object key = keyEnum.nextElement();
+                try {
+                    newMap.put(convert(key, keyType), convert(dic.get(key), valueType));
+                } catch (Exception t) {
+                    throw new Exception("Unable to convert from " + obj + " to " + type + "(error converting map entry)", t);
+                }
+            }
+        } else {
+            for (Map.Entry e : ((Map<Object,Object>) obj).entrySet()) {
+                try {
+                    newMap.put(convert(e.getKey(), keyType), convert(e.getValue(), valueType));
+                } catch (Exception t) {
+                    throw new Exception("Unable to convert from " + obj + " to " + type + "(error converting map entry)", t);
+                }
+            }
+        }
+        return newMap;
+    }
+
+    private Object convertToArray(Object obj, ReifiedType type) throws Exception {
+        if (obj instanceof Collection) {
+            obj = ((Collection) obj).toArray();
+        }
+        if (!obj.getClass().isArray()) {
+            throw new Exception("Unable to convert from " + obj + " to " + type);
+        }
+        ReifiedType componentType;
+        if (type.size() > 0) {
+            componentType = type.getActualTypeArgument(0);
+        } else {
+            componentType = new GenericType(type.getRawClass().getComponentType());
+        }
+        Object array = Array.newInstance(toClass(componentType), Array.getLength(obj));
+        for (int i = 0; i < Array.getLength(obj); i++) {
+            try {
+                Array.set(array, i, convert(Array.get(obj, i), componentType));
+            } catch (Exception t) {
+                throw new Exception("Unable to convert from " + obj + " to " + type + "(error converting array element)", t);
+            }
+        }
+        return array;
+    }
+
+    public static boolean isAssignable(Object source, ReifiedType target) {
+        return source == null
+                || (target.size() == 0
+                && unwrap(target.getRawClass()).isAssignableFrom(unwrap(source.getClass())));
+    }
+
+    private static Class unwrap(Class c) {
+        Class u = primitives.get(c);
+        return u != null ? u : c;
+    }
+
+    private static Class getMap(Class type) {
+        if (hasDefaultConstructor(type)) {
+            return type;
+        } else if (SortedMap.class.isAssignableFrom(type)) {
+            return TreeMap.class;
+        } else if (ConcurrentMap.class.isAssignableFrom(type)) {
+            return ConcurrentHashMap.class;
+        } else {
+            return LinkedHashMap.class;
+        }
+    }
+
+    private static Class getCollection(Class type) {
+        if (hasDefaultConstructor(type)) {
+            return type;
+        } else if (SortedSet.class.isAssignableFrom(type)) {
+            return TreeSet.class;
+        } else if (Set.class.isAssignableFrom(type)) {
+            return LinkedHashSet.class;
+        } else if (List.class.isAssignableFrom(type)) {
+            return ArrayList.class;
+        } else if (Queue.class.isAssignableFrom(type)) {
+            return LinkedList.class;
+        } else {
+            return ArrayList.class;
+        }
+    }
+
+    private static boolean hasDefaultConstructor(Class type) {
+        if (!Modifier.isPublic(type.getModifiers())) {
+            return false;
+        }
+        if (Modifier.isAbstract(type.getModifiers())) {
+            return false;
+        }
+        Constructor[] constructors = type.getConstructors();
+        for (Constructor constructor : constructors) {
+            if (Modifier.isPublic(constructor.getModifiers()) &&
+                    constructor.getParameterTypes().length == 0) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    private static final Map<Class, Class> primitives;
+    static {
+        primitives = new HashMap<Class, Class>();
+        primitives.put(byte.class, Byte.class);
+        primitives.put(short.class, Short.class);
+        primitives.put(char.class, Character.class);
+        primitives.put(int.class, Integer.class);
+        primitives.put(long.class, Long.class);
+        primitives.put(float.class, Float.class);
+        primitives.put(double.class, Double.class);
+        primitives.put(boolean.class, Boolean.class);
+    }
+
+    private Class toClass(ReifiedType type) {
+        return type.getRawClass();
+    }
+
+}

Added: karaf/trunk/shell/console/src/main/java/org/apache/felix/gogo/commands/converter/GenericType.java
URL: http://svn.apache.org/viewvc/karaf/trunk/shell/console/src/main/java/org/apache/felix/gogo/commands/converter/GenericType.java?rev=1549857&view=auto
==============================================================================
--- karaf/trunk/shell/console/src/main/java/org/apache/felix/gogo/commands/converter/GenericType.java (added)
+++ karaf/trunk/shell/console/src/main/java/org/apache/felix/gogo/commands/converter/GenericType.java Tue Dec 10 15:08:56 2013
@@ -0,0 +1,195 @@
+/*
+ * 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.felix.gogo.commands.converter;
+
+import java.lang.reflect.Array;
+import java.lang.reflect.GenericArrayType;
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+import java.lang.reflect.TypeVariable;
+import java.lang.reflect.WildcardType;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.osgi.framework.Bundle;
+
+public class GenericType extends ReifiedType {
+
+    private static final GenericType[] EMPTY = new GenericType[0];
+
+    private static final Map<String, Class> primitiveClasses = new HashMap<String, Class>();
+
+    static {
+        primitiveClasses.put("int", int.class);
+        primitiveClasses.put("short", short.class);
+        primitiveClasses.put("long", long.class);
+        primitiveClasses.put("byte", byte.class);
+        primitiveClasses.put("char", char.class);
+        primitiveClasses.put("float", float.class);
+        primitiveClasses.put("double", double.class);
+        primitiveClasses.put("boolean", boolean.class);
+    }
+
+    private GenericType[] parameters;
+
+    public GenericType(Type type) {
+        this(getConcreteClass(type), parametersOf(type));
+    }
+
+    public GenericType(Class clazz, GenericType... parameters) {
+        super(clazz);
+        this.parameters = parameters;
+    }
+
+    public static GenericType parse(String type, Object loader) throws ClassNotFoundException, IllegalArgumentException {
+        type = type.trim();
+        // Check if this is an array
+        if (type.endsWith("[]")) {
+            GenericType t = parse(type.substring(0, type.length() - 2), loader);
+            return new GenericType(Array.newInstance(t.getRawClass(), 0).getClass(), t);
+        }
+        // Check if this is a generic
+        int genericIndex = type.indexOf('<');
+        if (genericIndex > 0) {
+            if (!type.endsWith(">")) {
+                throw new IllegalArgumentException("Can not load type: " + type);
+            }
+            GenericType base = parse(type.substring(0, genericIndex), loader);
+            String[] params = type.substring(genericIndex + 1, type.length() - 1).split(",");
+            GenericType[] types = new GenericType[params.length];
+            for (int i = 0; i < params.length; i++) {
+                types[i] = parse(params[i], loader);
+            }
+            return new GenericType(base.getRawClass(), types);
+        }
+        // Primitive
+        if (primitiveClasses.containsKey(type)) {
+            return new GenericType(primitiveClasses.get(type));
+        }
+        // Class
+        if (loader instanceof ClassLoader) {
+            return new GenericType(((ClassLoader) loader).loadClass(type));
+        } else if (loader instanceof Bundle) {
+            return new GenericType(((Bundle) loader).loadClass(type));
+        } else {
+            throw new IllegalArgumentException("Unsupported loader: " + loader);
+        }
+    }
+
+    @Override
+    public ReifiedType getActualTypeArgument(int i) {
+        if (parameters.length == 0) {
+            return super.getActualTypeArgument(i);
+        }
+        return parameters[i];
+    }
+
+    @Override
+    public int size() {
+        return parameters.length;
+    }
+
+    @Override
+    public String toString() {
+        Class cl = getRawClass();
+        if (cl.isArray()) {
+            if (parameters.length > 0) {
+                return parameters[0].toString() + "[]";
+            } else {
+                return cl.getComponentType().getName() + "[]";
+            }
+        }
+        if (parameters.length > 0) {
+            StringBuilder sb = new StringBuilder();
+            sb.append(cl.getName());
+            sb.append("<");
+            for (int i = 0; i < parameters.length; i++) {
+                if (i > 0) {
+                    sb.append(",");
+                }
+                sb.append(parameters[i].toString());
+            }
+            sb.append(">");
+            return sb.toString();
+        }
+        return cl.getName();
+    }
+
+    static GenericType[] parametersOf(Type type ) {
+        if ( type instanceof Class ) {
+            Class clazz = (Class) type;
+            if (clazz.isArray()) {
+                GenericType t = new GenericType(clazz.getComponentType());
+                if (t.size() > 0) {
+                    return new GenericType[] { t };
+                } else {
+                    return EMPTY;
+                }
+            } else {
+                return EMPTY;
+            }
+        }
+        if ( type instanceof ParameterizedType ) {
+            ParameterizedType pt = (ParameterizedType) type;
+            Type [] parameters = pt.getActualTypeArguments();
+            GenericType[] gts = new GenericType[parameters.length];
+            for ( int i =0; i<gts.length; i++) {
+                gts[i] = new GenericType(parameters[i]);
+            }
+            return gts;
+        }
+        if ( type instanceof GenericArrayType ) {
+            return new GenericType[] { new GenericType(((GenericArrayType) type).getGenericComponentType()) };
+        }
+        throw new IllegalStateException();
+    }
+
+    static Class<?> getConcreteClass(Type type) {
+        Type ntype = collapse(type);
+        if ( ntype instanceof Class )
+            return (Class<?>) ntype;
+
+        if ( ntype instanceof ParameterizedType )
+            return getConcreteClass(collapse(((ParameterizedType)ntype).getRawType()));
+
+        throw new RuntimeException("Unknown type " + type );
+    }
+
+    static Type collapse(Type target) {
+        if (target instanceof Class || target instanceof ParameterizedType ) {
+            return target;
+        } else if (target instanceof TypeVariable) {
+            return collapse(((TypeVariable<?>) target).getBounds()[0]);
+        } else if (target instanceof GenericArrayType) {
+            Type t = collapse(((GenericArrayType) target)
+                    .getGenericComponentType());
+            while ( t instanceof ParameterizedType )
+                t = collapse(((ParameterizedType)t).getRawType());
+            return Array.newInstance((Class<?>)t, 0).getClass();
+        } else if (target instanceof WildcardType) {
+            WildcardType wct = (WildcardType) target;
+            if (wct.getLowerBounds().length == 0)
+                return collapse(wct.getUpperBounds()[0]);
+            else
+                return collapse(wct.getLowerBounds()[0]);
+        }
+        throw new RuntimeException("Huh? " + target);
+    }
+
+}

Added: karaf/trunk/shell/console/src/main/java/org/apache/felix/gogo/commands/converter/ReifiedType.java
URL: http://svn.apache.org/viewvc/karaf/trunk/shell/console/src/main/java/org/apache/felix/gogo/commands/converter/ReifiedType.java?rev=1549857&view=auto
==============================================================================
--- karaf/trunk/shell/console/src/main/java/org/apache/felix/gogo/commands/converter/ReifiedType.java (added)
+++ karaf/trunk/shell/console/src/main/java/org/apache/felix/gogo/commands/converter/ReifiedType.java Tue Dec 10 15:08:56 2013
@@ -0,0 +1,116 @@
+/*
+ * Copyright (c) OSGi Alliance (2008, 2009). All Rights Reserved.
+ *
+ * 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.apache.felix.gogo.commands.converter;
+
+/**
+ * Provides access to a concrete type and its optional generic type arguments.
+ *
+ * Java 5 and later support generic types. These types consist of a raw class
+ * with type arguments. This class models such a <code>Type</code> class but
+ * ensures that the type is <em>reified</em>. Reification means that the Type
+ * graph associated with a Java 5 <code>Type</code> instance is traversed
+ * until the type becomes a concrete class. In Java 1.4 a class has no
+ * arguments. This concrete class implements the Reified Type for Java 1.4.
+ *
+ * In Java 1.4, this class works with non-generic types. In that cases, a
+ * Reified Type provides access to the class and has zero type arguments, though
+ * a subclass that provide type arguments should be respected. Blueprint
+ * extender implementations can subclass this class and provide access to the
+ * generics type graph if used in a conversion. Such a subclass must
+ * <em>reify<em> the different Java 5 <code>Type</code> instances into the
+ * reified form. That is, a form where the raw Class is available with its optional type arguments as Reified Types.
+ *
+ * @Immutable
+ */
+public class ReifiedType {
+    final static ReifiedType ALL = new ReifiedType(Object.class);
+
+    private final Class clazz;
+
+    /**
+     * Create a Reified Type for a raw Java class without any generic arguments.
+     * Subclasses can provide the optional generic argument information. Without
+     * subclassing, this instance has no type arguments.
+     *
+     * @param clazz
+     *            The raw class of the Reified Type.
+     */
+    public ReifiedType(Class clazz) {
+        this.clazz = clazz;
+    }
+
+    /**
+     * Access to the raw class.
+     *
+     * The raw class represents the concrete class that is associated with a
+     * type declaration. This class could have been deduced from the generics
+     * type graph of the declaration. For example, in the following example:
+     *
+     * <pre>
+     * Map&lt;String, Object&gt; map;
+     * </pre>
+     *
+     * The raw class is the Map class.
+     *
+     * @return the collapsed raw class that represents this type.
+     */
+    public Class getRawClass() {
+        return clazz;
+    }
+
+    /**
+     * Access to a type argument.
+     *
+     * The type argument refers to a argument in a generic type declaration
+     * given by index <code>i</code>. This method returns a Reified Type that
+     * has Object as class when no generic type information is available. Any
+     * object is assignable to Object and therefore no conversion is then
+     * necessary, this is compatible with older Javas than 5. For this reason,
+     * the implementation in this class always returns the
+     * <code>Object<code> class, regardless of the given index.
+     *
+     * This method should be overridden by a subclass that provides access to
+     * the generic information.
+     *
+     * For example, in the following example:
+     *
+     * <pre>
+     * Map&lt;String, Object&gt; map;
+     * </pre>
+     *
+     * The type argument 0 is <code>String</code>, and type argument 1 is
+     * <code>Object</code>.
+     *
+     * @param i
+     *            The index of the type argument
+     * @return <code>ReifiedType(Object.class)<code>, subclasses must override this and return the generic argument at index <code>i</code>
+     */
+    public ReifiedType getActualTypeArgument(int i) {
+        return ALL;
+    }
+
+    /**
+     * Return the number of type arguments.
+     *
+     * This method should be overridden by a subclass to support Java 5 types.
+     *
+     * @return 0, subclasses must override this and return the number of generic
+     *         arguments
+     */
+    public int size() {
+        return 0;
+    }
+}