You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@karaf.apache.org by cs...@apache.org on 2014/02/24 11:27:59 UTC

[01/11] KARAF-2772 Extracting command-api

Repository: karaf
Updated Branches:
  refs/heads/command-api [created] 1ee78df92


http://git-wip-us.apache.org/repos/asf/karaf/blob/1ee78df9/shell/console/src/test/java/org/apache/karaf/shell/console/completer/BooleanCompleterTest.java
----------------------------------------------------------------------
diff --git a/shell/console/src/test/java/org/apache/karaf/shell/console/completer/BooleanCompleterTest.java b/shell/console/src/test/java/org/apache/karaf/shell/console/completer/BooleanCompleterTest.java
deleted file mode 100644
index 7cbf05c..0000000
--- a/shell/console/src/test/java/org/apache/karaf/shell/console/completer/BooleanCompleterTest.java
+++ /dev/null
@@ -1,62 +0,0 @@
-/**
- *
- * 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.console.completer;
-
-import org.apache.karaf.shell.commands.Action;
-import org.apache.karaf.shell.commands.Argument;
-import org.apache.karaf.shell.commands.basic.SimpleCommand;
-import org.apache.felix.service.command.CommandSession;
-import org.apache.karaf.shell.console.Completer;
-import org.junit.Test;
-
-import java.util.Arrays;
-
-import static org.junit.Assert.assertEquals;
-
-public class BooleanCompleterTest extends CompleterTestSupport {
-
-    @Test
-    public void testCompleteArgumnets() throws Exception {
-        CommandSession session = new DummyCommandSession();
-        Completer comp = new ArgumentCompleter(session, new SimpleCommand(MyAction.class), "my:action");
-
-        // arg 0
-        assertEquals(Arrays.asList("true "), complete(comp, "action t"));
-        assertEquals(Arrays.asList("false "), complete(comp, "action f"));
-
-        // arg 1
-        assertEquals(Arrays.asList("true "), complete(comp, "action false t"));
-        assertEquals(Arrays.asList("false "), complete(comp, "action true f"));
-
-        // unknown args
-        assertEquals(Arrays.asList(), complete(comp, "action c"));
-        assertEquals(Arrays.asList(), complete(comp, "action true true a"));
-    }
-
-    public static class MyAction implements Action {
-        @Argument(index = 0)
-        Boolean foo;
-        @Argument(index = 1)
-        boolean bar;
-
-        public Object execute(CommandSession session) throws Exception {
-            return null;
-        }
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/karaf/blob/1ee78df9/shell/console/src/test/java/org/apache/karaf/shell/console/completer/CompleterTestSupport.java
----------------------------------------------------------------------
diff --git a/shell/console/src/test/java/org/apache/karaf/shell/console/completer/CompleterTestSupport.java b/shell/console/src/test/java/org/apache/karaf/shell/console/completer/CompleterTestSupport.java
deleted file mode 100644
index e5341a5..0000000
--- a/shell/console/src/test/java/org/apache/karaf/shell/console/completer/CompleterTestSupport.java
+++ /dev/null
@@ -1,34 +0,0 @@
-/**
- *
- * 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.console.completer;
-
-import org.apache.karaf.shell.console.Completer;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * Useful base class for completion related test cases
- */
-public abstract class CompleterTestSupport {
-    protected List<String> complete(Completer completer, String buf) {
-        List<String> candidates = new ArrayList<String>();
-        completer.complete(buf, buf.length(), candidates);
-        return candidates;
-    }
-}

http://git-wip-us.apache.org/repos/asf/karaf/blob/1ee78df9/shell/console/src/test/java/org/apache/karaf/shell/console/completer/CompleterValuesTest.java
----------------------------------------------------------------------
diff --git a/shell/console/src/test/java/org/apache/karaf/shell/console/completer/CompleterValuesTest.java b/shell/console/src/test/java/org/apache/karaf/shell/console/completer/CompleterValuesTest.java
deleted file mode 100644
index 2ec4a81..0000000
--- a/shell/console/src/test/java/org/apache/karaf/shell/console/completer/CompleterValuesTest.java
+++ /dev/null
@@ -1,74 +0,0 @@
-/**
- *
- * 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.console.completer;
-
-import org.apache.karaf.shell.commands.Action;
-import org.apache.karaf.shell.commands.Argument;
-import org.apache.karaf.shell.commands.CompleterValues;
-import org.apache.karaf.shell.commands.basic.SimpleCommand;
-import org.apache.felix.service.command.CommandSession;
-import org.apache.karaf.shell.console.Completer;
-import org.junit.Test;
-
-import java.util.Arrays;
-import java.util.List;
-
-import static org.junit.Assert.assertEquals;
-
-public class CompleterValuesTest extends CompleterTestSupport {
-
-    @Test
-    public void testCompleteArgumnets() throws Exception {
-        CommandSession session = new DummyCommandSession();
-        Completer comp = new ArgumentCompleter(session, new SimpleCommand(MyAction.class), "my:action");
-
-        // arg 0
-        assertEquals(Arrays.asList("a1", "a2", "a3"), complete(comp, "action a"));
-        assertEquals(Arrays.asList("b4", "b5"), complete(comp, "action b"));
-
-        // arg 1
-        assertEquals(Arrays.asList("c2", "c3"), complete(comp, "action a1 c"));
-        assertEquals(Arrays.asList("d5", "d6", "d7"), complete(comp, "action b4 d"));
-
-        // unknown args
-        assertEquals(Arrays.asList(), complete(comp, "action c"));
-        assertEquals(Arrays.asList(), complete(comp, "action a1 d5 a"));
-    }
-
-    public static class MyAction implements Action {
-        @Argument(index = 0)
-        String foo;
-        @Argument(index = 1)
-        String bar;
-
-        public Object execute(CommandSession session) throws Exception {
-            return null;
-        }
-
-        @CompleterValues(index = 0)
-        public String[] getFooValues() {
-            return new String[]{"a1", "a2", "a3", "b4", "b5"};
-        }
-
-        @CompleterValues(index = 1)
-        public List<String> getBarValues() {
-            return Arrays.asList("c2", "c3", "d5", "d6", "d7");
-        }
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/karaf/blob/1ee78df9/shell/console/src/test/java/org/apache/karaf/shell/console/completer/CompletionTest.java
----------------------------------------------------------------------
diff --git a/shell/console/src/test/java/org/apache/karaf/shell/console/completer/CompletionTest.java b/shell/console/src/test/java/org/apache/karaf/shell/console/completer/CompletionTest.java
deleted file mode 100644
index 25d0485..0000000
--- a/shell/console/src/test/java/org/apache/karaf/shell/console/completer/CompletionTest.java
+++ /dev/null
@@ -1,172 +0,0 @@
-/**
- *
- * 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.console.completer;
-
-import java.util.Arrays;
-
-import org.apache.felix.service.command.CommandSession;
-import org.apache.karaf.shell.commands.Action;
-import org.apache.karaf.shell.commands.Argument;
-import org.apache.karaf.shell.commands.Option;
-import org.apache.karaf.shell.commands.basic.Context;
-import org.apache.karaf.shell.commands.basic.SimpleCommand;
-import org.apache.karaf.shell.commands.basic.SimpleSubShell;
-import org.apache.karaf.shell.console.CommandSessionHolder;
-import org.apache.karaf.shell.console.Completer;
-import org.apache.karaf.shell.console.ExitAction;
-import org.apache.karaf.shell.console.SessionProperties;
-import org.junit.Test;
-
-import static org.junit.Assert.assertEquals;
-
-public class CompletionTest extends CompleterTestSupport {
-
-    @Test
-    public void testSubShellCompletion() throws Exception {
-        Context context = new Context();
-        context.set("SCOPE", "*");
-        context.set(SessionProperties.COMPLETION_MODE, "subshell");
-        CommandSessionHolder.setSession(context.getSession());
-
-        context.addCommand("*", new SimpleSubShell("foo"), "foo");
-        context.addCommand("*", new SimpleCommand(ExitAction.class), "exit");
-        context.addCommand("foo", new SimpleCommand(MyAction.class), "my-action");
-        context.addCommand("foo", new SimpleCommand(MyActionTwoArguments.class), "one-action");
-        context.addCommand("bar", new SimpleCommand(MyAction.class), "one-action");
-        context.addCommand("bar", new SimpleCommand(MyActionTwoArguments.class), "another");
-
-        Completer comp = new CommandsCompleter(context.getSession());
-
-        context.execute("foo");
-        assertEquals(Arrays.asList("my-action "), complete(comp, "my"));
-        assertEquals(Arrays.asList("exit ", "my-action ", "one-action "), complete(comp, ""));
-        assertEquals(Arrays.asList(), complete(comp, "an"));
-        assertEquals(Arrays.asList("--check", "--foo", "--help", "--integer", "--string"),
-                     complete(comp, "my-action --"));
-        assertEquals(Arrays.asList("--dummy", "--help"), complete(comp, "one-action --"));
-
-        context.execute("exit");
-        assertEquals(Arrays.asList(), complete(comp, "my"));
-        assertEquals(Arrays.asList("exit ", "foo "), complete(comp, ""));
-        assertEquals(Arrays.asList(), complete(comp, "an"));
-    }
-
-    @Test
-    public void testFirstCompletion() throws Exception {
-        Context context = new Context();
-        context.set("SCOPE", "*");
-        context.set(SessionProperties.COMPLETION_MODE, "first");
-        CommandSessionHolder.setSession(context.getSession());
-
-        context.addCommand("*", new SimpleSubShell("foo"), "foo");
-        context.addCommand("*", new SimpleCommand(ExitAction.class), "exit");
-        context.addCommand("foo", new SimpleCommand(MyAction.class), "my-action");
-        context.addCommand("foo", new SimpleCommand(MyActionTwoArguments.class), "one-action");
-        context.addCommand("bar", new SimpleCommand(MyAction.class), "one-action");
-        context.addCommand("bar", new SimpleCommand(MyActionTwoArguments.class), "another");
-
-        Completer comp = new CommandsCompleter(context.getSession());
-
-        context.execute("foo");
-        assertEquals(Arrays.asList("my-action "), complete(comp, "my"));
-        assertEquals(Arrays.asList("my-action ", "one-action "), complete(comp, ""));
-        assertEquals(Arrays.asList("another "), complete(comp, "an"));
-        assertEquals(Arrays.asList("--check", "--foo", "--help", "--integer", "--string"),
-                     complete(comp, "my-action --"));
-        assertEquals(Arrays.asList("--dummy", "--help"), complete(comp, "one-action --"));
-
-        context.execute("exit");
-        assertEquals(Arrays.asList("my-action "), complete(comp, "my"));
-        assertEquals(Arrays.asList("*:exit", "*:foo", "another", "bar:another",
-                                   "bar:one-action", "exit", "foo",
-                                   "foo:my-action", "foo:one-action", "my-action",
-                                   "one-action", "one-action"), complete(comp, ""));
-        assertEquals(Arrays.asList("another "), complete(comp, "an"));
-    }
-
-    @Test
-    public void testGlobalCompletion() throws Exception {
-        Context context = new Context();
-        context.set("SCOPE", "*");
-        context.set(SessionProperties.COMPLETION_MODE, "global");
-        CommandSessionHolder.setSession(context.getSession());
-
-        context.addCommand("*", new SimpleSubShell("foo"), "foo");
-        context.addCommand("*", new SimpleCommand(ExitAction.class), "exit");
-        context.addCommand("foo", new SimpleCommand(MyAction.class), "my-action");
-        context.addCommand("foo", new SimpleCommand(MyActionTwoArguments.class), "one-action");
-        context.addCommand("bar", new SimpleCommand(MyAction.class), "one-action");
-        context.addCommand("bar", new SimpleCommand(MyActionTwoArguments.class), "another");
-
-        Completer comp = new CommandsCompleter(context.getSession());
-
-        context.execute("foo");
-        assertEquals(Arrays.asList("my-action "), complete(comp, "my"));
-        assertEquals(Arrays.asList("*:exit", "*:foo", "another", "bar:another",
-                                    "bar:one-action", "exit", "foo",
-                                   "foo:my-action", "foo:one-action", "my-action",
-                                   "one-action", "one-action"), complete(comp, ""));
-        assertEquals(Arrays.asList("another "), complete(comp, "an"));
-        assertEquals(Arrays.asList("--check", "--foo", "--help", "--integer", "--string"),
-                     complete(comp, "my-action --"));
-        assertEquals(Arrays.asList("--dummy", "--help"), complete(comp, "one-action --"));
-
-        context.execute("exit");
-        assertEquals(Arrays.asList("my-action "), complete(comp, "my"));
-        assertEquals(Arrays.asList("*:exit", "*:foo", "another", "bar:another",
-                                   "bar:one-action", "exit", "foo",
-                                   "foo:my-action", "foo:one-action", "my-action",
-                                   "one-action", "one-action"), complete(comp, ""));
-        assertEquals(Arrays.asList("another "), complete(comp, "an"));
-    }
-
-    public static class MyAction implements Action {
-        @Option(name = "-f", aliases = { "--foo" })
-        int f;
-
-        @Option(name = "-c", aliases = "--check")
-        boolean check;
-
-        @Option(name = "-s", aliases = "--string")
-        String string;
-
-        @Option(name = "-i", aliases = "--integer")
-        String integer;
-
-        public Object execute(CommandSession session) throws Exception {
-            return null;
-        }
-    }
-
-    public static class MyActionTwoArguments implements Action {
-
-        @Option(name = "--dummy")
-        boolean dummy;
-
-        @Argument(index = 0, name = "one", description = "one description", required = true, multiValued = false)
-        private String one;
-
-        @Argument(index = 1, name = "two", description = "two description", required = true, multiValued = false)
-        private String two;
-
-        public Object execute(CommandSession session) throws Exception {
-            return null;
-        }
-
-    }
-}

http://git-wip-us.apache.org/repos/asf/karaf/blob/1ee78df9/shell/console/src/test/java/org/apache/karaf/shell/console/completer/DummyCommandSession.java
----------------------------------------------------------------------
diff --git a/shell/console/src/test/java/org/apache/karaf/shell/console/completer/DummyCommandSession.java b/shell/console/src/test/java/org/apache/karaf/shell/console/completer/DummyCommandSession.java
deleted file mode 100644
index ac1a894..0000000
--- a/shell/console/src/test/java/org/apache/karaf/shell/console/completer/DummyCommandSession.java
+++ /dev/null
@@ -1,48 +0,0 @@
-/**
- *
- * 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.console.completer;
-
-import java.io.InputStream;
-import java.io.PrintStream;
-
-import org.apache.felix.service.command.CommandSession;
-
-public class DummyCommandSession implements CommandSession {
-    public Object convert(Class<?> type, Object instance) {
-        return null;
-    }
-    public CharSequence format(Object target, int level) {
-        return null;
-    }
-    public void put(String name, Object value) {
-    }
-    public Object get(String name) {
-        return null;
-    }
-    public PrintStream getConsole() {
-        return null;
-    }
-    public InputStream getKeyboard() {
-        return null;
-    }
-    public void close() {
-    }
-    public Object execute(CharSequence commandline) throws Exception {
-        return null;
-    }
-}

http://git-wip-us.apache.org/repos/asf/karaf/blob/1ee78df9/shell/console/src/test/java/org/apache/karaf/shell/console/completer/FileCompleterTest.java
----------------------------------------------------------------------
diff --git a/shell/console/src/test/java/org/apache/karaf/shell/console/completer/FileCompleterTest.java b/shell/console/src/test/java/org/apache/karaf/shell/console/completer/FileCompleterTest.java
deleted file mode 100644
index 0405371..0000000
--- a/shell/console/src/test/java/org/apache/karaf/shell/console/completer/FileCompleterTest.java
+++ /dev/null
@@ -1,56 +0,0 @@
-/**
- *
- * 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.console.completer;
-
-import java.io.File;
-import java.util.Arrays;
-
-import org.apache.karaf.shell.commands.Action;
-import org.apache.karaf.shell.commands.Argument;
-import org.apache.karaf.shell.commands.basic.SimpleCommand;
-import org.apache.felix.service.command.CommandSession;
-import org.apache.karaf.shell.console.Completer;
-import org.junit.Test;
-
-import static org.junit.Assert.assertEquals;
-
-public class FileCompleterTest extends CompleterTestSupport {
-
-    @Test
-    public void testCompleteArgumnets() throws Exception {
-        CommandSession session = new DummyCommandSession();
-        Completer comp = new ArgumentCompleter(session, new SimpleCommand(MyAction.class), "my:action");
-
-        // arg 0
-        assertEquals(Arrays.asList("src"+File.separator), complete(comp, "action s"));
-        assertEquals(Arrays.asList("main"+File.separator), complete(comp, "action src/m"));
-        assertEquals(Arrays.asList("java"+File.separator), complete(comp, "action src/main/j"));
-    }
-
-    public static class MyAction implements Action {
-        @Argument(index = 0)
-        File foo;
-        @Argument(index = 1)
-        File bar;
-
-        public Object execute(CommandSession session) throws Exception {
-            return null;
-        }
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/karaf/blob/1ee78df9/shell/help/pom.xml
----------------------------------------------------------------------
diff --git a/shell/help/pom.xml b/shell/help/pom.xml
index 2bb514e..3870f36 100644
--- a/shell/help/pom.xml
+++ b/shell/help/pom.xml
@@ -39,17 +39,12 @@
 
     <dependencies>
         <dependency>
-            <groupId>org.fusesource.jansi</groupId>
-            <artifactId>jansi</artifactId>
-            <scope>provided</scope>
-        </dependency>
-        <dependency>
             <groupId>org.apache.karaf.shell</groupId>
             <artifactId>org.apache.karaf.shell.table</artifactId>
         </dependency>
         <dependency>
             <groupId>org.apache.karaf.shell</groupId>
-            <artifactId>org.apache.karaf.shell.console</artifactId>
+            <artifactId>org.apache.karaf.shell.command.api</artifactId>
         </dependency>
         <dependency>
             <groupId>org.apache.felix</groupId>

http://git-wip-us.apache.org/repos/asf/karaf/blob/1ee78df9/shell/help/src/main/java/org/apache/karaf/shell/help/impl/CommandListHelpProvider.java
----------------------------------------------------------------------
diff --git a/shell/help/src/main/java/org/apache/karaf/shell/help/impl/CommandListHelpProvider.java b/shell/help/src/main/java/org/apache/karaf/shell/help/impl/CommandListHelpProvider.java
index 98102c4..6df6c9f 100644
--- a/shell/help/src/main/java/org/apache/karaf/shell/help/impl/CommandListHelpProvider.java
+++ b/shell/help/src/main/java/org/apache/karaf/shell/help/impl/CommandListHelpProvider.java
@@ -25,21 +25,17 @@ import java.util.Set;
 import java.util.SortedMap;
 import java.util.TreeMap;
 
-import jline.Terminal;
-
 import org.apache.felix.gogo.commands.Action;
 import org.apache.felix.service.command.CommandSession;
 import org.apache.felix.service.command.Function;
 import org.apache.karaf.shell.commands.Command;
 import org.apache.karaf.shell.commands.CommandWithAction;
-import org.apache.karaf.shell.commands.basic.AbstractCommand;
 import org.apache.karaf.shell.commands.meta.ActionMetaDataFactory;
 import org.apache.karaf.shell.console.HelpProvider;
 import org.apache.karaf.shell.console.NameScoping;
 import org.apache.karaf.shell.console.SessionProperties;
 import org.apache.karaf.shell.table.Col;
 import org.apache.karaf.shell.table.ShellTable;
-import org.fusesource.jansi.Ansi;
 import org.osgi.framework.BundleContext;
 import org.osgi.framework.ServiceReference;
 
@@ -113,10 +109,8 @@ public class CommandListHelpProvider implements HelpProvider {
     }
 
     protected void printMethodList(CommandSession session, PrintStream out, SortedMap<String, String> commands) {
-        Terminal term = (Terminal) session.get(".jline.terminal");
-        int termWidth = term != null ? term.getWidth() : 80;
-        out.println(Ansi.ansi().a(Ansi.Attribute.INTENSITY_BOLD).a("COMMANDS").a(Ansi.Attribute.RESET));
-        ShellTable table = new ShellTable().noHeaders().separator(" ").size(termWidth);
+        out.println("COMMANDS");
+        ShellTable table = new ShellTable().noHeaders().separator(" ").size(getWidth(session));
         table.column(new Col("Command").maxSize(35));
         table.column(new Col("Description"));
         for (Map.Entry<String,String> entry : commands.entrySet()) {
@@ -126,6 +120,11 @@ public class CommandListHelpProvider implements HelpProvider {
         table.print(out, true);
     }
     
+    private int getWidth(CommandSession session) {
+        Object cols = session.get("COLUMNS");
+        return  (cols != null && cols instanceof Integer) ? (Integer)cols : 80;
+    }
+    
     protected Function unProxy(Function function) {
         try {
             if (function.getClass().getName().contains("CommandProxy")) {

http://git-wip-us.apache.org/repos/asf/karaf/blob/1ee78df9/shell/pom.xml
----------------------------------------------------------------------
diff --git a/shell/pom.xml b/shell/pom.xml
index d8dc873..68dd0e8 100644
--- a/shell/pom.xml
+++ b/shell/pom.xml
@@ -34,6 +34,7 @@
     <name>Apache Karaf :: Shell</name>
 
     <modules>
+        <module>command-api</module>
         <module>commands</module>
         <module>console</module>
         <module>ssh</module>


[06/11] KARAF-2772 Extracting command-api

Posted by cs...@apache.org.
http://git-wip-us.apache.org/repos/asf/karaf/blob/1ee78df9/shell/command-api/src/main/resources/org/apache/karaf/shell/console/commands/karaf-shell-1.2.0.xsd
----------------------------------------------------------------------
diff --git a/shell/command-api/src/main/resources/org/apache/karaf/shell/console/commands/karaf-shell-1.2.0.xsd b/shell/command-api/src/main/resources/org/apache/karaf/shell/console/commands/karaf-shell-1.2.0.xsd
new file mode 100644
index 0000000..afc00f4
--- /dev/null
+++ b/shell/command-api/src/main/resources/org/apache/karaf/shell/console/commands/karaf-shell-1.2.0.xsd
@@ -0,0 +1,225 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+    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.
+-->
+
+<!-- $Rev: 699828 $ $Date: 2008-09-28 16:35:27 +0200 (Sun, 28 Sep 2008) $ -->
+
+<xsd:schema xmlns="http://karaf.apache.org/xmlns/shell/v1.2.0"
+        xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+        xmlns:bp="http://www.osgi.org/xmlns/blueprint/v1.0.0"
+        targetNamespace="http://karaf.apache.org/xmlns/shell/v1.2.0"
+        elementFormDefault="qualified"
+        attributeFormDefault="unqualified">
+
+    <xsd:import namespace="http://www.osgi.org/xmlns/blueprint/v1.0.0"/>
+
+    <xsd:annotation>
+        <xsd:documentation>
+            Defines the configuration elements for Apache Karaf commands support.
+        </xsd:documentation>
+    </xsd:annotation>
+
+    <xsd:element name="command-bundle">
+        <xsd:complexType>
+            <xsd:annotation>
+                <xsd:documentation>
+                    Defines a command bundle.
+                </xsd:documentation>
+            </xsd:annotation>
+            <xsd:sequence>
+                <xsd:element ref="command" minOccurs="0" maxOccurs="unbounded"/>
+            </xsd:sequence>
+            <xsd:attribute name="scan" type="Tscan"/>
+        </xsd:complexType>
+    </xsd:element>
+
+    <xsd:simpleType name="Tscan">
+        <xsd:restriction>
+            <xsd:simpleType>
+                <xsd:list itemType="Tpackage" />
+            </xsd:simpleType>
+            <xsd:minLength value="1" />
+        </xsd:restriction>
+    </xsd:simpleType>
+
+    <xsd:simpleType name="Tpackage">
+        <xsd:restriction base="xsd:string">
+            <xsd:pattern value="\*|[a-z]+(\.[a-z]+)+(\.\*)?" />
+        </xsd:restriction>
+    </xsd:simpleType>
+
+    <xsd:element name="command">
+        <xsd:complexType>
+            <xsd:annotation>
+                <xsd:documentation>
+                    Defines a command.
+                </xsd:documentation>
+            </xsd:annotation>
+            <xsd:sequence>
+                <!--
+                NOTE: Not using an xsd:choice here, as I can't seem to figure out how to get it to properly
+                      validate the min/max of the containted elements.  W/o the xsd:choice the validation
+                      works, though have to define elements in order :-(
+                -->
+                <xsd:element ref="action" minOccurs="1" maxOccurs="1"/>
+                <xsd:element ref="documenter" minOccurs="0" maxOccurs="1"/>
+                <xsd:choice minOccurs="0" maxOccurs="1">
+                    <xsd:element ref="completer"/>
+                    <xsd:element ref="completers"/>
+                </xsd:choice>
+                <xsd:element ref="optional-completers" minOccurs="0" maxOccurs="1"/>
+                <xsd:element ref="message-source" minOccurs="0" maxOccurs="1"/>
+            </xsd:sequence>
+        </xsd:complexType>
+    </xsd:element>
+
+    <xsd:group name="commandComponentElements">
+        <xsd:annotation>
+            <xsd:documentation>
+                Defines the valid elements for command components.  This is based on beans:beanElements,
+                stripping off the bits which are not valid in the command component context.
+            </xsd:documentation>
+        </xsd:annotation>
+		<xsd:sequence>
+			<xsd:choice minOccurs="0" maxOccurs="unbounded">
+                <xsd:element name="argument" type="bp:Targument"/>
+                <xsd:element name="property" type="bp:Tproperty"/>
+				<!--
+				NOTE: This seems to cause schema validation problems... not really sure why
+				<xsd:any namespace="##other" processContents="strict" minOccurs="0" maxOccurs="unbounded"/>
+				-->
+			</xsd:choice>
+		</xsd:sequence>
+	</xsd:group>
+
+    <xsd:attributeGroup name="commandComponentAttributes">
+        <xsd:annotation>
+            <xsd:documentation>
+                Defines the valid attributes for command components.  This is based on beans:beanAttributes,
+                stripping off the bits which are not valid in the command component context.
+            </xsd:documentation>
+        </xsd:annotation>
+		<xsd:attribute name="class" type="xsd:string"/>
+		<xsd:attribute name="depends-on" type="xsd:string"/>
+		<xsd:attribute name="init-method" type="xsd:string"/>
+		<xsd:attribute name="factory-method" type="xsd:string"/>
+		<xsd:attribute name="factory-bean" type="xsd:string"/>
+		<xsd:anyAttribute namespace="##other" processContents="lax"/>
+	</xsd:attributeGroup>
+
+    <xsd:complexType name="commandComponent" abstract="true">
+        <xsd:annotation>
+            <xsd:documentation>
+                Support for command component elements, which are all basically just beans.
+            </xsd:documentation>
+        </xsd:annotation>
+        <xsd:group ref="commandComponentElements"/>
+        <xsd:attributeGroup ref="commandComponentAttributes"/>
+    </xsd:complexType>
+
+    <xsd:element name="action">
+        <xsd:complexType>
+            <xsd:annotation>
+                <xsd:documentation>
+                    Defines a command action.
+                </xsd:documentation>
+            </xsd:annotation>
+            <xsd:complexContent>
+                <xsd:extension base="commandComponent"/>
+            </xsd:complexContent>
+        </xsd:complexType>
+    </xsd:element>
+
+    <xsd:element name="documenter">
+        <xsd:complexType>
+            <xsd:annotation>
+                <xsd:documentation>
+                    Defines a command documenter.
+                </xsd:documentation>
+            </xsd:annotation>
+            <xsd:complexContent>
+                <xsd:extension base="commandComponent"/>
+            </xsd:complexContent>
+        </xsd:complexType>
+    </xsd:element>
+
+    <xsd:element name="completer">
+        <xsd:complexType>
+            <xsd:annotation>
+                <xsd:documentation>
+                    Defines a command completer.
+                </xsd:documentation>
+            </xsd:annotation>
+            <xsd:complexContent>
+                <xsd:extension base="commandComponent"/>
+            </xsd:complexContent>
+        </xsd:complexType>
+    </xsd:element>
+
+    <xsd:element name="completers">
+        <xsd:complexType>
+            <xsd:annotation>
+                <xsd:documentation>
+                    Defines a configurable command completer with a set of completers.
+                </xsd:documentation>
+            </xsd:annotation>
+            <xsd:sequence>
+                <xsd:choice minOccurs="1" maxOccurs="unbounded">
+                    <xsd:element name="bean" type="bp:Tbean"/>
+                    <xsd:element name="ref" type="bp:Tref"/>
+                    <xsd:element name="null" type="bp:Tnull"/>
+                </xsd:choice>
+            </xsd:sequence>
+        </xsd:complexType>
+    </xsd:element>
+
+        <xsd:element name="optional-completers">
+        <xsd:complexType>
+            <xsd:annotation>
+                <xsd:documentation>
+                    Defines a configurable command completer with a set of completers.
+                    These completers are used for option value completion.
+                </xsd:documentation>
+            </xsd:annotation>
+            <xsd:complexContent>
+			<xsd:extension base="bp:TtypedCollection">
+				<xsd:sequence>
+					<xsd:element name="entry" type="bp:TmapEntry" minOccurs="0"
+						maxOccurs="unbounded" />
+				</xsd:sequence>
+				<xsd:attribute name="key-type" type="bp:Ttype" />
+			</xsd:extension>
+		</xsd:complexContent>
+        </xsd:complexType>
+    </xsd:element>
+
+    <xsd:element name="message-source">
+        <xsd:complexType>
+            <xsd:annotation>
+                <xsd:documentation>
+                    Defines a command message source.
+                </xsd:documentation>
+            </xsd:annotation>
+            <xsd:complexContent>
+                <xsd:extension base="commandComponent"/>
+            </xsd:complexContent>
+        </xsd:complexType>
+    </xsd:element>
+
+</xsd:schema>

http://git-wip-us.apache.org/repos/asf/karaf/blob/1ee78df9/shell/command-api/src/test/java/org/apache/karaf/shell/commands/basic/Context.java
----------------------------------------------------------------------
diff --git a/shell/command-api/src/test/java/org/apache/karaf/shell/commands/basic/Context.java b/shell/command-api/src/test/java/org/apache/karaf/shell/commands/basic/Context.java
new file mode 100644
index 0000000..e378789
--- /dev/null
+++ b/shell/command-api/src/test/java/org/apache/karaf/shell/commands/basic/Context.java
@@ -0,0 +1,67 @@
+/*
+ * 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.commands.basic;
+
+import org.apache.felix.gogo.runtime.CommandProcessorImpl;
+import org.apache.felix.gogo.runtime.CommandSessionImpl;
+import org.apache.felix.gogo.runtime.threadio.ThreadIOImpl;
+import org.apache.felix.service.command.CommandSession;
+
+public class Context extends CommandProcessorImpl
+{
+    public static final String EMPTY = "";
+    CommandSessionImpl session;
+    static ThreadIOImpl threadio;
+
+    static
+    {
+        threadio = new ThreadIOImpl();
+        threadio.start();
+    }
+
+    public Context()
+    {
+        super(threadio);
+        addCommand("shell", this, "addCommand");
+        addCommand("shell", this, "removeCommand");
+        addCommand("shell", this, "eval");
+        session = (CommandSessionImpl) createSession(System.in, System.out, System.err);
+    }
+
+
+    public Object execute(CharSequence source) throws Exception
+    {
+        return session.execute(source);
+    }
+
+    public void set(String name, Object value)
+    {
+        session.put(name, value);
+    }
+
+    public Object get(String name)
+    {
+        return session.get(name);
+    }
+
+    public CommandSession getSession() {
+        return session;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/karaf/blob/1ee78df9/shell/command-api/src/test/java/org/apache/karaf/shell/commands/basic/SimpleSubShell.java
----------------------------------------------------------------------
diff --git a/shell/command-api/src/test/java/org/apache/karaf/shell/commands/basic/SimpleSubShell.java b/shell/command-api/src/test/java/org/apache/karaf/shell/commands/basic/SimpleSubShell.java
new file mode 100644
index 0000000..77e9b57
--- /dev/null
+++ b/shell/command-api/src/test/java/org/apache/karaf/shell/commands/basic/SimpleSubShell.java
@@ -0,0 +1,39 @@
+/**
+ *
+ * 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.commands.basic;
+
+import org.apache.karaf.shell.commands.Action;
+import org.apache.karaf.shell.commands.basic.SimpleCommand;
+import org.apache.karaf.shell.console.SubShellAction;
+
+public class SimpleSubShell extends SimpleCommand {
+
+    private final String subshell;
+
+    public SimpleSubShell(String subshell) {
+        super(SubShellAction.class);
+        this.subshell = subshell;
+    }
+
+    @Override
+    public Action createNewAction() {
+        SubShellAction action = (SubShellAction) super.createNewAction();
+        action.setSubShell(subshell);
+        return action;
+    }
+}

http://git-wip-us.apache.org/repos/asf/karaf/blob/1ee78df9/shell/command-api/src/test/java/org/apache/karaf/shell/commands/basic/TestCommands.java
----------------------------------------------------------------------
diff --git a/shell/command-api/src/test/java/org/apache/karaf/shell/commands/basic/TestCommands.java b/shell/command-api/src/test/java/org/apache/karaf/shell/commands/basic/TestCommands.java
new file mode 100644
index 0000000..ce55446
--- /dev/null
+++ b/shell/command-api/src/test/java/org/apache/karaf/shell/commands/basic/TestCommands.java
@@ -0,0 +1,184 @@
+/*
+ * 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.commands.basic;
+
+import java.util.List;
+import java.util.Arrays;
+import java.io.IOException;
+import java.io.StringWriter;
+import java.io.BufferedReader;
+import java.io.InputStreamReader;
+
+import junit.framework.TestCase;
+
+import org.apache.karaf.shell.commands.Action;
+import org.apache.karaf.shell.commands.Argument;
+import org.apache.karaf.shell.commands.Command;
+import org.apache.karaf.shell.commands.CommandException;
+import org.apache.karaf.shell.commands.Option;
+import org.apache.karaf.shell.commands.basic.SimpleCommand;
+import org.apache.felix.service.command.CommandSession;
+import org.apache.karaf.shell.console.ExitAction;
+
+public class TestCommands extends TestCase {
+
+    public void testSubShellScope() throws Exception {
+        Context c = new Context();
+        c.set("SCOPE", "*");
+        c.addCommand("*", new SimpleSubShell("foo"), "foo");
+        c.addCommand("*", new SimpleCommand(ExitAction.class), "exit");
+
+        String scope = (String) c.get("SCOPE");
+        c.execute("foo");
+        assertEquals("foo:" + scope, c.get("SCOPE"));
+        c.execute("exit");
+        assertEquals(scope, c.get("SCOPE"));
+    }
+
+    public void testPrompt() throws Exception {
+        Context c = new Context();
+        c.addCommand("echo", this);
+        c.set("USER", "test");
+        c.set("APPLICATION", "karaf");
+        //c.set("SCOPE", "");
+        Object p = c.execute("echo \"@|bold ${USER}|@${APPLICATION}:@|bold ${SCOPE}|> \"");
+        System.out.println("Prompt: " + p);
+    }
+
+    public void testCommand() throws Exception {
+        Context c = new Context();
+        c.addCommand("*", this, "capture");
+        c.addCommand("*", new SimpleCommand(MyAction.class), "my-action");
+
+        // Test help
+        Object help = c.execute("my-action --help | capture");
+        assertTrue(help instanceof String);
+        assertTrue(((String) help).indexOf("My Action") >= 0);
+        assertTrue(((String) help).indexOf("First option") >= 0);
+        assertTrue(((String) help).indexOf("Bundle ids") >= 0);
+
+        // Test required argument
+        try {
+            c.execute("my-action");
+            fail("Action should have thrown an exception because of a missing argument");
+        } catch (CommandException e) {
+            // ignore
+        }
+
+        // Test required argument
+        assertEquals(Arrays.asList(3), c.execute("my-action 3"));
+
+        // Test required argument
+        assertEquals(Arrays.asList(3), c.execute("my-action 3"));
+
+        // Test required argument
+        assertEquals(Arrays.asList(3, 5), c.execute("my-action 3 5"));
+
+        // Test option
+        assertEquals(Arrays.asList(4), c.execute("my-action -i 3"));
+
+        // Test option alias
+        assertEquals(Arrays.asList(4), c.execute("my-action --increment 3"));
+    }
+
+    public void testCommandTwoArguments() throws Exception {
+        Context c = new Context();
+        c.addCommand("*", new SimpleCommand(MyActionTwoArguments.class), "my-action-two-arguments");
+
+        // test required arguments
+        try {
+            c.execute("my-action-two-arguments");
+            fail("Action should have thrown an exception because of a missing argument");
+        } catch (CommandException e) {
+            assertEquals("Argument one is required", e.getMessage());
+        }
+
+        try {
+            c.execute("my-action-two-arguments 1");
+            fail("Action should have thrown an exception because of a missing argument");
+        } catch (CommandException e) {
+            assertEquals("Argument two is required", e.getMessage());
+        }
+
+        c.execute("my-action-two-arguments 1 2");
+    }
+
+    public String capture() throws IOException {
+        StringWriter sw = new StringWriter();
+        BufferedReader rdr = new BufferedReader(new InputStreamReader(System.in));
+        String s = rdr.readLine();
+        while (s != null) {
+            sw.write(s);
+            s = rdr.readLine();
+        }
+        return sw.toString();
+    }
+
+    public CharSequence echo(Object args[]) {
+        if (args == null) {
+            return "";
+        }
+
+        StringBuilder sb = new StringBuilder();
+        String del = "";
+        for (Object arg : args) {
+            sb.append(del);
+            if (arg != null) {
+                sb.append(arg);
+                del = " ";
+            }
+        }
+        return sb;
+    }
+
+    @Command(scope = "test", name = "my-action", description = "My Action")
+    public static class MyAction implements Action {
+
+        @Option(name = "-i", aliases = {"--increment"}, description = "First option")
+        private boolean increment;
+
+        @Argument(name = "ids", description = "Bundle ids", required = true, multiValued = true)
+        private List<Integer> ids;
+
+        public Object execute(CommandSession session) throws Exception {
+            if (increment) {
+                for (int i = 0; i < ids.size(); i++) {
+                    ids.set(i, ids.get(i) + 1);
+                }
+            }
+            return ids;
+        }
+    }
+
+    @Command(scope = "test", name = "my-action-two-arguments", description = "My Action with two arguments")
+    public static class MyActionTwoArguments implements Action {
+
+        @Argument(index = 0, name = "one", description = "one description", required = true, multiValued = false)
+        private String one;
+
+        @Argument(index = 1, name = "two", description = "two description", required = true, multiValued = false)
+        private String two;
+
+        public Object execute(CommandSession session) throws Exception {
+            return null;
+        }
+
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/karaf/blob/1ee78df9/shell/command-api/src/test/java/org/apache/karaf/shell/commands/meta/TestFormatting.java
----------------------------------------------------------------------
diff --git a/shell/command-api/src/test/java/org/apache/karaf/shell/commands/meta/TestFormatting.java b/shell/command-api/src/test/java/org/apache/karaf/shell/commands/meta/TestFormatting.java
new file mode 100644
index 0000000..24762f5
--- /dev/null
+++ b/shell/command-api/src/test/java/org/apache/karaf/shell/commands/meta/TestFormatting.java
@@ -0,0 +1,35 @@
+/*
+ * 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.commands.meta;
+
+import java.io.ByteArrayOutputStream;
+import java.io.PrintStream;
+
+import junit.framework.TestCase;
+
+import org.apache.karaf.shell.commands.meta.ActionMetaData;
+
+public class TestFormatting extends TestCase {
+    
+    public void testFormat() throws Exception {
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        ActionMetaData.printFormatted("  ", "  This is a test with a long paragraph\n\n  with an indented paragraph\nAnd another one\n", 20, new PrintStream(baos, true), true);
+        System.err.println(baos.toString());
+    }
+}

http://git-wip-us.apache.org/repos/asf/karaf/blob/1ee78df9/shell/command-api/src/test/java/org/apache/karaf/shell/console/completer/ArgumentCompleterTest.java
----------------------------------------------------------------------
diff --git a/shell/command-api/src/test/java/org/apache/karaf/shell/console/completer/ArgumentCompleterTest.java b/shell/command-api/src/test/java/org/apache/karaf/shell/console/completer/ArgumentCompleterTest.java
new file mode 100644
index 0000000..e140ab3
--- /dev/null
+++ b/shell/command-api/src/test/java/org/apache/karaf/shell/console/completer/ArgumentCompleterTest.java
@@ -0,0 +1,145 @@
+/**
+ * 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.console.completer;
+
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.karaf.shell.commands.Action;
+import org.apache.karaf.shell.commands.Option;
+import org.apache.karaf.shell.commands.basic.SimpleCommand;
+import org.apache.felix.service.command.CommandSession;
+import org.apache.karaf.shell.console.CompletableFunction;
+import org.apache.karaf.shell.console.Completer;
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+public class ArgumentCompleterTest extends CompleterTestSupport {
+
+    @Test
+    public void testParser1() throws Exception {
+        Parser parser = new Parser("echo foo | cat bar ; ta", 23);
+        List<List<List<String>>> p = parser.program();
+        assertEquals(1, parser.c0);
+        assertEquals(0, parser.c1);
+        assertEquals(0, parser.c2);
+        assertEquals(2, parser.c3);
+    }
+
+    @Test
+    public void testParser2() throws Exception {
+        Parser parser = new Parser("echo foo ; cat bar | ta", 23);
+        List<List<List<String>>> p = parser.program();
+        assertEquals(1, parser.c0);
+        assertEquals(1, parser.c1);
+        assertEquals(0, parser.c2);
+        assertEquals(2, parser.c3);
+    }
+
+    @Test
+    public void testParser3() throws Exception {
+        Parser parser = new Parser("echo foo ; cat bar | ta", 22);
+        List<List<List<String>>> p = parser.program();
+        assertEquals(1, parser.c0);
+        assertEquals(1, parser.c1);
+        assertEquals(0, parser.c2);
+        assertEquals(1, parser.c3);
+    }
+
+    @Test
+    public void testParser4() throws Exception {
+        Parser parser = new Parser("echo foo ; cat bar | ta reta", 27);
+        List<List<List<String>>> p = parser.program();
+        assertEquals(1, parser.c0);
+        assertEquals(1, parser.c1);
+        assertEquals(1, parser.c2);
+        assertEquals(3, parser.c3);
+    }
+
+    @Test
+    public void testParser5() throws Exception {
+        Parser parser = new Parser("echo foo ; cat bar | ta reta", 24);
+        List<List<List<String>>> p = parser.program();
+        assertEquals(1, parser.c0);
+        assertEquals(1, parser.c1);
+        assertEquals(1, parser.c2);
+        assertEquals(0, parser.c3);
+    }
+
+    @Test
+    public void testCompleteOptions() throws Exception {
+        CommandSession session = new DummyCommandSession();
+        Completer comp = new ArgumentCompleter(session, new MyFunction(), "my:action");
+        assertEquals(Arrays.asList("--check", "--foo", "--help", "--integer", "--string", "-c", "-f","-i","-s"), complete(comp, "action -"));
+        assertEquals(Arrays.asList(), complete(comp, "action --foo "));
+        assertEquals(Arrays.asList("action "), complete(comp, "acti"));
+        assertEquals(Arrays.asList("my:action "), complete(comp, "my:ac"));
+        assertEquals(Arrays.asList("--foo "), complete(comp, "action --f"));
+        assertEquals(Arrays.asList("--help "), complete(comp, "action --h"));
+        assertEquals(Arrays.asList("-c "), complete(comp, "action -c"));
+        assertEquals(Arrays.asList("--check "), complete(comp, "action -f 2 --c"));
+        assertEquals(Arrays.asList("foo1 "), complete(comp, "action -f 2 --check foo1"));
+        assertEquals(Arrays.asList("bar1", "bar2"), complete(comp, "action -f 2 --check foo1 "));
+        assertEquals(Arrays.asList("one", "two"), complete(comp, "action -s "));
+        assertEquals(Arrays.asList("one", "two"), complete(comp, "action --string "));
+        assertEquals(Arrays.asList("two "), complete(comp, "action -s t"));
+        assertEquals(Arrays.asList("1", "2"), complete(comp, "action -i "));
+        assertEquals(Arrays.asList("1", "2"), complete(comp, "action --integer "));
+        assertEquals(Arrays.asList("2 "), complete(comp, "action -i 2"));
+    }
+
+    public static class MyFunction extends SimpleCommand implements CompletableFunction {
+        public MyFunction() {
+            super(MyAction.class);
+        }
+        public List<Completer> getCompleters() {
+            return Arrays.<Completer>asList(
+                    new StringsCompleter(Arrays.asList("foo1", "foo2")),
+                    new StringsCompleter(Arrays.asList("bar1", "bar2"))
+            );
+        }
+
+        public Map<String,Completer> getOptionalCompleters() {
+            Map<String,Completer> completers = new HashMap<String,Completer>();
+            completers.put("-s",new StringsCompleter(Arrays.asList("one","two")));
+            completers.put("--integer",new StringsCompleter(Arrays.asList("1","2")));
+            return completers;
+        }
+    }
+
+    public static class MyAction implements Action {
+        @Option(name = "-f", aliases = { "--foo" })
+        int f;
+
+        @Option(name = "-c", aliases = "--check")
+        boolean check;
+
+        @Option(name = "-s", aliases = "--string")
+        String string;
+
+        @Option(name = "-i", aliases = "--integer")
+        String integer;
+
+        public Object execute(CommandSession session) throws Exception {
+            return null;
+        }
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/karaf/blob/1ee78df9/shell/command-api/src/test/java/org/apache/karaf/shell/console/completer/BooleanCompleterTest.java
----------------------------------------------------------------------
diff --git a/shell/command-api/src/test/java/org/apache/karaf/shell/console/completer/BooleanCompleterTest.java b/shell/command-api/src/test/java/org/apache/karaf/shell/console/completer/BooleanCompleterTest.java
new file mode 100644
index 0000000..7cbf05c
--- /dev/null
+++ b/shell/command-api/src/test/java/org/apache/karaf/shell/console/completer/BooleanCompleterTest.java
@@ -0,0 +1,62 @@
+/**
+ *
+ * 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.console.completer;
+
+import org.apache.karaf.shell.commands.Action;
+import org.apache.karaf.shell.commands.Argument;
+import org.apache.karaf.shell.commands.basic.SimpleCommand;
+import org.apache.felix.service.command.CommandSession;
+import org.apache.karaf.shell.console.Completer;
+import org.junit.Test;
+
+import java.util.Arrays;
+
+import static org.junit.Assert.assertEquals;
+
+public class BooleanCompleterTest extends CompleterTestSupport {
+
+    @Test
+    public void testCompleteArgumnets() throws Exception {
+        CommandSession session = new DummyCommandSession();
+        Completer comp = new ArgumentCompleter(session, new SimpleCommand(MyAction.class), "my:action");
+
+        // arg 0
+        assertEquals(Arrays.asList("true "), complete(comp, "action t"));
+        assertEquals(Arrays.asList("false "), complete(comp, "action f"));
+
+        // arg 1
+        assertEquals(Arrays.asList("true "), complete(comp, "action false t"));
+        assertEquals(Arrays.asList("false "), complete(comp, "action true f"));
+
+        // unknown args
+        assertEquals(Arrays.asList(), complete(comp, "action c"));
+        assertEquals(Arrays.asList(), complete(comp, "action true true a"));
+    }
+
+    public static class MyAction implements Action {
+        @Argument(index = 0)
+        Boolean foo;
+        @Argument(index = 1)
+        boolean bar;
+
+        public Object execute(CommandSession session) throws Exception {
+            return null;
+        }
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/karaf/blob/1ee78df9/shell/command-api/src/test/java/org/apache/karaf/shell/console/completer/CompleterTestSupport.java
----------------------------------------------------------------------
diff --git a/shell/command-api/src/test/java/org/apache/karaf/shell/console/completer/CompleterTestSupport.java b/shell/command-api/src/test/java/org/apache/karaf/shell/console/completer/CompleterTestSupport.java
new file mode 100644
index 0000000..e5341a5
--- /dev/null
+++ b/shell/command-api/src/test/java/org/apache/karaf/shell/console/completer/CompleterTestSupport.java
@@ -0,0 +1,34 @@
+/**
+ *
+ * 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.console.completer;
+
+import org.apache.karaf.shell.console.Completer;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Useful base class for completion related test cases
+ */
+public abstract class CompleterTestSupport {
+    protected List<String> complete(Completer completer, String buf) {
+        List<String> candidates = new ArrayList<String>();
+        completer.complete(buf, buf.length(), candidates);
+        return candidates;
+    }
+}

http://git-wip-us.apache.org/repos/asf/karaf/blob/1ee78df9/shell/command-api/src/test/java/org/apache/karaf/shell/console/completer/CompleterValuesTest.java
----------------------------------------------------------------------
diff --git a/shell/command-api/src/test/java/org/apache/karaf/shell/console/completer/CompleterValuesTest.java b/shell/command-api/src/test/java/org/apache/karaf/shell/console/completer/CompleterValuesTest.java
new file mode 100644
index 0000000..2ec4a81
--- /dev/null
+++ b/shell/command-api/src/test/java/org/apache/karaf/shell/console/completer/CompleterValuesTest.java
@@ -0,0 +1,74 @@
+/**
+ *
+ * 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.console.completer;
+
+import org.apache.karaf.shell.commands.Action;
+import org.apache.karaf.shell.commands.Argument;
+import org.apache.karaf.shell.commands.CompleterValues;
+import org.apache.karaf.shell.commands.basic.SimpleCommand;
+import org.apache.felix.service.command.CommandSession;
+import org.apache.karaf.shell.console.Completer;
+import org.junit.Test;
+
+import java.util.Arrays;
+import java.util.List;
+
+import static org.junit.Assert.assertEquals;
+
+public class CompleterValuesTest extends CompleterTestSupport {
+
+    @Test
+    public void testCompleteArgumnets() throws Exception {
+        CommandSession session = new DummyCommandSession();
+        Completer comp = new ArgumentCompleter(session, new SimpleCommand(MyAction.class), "my:action");
+
+        // arg 0
+        assertEquals(Arrays.asList("a1", "a2", "a3"), complete(comp, "action a"));
+        assertEquals(Arrays.asList("b4", "b5"), complete(comp, "action b"));
+
+        // arg 1
+        assertEquals(Arrays.asList("c2", "c3"), complete(comp, "action a1 c"));
+        assertEquals(Arrays.asList("d5", "d6", "d7"), complete(comp, "action b4 d"));
+
+        // unknown args
+        assertEquals(Arrays.asList(), complete(comp, "action c"));
+        assertEquals(Arrays.asList(), complete(comp, "action a1 d5 a"));
+    }
+
+    public static class MyAction implements Action {
+        @Argument(index = 0)
+        String foo;
+        @Argument(index = 1)
+        String bar;
+
+        public Object execute(CommandSession session) throws Exception {
+            return null;
+        }
+
+        @CompleterValues(index = 0)
+        public String[] getFooValues() {
+            return new String[]{"a1", "a2", "a3", "b4", "b5"};
+        }
+
+        @CompleterValues(index = 1)
+        public List<String> getBarValues() {
+            return Arrays.asList("c2", "c3", "d5", "d6", "d7");
+        }
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/karaf/blob/1ee78df9/shell/command-api/src/test/java/org/apache/karaf/shell/console/completer/CompletionTest.java
----------------------------------------------------------------------
diff --git a/shell/command-api/src/test/java/org/apache/karaf/shell/console/completer/CompletionTest.java b/shell/command-api/src/test/java/org/apache/karaf/shell/console/completer/CompletionTest.java
new file mode 100644
index 0000000..25d0485
--- /dev/null
+++ b/shell/command-api/src/test/java/org/apache/karaf/shell/console/completer/CompletionTest.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.console.completer;
+
+import java.util.Arrays;
+
+import org.apache.felix.service.command.CommandSession;
+import org.apache.karaf.shell.commands.Action;
+import org.apache.karaf.shell.commands.Argument;
+import org.apache.karaf.shell.commands.Option;
+import org.apache.karaf.shell.commands.basic.Context;
+import org.apache.karaf.shell.commands.basic.SimpleCommand;
+import org.apache.karaf.shell.commands.basic.SimpleSubShell;
+import org.apache.karaf.shell.console.CommandSessionHolder;
+import org.apache.karaf.shell.console.Completer;
+import org.apache.karaf.shell.console.ExitAction;
+import org.apache.karaf.shell.console.SessionProperties;
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+
+public class CompletionTest extends CompleterTestSupport {
+
+    @Test
+    public void testSubShellCompletion() throws Exception {
+        Context context = new Context();
+        context.set("SCOPE", "*");
+        context.set(SessionProperties.COMPLETION_MODE, "subshell");
+        CommandSessionHolder.setSession(context.getSession());
+
+        context.addCommand("*", new SimpleSubShell("foo"), "foo");
+        context.addCommand("*", new SimpleCommand(ExitAction.class), "exit");
+        context.addCommand("foo", new SimpleCommand(MyAction.class), "my-action");
+        context.addCommand("foo", new SimpleCommand(MyActionTwoArguments.class), "one-action");
+        context.addCommand("bar", new SimpleCommand(MyAction.class), "one-action");
+        context.addCommand("bar", new SimpleCommand(MyActionTwoArguments.class), "another");
+
+        Completer comp = new CommandsCompleter(context.getSession());
+
+        context.execute("foo");
+        assertEquals(Arrays.asList("my-action "), complete(comp, "my"));
+        assertEquals(Arrays.asList("exit ", "my-action ", "one-action "), complete(comp, ""));
+        assertEquals(Arrays.asList(), complete(comp, "an"));
+        assertEquals(Arrays.asList("--check", "--foo", "--help", "--integer", "--string"),
+                     complete(comp, "my-action --"));
+        assertEquals(Arrays.asList("--dummy", "--help"), complete(comp, "one-action --"));
+
+        context.execute("exit");
+        assertEquals(Arrays.asList(), complete(comp, "my"));
+        assertEquals(Arrays.asList("exit ", "foo "), complete(comp, ""));
+        assertEquals(Arrays.asList(), complete(comp, "an"));
+    }
+
+    @Test
+    public void testFirstCompletion() throws Exception {
+        Context context = new Context();
+        context.set("SCOPE", "*");
+        context.set(SessionProperties.COMPLETION_MODE, "first");
+        CommandSessionHolder.setSession(context.getSession());
+
+        context.addCommand("*", new SimpleSubShell("foo"), "foo");
+        context.addCommand("*", new SimpleCommand(ExitAction.class), "exit");
+        context.addCommand("foo", new SimpleCommand(MyAction.class), "my-action");
+        context.addCommand("foo", new SimpleCommand(MyActionTwoArguments.class), "one-action");
+        context.addCommand("bar", new SimpleCommand(MyAction.class), "one-action");
+        context.addCommand("bar", new SimpleCommand(MyActionTwoArguments.class), "another");
+
+        Completer comp = new CommandsCompleter(context.getSession());
+
+        context.execute("foo");
+        assertEquals(Arrays.asList("my-action "), complete(comp, "my"));
+        assertEquals(Arrays.asList("my-action ", "one-action "), complete(comp, ""));
+        assertEquals(Arrays.asList("another "), complete(comp, "an"));
+        assertEquals(Arrays.asList("--check", "--foo", "--help", "--integer", "--string"),
+                     complete(comp, "my-action --"));
+        assertEquals(Arrays.asList("--dummy", "--help"), complete(comp, "one-action --"));
+
+        context.execute("exit");
+        assertEquals(Arrays.asList("my-action "), complete(comp, "my"));
+        assertEquals(Arrays.asList("*:exit", "*:foo", "another", "bar:another",
+                                   "bar:one-action", "exit", "foo",
+                                   "foo:my-action", "foo:one-action", "my-action",
+                                   "one-action", "one-action"), complete(comp, ""));
+        assertEquals(Arrays.asList("another "), complete(comp, "an"));
+    }
+
+    @Test
+    public void testGlobalCompletion() throws Exception {
+        Context context = new Context();
+        context.set("SCOPE", "*");
+        context.set(SessionProperties.COMPLETION_MODE, "global");
+        CommandSessionHolder.setSession(context.getSession());
+
+        context.addCommand("*", new SimpleSubShell("foo"), "foo");
+        context.addCommand("*", new SimpleCommand(ExitAction.class), "exit");
+        context.addCommand("foo", new SimpleCommand(MyAction.class), "my-action");
+        context.addCommand("foo", new SimpleCommand(MyActionTwoArguments.class), "one-action");
+        context.addCommand("bar", new SimpleCommand(MyAction.class), "one-action");
+        context.addCommand("bar", new SimpleCommand(MyActionTwoArguments.class), "another");
+
+        Completer comp = new CommandsCompleter(context.getSession());
+
+        context.execute("foo");
+        assertEquals(Arrays.asList("my-action "), complete(comp, "my"));
+        assertEquals(Arrays.asList("*:exit", "*:foo", "another", "bar:another",
+                                    "bar:one-action", "exit", "foo",
+                                   "foo:my-action", "foo:one-action", "my-action",
+                                   "one-action", "one-action"), complete(comp, ""));
+        assertEquals(Arrays.asList("another "), complete(comp, "an"));
+        assertEquals(Arrays.asList("--check", "--foo", "--help", "--integer", "--string"),
+                     complete(comp, "my-action --"));
+        assertEquals(Arrays.asList("--dummy", "--help"), complete(comp, "one-action --"));
+
+        context.execute("exit");
+        assertEquals(Arrays.asList("my-action "), complete(comp, "my"));
+        assertEquals(Arrays.asList("*:exit", "*:foo", "another", "bar:another",
+                                   "bar:one-action", "exit", "foo",
+                                   "foo:my-action", "foo:one-action", "my-action",
+                                   "one-action", "one-action"), complete(comp, ""));
+        assertEquals(Arrays.asList("another "), complete(comp, "an"));
+    }
+
+    public static class MyAction implements Action {
+        @Option(name = "-f", aliases = { "--foo" })
+        int f;
+
+        @Option(name = "-c", aliases = "--check")
+        boolean check;
+
+        @Option(name = "-s", aliases = "--string")
+        String string;
+
+        @Option(name = "-i", aliases = "--integer")
+        String integer;
+
+        public Object execute(CommandSession session) throws Exception {
+            return null;
+        }
+    }
+
+    public static class MyActionTwoArguments implements Action {
+
+        @Option(name = "--dummy")
+        boolean dummy;
+
+        @Argument(index = 0, name = "one", description = "one description", required = true, multiValued = false)
+        private String one;
+
+        @Argument(index = 1, name = "two", description = "two description", required = true, multiValued = false)
+        private String two;
+
+        public Object execute(CommandSession session) throws Exception {
+            return null;
+        }
+
+    }
+}

http://git-wip-us.apache.org/repos/asf/karaf/blob/1ee78df9/shell/command-api/src/test/java/org/apache/karaf/shell/console/completer/DummyCommandSession.java
----------------------------------------------------------------------
diff --git a/shell/command-api/src/test/java/org/apache/karaf/shell/console/completer/DummyCommandSession.java b/shell/command-api/src/test/java/org/apache/karaf/shell/console/completer/DummyCommandSession.java
new file mode 100644
index 0000000..ac1a894
--- /dev/null
+++ b/shell/command-api/src/test/java/org/apache/karaf/shell/console/completer/DummyCommandSession.java
@@ -0,0 +1,48 @@
+/**
+ *
+ * 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.console.completer;
+
+import java.io.InputStream;
+import java.io.PrintStream;
+
+import org.apache.felix.service.command.CommandSession;
+
+public class DummyCommandSession implements CommandSession {
+    public Object convert(Class<?> type, Object instance) {
+        return null;
+    }
+    public CharSequence format(Object target, int level) {
+        return null;
+    }
+    public void put(String name, Object value) {
+    }
+    public Object get(String name) {
+        return null;
+    }
+    public PrintStream getConsole() {
+        return null;
+    }
+    public InputStream getKeyboard() {
+        return null;
+    }
+    public void close() {
+    }
+    public Object execute(CharSequence commandline) throws Exception {
+        return null;
+    }
+}

http://git-wip-us.apache.org/repos/asf/karaf/blob/1ee78df9/shell/command-api/src/test/java/org/apache/karaf/shell/console/completer/FileCompleterTest.java
----------------------------------------------------------------------
diff --git a/shell/command-api/src/test/java/org/apache/karaf/shell/console/completer/FileCompleterTest.java b/shell/command-api/src/test/java/org/apache/karaf/shell/console/completer/FileCompleterTest.java
new file mode 100644
index 0000000..0405371
--- /dev/null
+++ b/shell/command-api/src/test/java/org/apache/karaf/shell/console/completer/FileCompleterTest.java
@@ -0,0 +1,56 @@
+/**
+ *
+ * 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.console.completer;
+
+import java.io.File;
+import java.util.Arrays;
+
+import org.apache.karaf.shell.commands.Action;
+import org.apache.karaf.shell.commands.Argument;
+import org.apache.karaf.shell.commands.basic.SimpleCommand;
+import org.apache.felix.service.command.CommandSession;
+import org.apache.karaf.shell.console.Completer;
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+
+public class FileCompleterTest extends CompleterTestSupport {
+
+    @Test
+    public void testCompleteArgumnets() throws Exception {
+        CommandSession session = new DummyCommandSession();
+        Completer comp = new ArgumentCompleter(session, new SimpleCommand(MyAction.class), "my:action");
+
+        // arg 0
+        assertEquals(Arrays.asList("src"+File.separator), complete(comp, "action s"));
+        assertEquals(Arrays.asList("main"+File.separator), complete(comp, "action src/m"));
+        assertEquals(Arrays.asList("java"+File.separator), complete(comp, "action src/main/j"));
+    }
+
+    public static class MyAction implements Action {
+        @Argument(index = 0)
+        File foo;
+        @Argument(index = 1)
+        File bar;
+
+        public Object execute(CommandSession session) throws Exception {
+            return null;
+        }
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/karaf/blob/1ee78df9/shell/commands/pom.xml
----------------------------------------------------------------------
diff --git a/shell/commands/pom.xml b/shell/commands/pom.xml
index 15fb8c7..1e857ca 100644
--- a/shell/commands/pom.xml
+++ b/shell/commands/pom.xml
@@ -40,7 +40,7 @@
     <dependencies>
         <dependency>
             <groupId>org.apache.karaf.shell</groupId>
-            <artifactId>org.apache.karaf.shell.console</artifactId>
+            <artifactId>org.apache.karaf.shell.command.api</artifactId>
         </dependency>
         <dependency>
             <groupId>org.apache.karaf.shell</groupId>

http://git-wip-us.apache.org/repos/asf/karaf/blob/1ee78df9/shell/console/pom.xml
----------------------------------------------------------------------
diff --git a/shell/console/pom.xml b/shell/console/pom.xml
index 6fe1ac0..56f7beb 100644
--- a/shell/console/pom.xml
+++ b/shell/console/pom.xml
@@ -38,6 +38,11 @@
     </properties>
 
     <dependencies>
+    	<dependency>
+    		<groupId>org.apache.karaf.shell</groupId>
+    		<artifactId>org.apache.karaf.shell.command.api</artifactId>
+    		<version>${project.version}</version>
+    	</dependency>
         <dependency>
             <groupId>org.slf4j</groupId>
             <artifactId>slf4j-api</artifactId>
@@ -67,16 +72,6 @@
         </dependency>
 
         <dependency>
-            <groupId>org.apache.aries.blueprint</groupId>
-            <artifactId>org.apache.aries.blueprint.api</artifactId>
-            <scope>provided</scope>
-        </dependency>
-        <dependency>
-            <groupId>org.apache.aries.blueprint</groupId>
-            <artifactId>org.apache.aries.blueprint.core</artifactId>
-            <scope>provided</scope>
-        </dependency>
-        <dependency>
             <groupId>org.apache.felix</groupId>
             <artifactId>org.apache.felix.gogo.runtime</artifactId>
             <scope>provided</scope>

http://git-wip-us.apache.org/repos/asf/karaf/blob/1ee78df9/shell/console/src/main/java/org/apache/felix/gogo/commands/Action.java
----------------------------------------------------------------------
diff --git a/shell/console/src/main/java/org/apache/felix/gogo/commands/Action.java b/shell/console/src/main/java/org/apache/felix/gogo/commands/Action.java
deleted file mode 100644
index 0727f24..0000000
--- a/shell/console/src/main/java/org/apache/felix/gogo/commands/Action.java
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * 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.apache.felix.service.command.CommandSession;
-
-@Deprecated
-public interface Action {
-
-    Object execute(CommandSession session) throws Exception;
-
-}
-

http://git-wip-us.apache.org/repos/asf/karaf/blob/1ee78df9/shell/console/src/main/java/org/apache/felix/gogo/commands/Argument.java
----------------------------------------------------------------------
diff --git a/shell/console/src/main/java/org/apache/felix/gogo/commands/Argument.java b/shell/console/src/main/java/org/apache/felix/gogo/commands/Argument.java
deleted file mode 100644
index 33c15bf..0000000
--- a/shell/console/src/main/java/org/apache/felix/gogo/commands/Argument.java
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * 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 java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.lang.annotation.Target;
-import java.lang.annotation.ElementType;
-
-/**
- * Represents a positional argument on a command line (as opposed to an optional named {@link Option}
- */
-@Retention(RetentionPolicy.RUNTIME)
-@Target({ElementType.FIELD})
-@Deprecated
-public @interface Argument {
-
-    public static final String DEFAULT_STRING = "DEFAULT";
-
-    String DEFAULT = "##default";
-
-    String name() default DEFAULT;
-
-    String description() default "";
-
-    boolean required() default false;
-
-    int index() default 0;
-
-    boolean multiValued() default false;
-
-    String valueToShowInHelp() default DEFAULT_STRING;
-}

http://git-wip-us.apache.org/repos/asf/karaf/blob/1ee78df9/shell/console/src/main/java/org/apache/felix/gogo/commands/Command.java
----------------------------------------------------------------------
diff --git a/shell/console/src/main/java/org/apache/felix/gogo/commands/Command.java b/shell/console/src/main/java/org/apache/felix/gogo/commands/Command.java
deleted file mode 100644
index 10c494a..0000000
--- a/shell/console/src/main/java/org/apache/felix/gogo/commands/Command.java
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * 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 java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.lang.annotation.Target;
-import java.lang.annotation.ElementType;
-
-/**
- * Used to denote a class represents a command which is executable within a shell/scope or as a
- * command line process.
- */
-@Retention(RetentionPolicy.RUNTIME)
-@Target({ElementType.TYPE})
-@Deprecated
-public @interface Command {
-    /**
-     * Returns the scope or sub shell of the command
-     */
-    String scope();
-
-    /**
-     * REturns the name of the command if used inside a shell
-     */
-    String name();
-
-    /**
-     * Returns the description of the command which is used to generate command line help
-     */
-    String description() default "";
-
-    /**
-     * Returns a detailed description of the command
-     */
-    String detailedDescription() default "";
-}

http://git-wip-us.apache.org/repos/asf/karaf/blob/1ee78df9/shell/console/src/main/java/org/apache/felix/gogo/commands/CommandException.java
----------------------------------------------------------------------
diff --git a/shell/console/src/main/java/org/apache/felix/gogo/commands/CommandException.java b/shell/console/src/main/java/org/apache/felix/gogo/commands/CommandException.java
deleted file mode 100644
index 3c02d63..0000000
--- a/shell/console/src/main/java/org/apache/felix/gogo/commands/CommandException.java
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * 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.apache.karaf.shell.commands.ansi.SimpleAnsi;
-
-/**
- * Base class for exceptions thrown when executing commands.
- */
-@Deprecated
-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
-                : SimpleAnsi.COLOR_RED + "Error executing command: " 
-                 + getMessage() != null ? getMessage() : getClass().getName()
-                 + SimpleAnsi.COLOR_DEFAULT;
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/karaf/blob/1ee78df9/shell/console/src/main/java/org/apache/felix/gogo/commands/CommandWithAction.java
----------------------------------------------------------------------
diff --git a/shell/console/src/main/java/org/apache/felix/gogo/commands/CommandWithAction.java b/shell/console/src/main/java/org/apache/felix/gogo/commands/CommandWithAction.java
deleted file mode 100644
index e09cf64..0000000
--- a/shell/console/src/main/java/org/apache/felix/gogo/commands/CommandWithAction.java
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * 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.apache.felix.service.command.Function;
-
-@Deprecated
-public interface CommandWithAction extends Function {
-
-    Class<? extends Action> getActionClass();
-
-    Action createNewAction();
-
-    void releaseAction(Action action) throws Exception;
-
-}

http://git-wip-us.apache.org/repos/asf/karaf/blob/1ee78df9/shell/console/src/main/java/org/apache/felix/gogo/commands/CompleterValues.java
----------------------------------------------------------------------
diff --git a/shell/console/src/main/java/org/apache/felix/gogo/commands/CompleterValues.java b/shell/console/src/main/java/org/apache/felix/gogo/commands/CompleterValues.java
deleted file mode 100644
index 99129e5..0000000
--- a/shell/console/src/main/java/org/apache/felix/gogo/commands/CompleterValues.java
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * 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 java.lang.annotation.ElementType;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.lang.annotation.Target;
-
-/**
- * Represents a method which can return a List or Array of values used for a
- * {@link org.apache.karaf.shell.console.Completer}
- * which is associated with the index of an
- * {@link org.apache.karaf.shell.commands.Argument}
- */
-@Retention(RetentionPolicy.RUNTIME)
-@Target({ElementType.METHOD, ElementType.FIELD})
-@Deprecated
-public @interface CompleterValues {
-    int index() default 0;
-}

http://git-wip-us.apache.org/repos/asf/karaf/blob/1ee78df9/shell/console/src/main/java/org/apache/felix/gogo/commands/Option.java
----------------------------------------------------------------------
diff --git a/shell/console/src/main/java/org/apache/felix/gogo/commands/Option.java b/shell/console/src/main/java/org/apache/felix/gogo/commands/Option.java
deleted file mode 100644
index 1ce6f75..0000000
--- a/shell/console/src/main/java/org/apache/felix/gogo/commands/Option.java
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * 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 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 "--"
- */
-@Retention(RetentionPolicy.RUNTIME)
-@Target({ElementType.FIELD})
-@Deprecated
-public @interface Option {
-
-    public static final String DEFAULT_STRING = "DEFAULT";
-
-    String name();
-
-    String[] aliases() default {};
-
-    String description() default "";
-
-    boolean required() default false;
-
-    boolean multiValued() default false;
-
-    String valueToShowInHelp() default DEFAULT_STRING;
-
-}

http://git-wip-us.apache.org/repos/asf/karaf/blob/1ee78df9/shell/console/src/main/java/org/apache/felix/gogo/commands/SubShell.java
----------------------------------------------------------------------
diff --git a/shell/console/src/main/java/org/apache/felix/gogo/commands/SubShell.java b/shell/console/src/main/java/org/apache/felix/gogo/commands/SubShell.java
deleted file mode 100644
index 0206964..0000000
--- a/shell/console/src/main/java/org/apache/felix/gogo/commands/SubShell.java
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * 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 java.lang.annotation.ElementType;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.lang.annotation.Target;
-
-@Retention(RetentionPolicy.RUNTIME)
-@Target({ElementType.TYPE})
-@Deprecated
-public @interface SubShell {
-
-    /**
-     * Returns the name of the command if used inside a shell
-     */
-    String name();
-
-    /**
-     * Returns the description of the command which is used to generate command line help
-     */
-    String description() default "";
-
-    /**
-     * Returns a detailed description of the command
-     */
-    String detailedDescription() default "";
-
-}

http://git-wip-us.apache.org/repos/asf/karaf/blob/1ee78df9/shell/console/src/main/java/org/apache/felix/gogo/commands/basic/AbstractCommand.java
----------------------------------------------------------------------
diff --git a/shell/console/src/main/java/org/apache/felix/gogo/commands/basic/AbstractCommand.java b/shell/console/src/main/java/org/apache/felix/gogo/commands/basic/AbstractCommand.java
deleted file mode 100644
index 809ed67..0000000
--- a/shell/console/src/main/java/org/apache/felix/gogo/commands/basic/AbstractCommand.java
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- * 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.basic;
-
-import java.util.List;
-
-import org.apache.felix.gogo.commands.Action;
-import org.apache.felix.gogo.commands.CommandWithAction;
-import org.apache.felix.service.command.CommandSession;
-import org.apache.felix.service.command.Function;
-
-@Deprecated
-public abstract class AbstractCommand implements Function, CommandWithAction {
-
-    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();
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/karaf/blob/1ee78df9/shell/console/src/main/java/org/apache/felix/gogo/commands/basic/ActionPreparator.java
----------------------------------------------------------------------
diff --git a/shell/console/src/main/java/org/apache/felix/gogo/commands/basic/ActionPreparator.java b/shell/console/src/main/java/org/apache/felix/gogo/commands/basic/ActionPreparator.java
deleted file mode 100644
index 65fe24e..0000000
--- a/shell/console/src/main/java/org/apache/felix/gogo/commands/basic/ActionPreparator.java
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * 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.basic;
-
-import java.util.List;
-
-import org.apache.felix.gogo.commands.Action;
-import org.apache.felix.service.command.CommandSession;
-
-@Deprecated
-public interface ActionPreparator {
-
-    boolean prepare(Action action, CommandSession session, List<Object> arguments) throws Exception;
-
-}


[11/11] git commit: KARAF-2772 Extracting command-api

Posted by cs...@apache.org.
KARAF-2772 Extracting command-api


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

Branch: refs/heads/command-api
Commit: 1ee78df92a1345d2236c0000d22962d69324b787
Parents: 6fdb03b
Author: Christian Schneider <ch...@die-schneider.net>
Authored: Mon Feb 24 11:27:32 2014 +0100
Committer: Christian Schneider <ch...@die-schneider.net>
Committed: Mon Feb 24 11:27:32 2014 +0100

----------------------------------------------------------------------
 jaas/modules/pom.xml                            |   1 +
 pom.xml                                         |   5 +
 shell/command-api/NOTICE                        |  71 +++
 shell/command-api/pom.xml                       | 131 +++++
 .../karaf/shell/console/branding-ssh.properties |  34 ++
 .../karaf/shell/console/branding.properties     |  33 ++
 .../org/apache/felix/gogo/commands/Action.java  |  29 +
 .../apache/felix/gogo/commands/Argument.java    |  49 ++
 .../org/apache/felix/gogo/commands/Command.java |  53 ++
 .../felix/gogo/commands/CommandException.java   |  63 +++
 .../felix/gogo/commands/CommandWithAction.java  |  32 ++
 .../felix/gogo/commands/CompleterValues.java    |  37 ++
 .../org/apache/felix/gogo/commands/Option.java  |  48 ++
 .../apache/felix/gogo/commands/SubShell.java    |  46 ++
 .../gogo/commands/basic/AbstractCommand.java    |  64 +++
 .../gogo/commands/basic/ActionPreparator.java   |  31 +
 .../commands/basic/DefaultActionPreparator.java | 566 ++++++++++++++++++
 .../commands/converter/DefaultConverter.java    | 402 +++++++++++++
 .../gogo/commands/converter/GenericType.java    | 196 +++++++
 .../gogo/commands/converter/ReifiedType.java    | 118 ++++
 .../org/apache/karaf/shell/commands/Action.java |  40 ++
 .../apache/karaf/shell/commands/Argument.java   |  48 ++
 .../apache/karaf/shell/commands/Command.java    |  53 ++
 .../karaf/shell/commands/CommandException.java  |  64 +++
 .../karaf/shell/commands/CommandWithAction.java |  31 +
 .../apache/karaf/shell/commands/Completer.java  |  34 ++
 .../karaf/shell/commands/CompleterValues.java   |  37 ++
 .../apache/karaf/shell/commands/HelpOption.java |  54 ++
 .../karaf/shell/commands/InfoProvider.java      |  30 +
 .../org/apache/karaf/shell/commands/Option.java |  46 ++
 .../karaf/shell/commands/ansi/SimpleAnsi.java   |  28 +
 .../shell/commands/basic/AbstractCommand.java   |  62 ++
 .../shell/commands/basic/ActionPreparator.java  |  42 ++
 .../commands/basic/DefaultActionPreparator.java | 223 ++++++++
 .../shell/commands/basic/SimpleCommand.java     |  82 +++
 .../commands/converter/DefaultConverter.java    | 401 +++++++++++++
 .../shell/commands/converter/GenericType.java   | 195 +++++++
 .../shell/commands/converter/ReifiedType.java   | 116 ++++
 .../shell/commands/meta/ActionMetaData.java     | 296 ++++++++++
 .../commands/meta/ActionMetaDataFactory.java    | 251 ++++++++
 .../karaf/shell/console/AbstractAction.java     |  50 ++
 .../shell/console/BlueprintContainerAware.java  |  27 +
 .../karaf/shell/console/BundleContextAware.java |  27 +
 .../shell/console/CloseShellException.java      |  27 +
 .../shell/console/CommandSessionHolder.java     |  38 ++
 .../shell/console/CompletableFunction.java      |  31 +
 .../apache/karaf/shell/console/Completer.java   |  27 +
 .../org/apache/karaf/shell/console/Console.java |  29 +
 .../apache/karaf/shell/console/ExitAction.java  |  46 ++
 .../karaf/shell/console/HelpProvider.java       |  26 +
 .../karaf/shell/console/MultiException.java     |  95 ++++
 .../apache/karaf/shell/console/NameScoping.java |  79 +++
 .../karaf/shell/console/OsgiCommandSupport.java | 103 ++++
 .../karaf/shell/console/SessionProperties.java  |  28 +
 .../apache/karaf/shell/console/SubShell.java    |  38 ++
 .../karaf/shell/console/SubShellAction.java     |  39 ++
 .../console/commands/AnnotatedSubShell.java     |  43 ++
 .../shell/console/commands/BasicSubShell.java   |  51 ++
 .../console/commands/BlueprintCommand.java      | 110 ++++
 .../shell/console/commands/GenericType.java     | 195 +++++++
 .../console/commands/NamespaceHandler.java      | 523 +++++++++++++++++
 .../commands/NumberToStringConverter.java       |  36 ++
 .../console/completer/AggregateCompleter.java   |  91 +++
 .../console/completer/ArgumentCompleter.java    | 567 +++++++++++++++++++
 .../completer/CommandNamesCompleter.java        | 103 ++++
 .../console/completer/CommandsCompleter.java    | 321 +++++++++++
 .../shell/console/completer/FileCompleter.java  | 147 +++++
 .../shell/console/completer/NullCompleter.java  |  32 ++
 .../console/completer/OldArgumentCompleter.java | 448 +++++++++++++++
 .../karaf/shell/console/completer/Parser.java   | 396 +++++++++++++
 .../console/completer/StringsCompleter.java     | 103 ++++
 .../org/apache/karaf/shell/inject/Destroy.java  |  29 +
 .../org/apache/karaf/shell/inject/Init.java     |  29 +
 .../apache/karaf/shell/inject/Reference.java    |  29 +
 .../org/apache/karaf/shell/inject/Service.java  |  29 +
 .../blueprint/shell-namespacehandler.xml        |  42 ++
 .../src/main/resources/OSGI-INF/bundle.info     |  14 +
 .../console/commands/karaf-shell-1.0.0.xsd      | 217 +++++++
 .../console/commands/karaf-shell-1.1.0.xsd      | 209 +++++++
 .../console/commands/karaf-shell-1.2.0.xsd      | 225 ++++++++
 .../karaf/shell/commands/basic/Context.java     |  67 +++
 .../shell/commands/basic/SimpleSubShell.java    |  39 ++
 .../shell/commands/basic/TestCommands.java      | 184 ++++++
 .../shell/commands/meta/TestFormatting.java     |  35 ++
 .../completer/ArgumentCompleterTest.java        | 145 +++++
 .../console/completer/BooleanCompleterTest.java |  62 ++
 .../console/completer/CompleterTestSupport.java |  34 ++
 .../console/completer/CompleterValuesTest.java  |  74 +++
 .../shell/console/completer/CompletionTest.java | 172 ++++++
 .../console/completer/DummyCommandSession.java  |  48 ++
 .../console/completer/FileCompleterTest.java    |  56 ++
 shell/commands/pom.xml                          |   2 +-
 shell/console/pom.xml                           |  15 +-
 .../org/apache/felix/gogo/commands/Action.java  |  29 -
 .../apache/felix/gogo/commands/Argument.java    |  49 --
 .../org/apache/felix/gogo/commands/Command.java |  53 --
 .../felix/gogo/commands/CommandException.java   |  63 ---
 .../felix/gogo/commands/CommandWithAction.java  |  32 --
 .../felix/gogo/commands/CompleterValues.java    |  37 --
 .../org/apache/felix/gogo/commands/Option.java  |  48 --
 .../apache/felix/gogo/commands/SubShell.java    |  46 --
 .../gogo/commands/basic/AbstractCommand.java    |  64 ---
 .../gogo/commands/basic/ActionPreparator.java   |  31 -
 .../commands/basic/DefaultActionPreparator.java | 566 ------------------
 .../commands/converter/DefaultConverter.java    | 402 -------------
 .../gogo/commands/converter/GenericType.java    | 196 -------
 .../gogo/commands/converter/ReifiedType.java    | 118 ----
 .../org/apache/karaf/shell/commands/Action.java |  40 --
 .../apache/karaf/shell/commands/Argument.java   |  48 --
 .../apache/karaf/shell/commands/Command.java    |  53 --
 .../karaf/shell/commands/CommandException.java  |  64 ---
 .../karaf/shell/commands/CommandWithAction.java |  31 -
 .../apache/karaf/shell/commands/Completer.java  |  34 --
 .../karaf/shell/commands/CompleterValues.java   |  37 --
 .../apache/karaf/shell/commands/HelpOption.java |  54 --
 .../karaf/shell/commands/InfoProvider.java      |  30 -
 .../org/apache/karaf/shell/commands/Option.java |  46 --
 .../karaf/shell/commands/ansi/SimpleAnsi.java   |  30 -
 .../shell/commands/basic/AbstractCommand.java   |  62 --
 .../shell/commands/basic/ActionPreparator.java  |  42 --
 .../commands/basic/DefaultActionPreparator.java | 223 --------
 .../shell/commands/basic/SimpleCommand.java     |  82 ---
 .../commands/converter/DefaultConverter.java    | 401 -------------
 .../shell/commands/converter/GenericType.java   | 195 -------
 .../shell/commands/converter/ReifiedType.java   | 116 ----
 .../shell/commands/meta/ActionMetaData.java     | 296 ----------
 .../commands/meta/ActionMetaDataFactory.java    | 251 --------
 .../karaf/shell/console/AbstractAction.java     |  50 --
 .../shell/console/BlueprintContainerAware.java  |  27 -
 .../karaf/shell/console/BundleContextAware.java |  27 -
 .../shell/console/CloseShellException.java      |  27 -
 .../shell/console/CommandSessionHolder.java     |  38 --
 .../shell/console/CompletableFunction.java      |  31 -
 .../apache/karaf/shell/console/Completer.java   |  27 -
 .../org/apache/karaf/shell/console/Console.java |  29 -
 .../apache/karaf/shell/console/ExitAction.java  |  46 --
 .../karaf/shell/console/HelpProvider.java       |  26 -
 .../karaf/shell/console/MultiException.java     |  95 ----
 .../apache/karaf/shell/console/NameScoping.java |  79 ---
 .../karaf/shell/console/OsgiCommandSupport.java | 103 ----
 .../karaf/shell/console/SessionProperties.java  |  28 -
 .../apache/karaf/shell/console/SubShell.java    |  38 --
 .../karaf/shell/console/SubShellAction.java     |  39 --
 .../console/commands/AnnotatedSubShell.java     |  43 --
 .../shell/console/commands/BasicSubShell.java   |  51 --
 .../console/commands/BlueprintCommand.java      | 110 ----
 .../shell/console/commands/GenericType.java     | 195 -------
 .../console/commands/NamespaceHandler.java      | 523 -----------------
 .../commands/NumberToStringConverter.java       |  36 --
 .../console/completer/AggregateCompleter.java   |  91 ---
 .../console/completer/ArgumentCompleter.java    | 567 -------------------
 .../completer/CommandNamesCompleter.java        | 103 ----
 .../console/completer/CommandsCompleter.java    | 321 -----------
 .../shell/console/completer/FileCompleter.java  | 147 -----
 .../shell/console/completer/NullCompleter.java  |  32 --
 .../console/completer/OldArgumentCompleter.java | 448 ---------------
 .../karaf/shell/console/completer/Parser.java   | 396 -------------
 .../console/completer/StringsCompleter.java     | 103 ----
 .../org/apache/karaf/shell/inject/Destroy.java  |  29 -
 .../org/apache/karaf/shell/inject/Init.java     |  29 -
 .../apache/karaf/shell/inject/Reference.java    |  29 -
 .../org/apache/karaf/shell/inject/Service.java  |  29 -
 .../karaf/shell/commands/basic/Context.java     |  67 ---
 .../shell/commands/basic/SimpleSubShell.java    |  39 --
 .../shell/commands/basic/TestCommands.java      | 184 ------
 .../shell/commands/meta/TestFormatting.java     |  35 --
 .../completer/ArgumentCompleterTest.java        | 145 -----
 .../console/completer/BooleanCompleterTest.java |  62 --
 .../console/completer/CompleterTestSupport.java |  34 --
 .../console/completer/CompleterValuesTest.java  |  74 ---
 .../shell/console/completer/CompletionTest.java | 172 ------
 .../console/completer/DummyCommandSession.java  |  48 --
 .../console/completer/FileCompleterTest.java    |  56 --
 shell/help/pom.xml                              |   7 +-
 .../help/impl/CommandListHelpProvider.java      |  15 +-
 shell/pom.xml                                   |   1 +
 176 files changed, 9772 insertions(+), 8802 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/karaf/blob/1ee78df9/jaas/modules/pom.xml
----------------------------------------------------------------------
diff --git a/jaas/modules/pom.xml b/jaas/modules/pom.xml
index a74adc2..908674e 100644
--- a/jaas/modules/pom.xml
+++ b/jaas/modules/pom.xml
@@ -57,6 +57,7 @@
         <dependency>
             <groupId>org.apache.felix</groupId>
             <artifactId>org.apache.felix.fileinstall</artifactId>
+            <scope>provided</scope>
         </dependency>
 
         <dependency>

http://git-wip-us.apache.org/repos/asf/karaf/blob/1ee78df9/pom.xml
----------------------------------------------------------------------
diff --git a/pom.xml b/pom.xml
index 1b5d4e9..d80d27c 100644
--- a/pom.xml
+++ b/pom.xml
@@ -636,6 +636,11 @@
 
             <dependency>
                 <groupId>org.apache.karaf.shell</groupId>
+                <artifactId>org.apache.karaf.shell.command.api</artifactId>
+                <version>${project.version}</version>
+            </dependency>
+            <dependency>
+                <groupId>org.apache.karaf.shell</groupId>
                 <artifactId>org.apache.karaf.shell.console</artifactId>
                 <version>${project.version}</version>
             </dependency>

http://git-wip-us.apache.org/repos/asf/karaf/blob/1ee78df9/shell/command-api/NOTICE
----------------------------------------------------------------------
diff --git a/shell/command-api/NOTICE b/shell/command-api/NOTICE
new file mode 100644
index 0000000..de8d101
--- /dev/null
+++ b/shell/command-api/NOTICE
@@ -0,0 +1,71 @@
+Apache Karaf
+Copyright 2010-2013 The Apache Software Foundation
+
+
+I. Included Software
+
+This product includes software developed at
+The Apache Software Foundation (http://www.apache.org/).
+Licensed under the Apache License 2.0.
+
+This product uses software developed at
+The OSGi Alliance (http://www.osgi.org/).
+Copyright (c) OSGi Alliance (2000, 2010).
+Licensed under the Apache License 2.0.
+
+This product includes software developed at
+OW2 (http://www.ow2.org/).
+Licensed under the BSD License.
+
+This product includes software developed at
+OPS4J (http://www.ops4j.org/).
+Licensed under the Apache License 2.0.
+
+This product includes software developed at
+Eclipse Foundation (http://www.eclipse.org/).
+Licensed under the EPL.
+
+This product includes software written by
+Antony Lesuisse.
+Licensed under Public Domain.
+
+
+II. Used Software
+
+This product uses software developed at
+FUSE Source (http://www.fusesource.org/).
+Licensed under the Apache License 2.0.
+
+This product uses software developed at
+AOP Alliance (http://aopalliance.sourceforge.net/).
+Licensed under the Public Domain.
+
+This product uses software developed at
+Tanuki Software (http://www.tanukisoftware.com/).
+Licensed under the Apache License 2.0.
+
+This product uses software developed at
+Jasypt (http://jasypt.sourceforge.net/).
+Licensed under the Apache License 2.0.
+
+This product uses software developed at
+JLine (http://jline.sourceforge.net).
+Licensed under the BSD License.
+
+This product uses software developed at
+SLF4J (http://www.slf4j.org/).
+Licensed under the MIT License.
+
+This product uses software developed at
+SpringSource (http://www.springsource.org/).
+Licensed under the Apache License 2.0.
+
+This product includes software from http://www.json.org.
+Copyright (c) 2002 JSON.org
+
+
+III. License Summary
+- Apache License 2.0
+- BSD License
+- EPL License
+- MIT License

http://git-wip-us.apache.org/repos/asf/karaf/blob/1ee78df9/shell/command-api/pom.xml
----------------------------------------------------------------------
diff --git a/shell/command-api/pom.xml b/shell/command-api/pom.xml
new file mode 100644
index 0000000..7e453f8
--- /dev/null
+++ b/shell/command-api/pom.xml
@@ -0,0 +1,131 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+
+    <!--
+
+        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.
+    -->
+
+    <modelVersion>4.0.0</modelVersion>
+
+    <parent>
+        <groupId>org.apache.karaf.shell</groupId>
+        <artifactId>shell</artifactId>
+        <version>3.1.0-SNAPSHOT</version>
+        <relativePath>../pom.xml</relativePath>
+    </parent>
+
+    <artifactId>org.apache.karaf.shell.command.api</artifactId>
+    <packaging>bundle</packaging>
+    <name>Apache Karaf :: Shell :: Command :: API</name>
+    <description>Action and Command API, support classes for implementing commands</description>
+
+    <properties>
+        <appendedResourcesDirectory>${basedir}/../../etc/appended-resources</appendedResourcesDirectory>
+    </properties>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>slf4j-api</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.osgi</groupId>
+            <artifactId>org.osgi.core</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.osgi</groupId>
+            <artifactId>org.osgi.compendium</artifactId>
+            <scope>provided</scope>
+        </dependency>
+
+        <dependency>
+            <groupId>org.apache.aries.blueprint</groupId>
+            <artifactId>org.apache.aries.blueprint.api</artifactId>
+            <scope>provided</scope>
+            <optional>true</optional>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.aries.blueprint</groupId>
+            <artifactId>org.apache.aries.blueprint.core</artifactId>
+            <scope>provided</scope>
+            <optional>true</optional>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.felix</groupId>
+            <artifactId>org.apache.felix.gogo.runtime</artifactId>
+            <scope>provided</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <resources>
+            <resource>
+                <directory>${project.basedir}/src/main/resources</directory>
+                <includes>
+                    <include>**/*</include>
+                </includes>
+            </resource>
+            <resource>
+                <directory>${project.basedir}/src/main/resources</directory>
+                <filtering>true</filtering>
+                <includes>
+                    <include>**/*.info</include>
+                </includes>
+            </resource>
+            <resource>
+                <directory>${project.basedir}/src/main/filtered-resources</directory>
+                <filtering>true</filtering>
+                <includes>
+                    <include>**/*</include>
+                </includes>
+            </resource>
+        </resources>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.felix</groupId>
+                <artifactId>maven-bundle-plugin</artifactId>
+                <configuration>
+                    <instructions>
+                        <Import-Package>
+                            *
+                        </Import-Package>
+                        <Export-Package>
+                     	    org.apache.karaf.shell.commands;version=${project.version},
+                            org.apache.karaf.shell.commands;version=2.3.0,
+                            org.apache.karaf.shell.commands.basic;version=${project.version},
+                            org.apache.karaf.shell.commands.basic;version=2.3.0,
+                            org.apache.karaf.shell.commands.converter;version=${project.version},
+                            org.apache.karaf.shell.commands.converter;version=2.3.0,
+                            org.apache.karaf.shell.commands.meta;version=${project.version},
+                            org.apache.karaf.shell.commands.meta;version=2.3.0,
+                            org.apache.karaf.shell.console;version=${project.version},
+                            org.apache.karaf.shell.console;version=2.3.0,
+                            org.apache.karaf.shell.console.commands;version=${project.version},
+                            org.apache.karaf.shell.console.commands;version=2.3.0,
+                            org.apache.karaf.shell.console.completer;version=${project.version},
+                            org.apache.karaf.shell.console.completer;version=2.3.0,
+                            org.apache.karaf.shell.inject;version=${project.version},
+                        </Export-Package>
+                    </instructions>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>

http://git-wip-us.apache.org/repos/asf/karaf/blob/1ee78df9/shell/command-api/src/main/filtered-resources/org/apache/karaf/shell/console/branding-ssh.properties
----------------------------------------------------------------------
diff --git a/shell/command-api/src/main/filtered-resources/org/apache/karaf/shell/console/branding-ssh.properties b/shell/command-api/src/main/filtered-resources/org/apache/karaf/shell/console/branding-ssh.properties
new file mode 100644
index 0000000..978484b
--- /dev/null
+++ b/shell/command-api/src/main/filtered-resources/org/apache/karaf/shell/console/branding-ssh.properties
@@ -0,0 +1,34 @@
+##
+## 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.
+##
+
+welcome = \
+\u001B[36m        __ __                  ____      \u001B[0m\r\n\
+\u001B[36m       / //_/____ __________ _/ __/      \u001B[0m\r\n\
+\u001B[36m      / ,<  / __ `/ ___/ __ `/ /_        \u001B[0m\r\n\
+\u001B[36m     / /| |/ /_/ / /  / /_/ / __/        \u001B[0m\r\n\
+\u001B[36m    /_/ |_|\\__,_/_/   \\__,_/_/         \u001B[0m\r\n\
+\r\n\
+\u001B[1m  Apache Karaf\u001B[0m (${project.version})\r\n\
+\r\n\
+Hit '\u001B[1m<tab>\u001B[0m' for a list of available commands\r\n\
+   and '\u001B[1m[cmd] --help\u001B[0m' for help on a specific command.\r\n\
+Hit '\u001B[1msystem:shutdown\u001B[0m' to shutdown Karaf.\r\n\
+Hit '\u001B[1m<ctrl-d>\u001B[0m' or type '\u001B[1mlogout\u001B[0m' to disconnect shell from current session.\r\n
+
+

http://git-wip-us.apache.org/repos/asf/karaf/blob/1ee78df9/shell/command-api/src/main/filtered-resources/org/apache/karaf/shell/console/branding.properties
----------------------------------------------------------------------
diff --git a/shell/command-api/src/main/filtered-resources/org/apache/karaf/shell/console/branding.properties b/shell/command-api/src/main/filtered-resources/org/apache/karaf/shell/console/branding.properties
new file mode 100644
index 0000000..3fbcb0a
--- /dev/null
+++ b/shell/command-api/src/main/filtered-resources/org/apache/karaf/shell/console/branding.properties
@@ -0,0 +1,33 @@
+##
+## 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.
+##
+
+welcome = \
+\u001B[36m        __ __                  ____      \u001B[0m\r\n\
+\u001B[36m       / //_/____ __________ _/ __/      \u001B[0m\r\n\
+\u001B[36m      / ,<  / __ `/ ___/ __ `/ /_        \u001B[0m\r\n\
+\u001B[36m     / /| |/ /_/ / /  / /_/ / __/        \u001B[0m\r\n\
+\u001B[36m    /_/ |_|\\__,_/_/   \\__,_/_/         \u001B[0m\r\n\
+\r\n\
+\u001B[1m  Apache Karaf\u001B[0m (${project.version})\r\n\
+\r\n\
+Hit '\u001B[1m<tab>\u001B[0m' for a list of available commands\r\n\
+   and '\u001B[1m[cmd] --help\u001B[0m' for help on a specific command.\r\n\
+Hit '\u001B[1m<ctrl-d>\u001B[0m' or type '\u001B[1msystem:shutdown\u001B[0m' or '\u001B[1mlogout\u001B[0m' to shutdown Karaf.\r\n
+
+

http://git-wip-us.apache.org/repos/asf/karaf/blob/1ee78df9/shell/command-api/src/main/java/org/apache/felix/gogo/commands/Action.java
----------------------------------------------------------------------
diff --git a/shell/command-api/src/main/java/org/apache/felix/gogo/commands/Action.java b/shell/command-api/src/main/java/org/apache/felix/gogo/commands/Action.java
new file mode 100644
index 0000000..0727f24
--- /dev/null
+++ b/shell/command-api/src/main/java/org/apache/felix/gogo/commands/Action.java
@@ -0,0 +1,29 @@
+/*
+ * 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.apache.felix.service.command.CommandSession;
+
+@Deprecated
+public interface Action {
+
+    Object execute(CommandSession session) throws Exception;
+
+}
+

http://git-wip-us.apache.org/repos/asf/karaf/blob/1ee78df9/shell/command-api/src/main/java/org/apache/felix/gogo/commands/Argument.java
----------------------------------------------------------------------
diff --git a/shell/command-api/src/main/java/org/apache/felix/gogo/commands/Argument.java b/shell/command-api/src/main/java/org/apache/felix/gogo/commands/Argument.java
new file mode 100644
index 0000000..33c15bf
--- /dev/null
+++ b/shell/command-api/src/main/java/org/apache/felix/gogo/commands/Argument.java
@@ -0,0 +1,49 @@
+/*
+ * 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 java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import java.lang.annotation.ElementType;
+
+/**
+ * Represents a positional argument on a command line (as opposed to an optional named {@link Option}
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.FIELD})
+@Deprecated
+public @interface Argument {
+
+    public static final String DEFAULT_STRING = "DEFAULT";
+
+    String DEFAULT = "##default";
+
+    String name() default DEFAULT;
+
+    String description() default "";
+
+    boolean required() default false;
+
+    int index() default 0;
+
+    boolean multiValued() default false;
+
+    String valueToShowInHelp() default DEFAULT_STRING;
+}

http://git-wip-us.apache.org/repos/asf/karaf/blob/1ee78df9/shell/command-api/src/main/java/org/apache/felix/gogo/commands/Command.java
----------------------------------------------------------------------
diff --git a/shell/command-api/src/main/java/org/apache/felix/gogo/commands/Command.java b/shell/command-api/src/main/java/org/apache/felix/gogo/commands/Command.java
new file mode 100644
index 0000000..10c494a
--- /dev/null
+++ b/shell/command-api/src/main/java/org/apache/felix/gogo/commands/Command.java
@@ -0,0 +1,53 @@
+/*
+ * 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 java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import java.lang.annotation.ElementType;
+
+/**
+ * Used to denote a class represents a command which is executable within a shell/scope or as a
+ * command line process.
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.TYPE})
+@Deprecated
+public @interface Command {
+    /**
+     * Returns the scope or sub shell of the command
+     */
+    String scope();
+
+    /**
+     * REturns the name of the command if used inside a shell
+     */
+    String name();
+
+    /**
+     * Returns the description of the command which is used to generate command line help
+     */
+    String description() default "";
+
+    /**
+     * Returns a detailed description of the command
+     */
+    String detailedDescription() default "";
+}

http://git-wip-us.apache.org/repos/asf/karaf/blob/1ee78df9/shell/command-api/src/main/java/org/apache/felix/gogo/commands/CommandException.java
----------------------------------------------------------------------
diff --git a/shell/command-api/src/main/java/org/apache/felix/gogo/commands/CommandException.java b/shell/command-api/src/main/java/org/apache/felix/gogo/commands/CommandException.java
new file mode 100644
index 0000000..3c02d63
--- /dev/null
+++ b/shell/command-api/src/main/java/org/apache/felix/gogo/commands/CommandException.java
@@ -0,0 +1,63 @@
+/*
+ * 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.apache.karaf.shell.commands.ansi.SimpleAnsi;
+
+/**
+ * Base class for exceptions thrown when executing commands.
+ */
+@Deprecated
+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
+                : SimpleAnsi.COLOR_RED + "Error executing command: " 
+                 + getMessage() != null ? getMessage() : getClass().getName()
+                 + SimpleAnsi.COLOR_DEFAULT;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/karaf/blob/1ee78df9/shell/command-api/src/main/java/org/apache/felix/gogo/commands/CommandWithAction.java
----------------------------------------------------------------------
diff --git a/shell/command-api/src/main/java/org/apache/felix/gogo/commands/CommandWithAction.java b/shell/command-api/src/main/java/org/apache/felix/gogo/commands/CommandWithAction.java
new file mode 100644
index 0000000..e09cf64
--- /dev/null
+++ b/shell/command-api/src/main/java/org/apache/felix/gogo/commands/CommandWithAction.java
@@ -0,0 +1,32 @@
+/*
+ * 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.apache.felix.service.command.Function;
+
+@Deprecated
+public interface CommandWithAction extends Function {
+
+    Class<? extends Action> getActionClass();
+
+    Action createNewAction();
+
+    void releaseAction(Action action) throws Exception;
+
+}

http://git-wip-us.apache.org/repos/asf/karaf/blob/1ee78df9/shell/command-api/src/main/java/org/apache/felix/gogo/commands/CompleterValues.java
----------------------------------------------------------------------
diff --git a/shell/command-api/src/main/java/org/apache/felix/gogo/commands/CompleterValues.java b/shell/command-api/src/main/java/org/apache/felix/gogo/commands/CompleterValues.java
new file mode 100644
index 0000000..99129e5
--- /dev/null
+++ b/shell/command-api/src/main/java/org/apache/felix/gogo/commands/CompleterValues.java
@@ -0,0 +1,37 @@
+/*
+ * 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 java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Represents a method which can return a List or Array of values used for a
+ * {@link org.apache.karaf.shell.console.Completer}
+ * which is associated with the index of an
+ * {@link org.apache.karaf.shell.commands.Argument}
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.METHOD, ElementType.FIELD})
+@Deprecated
+public @interface CompleterValues {
+    int index() default 0;
+}

http://git-wip-us.apache.org/repos/asf/karaf/blob/1ee78df9/shell/command-api/src/main/java/org/apache/felix/gogo/commands/Option.java
----------------------------------------------------------------------
diff --git a/shell/command-api/src/main/java/org/apache/felix/gogo/commands/Option.java b/shell/command-api/src/main/java/org/apache/felix/gogo/commands/Option.java
new file mode 100644
index 0000000..1ce6f75
--- /dev/null
+++ b/shell/command-api/src/main/java/org/apache/felix/gogo/commands/Option.java
@@ -0,0 +1,48 @@
+/*
+ * 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 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 "--"
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.FIELD})
+@Deprecated
+public @interface Option {
+
+    public static final String DEFAULT_STRING = "DEFAULT";
+
+    String name();
+
+    String[] aliases() default {};
+
+    String description() default "";
+
+    boolean required() default false;
+
+    boolean multiValued() default false;
+
+    String valueToShowInHelp() default DEFAULT_STRING;
+
+}

http://git-wip-us.apache.org/repos/asf/karaf/blob/1ee78df9/shell/command-api/src/main/java/org/apache/felix/gogo/commands/SubShell.java
----------------------------------------------------------------------
diff --git a/shell/command-api/src/main/java/org/apache/felix/gogo/commands/SubShell.java b/shell/command-api/src/main/java/org/apache/felix/gogo/commands/SubShell.java
new file mode 100644
index 0000000..0206964
--- /dev/null
+++ b/shell/command-api/src/main/java/org/apache/felix/gogo/commands/SubShell.java
@@ -0,0 +1,46 @@
+/*
+ * 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 java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.TYPE})
+@Deprecated
+public @interface SubShell {
+
+    /**
+     * Returns the name of the command if used inside a shell
+     */
+    String name();
+
+    /**
+     * Returns the description of the command which is used to generate command line help
+     */
+    String description() default "";
+
+    /**
+     * Returns a detailed description of the command
+     */
+    String detailedDescription() default "";
+
+}

http://git-wip-us.apache.org/repos/asf/karaf/blob/1ee78df9/shell/command-api/src/main/java/org/apache/felix/gogo/commands/basic/AbstractCommand.java
----------------------------------------------------------------------
diff --git a/shell/command-api/src/main/java/org/apache/felix/gogo/commands/basic/AbstractCommand.java b/shell/command-api/src/main/java/org/apache/felix/gogo/commands/basic/AbstractCommand.java
new file mode 100644
index 0000000..809ed67
--- /dev/null
+++ b/shell/command-api/src/main/java/org/apache/felix/gogo/commands/basic/AbstractCommand.java
@@ -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.basic;
+
+import java.util.List;
+
+import org.apache.felix.gogo.commands.Action;
+import org.apache.felix.gogo.commands.CommandWithAction;
+import org.apache.felix.service.command.CommandSession;
+import org.apache.felix.service.command.Function;
+
+@Deprecated
+public abstract class AbstractCommand implements Function, CommandWithAction {
+
+    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();
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/karaf/blob/1ee78df9/shell/command-api/src/main/java/org/apache/felix/gogo/commands/basic/ActionPreparator.java
----------------------------------------------------------------------
diff --git a/shell/command-api/src/main/java/org/apache/felix/gogo/commands/basic/ActionPreparator.java b/shell/command-api/src/main/java/org/apache/felix/gogo/commands/basic/ActionPreparator.java
new file mode 100644
index 0000000..65fe24e
--- /dev/null
+++ b/shell/command-api/src/main/java/org/apache/felix/gogo/commands/basic/ActionPreparator.java
@@ -0,0 +1,31 @@
+/*
+ * 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.basic;
+
+import java.util.List;
+
+import org.apache.felix.gogo.commands.Action;
+import org.apache.felix.service.command.CommandSession;
+
+@Deprecated
+public interface ActionPreparator {
+
+    boolean prepare(Action action, CommandSession session, List<Object> arguments) throws Exception;
+
+}

http://git-wip-us.apache.org/repos/asf/karaf/blob/1ee78df9/shell/command-api/src/main/java/org/apache/felix/gogo/commands/basic/DefaultActionPreparator.java
----------------------------------------------------------------------
diff --git a/shell/command-api/src/main/java/org/apache/felix/gogo/commands/basic/DefaultActionPreparator.java b/shell/command-api/src/main/java/org/apache/felix/gogo/commands/basic/DefaultActionPreparator.java
new file mode 100644
index 0000000..6b7865e
--- /dev/null
+++ b/shell/command-api/src/main/java/org/apache/felix/gogo/commands/basic/DefaultActionPreparator.java
@@ -0,0 +1,566 @@
+/*
+ * 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.basic;
+
+import static org.apache.karaf.shell.commands.ansi.SimpleAnsi.COLOR_DEFAULT;
+import static org.apache.karaf.shell.commands.ansi.SimpleAnsi.COLOR_RED;
+import static org.apache.karaf.shell.commands.ansi.SimpleAnsi.INTENSITY_BOLD;
+import static org.apache.karaf.shell.commands.ansi.SimpleAnsi.INTENSITY_NORMAL;
+
+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 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;
+
+@Deprecated
+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(COLOR_RED
+                                                   + "Error executing command "
+                                                   + command.scope() + ":" 
+                                                   + INTENSITY_BOLD + command.name() + INTENSITY_NORMAL
+                                                   + " undefined option "
+                                                   + INTENSITY_BOLD + param + INTENSITY_NORMAL
+                                                   + COLOR_DEFAULT,
+                                                   "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(COLOR_RED
+                                                   + "Error executing command "
+                                                   + command.scope() + ":" 
+                                                   + INTENSITY_BOLD + command.name() + INTENSITY_NORMAL
+                                                   + " missing value for option "
+                                                   + INTENSITY_BOLD + param + INTENSITY_NORMAL
+                                                   + COLOR_DEFAULT,
+                                                   "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(COLOR_RED
+                                                   + "Error executing command "
+                                                   + command.scope() + ":" 
+                                                   + INTENSITY_BOLD + command.name() + INTENSITY_NORMAL
+                                                   + ": too many arguments specified"
+                                                   + INTENSITY_BOLD + param + INTENSITY_NORMAL
+                                                   + COLOR_DEFAULT,
+                                                   "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(COLOR_RED
+                                               + "Error executing command "
+                                               + command.scope() + ":" 
+                                               + INTENSITY_BOLD + command.name() + INTENSITY_NORMAL
+                                               + ": option "
+                                               + INTENSITY_BOLD + option.name() + INTENSITY_NORMAL
+                                               + " is required"
+                                               + COLOR_DEFAULT,
+                            "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(COLOR_RED
+                                               + "Error executing command "
+                                               + command.scope() + ":" 
+                                               + INTENSITY_BOLD + command.name() + INTENSITY_NORMAL
+                                               + ": argument "
+                                               + INTENSITY_BOLD + argument.name() + INTENSITY_NORMAL
+                                               + " is required"
+                                               + COLOR_DEFAULT,
+                                               "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(COLOR_RED
+                                               + "Error executing command "
+                                               + command.scope() + ":" 
+                                               + INTENSITY_BOLD + command.name() + INTENSITY_NORMAL
+                                               + ": unable to convert option "
+                                               + INTENSITY_BOLD + entry.getKey().name() + INTENSITY_NORMAL
+                                               + " with value '" + entry.getValue() + "' to type "
+                                               + new GenericType(field.getGenericType()).toString()
+                                               + COLOR_DEFAULT,
+                            "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(COLOR_RED
+                                               + "Error executing command "
+                                               + command.scope() + ":" 
+                                               + INTENSITY_BOLD + command.name() + INTENSITY_NORMAL
+                                               + ": unable to convert argument "
+                                               + INTENSITY_BOLD + entry.getKey().name() + INTENSITY_NORMAL
+                                               + " with value '" + entry.getValue() + "' to type "
+                                               + new GenericType(field.getGenericType()).toString()
+                                               + COLOR_DEFAULT,
+                            "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) {
+            
+            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(INTENSITY_BOLD + "DESCRIPTION" + INTENSITY_NORMAL);
+                out.print("        ");
+                if (command.name() != null) {
+                    if (globalScope) {
+                        out.println(INTENSITY_BOLD + command.name() + INTENSITY_NORMAL);
+                    } else {
+                        out.println(command.scope() + ":" + INTENSITY_BOLD + command.name() + INTENSITY_NORMAL);
+                    }
+                    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()));
+                    }
+                }
+            }
+            int width = getWidth(session);
+            out.println(INTENSITY_BOLD + "SYNTAX" + INTENSITY_NORMAL);
+            out.print("        ");
+            out.println(syntax.toString());
+            out.println();
+            if (arguments.size() > 0) {
+                out.println(INTENSITY_BOLD + "ARGUMENTS" + INTENSITY_NORMAL);
+                for (Argument argument : arguments) {
+                    out.print("        ");
+                    out.println(INTENSITY_BOLD + argument.name() + INTENSITY_NORMAL);
+                    
+                    printFormatted("                ", argument.description(), width, 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(INTENSITY_BOLD + "OPTIONS" + INTENSITY_NORMAL);
+                for (Option option : options) {
+                    String opt = option.name();
+                    for (String alias : option.aliases()) {
+                        opt += ", " + alias;
+                    }
+                    out.print("        ");
+                    out.println(INTENSITY_BOLD + opt + INTENSITY_NORMAL);
+                    printFormatted("                ", option.description(), width, 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(INTENSITY_BOLD + "DETAILS" + INTENSITY_NORMAL);
+                String desc = loadDescription(action.getClass(), command.detailedDescription());
+                printFormatted("        ", desc, width, 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);
+    }
+
+    private int getWidth(CommandSession session) {
+        Object cols = session.get("COLUMNS");
+        return  (cols != null && cols instanceof Integer) ? (Integer)cols : 80;
+    }
+}


[02/11] KARAF-2772 Extracting command-api

Posted by cs...@apache.org.
http://git-wip-us.apache.org/repos/asf/karaf/blob/1ee78df9/shell/console/src/main/java/org/apache/karaf/shell/console/completer/CommandNamesCompleter.java
----------------------------------------------------------------------
diff --git a/shell/console/src/main/java/org/apache/karaf/shell/console/completer/CommandNamesCompleter.java b/shell/console/src/main/java/org/apache/karaf/shell/console/completer/CommandNamesCompleter.java
deleted file mode 100644
index 23f5447..0000000
--- a/shell/console/src/main/java/org/apache/karaf/shell/console/completer/CommandNamesCompleter.java
+++ /dev/null
@@ -1,103 +0,0 @@
-/*
- * 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.console.completer;
-
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-import java.util.concurrent.CopyOnWriteArraySet;
-
-import org.apache.felix.service.command.CommandProcessor;
-import org.apache.felix.service.command.CommandSession;
-import org.apache.karaf.shell.console.CommandSessionHolder;
-import org.apache.karaf.shell.console.Completer;
-import org.osgi.framework.BundleContext;
-import org.osgi.framework.FrameworkUtil;
-import org.osgi.framework.ServiceEvent;
-import org.osgi.framework.ServiceListener;
-
-/**
- * Completes command names
- */
-public class CommandNamesCompleter implements Completer {
-
-    public static final String COMMANDS = ".commands";
-
-    private CommandSession session;
-    private final Set<String> commands = new CopyOnWriteArraySet<String>();
-
-    public CommandNamesCompleter() {
-        this(CommandSessionHolder.getSession());
-    }
-
-    public CommandNamesCompleter(CommandSession session) {
-        this.session = session;
-
-        try {
-            new CommandTracker();
-        } catch (Throwable t) {
-            // Ignore in case we're not in OSGi
-        }
-    }
-
-
-    public int complete(String buffer, int cursor, List<String> candidates) {
-        if (session == null) {
-            session = CommandSessionHolder.getSession();
-        }
-        checkData();
-        int res = new StringsCompleter(commands).complete(buffer, cursor, candidates);
-        Collections.sort(candidates);
-        return res;
-    }
-
-    @SuppressWarnings("unchecked")
-    protected void checkData() {
-        if (commands.isEmpty()) {
-            Set<String> names = new HashSet<String>((Set<String>) session.get(COMMANDS));
-            for (String name : names) {
-                commands.add(name);
-                if (name.indexOf(':') > 0) {
-                    commands.add(name.substring(0, name.indexOf(':')));
-                }
-            }
-        }
-    }
-
-    private class CommandTracker {
-        public CommandTracker() throws Exception {
-            BundleContext context = FrameworkUtil.getBundle(getClass()).getBundleContext();
-            if (context == null) {
-                throw new IllegalStateException("Bundle is stopped");
-            }
-            ServiceListener listener = new ServiceListener() {
-                public void serviceChanged(ServiceEvent event) {
-                    commands.clear();
-                }
-            };
-            context.addServiceListener(listener,
-                    String.format("(&(%s=*)(%s=*))",
-                            CommandProcessor.COMMAND_SCOPE,
-                            CommandProcessor.COMMAND_FUNCTION));
-        }
-    }
-
-}
-

http://git-wip-us.apache.org/repos/asf/karaf/blob/1ee78df9/shell/console/src/main/java/org/apache/karaf/shell/console/completer/CommandsCompleter.java
----------------------------------------------------------------------
diff --git a/shell/console/src/main/java/org/apache/karaf/shell/console/completer/CommandsCompleter.java b/shell/console/src/main/java/org/apache/karaf/shell/console/completer/CommandsCompleter.java
deleted file mode 100644
index ba95b0c..0000000
--- a/shell/console/src/main/java/org/apache/karaf/shell/console/completer/CommandsCompleter.java
+++ /dev/null
@@ -1,321 +0,0 @@
-/*
- * 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.console.completer;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.TreeMap;
-
-import org.apache.felix.gogo.runtime.CommandProxy;
-import org.apache.felix.service.command.CommandProcessor;
-import org.apache.felix.service.command.CommandSession;
-import org.apache.felix.service.command.Function;
-import org.apache.karaf.shell.commands.CommandWithAction;
-import org.apache.karaf.shell.console.CommandSessionHolder;
-import org.apache.karaf.shell.console.Completer;
-import org.apache.karaf.shell.console.SessionProperties;
-import org.osgi.framework.BundleContext;
-import org.osgi.framework.FrameworkUtil;
-import org.osgi.framework.ServiceEvent;
-import org.osgi.framework.ServiceListener;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-/**
- * Like the {@link org.apache.karaf.shell.console.completer.CommandsCompleter} but does not use OSGi but is
- * instead used from the non-OSGi {@link org.apache.karaf.shell.console.impl.Main}
- */
-public class CommandsCompleter implements Completer {
-
-    public static final String COMMANDS = ".commands";
-
-    private static final Logger LOGGER = LoggerFactory.getLogger(CommandsCompleter.class);
-
-    private CommandSession session;
-    private final Map<String, Completer> globalCompleters = new HashMap<String, Completer>();
-    private final Map<String, Completer> localCompleters = new HashMap<String, Completer>();
-    private final Set<String> commands = new HashSet<String>();
-
-    public CommandsCompleter() {
-        this(CommandSessionHolder.getSession());
-    }
-
-    public CommandsCompleter(CommandSession session) {
-        this.session = session;
-        try {
-            new CommandTracker();
-        } catch (Throwable t) {
-            // Ignore in case we're not in OSGi
-        }
-    }
-
-
-    public int complete(String buffer, int cursor, List<String> candidates) {
-        if (session == null) {
-            session = CommandSessionHolder.getSession();
-        }
-
-        List<String> scopes = getCurrentScopes();
-        Map<String, Completer>[] allCompleters = checkData();
-        sort(allCompleters, scopes);
-
-        String subShell = getCurrentSubShell();
-        String completion = getCompletionType();
-
-        // SUBSHELL mode
-        if ("SUBSHELL".equalsIgnoreCase(completion)) {
-            if (subShell.isEmpty()) {
-                subShell = "*";
-            }
-            List<Completer> completers = new ArrayList<Completer>();
-            for (String name : allCompleters[1].keySet()) {
-                if (name.startsWith(subShell)) {
-                    completers.add(allCompleters[1].get(name));
-                }
-            }
-            if (!subShell.equals("*")) {
-                completers.add(new StringsCompleter(new String[] { "exit" }));
-            }
-            int res = new AggregateCompleter(completers).complete(buffer, cursor, candidates);
-            Collections.sort(candidates);
-            return res;
-        }
-
-        if ("FIRST".equalsIgnoreCase(completion)) {
-            if (!subShell.isEmpty()) {
-                List<Completer> completers = new ArrayList<Completer>();
-                for (String name : allCompleters[1].keySet()) {
-                    if (name.startsWith(subShell)) {
-                        completers.add(allCompleters[1].get(name));
-                    }
-                }
-                int res = new AggregateCompleter(completers).complete(buffer, cursor, candidates);
-                if (!candidates.isEmpty()) {
-                    Collections.sort(candidates);
-                    return res;
-                }
-            }
-            List<Completer> compl = new ArrayList<Completer>();
-            compl.add(new StringsCompleter(getAliases()));
-            compl.addAll(allCompleters[0].values());
-            int res = new AggregateCompleter(compl).complete(buffer, cursor, candidates);
-            Collections.sort(candidates);
-            return res;
-        }
-
-        List<Completer> compl = new ArrayList<Completer>();
-        compl.add(new StringsCompleter(getAliases()));
-        compl.addAll(allCompleters[0].values());
-        int res = new AggregateCompleter(compl).complete(buffer, cursor, candidates);
-        Collections.sort(candidates);
-        return res;
-    }
-
-    protected void sort(Map<String, Completer>[] completers, List<String> scopes) {
-        ScopeComparator comparator = new ScopeComparator(scopes);
-        for (int i = 0; i < completers.length; i++) {
-            Map<String, Completer> map = new TreeMap<String, Completer>(comparator);
-            map.putAll(completers[i]);
-            completers[i] = map;
-        }
-    }
-
-    protected static class ScopeComparator implements Comparator<String> {
-        private final List<String> scopes;
-        public ScopeComparator(List<String> scopes) {
-            this.scopes = scopes;
-        }
-        @Override
-        public int compare(String o1, String o2) {
-            String[] p1 = o1.split(":");
-            String[] p2 = o2.split(":");
-            int p = 0;
-            while (p < p1.length && p < p2.length) {
-                int i1 = scopes.indexOf(p1[p]);
-                int i2 = scopes.indexOf(p2[p]);
-                if (i1 < 0) {
-                    if (i2 < 0) {
-                        int c = p1[p].compareTo(p2[p]);
-                        if (c != 0) {
-                            return c;
-                        } else {
-                            p++;
-                        }
-                    } else {
-                        return +1;
-                    }
-                } else if (i2 < 0) {
-                    return -1;
-                } else if (i1 < i2) {
-                    return -1;
-                } else if (i1 > i2) {
-                    return +1;
-                } else {
-                    p++;
-                }
-            }
-            return 0;
-        }
-    }
-
-    protected List<String> getCurrentScopes() {
-        String scopes = (String) session.get("SCOPE");
-        return Arrays.asList(scopes.split(":"));
-    }
-
-    protected String getCurrentSubShell() {
-        String s = (String) session.get("SUBSHELL");
-        if (s == null) {
-            s = "";
-        }
-        return s;
-    }
-
-    protected String getCompletionType() {
-        String completion = (String) session.get(SessionProperties.COMPLETION_MODE);
-        if (completion == null) {
-            completion = "GLOBAL";
-        }
-        return completion;
-    }
-
-    protected String stripScope(String name) {
-        int index = name.indexOf(":");
-        return index > 0 ? name.substring(index + 1) : name;
-    }
-
-    @SuppressWarnings({
-        "unchecked", "deprecation"
-    })
-    protected Map<String, Completer>[] checkData() {
-        // Copy the set to avoid concurrent modification exceptions
-        // TODO: fix that in gogo instead
-        Set<String> names;
-        boolean update;
-        synchronized (this) {
-            names = new HashSet<String>((Set<String>) session.get(COMMANDS));
-            update = !names.equals(commands);
-        }
-        if (update) {
-            // get command aliases
-            Set<String> commands = new HashSet<String>();
-            Map<String, Completer> global = new HashMap<String, Completer>();
-            Map<String, Completer> local = new HashMap<String, Completer>();
-
-            // add argument completers for each command
-            for (String command : names) {
-                String rawCommand = stripScope(command);
-                Function function = (Function) session.get(command);
-                function = unProxy(function);
-                if (function instanceof CommandWithAction) {
-                    try {
-                        global.put(command, new ArgumentCompleter(session, (CommandWithAction) function, command));
-                        local.put(command, new ArgumentCompleter(session, (CommandWithAction) function, rawCommand));
-                    } catch (Throwable t) {
-                        LOGGER.debug("Unable to create completers for command '" + command + "'", t);
-                    }
-                }
-                else if (function instanceof org.apache.felix.gogo.commands.CommandWithAction) {
-                    try {
-                        global.put(command, new OldArgumentCompleter(session, (org.apache.felix.gogo.commands.CommandWithAction) function, command));
-                        local.put(command, new OldArgumentCompleter(session, (org.apache.felix.gogo.commands.CommandWithAction) function, rawCommand));
-                    } catch (Throwable t) {
-                        LOGGER.debug("Unable to create completers for command '" + command + "'", t);
-                    }
-                }
-                commands.add(command);
-            }
-
-            synchronized (this) {
-                this.commands.clear();
-                this.globalCompleters.clear();
-                this.localCompleters.clear();
-                this.commands.addAll(commands);
-                this.globalCompleters.putAll(global);
-                this.localCompleters.putAll(local);
-            }
-        }
-        synchronized (this) {
-            return new Map[] {
-                    new HashMap<String, Completer>(this.globalCompleters),
-                    new HashMap<String, Completer>(this.localCompleters)
-            };
-        }
-    }
-
-    /**
-     * Get the aliases defined in the console session.
-     *
-     * @return the aliases set
-     */
-    @SuppressWarnings("unchecked")
-    private Set<String> getAliases() {
-        Set<String> vars = ((Set<String>) session.get(null));
-        Set<String> aliases = new HashSet<String>();
-        for (String var : vars) {
-            Object content = session.get(var);
-            if (content != null && "org.apache.felix.gogo.runtime.Closure".equals(content.getClass().getName())) {
-                aliases.add(var);
-            }
-        }
-        return aliases;
-    }
-
-    public static Function unProxy(Function function) {
-        if (function == null || function.getClass() != CommandProxy.class) {
-            return function;
-        }
-        CommandProxy proxy = (CommandProxy)function;
-        Object target = proxy.getTarget();
-        try {
-            return target instanceof Function ? (Function)target : function;
-        } finally {
-            proxy.ungetTarget();
-        }
-    }
-
-    private class CommandTracker {
-        public CommandTracker() throws Exception {
-            BundleContext context = FrameworkUtil.getBundle(getClass()).getBundleContext();
-            if (context == null) {
-                throw new IllegalStateException("Bundle is stopped");
-            }
-            ServiceListener listener = new ServiceListener() {
-                public void serviceChanged(ServiceEvent event) {
-                    synchronized (CommandsCompleter.this) {
-                        commands.clear();
-                    }
-                }
-            };
-            context.addServiceListener(listener,
-                    String.format("(&(%s=*)(%s=*))",
-                            CommandProcessor.COMMAND_SCOPE,
-                            CommandProcessor.COMMAND_FUNCTION));
-        }
-    }
-
-}
-

http://git-wip-us.apache.org/repos/asf/karaf/blob/1ee78df9/shell/console/src/main/java/org/apache/karaf/shell/console/completer/FileCompleter.java
----------------------------------------------------------------------
diff --git a/shell/console/src/main/java/org/apache/karaf/shell/console/completer/FileCompleter.java b/shell/console/src/main/java/org/apache/karaf/shell/console/completer/FileCompleter.java
deleted file mode 100644
index dbb3034..0000000
--- a/shell/console/src/main/java/org/apache/karaf/shell/console/completer/FileCompleter.java
+++ /dev/null
@@ -1,147 +0,0 @@
-/**
- *
- * 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.console.completer;
-
-import java.io.File;
-import java.util.List;
-
-import org.apache.felix.service.command.CommandSession;
-import org.apache.karaf.shell.console.Completer;
-
-/**
- * A file name completer takes the buffer and issues a list of
- * potential completions.
- * <p/>
- * This completer tries to behave as similar as possible to
- * <i>bash</i>'s file name completion (using GNU readline)
- * with the following exceptions:
- * <p/>
- * <ul>
- * <li>Candidates that are directories will end with "/"</li>
- * <li>Wildcard regular expressions are not evaluated or replaced</li>
- * <li>The "~" character can be used to represent the user's home,
- * but it cannot complete to other users' homes, since java does
- * not provide any way of determining that easily</li>
- * </ul>
- *
- * @author <a href="mailto:mwp1@cornell.edu">Marc Prud'hommeaux</a>
- * @author <a href="mailto:jason@planet57.com">Jason Dillon</a>
- * @since 2.3
- */
-public class FileCompleter implements Completer
-{
-    private static String OS = System.getProperty("os.name").toLowerCase();
-
-    // TODO: Handle files with spaces in them
-
-    private static final boolean OS_IS_WINDOWS = isWindows();
-    
-    public FileCompleter(CommandSession session) {
-    }
-
-    public static boolean isWindows() {
-        return (OS.indexOf("win") >= 0);
-
-    }
-
-    public int complete(String buffer, final int cursor, final List<String> candidates) {
-        // buffer can be null
-        if (candidates == null) {
-            return 0;
-        }
-
-        if (buffer == null) {
-            buffer = "";
-        }
-
-        if (OS_IS_WINDOWS) {
-            buffer = buffer.replace('/', '\\');
-        }
-
-        String translated = buffer;
-
-        File homeDir = getUserHome();
-
-        // Special character: ~ maps to the user's home directory
-        if (translated.startsWith("~" + separator())) {
-            translated = homeDir.getPath() + translated.substring(1);
-        }
-        else if (translated.startsWith("~")) {
-            translated = homeDir.getParentFile().getAbsolutePath();
-        }
-        else if (!(translated.startsWith(separator()))) {
-            String cwd = getUserDir().getAbsolutePath();
-            translated = cwd + separator() + translated;
-        }
-
-        File file = new File(translated);
-        final File dir;
-
-        if (translated.endsWith(separator())) {
-            dir = file;
-        }
-        else {
-            dir = file.getParentFile();
-        }
-
-        File[] entries = dir == null ? new File[0] : dir.listFiles();
-
-        return matchFiles(buffer, translated, entries, candidates);
-    }
-
-    protected String separator() {
-        return File.separator;
-    }
-
-    protected File getUserHome() {
-        return new File(System.getProperty("user.home"));
-    }
-
-    protected File getUserDir() {
-        return new File(".");
-    }
-
-    protected int matchFiles(final String buffer, final String translated, final File[] files, final List<String> candidates) {
-        if (files == null) {
-            return -1;
-        }
-
-        int matches = 0;
-
-        // first pass: just count the matches
-        for (File file : files) {
-            if (file.getAbsolutePath().startsWith(translated)) {
-                matches++;
-            }
-        }
-        for (File file : files) {
-            if (file.getAbsolutePath().startsWith(translated)) {
-                CharSequence name = file.getName() + (matches == 1 && file.isDirectory() ? separator() : " ");
-                candidates.add(render(file, name).toString());
-            }
-        }
-
-        final int index = buffer.lastIndexOf(separator());
-
-        return index + separator().length();
-    }
-
-    protected CharSequence render(final File file, final CharSequence name) {
-        return name;
-    }
-}

http://git-wip-us.apache.org/repos/asf/karaf/blob/1ee78df9/shell/console/src/main/java/org/apache/karaf/shell/console/completer/NullCompleter.java
----------------------------------------------------------------------
diff --git a/shell/console/src/main/java/org/apache/karaf/shell/console/completer/NullCompleter.java b/shell/console/src/main/java/org/apache/karaf/shell/console/completer/NullCompleter.java
deleted file mode 100644
index 13da2df..0000000
--- a/shell/console/src/main/java/org/apache/karaf/shell/console/completer/NullCompleter.java
+++ /dev/null
@@ -1,32 +0,0 @@
-/*
- * 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.console.completer;
-
-import java.util.List;
-
-import org.apache.karaf.shell.console.Completer;
-
-public class NullCompleter implements Completer {
-
-    public static final NullCompleter INSTANCE = new NullCompleter();
-
-    public int complete(String buffer, int cursor, List<String> candidates) {
-        return -1;
-    }
-}

http://git-wip-us.apache.org/repos/asf/karaf/blob/1ee78df9/shell/console/src/main/java/org/apache/karaf/shell/console/completer/OldArgumentCompleter.java
----------------------------------------------------------------------
diff --git a/shell/console/src/main/java/org/apache/karaf/shell/console/completer/OldArgumentCompleter.java b/shell/console/src/main/java/org/apache/karaf/shell/console/completer/OldArgumentCompleter.java
deleted file mode 100644
index 583d6f8..0000000
--- a/shell/console/src/main/java/org/apache/karaf/shell/console/completer/OldArgumentCompleter.java
+++ /dev/null
@@ -1,448 +0,0 @@
-/*
- * 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.console.completer;
-
-import java.io.File;
-import java.lang.reflect.Field;
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.EnumSet;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
-import org.apache.felix.gogo.commands.Action;
-import org.apache.felix.gogo.commands.Argument;
-import org.apache.felix.gogo.commands.CommandWithAction;
-import org.apache.felix.gogo.commands.CompleterValues;
-import org.apache.felix.gogo.commands.Option;
-import org.apache.felix.service.command.CommandSession;
-import org.apache.karaf.shell.console.CommandSessionHolder;
-import org.apache.karaf.shell.console.CompletableFunction;
-import org.apache.karaf.shell.console.Completer;
-import org.apache.karaf.shell.console.NameScoping;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-public class OldArgumentCompleter implements Completer {
-
-    private static final Logger LOGGER = LoggerFactory.getLogger(OldArgumentCompleter.class);
-
-    public static final String ARGUMENTS_LIST = "ARGUMENTS_LIST";
-
-    final Completer commandCompleter;
-    final Completer optionsCompleter;
-    final List<Completer> argsCompleters;
-    final Map<String, Completer> optionalCompleters;
-    final CommandWithAction function;
-    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;
-
-    @SuppressWarnings({ "unchecked", "rawtypes" })
-    public OldArgumentCompleter(CommandSession session, CommandWithAction function, String command) {
-        this.function = function;
-        // Command name completer
-        commandCompleter = new StringsCompleter(getNames(session, command));
-        // Build options completer
-        for (Class<?> type = function.getActionClass(); type != null; type = type.getSuperclass()) {
-            for (Field field : type.getDeclaredFields()) {
-                Option option = field.getAnnotation(Option.class);
-                if (option != null) {
-                    fields.put(option, field);
-                    options.put(option.name(), option);
-                    String[] aliases = option.aliases();
-                    if (aliases != null) {
-                        for (String alias : aliases) {
-                            options.put(alias, option);
-                        }
-                    }
-                }
-                Argument argument = field.getAnnotation(Argument.class);
-                if (argument != null) {
-                    Integer key = argument.index();
-                    if (arguments.containsKey(key)) {
-                        LOGGER.warn("Duplicate @Argument annotations on class " + type.getName() + " for index: " + key + " see: " + field);
-                    } else {
-                        arguments.put(key, field);
-                    }
-                }
-            }
-        }
-//        options.put(HelpOption.HELP.name(), HelpOption.HELP);
-        optionsCompleter = new StringsCompleter(options.keySet());
-        // Build arguments completers
-        argsCompleters = new ArrayList<Completer>();
-
-        if (function instanceof CompletableFunction) {
-            Map<String, Completer> opt;
-            try {
-                //
-                opt = ((CompletableFunction) function).getOptionalCompleters();
-            } catch (Throwable t) {
-                opt = new HashMap<String, Completer>();
-            }
-            optionalCompleters = opt;
-            List<Completer> fcl = ((CompletableFunction) function).getCompleters();
-            if (fcl != null) {
-                for (Completer c : fcl) {
-                    argsCompleters.add(c == null ? NullCompleter.INSTANCE : c);
-                }
-            } else {
-                argsCompleters.add(NullCompleter.INSTANCE);
-            }
-        } else {
-            optionalCompleters = new HashMap<String, Completer>();
-            final Map<Integer, Method> methods = new HashMap<Integer, Method>();
-            for (Class<?> type = function.getActionClass(); type != null; type = type.getSuperclass()) {
-                for (Method method : type.getDeclaredMethods()) {
-                    CompleterValues completerMethod = method.getAnnotation(CompleterValues.class);
-                    if (completerMethod != null) {
-                        int index = completerMethod.index();
-                        Integer key = index;
-                        if (index >= arguments.size() || index < 0) {
-                            LOGGER.warn("Index out of range on @CompleterValues on class " + type.getName() + " for index: " + key + " see: " + method);
-                        }
-                        if (methods.containsKey(key)) {
-                            LOGGER.warn("Duplicate @CompleterMethod annotations on class " + type.getName() + " for index: " + key + " see: " + method);
-                        } else {
-                            methods.put(key, method);
-                        }
-                    }
-                }
-            }
-            for (int i = 0, size = arguments.size(); i < size; i++) {
-                Completer argCompleter = NullCompleter.INSTANCE;
-                Method method = methods.get(i);
-                if (method != null) {
-                    // lets invoke the method
-                    Action action = function.createNewAction();
-                    try {
-                        Object value = method.invoke(action);
-                        if (value instanceof String[]) {
-                            argCompleter = new StringsCompleter((String[]) value);
-                        } else if (value instanceof Collection) {
-                            argCompleter = new StringsCompleter((Collection<String>) value);
-                        } else {
-                            LOGGER.warn("Could not use value " + value + " as set of completions!");
-                        }
-                    } catch (IllegalAccessException e) {
-                        LOGGER.warn("Could not invoke @CompleterMethod on " + function + ". " + e, e);
-                    } catch (InvocationTargetException e) {
-                        Throwable target = e.getTargetException();
-                        if (target == null) {
-                            target = e;
-                        }
-                        LOGGER.warn("Could not invoke @CompleterMethod on " + function + ". " + target, target);
-                    } finally {
-                        try {
-                            function.releaseAction(action);
-                        } catch (Exception e) {
-                            LOGGER.warn("Failed to release action: " + action + ". " + e, e);
-                        }
-                    }
-                } else {
-                    Field field = arguments.get(i);
-                    Class<?> type = field.getType();
-                    if (type.isAssignableFrom(File.class)) {
-                        argCompleter = new FileCompleter(session);
-                    } else if (type.isAssignableFrom(Boolean.class) || type.isAssignableFrom(boolean.class)) {
-                        argCompleter = new StringsCompleter(new String[] {"false", "true"}, false);
-                    } else if (type.isAssignableFrom(Enum.class)) {
-                        Set<String> values = new HashSet<String>();
-                        for (Object o : EnumSet.allOf((Class<Enum>) type)) {
-                            values.add(o.toString());
-                        }
-                        argCompleter = new StringsCompleter(values, false);
-                    } else {
-                        // TODO any other completers we can add?
-                    }
-                }
-                argsCompleters.add(argCompleter);
-            }
-        }
-    }
-
-    private String[] getNames(CommandSession session, String scopedCommand) {
-        String command = NameScoping.getCommandNameWithoutGlobalPrefix(session, scopedCommand);
-        String[] s = command.split(":");
-        if (s.length == 1) {
-            return s;
-        } else {
-            return new String[] { command, s[1] };
-        }
-    }
-
-    /**
-     *  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(final String buffer, final int cursor,
-                        final List<String> candidates) {
-        ArgumentList list = delimit(buffer, cursor);
-        int argpos = list.getArgumentPosition();
-        int argIndex = list.getCursorArgumentIndex();
-
-        //Store the argument list so that it can be used by completers.
-        CommandSession commandSession = CommandSessionHolder.getSession();
-        if(commandSession != null) {
-            commandSession.put(ARGUMENTS_LIST,list);
-        }
-
-        Completer comp = null;
-        String[] args = list.getArguments();
-        int index = 0;
-        // First argument is command name
-        if (index < argIndex) {
-            // Verify command name
-            if (!verifyCompleter(commandCompleter, args[index])) {
-                return -1;
-            }
-            index++;
-        } else {
-            comp = commandCompleter;
-        }
-        // Now, check options
-        if (comp == null) {
-            while (index < argIndex && args[index].startsWith("-")) {
-                if (!verifyCompleter(optionsCompleter, args[index])) {
-                    return -1;
-                }
-                Option option = options.get(args[index]);
-                if (option == null) {
-                    return -1;
-                }
-                Field field = fields.get(option);
-                if (field != null && field.getType() != boolean.class && field.getType() != Boolean.class) {
-                    if (++index == argIndex) {
-                        comp = NullCompleter.INSTANCE;
-                    }
-                }
-                index++;
-            }
-            if (comp == null && index >= argIndex && index < args.length && args[index].startsWith("-")) {
-                comp = optionsCompleter;
-            }
-        }
-        //Now check for if last Option has a completer
-        int lastAgurmentIndex = argIndex - 1;
-        if (lastAgurmentIndex >= 1) {
-            Option lastOption = options.get(args[lastAgurmentIndex]);
-            if (lastOption != null) {
-
-                Field lastField = fields.get(lastOption);
-                if (lastField != null && lastField.getType() != boolean.class && lastField.getType() != Boolean.class) {
-                    Option option = lastField.getAnnotation(Option.class);
-                    if (option != null) {
-                        Completer optionValueCompleter = null;
-                        String name = option.name();
-                        if (optionalCompleters != null && name != null) {
-                            optionValueCompleter = optionalCompleters.get(name);
-                            if (optionValueCompleter == null) {
-                                String[] aliases = option.aliases();
-                                if (aliases.length > 0) {
-                                    for (int i = 0; i < aliases.length && optionValueCompleter == null; i++) {
-                                        optionValueCompleter = optionalCompleters.get(option.aliases()[i]);
-                                    }
-                                }
-                            }
-                        }
-                        if(optionValueCompleter != null) {
-                            comp = optionValueCompleter;
-                        }
-                    }
-                }
-            }
-        }
-
-        // Check arguments
-        if (comp == null) {
-            int indexArg = 0;
-            while (index < argIndex) {
-                Completer sub = argsCompleters.get(indexArg >= argsCompleters.size() ? argsCompleters.size() - 1 : indexArg);
-                if (!verifyCompleter(sub, args[index])) {
-                    return -1;
-                }
-                index++;
-                indexArg++;
-            }
-            comp = argsCompleters.get(indexArg >= argsCompleters.size() ? argsCompleters.size() - 1 : indexArg);
-        }
-
-        int ret = comp.complete(list.getCursorArgument(), argpos, candidates);
-
-        if (ret == -1) {
-            return -1;
-        }
-
-        int pos = ret + (list.getBufferPosition() - argpos);
-
-        /**
-         *  Special case: when completing in the middle of a line, and the
-         *  area under the cursor is a delimiter, then trim any delimiters
-         *  from the candidates, since we do not need to have an extra
-         *  delimiter.
-         *
-         *  E.g., if we have a completion for "foo", and we
-         *  enter "f bar" into the buffer, and move to after the "f"
-         *  and hit TAB, we want "foo bar" instead of "foo  bar".
-         */
-
-        if ((buffer != null) && (cursor != buffer.length()) && isDelimiter(buffer, cursor)) {
-            for (int i = 0; i < candidates.size(); i++) {
-                String val = candidates.get(i);
-
-                while ((val.length() > 0)
-                    && isDelimiter(val, val.length() - 1)) {
-                    val = val.substring(0, val.length() - 1);
-                }
-
-                candidates.set(i, val);
-            }
-        }
-
-        return pos;
-    }
-
-    protected boolean verifyCompleter(Completer completer, String argument) {
-        List<String> candidates = new ArrayList<String>();
-        return completer.complete(argument, argument.length(), candidates) != -1 && !candidates.isEmpty();
-    }
-
-    public ArgumentList delimit(final String buffer, final int cursor) {
-        Parser parser = new Parser(buffer, cursor);
-        try {
-            List<List<List<String>>> program = parser.program();
-            List<String> pipe = program.get(parser.c0).get(parser.c1);
-            return new ArgumentList(pipe.toArray(new String[pipe.size()]), parser.c2, parser.c3, cursor);
-        } catch (Throwable t) {
-            return new ArgumentList(new String[] { buffer }, 0, cursor, cursor);
-        }
-    }
-
-    /**
-     *  Returns true if the specified character is a whitespace
-     *  parameter. Check to ensure that the character is not
-     *  escaped and returns true from
-     *  {@link #isDelimiterChar}.
-     *
-     *  @param  buffer the complete command buffer
-     *  @param  pos    the index of the character in the buffer
-     *  @return        true if the character should be a delimiter
-     */
-    public boolean isDelimiter(final String buffer, final int pos) {
-        return !isEscaped(buffer, pos) && isDelimiterChar(buffer, pos);
-    }
-
-    public boolean isEscaped(final String buffer, final int pos) {
-        return pos > 0 && buffer.charAt(pos) == '\\' && !isEscaped(buffer, pos - 1);
-    }
-
-    /**
-     *  The character is a delimiter if it is whitespace, and the
-     *  preceeding character is not an escape character.
-     */
-    public boolean isDelimiterChar(String buffer, int pos) {
-        return Character.isWhitespace(buffer.charAt(pos));
-    }
-
-    /**
-     *  The result of a delimited buffer.
-     */
-    public static class ArgumentList {
-        private String[] arguments;
-        private int cursorArgumentIndex;
-        private int argumentPosition;
-        private int bufferPosition;
-
-        /**
-         *  @param  arguments           the array of tokens
-         *  @param  cursorArgumentIndex the token index of the cursor
-         *  @param  argumentPosition    the position of the cursor in the
-         *                              current token
-         *  @param  bufferPosition      the position of the cursor in
-         *                              the whole buffer
-         */
-        public ArgumentList(String[] arguments, int cursorArgumentIndex,
-            int argumentPosition, int bufferPosition) {
-            this.arguments = arguments;
-            this.cursorArgumentIndex = cursorArgumentIndex;
-            this.argumentPosition = argumentPosition;
-            this.bufferPosition = bufferPosition;
-        }
-
-        public void setCursorArgumentIndex(int cursorArgumentIndex) {
-            this.cursorArgumentIndex = cursorArgumentIndex;
-        }
-
-        public int getCursorArgumentIndex() {
-            return this.cursorArgumentIndex;
-        }
-
-        public String getCursorArgument() {
-            if ((cursorArgumentIndex < 0)
-                || (cursorArgumentIndex >= arguments.length)) {
-                return null;
-            }
-
-            return arguments[cursorArgumentIndex];
-        }
-
-        public void setArgumentPosition(int argumentPosition) {
-            this.argumentPosition = argumentPosition;
-        }
-
-        public int getArgumentPosition() {
-            return this.argumentPosition;
-        }
-
-        public void setArguments(String[] arguments) {
-            this.arguments = arguments;
-        }
-
-        public String[] getArguments() {
-            return this.arguments;
-        }
-
-        public void setBufferPosition(int bufferPosition) {
-            this.bufferPosition = bufferPosition;
-        }
-
-        public int getBufferPosition() {
-            return this.bufferPosition;
-        }
-    }
-}

http://git-wip-us.apache.org/repos/asf/karaf/blob/1ee78df9/shell/console/src/main/java/org/apache/karaf/shell/console/completer/Parser.java
----------------------------------------------------------------------
diff --git a/shell/console/src/main/java/org/apache/karaf/shell/console/completer/Parser.java b/shell/console/src/main/java/org/apache/karaf/shell/console/completer/Parser.java
deleted file mode 100644
index dcdab7d..0000000
--- a/shell/console/src/main/java/org/apache/karaf/shell/console/completer/Parser.java
+++ /dev/null
@@ -1,396 +0,0 @@
-/*
- * 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.
- */
-// DWB14: parser loops if // comment at start of program
-// DWB15: allow program to have trailing ';'
-package org.apache.karaf.shell.console.completer;
-
-import java.util.ArrayList;
-import java.util.List;
-
-public class Parser {
-
-    int current = 0;
-    String text;
-    boolean escaped;
-    static final String SPECIAL = "<;|{[\"'$`(=";
-
-    List<List<List<String>>> program;
-    List<List<String>> statements;
-    List<String> statement;
-    int cursor;
-    int start = -1;
-    int c0;
-    int c1;
-    int c2;
-    int c3;
-
-    public Parser(String text, int cursor) {
-        this.text = text;
-        this.cursor = cursor;
-    }
-
-    void ws() {
-        // derek: BUGFIX: loop if comment  at beginning of input
-        //while (!eof() && Character.isWhitespace(peek())) {
-        while (!eof() && (!escaped && Character.isWhitespace(peek()) || current == 0)) {
-            if (current != 0 || !escaped && Character.isWhitespace(peek())) {
-                current++;
-            }
-            if (peek() == '/' && current < text.length() - 2
-                && text.charAt(current + 1) == '/') {
-                comment();
-            }
-            if (current == 0) {
-                break;
-            }
-        }
-    }
-
-    private void comment() {
-        while (!eof() && peek() != '\n' && peek() != '\r') {
-            next();
-        }
-    }
-
-    boolean eof() {
-        return current >= text.length();
-    }
-
-    char peek() {
-        return peek(false);
-    }
-
-    char peek(boolean increment) {
-        escaped = false;
-        if (eof()) {
-            return 0;
-        }
-
-        int last = current;
-        char c = text.charAt(current++);
-
-        if (c == '\\') {
-            escaped = true;
-            if (eof()) {
-                throw new RuntimeException("Eof found after \\");
-            }
-
-            c = text.charAt(current++);
-
-            switch (c) {
-                case 't':
-                    c = '\t';
-                    break;
-                case '\r':
-                case '\n':
-                    c = ' ';
-                    break;
-                case 'b':
-                    c = '\b';
-                    break;
-                case 'f':
-                    c = '\f';
-                    break;
-                case 'n':
-                    c = '\n';
-                    break;
-                case 'r':
-                    c = '\r';
-                    break;
-                case 'u':
-                    c = unicode();
-                    current += 4;
-                    break;
-                default:
-                    // We just take the next character literally
-                    // but have the escaped flag set, important for {},[] etc
-            }
-        }
-        if (cursor > last && cursor <= current) {
-            c0 = program != null ? program.size() : 0;
-            c1 = statements != null ? statements.size() : 0;
-            c2 = statement != null ? statement.size() : 0;
-            c3 = (start >= 0) ? current - start : 0;
-        }
-        if (!increment) {
-            current = last;
-        }
-        return c;
-    }
-
-    public List<List<List<String>>> program() {
-        program = new ArrayList<List<List<String>>>();
-        ws();
-        if (!eof()) {
-            program.add(pipeline());
-            while (peek() == ';') {
-                current++;
-                List<List<String>> pipeline = pipeline();
-                program.add(pipeline);
-            }
-        }
-        if (!eof()) {
-            throw new RuntimeException("Program has trailing text: " + context(current));
-        }
-
-        List<List<List<String>>> p = program;
-        program = null;
-        return p;
-    }
-
-    CharSequence context(int around) {
-        return text.subSequence(Math.max(0, current - 20), Math.min(text.length(),
-            current + 4));
-    }
-
-    public List<List<String>> pipeline() {
-        statements = new ArrayList<List<String>>();
-        statements.add(statement());
-        while (peek() == '|') {
-            current++;
-            ws();
-            if (!eof()) {
-                statements.add(statement());
-            }
-            else {
-                statements.add(new ArrayList<String>());
-                break;
-            }
-        }
-        List<List<String>> s = statements;
-        statements = null;
-        return s;
-    }
-
-    public List<String> statement() {
-        statement = new ArrayList<String>();
-        statement.add(value());
-        while (!eof()) {
-            ws();
-            if (peek() == '|' || peek() == ';') {
-                break;
-            }
-
-            if (!eof()) {
-                statement.add(messy());
-            }
-        }
-        List<String> s = statement;
-        statement = null;
-        return s;
-    }
-
-    public String messy()
-    {
-        start = current;
-        char c = peek();
-        if (c > 0 && SPECIAL.indexOf(c) < 0) {
-            current++;
-            try {
-                while (!eof()) {
-                    c = peek();
-                    if (!escaped && (c == ';' || c == '|' || Character.isWhitespace(c))) {
-                        break;
-                    }
-                    next();
-                }
-                return text.substring(start, current);
-            } finally {
-                start = -1;
-            }
-        }
-        else {
-            return value();
-        }
-    }
-
-    String value() {
-        ws();
-
-        start = current;
-        try {
-            char c = next();
-            if (!escaped) {
-                switch (c) {
-                    case '{':
-                        return text.substring(start, find('}', '{'));
-                    case '(':
-                        return text.substring(start, find(')', '('));
-                    case '[':
-                        return text.substring(start, find(']', '['));
-                    case '<':
-                        return text.substring(start, find('>', '<'));
-                    case '=':
-                        return text.substring(start, current);
-                    case '"':
-                    case '\'':
-                        quote(c);
-                        break;
-                }
-            }
-
-            // Some identifier or number
-            while (!eof()) {
-                c = peek();
-                if (!escaped) {
-                    if (Character.isWhitespace(c) || c == ';' || c == '|' || c == '=') {
-                        break;
-                    }
-                    else if (c == '{') {
-                        next();
-                        find('}', '{');
-                    }
-                    else if (c == '(') {
-                        next();
-                        find(')', '(');
-                    }
-                    else if (c == '<') {
-                        next();
-                        find('>', '<');
-                    }
-                    else if (c == '[') {
-                        next();
-                        find(']', '[');
-                    }
-                    else if (c == '\'' || c == '"') {
-                        next();
-                        quote(c);
-                        next();
-                    }
-                    else {
-                        next();
-                    }
-                }
-                else {
-                    next();
-                }
-            }
-            return text.substring(start, current);
-        } finally {
-            start = -1;
-        }
-    }
-
-    boolean escaped() {
-        return escaped;
-    }
-
-    char next() {
-        return peek(true);
-    }
-
-    char unicode() {
-        if (current + 4 > text.length()) {
-            throw new IllegalArgumentException("Unicode \\u escape at eof at pos ..."
-                + context(current) + "...");
-        }
-
-        String s = text.subSequence(current, current + 4).toString();
-        int n = Integer.parseInt(s, 16);
-        return (char) n;
-    }
-
-    int find(char target, char deeper) {
-        int start = current;
-        int level = 1;
-
-        while (level != 0) {
-            if (eof()) {
-                throw new RuntimeException("Eof found in the middle of a compound for '"
-                    + target + deeper + "', begins at " + context(start));
-            }
-
-            char c = next();
-            if (!escaped) {
-                if (c == target) {
-                    level--;
-                } else {
-                    if (c == deeper) {
-                        level++;
-                    } else {
-                        if (c == '"') {
-                            quote('"');
-                        } else {
-                            if (c == '\'') {
-                                quote('\'');
-                            }
-                            else {
-                                if (c == '`') {
-                                    quote('`');
-                                }
-                            }
-                        }
-                    }
-                }
-            }
-        }
-        return current;
-    }
-
-    int quote(char which) {
-        while (!eof() && (peek() != which || escaped)) {
-            next();
-        }
-
-        return current++;
-    }
-
-    CharSequence findVar() {
-        int start = current;
-        char c = peek();
-
-        if (c == '{') {
-            next();
-            int end = find('}', '{');
-            return text.subSequence(start, end);
-        }
-        if (c == '(') {
-            next();
-            int end = find(')', '(');
-            return text.subSequence(start, end);
-        }
-
-        if (Character.isJavaIdentifierPart(c)) {
-            while (c == '$') {
-                c = next();
-            }
-            while (!eof() && (Character.isJavaIdentifierPart(c) || c == '.') && c != '$') {
-                next();
-                c = peek();
-            }
-            return text.subSequence(start, current);
-        }
-        throw new IllegalArgumentException(
-            "Reference to variable does not match syntax of a variable: "
-                + context(start));
-    }
-
-    public String toString() {
-        return "..." + context(current) + "...";
-    }
-
-    public String unescape() {
-        StringBuilder sb = new StringBuilder();
-        while (!eof()) {
-            sb.append(next());
-        }
-        return sb.toString();
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/karaf/blob/1ee78df9/shell/console/src/main/java/org/apache/karaf/shell/console/completer/StringsCompleter.java
----------------------------------------------------------------------
diff --git a/shell/console/src/main/java/org/apache/karaf/shell/console/completer/StringsCompleter.java b/shell/console/src/main/java/org/apache/karaf/shell/console/completer/StringsCompleter.java
deleted file mode 100644
index a3f7c6d..0000000
--- a/shell/console/src/main/java/org/apache/karaf/shell/console/completer/StringsCompleter.java
+++ /dev/null
@@ -1,103 +0,0 @@
-/*
- * 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.console.completer;
-
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.List;
-import java.util.SortedSet;
-import java.util.TreeSet;
-
-import org.apache.karaf.shell.console.Completer;
-
-/**
- * Completer for a set of strings.
- */
-public class StringsCompleter
-    implements Completer
-{
-    private final SortedSet<String> strings = new TreeSet<String>();
-    private final boolean caseSensitive;
-
-    public StringsCompleter() {
-        this(true);
-    }
-
-
-    public StringsCompleter(final boolean caseSensitive) {
-        this.caseSensitive = caseSensitive;
-    }
-
-    public StringsCompleter(final Collection<String> strings) {
-        this();
-        assert strings != null;
-        getStrings().addAll(strings);
-    }
-
-    public StringsCompleter(final String[] strings, boolean caseSensitive) {
-        this(Arrays.asList(strings), caseSensitive);
-    }
-
-    public StringsCompleter(final Collection<String> strings, boolean caseSensitive) {
-        this(caseSensitive);
-        assert strings != null;
-        getStrings().addAll(strings);
-    }
-
-    public StringsCompleter(final String[] strings) {
-        this(Arrays.asList(strings));
-    }
-
-    public SortedSet<String> getStrings() {
-        return strings;
-    }
-
-    @SuppressWarnings({ "rawtypes", "unchecked" })
-    public int complete(String buffer, final int cursor, final List candidates) {
-        // buffer could be null
-        assert candidates != null;
-
-        if (buffer == null) {
-            buffer = "";
-        }
-        if (!caseSensitive) {
-            buffer = buffer.toLowerCase();
-        }
-
-        // KARAF-421, use getStrings() instead strings field.
-        SortedSet<String> matches = getStrings().tailSet(buffer);
-
-        for (String match : matches) {
-            String s = caseSensitive ? match : match.toLowerCase();
-            if (!s.startsWith(buffer)) {
-                break;
-            }
-
-            // noinspection unchecked
-            candidates.add(match);
-        }
-
-        if (candidates.size() == 1) {
-            // noinspection unchecked
-            candidates.set(0, candidates.get(0) + " ");
-        }
-
-        return candidates.isEmpty() ? -1 : 0;
-    }
-}

http://git-wip-us.apache.org/repos/asf/karaf/blob/1ee78df9/shell/console/src/main/java/org/apache/karaf/shell/inject/Destroy.java
----------------------------------------------------------------------
diff --git a/shell/console/src/main/java/org/apache/karaf/shell/inject/Destroy.java b/shell/console/src/main/java/org/apache/karaf/shell/inject/Destroy.java
deleted file mode 100644
index 4df9fe3..0000000
--- a/shell/console/src/main/java/org/apache/karaf/shell/inject/Destroy.java
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * 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.inject;
-
-import java.lang.annotation.ElementType;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.lang.annotation.Target;
-
-@Retention(RetentionPolicy.RUNTIME)
-@Target({ElementType.METHOD})
-public @interface Destroy {
-}

http://git-wip-us.apache.org/repos/asf/karaf/blob/1ee78df9/shell/console/src/main/java/org/apache/karaf/shell/inject/Init.java
----------------------------------------------------------------------
diff --git a/shell/console/src/main/java/org/apache/karaf/shell/inject/Init.java b/shell/console/src/main/java/org/apache/karaf/shell/inject/Init.java
deleted file mode 100644
index 154b883..0000000
--- a/shell/console/src/main/java/org/apache/karaf/shell/inject/Init.java
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * 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.inject;
-
-import java.lang.annotation.ElementType;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.lang.annotation.Target;
-
-@Retention(RetentionPolicy.RUNTIME)
-@Target({ElementType.METHOD})
-public @interface Init {
-}

http://git-wip-us.apache.org/repos/asf/karaf/blob/1ee78df9/shell/console/src/main/java/org/apache/karaf/shell/inject/Reference.java
----------------------------------------------------------------------
diff --git a/shell/console/src/main/java/org/apache/karaf/shell/inject/Reference.java b/shell/console/src/main/java/org/apache/karaf/shell/inject/Reference.java
deleted file mode 100644
index b74d609..0000000
--- a/shell/console/src/main/java/org/apache/karaf/shell/inject/Reference.java
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * 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.inject;
-
-import java.lang.annotation.ElementType;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.lang.annotation.Target;
-
-@Retention(RetentionPolicy.RUNTIME)
-@Target({ElementType.FIELD})
-public @interface Reference {
-}

http://git-wip-us.apache.org/repos/asf/karaf/blob/1ee78df9/shell/console/src/main/java/org/apache/karaf/shell/inject/Service.java
----------------------------------------------------------------------
diff --git a/shell/console/src/main/java/org/apache/karaf/shell/inject/Service.java b/shell/console/src/main/java/org/apache/karaf/shell/inject/Service.java
deleted file mode 100644
index ff3f3a3..0000000
--- a/shell/console/src/main/java/org/apache/karaf/shell/inject/Service.java
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * 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.inject;
-
-import java.lang.annotation.ElementType;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.lang.annotation.Target;
-
-@Retention(RetentionPolicy.RUNTIME)
-@Target({ElementType.TYPE})
-public @interface Service {
-}

http://git-wip-us.apache.org/repos/asf/karaf/blob/1ee78df9/shell/console/src/test/java/org/apache/karaf/shell/commands/basic/Context.java
----------------------------------------------------------------------
diff --git a/shell/console/src/test/java/org/apache/karaf/shell/commands/basic/Context.java b/shell/console/src/test/java/org/apache/karaf/shell/commands/basic/Context.java
deleted file mode 100644
index e378789..0000000
--- a/shell/console/src/test/java/org/apache/karaf/shell/commands/basic/Context.java
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- * 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.commands.basic;
-
-import org.apache.felix.gogo.runtime.CommandProcessorImpl;
-import org.apache.felix.gogo.runtime.CommandSessionImpl;
-import org.apache.felix.gogo.runtime.threadio.ThreadIOImpl;
-import org.apache.felix.service.command.CommandSession;
-
-public class Context extends CommandProcessorImpl
-{
-    public static final String EMPTY = "";
-    CommandSessionImpl session;
-    static ThreadIOImpl threadio;
-
-    static
-    {
-        threadio = new ThreadIOImpl();
-        threadio.start();
-    }
-
-    public Context()
-    {
-        super(threadio);
-        addCommand("shell", this, "addCommand");
-        addCommand("shell", this, "removeCommand");
-        addCommand("shell", this, "eval");
-        session = (CommandSessionImpl) createSession(System.in, System.out, System.err);
-    }
-
-
-    public Object execute(CharSequence source) throws Exception
-    {
-        return session.execute(source);
-    }
-
-    public void set(String name, Object value)
-    {
-        session.put(name, value);
-    }
-
-    public Object get(String name)
-    {
-        return session.get(name);
-    }
-
-    public CommandSession getSession() {
-        return session;
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/karaf/blob/1ee78df9/shell/console/src/test/java/org/apache/karaf/shell/commands/basic/SimpleSubShell.java
----------------------------------------------------------------------
diff --git a/shell/console/src/test/java/org/apache/karaf/shell/commands/basic/SimpleSubShell.java b/shell/console/src/test/java/org/apache/karaf/shell/commands/basic/SimpleSubShell.java
deleted file mode 100644
index 77e9b57..0000000
--- a/shell/console/src/test/java/org/apache/karaf/shell/commands/basic/SimpleSubShell.java
+++ /dev/null
@@ -1,39 +0,0 @@
-/**
- *
- * 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.commands.basic;
-
-import org.apache.karaf.shell.commands.Action;
-import org.apache.karaf.shell.commands.basic.SimpleCommand;
-import org.apache.karaf.shell.console.SubShellAction;
-
-public class SimpleSubShell extends SimpleCommand {
-
-    private final String subshell;
-
-    public SimpleSubShell(String subshell) {
-        super(SubShellAction.class);
-        this.subshell = subshell;
-    }
-
-    @Override
-    public Action createNewAction() {
-        SubShellAction action = (SubShellAction) super.createNewAction();
-        action.setSubShell(subshell);
-        return action;
-    }
-}

http://git-wip-us.apache.org/repos/asf/karaf/blob/1ee78df9/shell/console/src/test/java/org/apache/karaf/shell/commands/basic/TestCommands.java
----------------------------------------------------------------------
diff --git a/shell/console/src/test/java/org/apache/karaf/shell/commands/basic/TestCommands.java b/shell/console/src/test/java/org/apache/karaf/shell/commands/basic/TestCommands.java
deleted file mode 100644
index ce55446..0000000
--- a/shell/console/src/test/java/org/apache/karaf/shell/commands/basic/TestCommands.java
+++ /dev/null
@@ -1,184 +0,0 @@
-/*
- * 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.commands.basic;
-
-import java.util.List;
-import java.util.Arrays;
-import java.io.IOException;
-import java.io.StringWriter;
-import java.io.BufferedReader;
-import java.io.InputStreamReader;
-
-import junit.framework.TestCase;
-
-import org.apache.karaf.shell.commands.Action;
-import org.apache.karaf.shell.commands.Argument;
-import org.apache.karaf.shell.commands.Command;
-import org.apache.karaf.shell.commands.CommandException;
-import org.apache.karaf.shell.commands.Option;
-import org.apache.karaf.shell.commands.basic.SimpleCommand;
-import org.apache.felix.service.command.CommandSession;
-import org.apache.karaf.shell.console.ExitAction;
-
-public class TestCommands extends TestCase {
-
-    public void testSubShellScope() throws Exception {
-        Context c = new Context();
-        c.set("SCOPE", "*");
-        c.addCommand("*", new SimpleSubShell("foo"), "foo");
-        c.addCommand("*", new SimpleCommand(ExitAction.class), "exit");
-
-        String scope = (String) c.get("SCOPE");
-        c.execute("foo");
-        assertEquals("foo:" + scope, c.get("SCOPE"));
-        c.execute("exit");
-        assertEquals(scope, c.get("SCOPE"));
-    }
-
-    public void testPrompt() throws Exception {
-        Context c = new Context();
-        c.addCommand("echo", this);
-        c.set("USER", "test");
-        c.set("APPLICATION", "karaf");
-        //c.set("SCOPE", "");
-        Object p = c.execute("echo \"@|bold ${USER}|@${APPLICATION}:@|bold ${SCOPE}|> \"");
-        System.out.println("Prompt: " + p);
-    }
-
-    public void testCommand() throws Exception {
-        Context c = new Context();
-        c.addCommand("*", this, "capture");
-        c.addCommand("*", new SimpleCommand(MyAction.class), "my-action");
-
-        // Test help
-        Object help = c.execute("my-action --help | capture");
-        assertTrue(help instanceof String);
-        assertTrue(((String) help).indexOf("My Action") >= 0);
-        assertTrue(((String) help).indexOf("First option") >= 0);
-        assertTrue(((String) help).indexOf("Bundle ids") >= 0);
-
-        // Test required argument
-        try {
-            c.execute("my-action");
-            fail("Action should have thrown an exception because of a missing argument");
-        } catch (CommandException e) {
-            // ignore
-        }
-
-        // Test required argument
-        assertEquals(Arrays.asList(3), c.execute("my-action 3"));
-
-        // Test required argument
-        assertEquals(Arrays.asList(3), c.execute("my-action 3"));
-
-        // Test required argument
-        assertEquals(Arrays.asList(3, 5), c.execute("my-action 3 5"));
-
-        // Test option
-        assertEquals(Arrays.asList(4), c.execute("my-action -i 3"));
-
-        // Test option alias
-        assertEquals(Arrays.asList(4), c.execute("my-action --increment 3"));
-    }
-
-    public void testCommandTwoArguments() throws Exception {
-        Context c = new Context();
-        c.addCommand("*", new SimpleCommand(MyActionTwoArguments.class), "my-action-two-arguments");
-
-        // test required arguments
-        try {
-            c.execute("my-action-two-arguments");
-            fail("Action should have thrown an exception because of a missing argument");
-        } catch (CommandException e) {
-            assertEquals("Argument one is required", e.getMessage());
-        }
-
-        try {
-            c.execute("my-action-two-arguments 1");
-            fail("Action should have thrown an exception because of a missing argument");
-        } catch (CommandException e) {
-            assertEquals("Argument two is required", e.getMessage());
-        }
-
-        c.execute("my-action-two-arguments 1 2");
-    }
-
-    public String capture() throws IOException {
-        StringWriter sw = new StringWriter();
-        BufferedReader rdr = new BufferedReader(new InputStreamReader(System.in));
-        String s = rdr.readLine();
-        while (s != null) {
-            sw.write(s);
-            s = rdr.readLine();
-        }
-        return sw.toString();
-    }
-
-    public CharSequence echo(Object args[]) {
-        if (args == null) {
-            return "";
-        }
-
-        StringBuilder sb = new StringBuilder();
-        String del = "";
-        for (Object arg : args) {
-            sb.append(del);
-            if (arg != null) {
-                sb.append(arg);
-                del = " ";
-            }
-        }
-        return sb;
-    }
-
-    @Command(scope = "test", name = "my-action", description = "My Action")
-    public static class MyAction implements Action {
-
-        @Option(name = "-i", aliases = {"--increment"}, description = "First option")
-        private boolean increment;
-
-        @Argument(name = "ids", description = "Bundle ids", required = true, multiValued = true)
-        private List<Integer> ids;
-
-        public Object execute(CommandSession session) throws Exception {
-            if (increment) {
-                for (int i = 0; i < ids.size(); i++) {
-                    ids.set(i, ids.get(i) + 1);
-                }
-            }
-            return ids;
-        }
-    }
-
-    @Command(scope = "test", name = "my-action-two-arguments", description = "My Action with two arguments")
-    public static class MyActionTwoArguments implements Action {
-
-        @Argument(index = 0, name = "one", description = "one description", required = true, multiValued = false)
-        private String one;
-
-        @Argument(index = 1, name = "two", description = "two description", required = true, multiValued = false)
-        private String two;
-
-        public Object execute(CommandSession session) throws Exception {
-            return null;
-        }
-
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/karaf/blob/1ee78df9/shell/console/src/test/java/org/apache/karaf/shell/commands/meta/TestFormatting.java
----------------------------------------------------------------------
diff --git a/shell/console/src/test/java/org/apache/karaf/shell/commands/meta/TestFormatting.java b/shell/console/src/test/java/org/apache/karaf/shell/commands/meta/TestFormatting.java
deleted file mode 100644
index 24762f5..0000000
--- a/shell/console/src/test/java/org/apache/karaf/shell/commands/meta/TestFormatting.java
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * 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.commands.meta;
-
-import java.io.ByteArrayOutputStream;
-import java.io.PrintStream;
-
-import junit.framework.TestCase;
-
-import org.apache.karaf.shell.commands.meta.ActionMetaData;
-
-public class TestFormatting extends TestCase {
-    
-    public void testFormat() throws Exception {
-        ByteArrayOutputStream baos = new ByteArrayOutputStream();
-        ActionMetaData.printFormatted("  ", "  This is a test with a long paragraph\n\n  with an indented paragraph\nAnd another one\n", 20, new PrintStream(baos, true), true);
-        System.err.println(baos.toString());
-    }
-}

http://git-wip-us.apache.org/repos/asf/karaf/blob/1ee78df9/shell/console/src/test/java/org/apache/karaf/shell/console/completer/ArgumentCompleterTest.java
----------------------------------------------------------------------
diff --git a/shell/console/src/test/java/org/apache/karaf/shell/console/completer/ArgumentCompleterTest.java b/shell/console/src/test/java/org/apache/karaf/shell/console/completer/ArgumentCompleterTest.java
deleted file mode 100644
index e140ab3..0000000
--- a/shell/console/src/test/java/org/apache/karaf/shell/console/completer/ArgumentCompleterTest.java
+++ /dev/null
@@ -1,145 +0,0 @@
-/**
- * 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.console.completer;
-
-import java.util.Arrays;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-import org.apache.karaf.shell.commands.Action;
-import org.apache.karaf.shell.commands.Option;
-import org.apache.karaf.shell.commands.basic.SimpleCommand;
-import org.apache.felix.service.command.CommandSession;
-import org.apache.karaf.shell.console.CompletableFunction;
-import org.apache.karaf.shell.console.Completer;
-import org.junit.Test;
-
-import static org.junit.Assert.*;
-
-public class ArgumentCompleterTest extends CompleterTestSupport {
-
-    @Test
-    public void testParser1() throws Exception {
-        Parser parser = new Parser("echo foo | cat bar ; ta", 23);
-        List<List<List<String>>> p = parser.program();
-        assertEquals(1, parser.c0);
-        assertEquals(0, parser.c1);
-        assertEquals(0, parser.c2);
-        assertEquals(2, parser.c3);
-    }
-
-    @Test
-    public void testParser2() throws Exception {
-        Parser parser = new Parser("echo foo ; cat bar | ta", 23);
-        List<List<List<String>>> p = parser.program();
-        assertEquals(1, parser.c0);
-        assertEquals(1, parser.c1);
-        assertEquals(0, parser.c2);
-        assertEquals(2, parser.c3);
-    }
-
-    @Test
-    public void testParser3() throws Exception {
-        Parser parser = new Parser("echo foo ; cat bar | ta", 22);
-        List<List<List<String>>> p = parser.program();
-        assertEquals(1, parser.c0);
-        assertEquals(1, parser.c1);
-        assertEquals(0, parser.c2);
-        assertEquals(1, parser.c3);
-    }
-
-    @Test
-    public void testParser4() throws Exception {
-        Parser parser = new Parser("echo foo ; cat bar | ta reta", 27);
-        List<List<List<String>>> p = parser.program();
-        assertEquals(1, parser.c0);
-        assertEquals(1, parser.c1);
-        assertEquals(1, parser.c2);
-        assertEquals(3, parser.c3);
-    }
-
-    @Test
-    public void testParser5() throws Exception {
-        Parser parser = new Parser("echo foo ; cat bar | ta reta", 24);
-        List<List<List<String>>> p = parser.program();
-        assertEquals(1, parser.c0);
-        assertEquals(1, parser.c1);
-        assertEquals(1, parser.c2);
-        assertEquals(0, parser.c3);
-    }
-
-    @Test
-    public void testCompleteOptions() throws Exception {
-        CommandSession session = new DummyCommandSession();
-        Completer comp = new ArgumentCompleter(session, new MyFunction(), "my:action");
-        assertEquals(Arrays.asList("--check", "--foo", "--help", "--integer", "--string", "-c", "-f","-i","-s"), complete(comp, "action -"));
-        assertEquals(Arrays.asList(), complete(comp, "action --foo "));
-        assertEquals(Arrays.asList("action "), complete(comp, "acti"));
-        assertEquals(Arrays.asList("my:action "), complete(comp, "my:ac"));
-        assertEquals(Arrays.asList("--foo "), complete(comp, "action --f"));
-        assertEquals(Arrays.asList("--help "), complete(comp, "action --h"));
-        assertEquals(Arrays.asList("-c "), complete(comp, "action -c"));
-        assertEquals(Arrays.asList("--check "), complete(comp, "action -f 2 --c"));
-        assertEquals(Arrays.asList("foo1 "), complete(comp, "action -f 2 --check foo1"));
-        assertEquals(Arrays.asList("bar1", "bar2"), complete(comp, "action -f 2 --check foo1 "));
-        assertEquals(Arrays.asList("one", "two"), complete(comp, "action -s "));
-        assertEquals(Arrays.asList("one", "two"), complete(comp, "action --string "));
-        assertEquals(Arrays.asList("two "), complete(comp, "action -s t"));
-        assertEquals(Arrays.asList("1", "2"), complete(comp, "action -i "));
-        assertEquals(Arrays.asList("1", "2"), complete(comp, "action --integer "));
-        assertEquals(Arrays.asList("2 "), complete(comp, "action -i 2"));
-    }
-
-    public static class MyFunction extends SimpleCommand implements CompletableFunction {
-        public MyFunction() {
-            super(MyAction.class);
-        }
-        public List<Completer> getCompleters() {
-            return Arrays.<Completer>asList(
-                    new StringsCompleter(Arrays.asList("foo1", "foo2")),
-                    new StringsCompleter(Arrays.asList("bar1", "bar2"))
-            );
-        }
-
-        public Map<String,Completer> getOptionalCompleters() {
-            Map<String,Completer> completers = new HashMap<String,Completer>();
-            completers.put("-s",new StringsCompleter(Arrays.asList("one","two")));
-            completers.put("--integer",new StringsCompleter(Arrays.asList("1","2")));
-            return completers;
-        }
-    }
-
-    public static class MyAction implements Action {
-        @Option(name = "-f", aliases = { "--foo" })
-        int f;
-
-        @Option(name = "-c", aliases = "--check")
-        boolean check;
-
-        @Option(name = "-s", aliases = "--string")
-        String string;
-
-        @Option(name = "-i", aliases = "--integer")
-        String integer;
-
-        public Object execute(CommandSession session) throws Exception {
-            return null;
-        }
-    }
-
-}


[04/11] KARAF-2772 Extracting command-api

Posted by cs...@apache.org.
http://git-wip-us.apache.org/repos/asf/karaf/blob/1ee78df9/shell/console/src/main/java/org/apache/karaf/shell/commands/basic/DefaultActionPreparator.java
----------------------------------------------------------------------
diff --git a/shell/console/src/main/java/org/apache/karaf/shell/commands/basic/DefaultActionPreparator.java b/shell/console/src/main/java/org/apache/karaf/shell/commands/basic/DefaultActionPreparator.java
deleted file mode 100644
index 3ad8d29..0000000
--- a/shell/console/src/main/java/org/apache/karaf/shell/commands/basic/DefaultActionPreparator.java
+++ /dev/null
@@ -1,223 +0,0 @@
-/*
- * 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.commands.basic;
-
-import static org.apache.karaf.shell.commands.ansi.SimpleAnsi.COLOR_DEFAULT;
-import static org.apache.karaf.shell.commands.ansi.SimpleAnsi.COLOR_RED;
-import static org.apache.karaf.shell.commands.ansi.SimpleAnsi.INTENSITY_BOLD;
-import static org.apache.karaf.shell.commands.ansi.SimpleAnsi.INTENSITY_NORMAL;
-
-import java.lang.reflect.Field;
-import java.lang.reflect.Type;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-
-import org.apache.felix.gogo.commands.Action;
-import org.apache.felix.service.command.CommandSession;
-import org.apache.karaf.shell.commands.Argument;
-import org.apache.karaf.shell.commands.Command;
-import org.apache.karaf.shell.commands.CommandException;
-import org.apache.karaf.shell.commands.HelpOption;
-import org.apache.karaf.shell.commands.Option;
-import org.apache.karaf.shell.commands.converter.DefaultConverter;
-import org.apache.karaf.shell.commands.converter.GenericType;
-import org.apache.karaf.shell.commands.meta.ActionMetaData;
-import org.apache.karaf.shell.commands.meta.ActionMetaDataFactory;
-import org.apache.karaf.shell.console.NameScoping;
-
-public class DefaultActionPreparator implements ActionPreparator {
-
-    public boolean prepare(Action action, CommandSession session, List<Object> params) throws Exception {
-        ActionMetaData actionMetaData = new ActionMetaDataFactory().create(action.getClass());
-        Map<Option, Field> options = actionMetaData.getOptions();
-        Map<Argument, Field> arguments = actionMetaData.getArguments();
-        List<Argument> orderedArguments = actionMetaData.getOrderedArguments();
-        Command command2 = actionMetaData.getCommand();
-
-        if (command2 == null) {
-            // to avoid NPE with subshell
-            return true;
-        }
-
-        String commandErrorSt = (command2 != null) ? COLOR_RED
-                + "Error executing command " + command2.scope() + ":" 
-                + INTENSITY_BOLD + command2.name() + INTENSITY_NORMAL
-                + COLOR_DEFAULT + ": " : "";
-        for (Iterator<Object> it = params.iterator(); it.hasNext(); ) {
-            Object param = it.next();
-            if (HelpOption.HELP.name().equals(param)) {
-                int termWidth = getWidth(session);
-                boolean globalScope = NameScoping.isGlobalScope(session, actionMetaData.getCommand().scope());
-                actionMetaData.printUsage(action, System.out, globalScope, termWidth);
-                return false;
-            }
-        }
-        
-        // 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();
-
-            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) {
-                    throw new CommandException(commandErrorSt 
-                                + "undefined option " + INTENSITY_BOLD + param + INTENSITY_NORMAL + "\n"
-                                + "Try <command> --help' for more information.",
-                                        "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) {
-                        throw new CommandException(commandErrorSt
-                                + "missing value for option " + INTENSITY_BOLD + param + INTENSITY_NORMAL,
-                                "Missing value for option: " + param
-                        );
-                }
-                if (option.multiValued()) {
-                    @SuppressWarnings("unchecked")
-                    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()) {
-                        throw new CommandException(commandErrorSt +
-                                "too many arguments specified",
-                                "Too many arguments specified"
-                        );
-                }
-                Argument argument = orderedArguments.get(argIndex);
-                if (!argument.multiValued()) {
-                    argIndex++;
-                }
-                if (argument.multiValued()) {
-                    @SuppressWarnings("unchecked")
-                    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) {
-                    throw new CommandException(commandErrorSt +
-                            "option " + INTENSITY_BOLD + option.name() + INTENSITY_NORMAL + " is required",
-                            "Option " + option.name() + " is required"
-                    );
-            }
-        }
-        for (Argument argument : orderedArguments) {
-            if (argument.required() && argumentValues.get(argument) == null) {
-                    throw new CommandException(commandErrorSt +
-                            "argument " + INTENSITY_BOLD + argument.name() + INTENSITY_NORMAL + " is required",
-                            "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) {
-                    throw new CommandException(commandErrorSt +
-                            "unable to convert option " + INTENSITY_BOLD + entry.getKey().name() + INTENSITY_NORMAL + " with value '"
-                            + entry.getValue() + "' to type " + new GenericType(field.getGenericType()).toString(),
-                            "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) {
-                    throw new CommandException(commandErrorSt +
-                            "unable to convert argument " + INTENSITY_BOLD + entry.getKey().name() + INTENSITY_NORMAL + " with value '"
-                            + entry.getValue() + "' to type " + new GenericType(field.getGenericType()).toString(),
-                            "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 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);
-    }
-
-    private int getWidth(CommandSession session) {
-        Object cols = session.get("COLUMNS");
-        return  (cols != null && cols instanceof Integer) ? (Integer)cols : 80;
-    }
-}

http://git-wip-us.apache.org/repos/asf/karaf/blob/1ee78df9/shell/console/src/main/java/org/apache/karaf/shell/commands/basic/SimpleCommand.java
----------------------------------------------------------------------
diff --git a/shell/console/src/main/java/org/apache/karaf/shell/commands/basic/SimpleCommand.java b/shell/console/src/main/java/org/apache/karaf/shell/commands/basic/SimpleCommand.java
deleted file mode 100644
index 4ea5dfc..0000000
--- a/shell/console/src/main/java/org/apache/karaf/shell/commands/basic/SimpleCommand.java
+++ /dev/null
@@ -1,82 +0,0 @@
-/*
- * 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.commands.basic;
-
-import java.util.Hashtable;
-
-import org.apache.felix.service.command.Function;
-import org.apache.karaf.shell.commands.Action;
-import org.apache.karaf.shell.commands.Command;
-import org.apache.karaf.shell.commands.CommandWithAction;
-import org.osgi.framework.ServiceRegistration;
-import org.osgi.framework.BundleContext;
-
-/**
- * A very simple {@link Function} which creates {@link Action} based on a class name.
- */
-public class SimpleCommand extends AbstractCommand {
-
-    private Class<? extends Action> actionClass;
-
-    public SimpleCommand()
-    {
-    }
-
-    public SimpleCommand(Class<? extends Action> actionClass)
-    {
-        this.actionClass = actionClass;
-    }
-
-    public Class<? extends Action> getActionClass()
-    {
-        return actionClass;
-    }
-
-    public void setActionClass(Class<? extends Action> actionClass)
-    {
-        this.actionClass = actionClass;
-    }
-
-    public Action createNewAction() {
-        try {
-            return actionClass.newInstance();
-        } catch (InstantiationException e) {
-            throw new RuntimeException(e);
-        } catch (IllegalAccessException e) {
-            throw new RuntimeException(e);
-        }
-    }
-
-    public static ServiceRegistration export(BundleContext context, Class<? extends Action> actionClass)
-    {
-        Command cmd = actionClass.getAnnotation(Command.class);
-        if (cmd == null)
-        {
-            throw new IllegalArgumentException("Action class is not annotated with @Command");
-        }
-        Hashtable<String, String> props = new Hashtable<String, String>();
-        props.put("osgi.command.scope", cmd.scope());
-        props.put("osgi.command.function", cmd.name());
-        SimpleCommand command = new SimpleCommand(actionClass);
-        return context.registerService(
-                new String[] { Function.class.getName(), CommandWithAction.class.getName() },
-                command, props);
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/karaf/blob/1ee78df9/shell/console/src/main/java/org/apache/karaf/shell/commands/converter/DefaultConverter.java
----------------------------------------------------------------------
diff --git a/shell/console/src/main/java/org/apache/karaf/shell/commands/converter/DefaultConverter.java b/shell/console/src/main/java/org/apache/karaf/shell/commands/converter/DefaultConverter.java
deleted file mode 100644
index 54fe81a..0000000
--- a/shell/console/src/main/java/org/apache/karaf/shell/commands/converter/DefaultConverter.java
+++ /dev/null
@@ -1,401 +0,0 @@
-/*
- * 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.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();
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/karaf/blob/1ee78df9/shell/console/src/main/java/org/apache/karaf/shell/commands/converter/GenericType.java
----------------------------------------------------------------------
diff --git a/shell/console/src/main/java/org/apache/karaf/shell/commands/converter/GenericType.java b/shell/console/src/main/java/org/apache/karaf/shell/commands/converter/GenericType.java
deleted file mode 100644
index 2f2b5e0..0000000
--- a/shell/console/src/main/java/org/apache/karaf/shell/commands/converter/GenericType.java
+++ /dev/null
@@ -1,195 +0,0 @@
-/*
- * 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.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);
-	}
-
-}

http://git-wip-us.apache.org/repos/asf/karaf/blob/1ee78df9/shell/console/src/main/java/org/apache/karaf/shell/commands/converter/ReifiedType.java
----------------------------------------------------------------------
diff --git a/shell/console/src/main/java/org/apache/karaf/shell/commands/converter/ReifiedType.java b/shell/console/src/main/java/org/apache/karaf/shell/commands/converter/ReifiedType.java
deleted file mode 100644
index dda4477..0000000
--- a/shell/console/src/main/java/org/apache/karaf/shell/commands/converter/ReifiedType.java
+++ /dev/null
@@ -1,116 +0,0 @@
-/*
- * 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.karaf.shell.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;
-	}
-}

http://git-wip-us.apache.org/repos/asf/karaf/blob/1ee78df9/shell/console/src/main/java/org/apache/karaf/shell/commands/meta/ActionMetaData.java
----------------------------------------------------------------------
diff --git a/shell/console/src/main/java/org/apache/karaf/shell/commands/meta/ActionMetaData.java b/shell/console/src/main/java/org/apache/karaf/shell/commands/meta/ActionMetaData.java
deleted file mode 100644
index e0bd11a..0000000
--- a/shell/console/src/main/java/org/apache/karaf/shell/commands/meta/ActionMetaData.java
+++ /dev/null
@@ -1,296 +0,0 @@
-/*
- * 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.commands.meta;
-
-import static org.apache.karaf.shell.commands.ansi.SimpleAnsi.INTENSITY_BOLD;
-import static org.apache.karaf.shell.commands.ansi.SimpleAnsi.INTENSITY_NORMAL;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.io.PrintStream;
-import java.io.Reader;
-import java.io.StringWriter;
-import java.lang.reflect.Field;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-import org.apache.felix.gogo.commands.Action;
-import org.apache.karaf.shell.commands.Argument;
-import org.apache.karaf.shell.commands.Command;
-import org.apache.karaf.shell.commands.HelpOption;
-import org.apache.karaf.shell.commands.Option;
-import org.apache.karaf.shell.commands.ansi.SimpleAnsi;
-import org.apache.karaf.shell.console.Completer;
-
-public class ActionMetaData {
-
-    private final Class<? extends Action> actionClass;
-    private final Command command;
-    private final Map<Option, Field> options;
-    private final Map<Argument, Field> arguments;
-    List<Argument> orderedArguments;
-    private final Completer[] completers;
-
-    public ActionMetaData(Class<? extends Action> actionClass, Command command, Map<Option, Field> options, Map<Argument, Field> args,
-            List<Argument> orderedArguments, Completer[] completers) {
-        super();
-        this.actionClass = actionClass;
-        this.command = command;
-        this.options = options;
-        this.arguments = args;
-        this.orderedArguments = orderedArguments;
-        this.completers = completers;
-    }
-
-    public Class<? extends Action> getActionClass() {
-        return actionClass;
-    }
-    
-    public Command getCommand() {
-        return command;
-    }
-
-    public Map<Option, Field> getOptions() {
-        return options;
-    }
-
-    public Map<Argument, Field> getArguments() {
-        return arguments;
-    }
-
-    public Completer[] getCompleters() {
-        return completers;
-    }
-
-    public List<Argument> getOrderedArguments() {
-        return orderedArguments;
-    }
-
-    public void printUsage(Action action, PrintStream out, boolean globalScope, int termWidth) {
-        if (command != null) {
-            List<Argument> argumentsSet = new ArrayList<Argument>(arguments.keySet());
-            Collections.sort(argumentsSet, new Comparator<Argument>() {
-                public int compare(Argument o1, Argument o2) {
-                    return Integer.valueOf(o1.index()).compareTo(Integer.valueOf(o2.index()));
-                }
-            });
-            Set<Option> optionsSet = new HashSet<Option>(options.keySet());
-            optionsSet.add(HelpOption.HELP);
-            if (command != null && (command.description() != null || command.name() != null)) {
-                out.println(INTENSITY_BOLD + "DESCRIPTION" + INTENSITY_NORMAL);
-                out.print("        ");
-                if (command.name() != null) {
-                    if (globalScope) {
-                        out.println(INTENSITY_BOLD + command.name() + INTENSITY_NORMAL);
-                    } else {
-                        out.println(command.scope() + ":" + INTENSITY_BOLD + command.name() + INTENSITY_NORMAL);
-                    }
-                    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 : argumentsSet) {
-                    if (!argument.required()) {
-                        syntax.append(String.format("[%s] ", argument.name()));
-                    } else {
-                        syntax.append(String.format("%s ", argument.name()));
-                    }
-                }
-            }
-
-            out.println(INTENSITY_BOLD + "SYNTAX" + INTENSITY_NORMAL);
-            out.print("        ");
-            out.println(syntax.toString());
-            out.println();
-            if (arguments.size() > 0) {
-                out.println(INTENSITY_BOLD + "ARGUMENTS" + INTENSITY_NORMAL);
-                for (Argument argument : argumentsSet) {
-                    out.print("        ");
-                    out.println(INTENSITY_BOLD + argument.name() + INTENSITY_NORMAL);
-                    ActionMetaData.printFormatted("                ", argument.description(), termWidth, out, true);
-                    if (!argument.required()) {
-                        if (argument.valueToShowInHelp() != null && argument.valueToShowInHelp().length() != 0) {
-                            if (Argument.DEFAULT_STRING.equals(argument.valueToShowInHelp())) {
-                                Object o = getDefaultValue(action, argument);
-                                String defaultValue = getDefaultValueString(o);
-                                if (defaultValue != null) {
-                                    printDefaultsTo(out, defaultValue);
-                                }
-                            } else {
-                                printDefaultsTo(out, argument.valueToShowInHelp());
-                            }
-                        }
-                    }
-                }
-                out.println();
-            }
-            if (options.size() > 0) {
-                out.println(INTENSITY_BOLD + "OPTIONS" + INTENSITY_NORMAL);
-                for (Option option : optionsSet) {
-                    String opt = option.name();
-                    for (String alias : option.aliases()) {
-                        opt += ", " + alias;
-                    }
-                    out.print("        ");
-                    out.println(INTENSITY_BOLD + opt + INTENSITY_NORMAL);
-                    ActionMetaData.printFormatted("                ", option.description(), termWidth, out, true);
-                    if (option.valueToShowInHelp() != null && option.valueToShowInHelp().length() != 0) {
-                        if (Option.DEFAULT_STRING.equals(option.valueToShowInHelp())) {
-                            Object o = getDefaultValue(action, option);
-                            String defaultValue = getDefaultValueString(o);
-                            if (defaultValue != null) {
-                                printDefaultsTo(out, defaultValue);
-                            }
-                        } else {
-                            printDefaultsTo(out, option.valueToShowInHelp());
-                        }
-                    }
-                }
-                out.println();
-            }
-            if (command.detailedDescription().length() > 0) {
-                out.println(INTENSITY_BOLD + "DETAILS" + INTENSITY_NORMAL);
-                String desc = getDetailedDescription();
-                ActionMetaData.printFormatted("        ", desc, termWidth, out, true);
-            }
-        }
-    }
-    
-    public Object getDefaultValue(Action action, Argument argument) {
-        try {
-            arguments.get(argument).setAccessible(true);
-            return arguments.get(argument).get(action);
-        } catch (Exception e) {
-            return null;
-        }
-    }
-    
-    public Object getDefaultValue(Action action, Option option) {
-        try {
-            options.get(option).setAccessible(true);
-            return options.get(option).get(action);
-        } catch (Exception e) {
-            return null;
-        }
-    }
-    
-    public String getDetailedDescription() {
-        String desc = command.detailedDescription();
-        return loadDescription(actionClass, desc);
-    }
-    
-    private String loadDescription(Class<?> clazz, String desc) {
-        if (desc != null && desc.startsWith("classpath:")) {
-            desc = loadClassPathResource(clazz, desc.substring("classpath:".length()));
-        }
-        return desc;
-    }
-    
-    public String getDefaultValueString(Object o) {
-        if (o != null && (!(o instanceof Boolean) || ((Boolean)o))
-            && (!(o instanceof Number) || ((Number)o).doubleValue() != 0.0)) {
-            return o.toString();
-        } else {
-            return null;
-        }
-    }
-
-    private void printDefaultsTo(PrintStream out, String value) {
-        out.println("                (defaults to " + value + ")");
-    }
-
-    static void printFormatted(String prefix, String str, int termWidth, PrintStream out, boolean prefixFirstLine) {
-        int pfxLen = prefix.length();
-        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;
-            }
-        }
-    }
-
-    private String loadClassPathResource(Class<?> clazz, String path) {
-        InputStream is = clazz.getResourceAsStream(path);
-        if (is == null) {
-            is = clazz.getClassLoader().getResourceAsStream(path);
-        }
-        if (is == null) {
-            return "Unable to load description from " + path;
-        }
-    
-        try {
-            Reader r = new InputStreamReader(is);
-            StringWriter sw = new StringWriter();
-            int c;
-            while ((c = r.read()) != -1) {
-                sw.append((char) c);
-            }
-            return sw.toString();
-        } catch (IOException e) {
-            return "Unable to load description from " + path;
-        } finally {
-            try {
-                is.close();
-            } catch (IOException e) {
-                // Ignore
-            }
-        }
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/karaf/blob/1ee78df9/shell/console/src/main/java/org/apache/karaf/shell/commands/meta/ActionMetaDataFactory.java
----------------------------------------------------------------------
diff --git a/shell/console/src/main/java/org/apache/karaf/shell/commands/meta/ActionMetaDataFactory.java b/shell/console/src/main/java/org/apache/karaf/shell/commands/meta/ActionMetaDataFactory.java
deleted file mode 100644
index 91bc48f..0000000
--- a/shell/console/src/main/java/org/apache/karaf/shell/commands/meta/ActionMetaDataFactory.java
+++ /dev/null
@@ -1,251 +0,0 @@
-/*
- * 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.commands.meta;
-
-import java.lang.annotation.Annotation;
-import java.lang.reflect.Field;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-import org.apache.felix.gogo.commands.Action;
-import org.apache.karaf.shell.commands.Argument;
-import org.apache.karaf.shell.commands.Command;
-import org.apache.karaf.shell.commands.Option;
-
-public class ActionMetaDataFactory {
-
-    public ActionMetaData create(Class<? extends Action> actionClass) {
-        Command command = getCommand(actionClass);
-        Map<Option, Field> options = new HashMap<Option, Field>();
-        Map<Argument, Field> arguments = new HashMap<Argument, Field>();
-        List<Argument> orderedArguments = new ArrayList<Argument>();
-
-        for (Class<?> type = actionClass; type != null; type = type.getSuperclass()) {
-            for (Field field : type.getDeclaredFields()) {
-                Option option = field.getAnnotation(Option.class);
-                if (option == null) {
-                    option = getAndConvertDeprecatedOption(field);
-                }
-                if (option != null) {
-                    options.put(option, field);
-                }
-
-                Argument argument = field.getAnnotation(Argument.class);
-                if (argument == null) {
-                    argument = getAndConvertDeprecatedArgument(field);
-                }
-                if (argument != null) {
-                    argument = replaceDefaultArgument(field, argument);
-                    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 + " on Action " + actionClass.getName());
-                    }
-                    orderedArguments.set(index, argument);
-                }
-            }
-        }
-        assertIndexesAreCorrect(actionClass, orderedArguments);
-
-        return new ActionMetaData(actionClass, command, options, arguments, orderedArguments, null);
-    }
-
-    public Command getCommand(Class<? extends Action> actionClass) {
-        Command command = actionClass.getAnnotation(Command.class);
-        if (command == null) {
-            command = getAndConvertDeprecatedCommand(actionClass);
-        }
-        return command;
-    }
-
-    @SuppressWarnings("deprecation")
-    public Command getAndConvertDeprecatedCommand(Class<? extends Action> actionClass) {
-        final org.apache.felix.gogo.commands.Command oldCommand = actionClass.getAnnotation(org.apache.felix.gogo.commands.Command.class);
-        if (oldCommand == null) {
-            return null;
-        }
-        return new Command() {
-            
-            @Override
-            public Class<? extends Annotation> annotationType() {
-                return Command.class;
-            }
-            
-            @Override
-            public String scope() {
-                return oldCommand.scope();
-            }
-            
-            @Override
-            public String name() {
-                return oldCommand.name();
-            }
-            
-            @Override
-            public String detailedDescription() {
-                return oldCommand.detailedDescription();
-            }
-
-            @Override
-            public String description() {
-                return oldCommand.description();
-            }
-        };
-    }
-
-    @SuppressWarnings("deprecation")
-    private Option getAndConvertDeprecatedOption(Field field) {
-        final org.apache.felix.gogo.commands.Option oldOption = field.getAnnotation(org.apache.felix.gogo.commands.Option.class);
-        if (oldOption == null) {
-            return null;
-        }
-        return new Option() {
-            
-            @Override
-            public Class<? extends Annotation> annotationType() {
-                return Option.class;
-            }
-            
-            @Override
-            public String valueToShowInHelp() {
-                return oldOption.valueToShowInHelp();
-            }
-            
-            @Override
-            public boolean required() {
-                return oldOption.required();
-            }
-            
-            @Override
-            public String name() {
-                return oldOption.name();
-            }
-            
-            @Override
-            public boolean multiValued() {
-                return oldOption.multiValued();
-            }
-            
-            @Override
-            public String description() {
-                return oldOption.description();
-            }
-            
-            @Override
-            public String[] aliases() {
-                return oldOption.aliases();
-            }
-        };
-    }
-    
-    @SuppressWarnings("deprecation")
-    private Argument getAndConvertDeprecatedArgument(Field field) {
-        final org.apache.felix.gogo.commands.Argument oldArgument = field.getAnnotation(org.apache.felix.gogo.commands.Argument.class);
-        if (oldArgument == null) {
-            return null;
-        }
-        return new Argument() {
-            
-            @Override
-            public Class<? extends Annotation> annotationType() {
-                return Argument.class;
-            }
-            
-            @Override
-            public String valueToShowInHelp() {
-                return oldArgument.valueToShowInHelp();
-            }
-            
-            @Override
-            public boolean required() {
-                return oldArgument.required();
-            }
-            
-            @Override
-            public String name() {
-                return oldArgument.name();
-            }
-            
-            @Override
-            public boolean multiValued() {
-                return oldArgument.multiValued();
-            }
-            
-            @Override
-            public int index() {
-                return oldArgument.index();
-            }
-            
-            @Override
-            public String description() {
-                return oldArgument.description();
-            }
-        };
-    }
-
-    private Argument replaceDefaultArgument(Field field, Argument argument) {
-        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();
-                }
-            };
-        }
-        return argument;
-    }
-    
-    private void assertIndexesAreCorrect(Class<? extends Action> actionClass, List<Argument> orderedArguments) {
-        for (int i = 0; i < orderedArguments.size(); i++) {
-            if (orderedArguments.get(i) == null) {
-                throw new IllegalArgumentException("Missing argument for index: " + i + " on Action " + actionClass.getName());
-            }
-        }
-    }
-}

http://git-wip-us.apache.org/repos/asf/karaf/blob/1ee78df9/shell/console/src/main/java/org/apache/karaf/shell/console/AbstractAction.java
----------------------------------------------------------------------
diff --git a/shell/console/src/main/java/org/apache/karaf/shell/console/AbstractAction.java b/shell/console/src/main/java/org/apache/karaf/shell/console/AbstractAction.java
deleted file mode 100644
index f9990c5..0000000
--- a/shell/console/src/main/java/org/apache/karaf/shell/console/AbstractAction.java
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * 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.console;
-
-import org.apache.karaf.shell.commands.Action;
-import org.apache.felix.service.command.CommandSession;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-public abstract class AbstractAction implements Action {
-
-    protected final Logger log = LoggerFactory.getLogger(getClass());
-    protected CommandSession session;
-
-    public Object execute(CommandSession session) throws Exception {
-        this.session = session;
-        return doExecute();
-    }
-
-    protected abstract Object doExecute() throws Exception;
-    
-    /**
-     * This is for long running commands to be interrupted by ctrl-c
-     * 
-     * @throws InterruptedException
-     */
-    public static void checkInterrupted() throws InterruptedException {
-        Thread.yield(); 
-        if (Thread.currentThread().isInterrupted()) {
-            throw new InterruptedException();
-        }
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/karaf/blob/1ee78df9/shell/console/src/main/java/org/apache/karaf/shell/console/BlueprintContainerAware.java
----------------------------------------------------------------------
diff --git a/shell/console/src/main/java/org/apache/karaf/shell/console/BlueprintContainerAware.java b/shell/console/src/main/java/org/apache/karaf/shell/console/BlueprintContainerAware.java
deleted file mode 100644
index d566f48..0000000
--- a/shell/console/src/main/java/org/apache/karaf/shell/console/BlueprintContainerAware.java
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * 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.console;
-
-import org.osgi.service.blueprint.container.BlueprintContainer;
-
-public interface BlueprintContainerAware {
-
-    void setBlueprintContainer(BlueprintContainer blueprintContainer);
-
-}

http://git-wip-us.apache.org/repos/asf/karaf/blob/1ee78df9/shell/console/src/main/java/org/apache/karaf/shell/console/BundleContextAware.java
----------------------------------------------------------------------
diff --git a/shell/console/src/main/java/org/apache/karaf/shell/console/BundleContextAware.java b/shell/console/src/main/java/org/apache/karaf/shell/console/BundleContextAware.java
deleted file mode 100644
index bbd03c5..0000000
--- a/shell/console/src/main/java/org/apache/karaf/shell/console/BundleContextAware.java
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * 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.console;
-
-import org.osgi.framework.BundleContext;
-
-public interface BundleContextAware {
-
-    void setBundleContext(BundleContext bundleContext);
-
-}

http://git-wip-us.apache.org/repos/asf/karaf/blob/1ee78df9/shell/console/src/main/java/org/apache/karaf/shell/console/CloseShellException.java
----------------------------------------------------------------------
diff --git a/shell/console/src/main/java/org/apache/karaf/shell/console/CloseShellException.java b/shell/console/src/main/java/org/apache/karaf/shell/console/CloseShellException.java
deleted file mode 100644
index 81cee88..0000000
--- a/shell/console/src/main/java/org/apache/karaf/shell/console/CloseShellException.java
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * 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.console;
-
-/**
- * Exception thrown to indicate the console to close.
- */
-@SuppressWarnings("serial")
-public class CloseShellException extends Exception {
-
-}

http://git-wip-us.apache.org/repos/asf/karaf/blob/1ee78df9/shell/console/src/main/java/org/apache/karaf/shell/console/CommandSessionHolder.java
----------------------------------------------------------------------
diff --git a/shell/console/src/main/java/org/apache/karaf/shell/console/CommandSessionHolder.java b/shell/console/src/main/java/org/apache/karaf/shell/console/CommandSessionHolder.java
deleted file mode 100644
index f07554f..0000000
--- a/shell/console/src/main/java/org/apache/karaf/shell/console/CommandSessionHolder.java
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * 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.console;
-
-import org.apache.felix.service.command.CommandSession;
-
-public class CommandSessionHolder {
-
-    private static final ThreadLocal<CommandSession> session = new ThreadLocal<CommandSession>();
-
-    public static CommandSession getSession() {
-        return session.get();
-    }
-
-    public static void setSession(CommandSession commandSession) {
-        session.set(commandSession);
-    }
-
-    public static void unset() {
-        session.remove();
-    }
-}

http://git-wip-us.apache.org/repos/asf/karaf/blob/1ee78df9/shell/console/src/main/java/org/apache/karaf/shell/console/CompletableFunction.java
----------------------------------------------------------------------
diff --git a/shell/console/src/main/java/org/apache/karaf/shell/console/CompletableFunction.java b/shell/console/src/main/java/org/apache/karaf/shell/console/CompletableFunction.java
deleted file mode 100644
index fcb96da..0000000
--- a/shell/console/src/main/java/org/apache/karaf/shell/console/CompletableFunction.java
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * 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.console;
-
-import java.util.List;
-import java.util.Map;
-
-import org.apache.felix.service.command.Function;
-
-public interface CompletableFunction extends Function {
-
-    List<Completer> getCompleters();
-    Map<String, Completer> getOptionalCompleters();
-
-}

http://git-wip-us.apache.org/repos/asf/karaf/blob/1ee78df9/shell/console/src/main/java/org/apache/karaf/shell/console/Completer.java
----------------------------------------------------------------------
diff --git a/shell/console/src/main/java/org/apache/karaf/shell/console/Completer.java b/shell/console/src/main/java/org/apache/karaf/shell/console/Completer.java
deleted file mode 100644
index 59f7897..0000000
--- a/shell/console/src/main/java/org/apache/karaf/shell/console/Completer.java
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * 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.console;
-
-import java.util.List;
-
-public interface Completer {
-
-    int complete(String buffer, int cursor, List<String> candidates);
-
-}

http://git-wip-us.apache.org/repos/asf/karaf/blob/1ee78df9/shell/console/src/main/java/org/apache/karaf/shell/console/Console.java
----------------------------------------------------------------------
diff --git a/shell/console/src/main/java/org/apache/karaf/shell/console/Console.java b/shell/console/src/main/java/org/apache/karaf/shell/console/Console.java
deleted file mode 100644
index fcca7ad..0000000
--- a/shell/console/src/main/java/org/apache/karaf/shell/console/Console.java
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * 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.console;
-
-import org.apache.felix.service.command.CommandSession;
-
-public interface Console extends Runnable {
-
-    CommandSession getSession();
-
-    void close(boolean closedByUser);
-
-}

http://git-wip-us.apache.org/repos/asf/karaf/blob/1ee78df9/shell/console/src/main/java/org/apache/karaf/shell/console/ExitAction.java
----------------------------------------------------------------------
diff --git a/shell/console/src/main/java/org/apache/karaf/shell/console/ExitAction.java b/shell/console/src/main/java/org/apache/karaf/shell/console/ExitAction.java
deleted file mode 100644
index 1449315..0000000
--- a/shell/console/src/main/java/org/apache/karaf/shell/console/ExitAction.java
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * 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.console;
-
-import org.apache.karaf.shell.commands.Command;
-
-/**
- * Exit from the current sub-shell and get back to the previous one.
- */
-@Command(scope = "*", name = "exit", description = "Exit from the current shell")
-public class ExitAction extends AbstractAction {
-    
-    public Object doExecute() throws Exception {
-        // get the current sub-shell
-        String currentSubShell = (String) session.get("SUBSHELL");
-        if (!currentSubShell.isEmpty()) {
-            if (currentSubShell.contains(":")) {
-                int index = currentSubShell.lastIndexOf(":");
-                session.put("SUBSHELL", currentSubShell.substring(0, index));
-            } else {
-                session.put("SUBSHELL", "");
-            }
-            String currentScope = (String) session.get("SCOPE");
-            int index = currentScope.indexOf(":");
-            session.put("SCOPE", currentScope.substring(index + 1));
-        }
-        return null;
-    }
-    
-}

http://git-wip-us.apache.org/repos/asf/karaf/blob/1ee78df9/shell/console/src/main/java/org/apache/karaf/shell/console/HelpProvider.java
----------------------------------------------------------------------
diff --git a/shell/console/src/main/java/org/apache/karaf/shell/console/HelpProvider.java b/shell/console/src/main/java/org/apache/karaf/shell/console/HelpProvider.java
deleted file mode 100644
index 7e3bca7..0000000
--- a/shell/console/src/main/java/org/apache/karaf/shell/console/HelpProvider.java
+++ /dev/null
@@ -1,26 +0,0 @@
-/**
- *
- * 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.console;
-
-import org.apache.felix.service.command.CommandSession;
-
-public interface HelpProvider {
-    
-    String getHelp(CommandSession session, String path);
-    
-}

http://git-wip-us.apache.org/repos/asf/karaf/blob/1ee78df9/shell/console/src/main/java/org/apache/karaf/shell/console/MultiException.java
----------------------------------------------------------------------
diff --git a/shell/console/src/main/java/org/apache/karaf/shell/console/MultiException.java b/shell/console/src/main/java/org/apache/karaf/shell/console/MultiException.java
deleted file mode 100644
index 6e6185c..0000000
--- a/shell/console/src/main/java/org/apache/karaf/shell/console/MultiException.java
+++ /dev/null
@@ -1,95 +0,0 @@
-/*
- * 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.console;
-
-import java.io.PrintStream;
-import java.io.PrintWriter;
-import java.util.ArrayList;
-import java.util.List;
-
-@SuppressWarnings("serial")
-public class MultiException extends Exception {
-
-    private List<Exception> exceptions = new ArrayList<Exception>();
-
-    public MultiException(String message) {
-        super(message);
-    }
-
-    public MultiException(String message, List<Exception> exceptions) {
-        super(message);
-        this.exceptions = exceptions;
-    }
-
-    public void addException(Exception e) {
-        exceptions.add(e);
-    }
-
-    public void throwIfExceptions() throws MultiException {
-        if (!exceptions.isEmpty()) {
-            throw this;
-        }
-    }
-    
-    public Throwable[] getCauses() {
-        return exceptions.toArray(new Throwable[exceptions.size()]);
-    }
-
-    @Override
-    public void printStackTrace()
-    {
-        super.printStackTrace();
-        for (Exception e : exceptions) {
-            e.printStackTrace();
-        }
-    }
-
-
-    /* ------------------------------------------------------------------------------- */
-    /**
-     * @see java.lang.Throwable#printStackTrace(java.io.PrintStream)
-     */
-    @Override
-    public void printStackTrace(PrintStream out)
-    {
-        super.printStackTrace(out);
-        for (Exception e : exceptions) {
-            e.printStackTrace(out);
-        }
-    }
-
-    @Override
-    public void printStackTrace(PrintWriter out)
-    {
-        super.printStackTrace(out);
-        for (Exception e : exceptions) {
-            e.printStackTrace(out);
-        }
-    }
-
-    public static void throwIf(String message, List<Exception> exceptions) throws MultiException {
-        if (exceptions != null && !exceptions.isEmpty()) {
-            StringBuilder sb = new StringBuilder(message);
-            sb.append(":");
-            for (Exception e : exceptions) {
-                sb.append("\n\t");
-                sb.append(e.getMessage());
-            }
-            throw new MultiException(sb.toString(), exceptions);
-        }
-    }
-}


[03/11] KARAF-2772 Extracting command-api

Posted by cs...@apache.org.
http://git-wip-us.apache.org/repos/asf/karaf/blob/1ee78df9/shell/console/src/main/java/org/apache/karaf/shell/console/NameScoping.java
----------------------------------------------------------------------
diff --git a/shell/console/src/main/java/org/apache/karaf/shell/console/NameScoping.java b/shell/console/src/main/java/org/apache/karaf/shell/console/NameScoping.java
deleted file mode 100644
index 355464d..0000000
--- a/shell/console/src/main/java/org/apache/karaf/shell/console/NameScoping.java
+++ /dev/null
@@ -1,79 +0,0 @@
-/**
- *
- * 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.console;
-
-import org.apache.felix.service.command.CommandSession;
-
-
-/**
- * A helper class for name scoping
- */
-public class NameScoping {
-
-    public static final String MULTI_SCOPE_MODE_KEY = "MULTI_SCOPE_MODE";
-
-    /**
-     * Returns the name of the command which can omit the global scope prefix if the command starts with the
-     * same prefix as the current application
-     */
-    public static String getCommandNameWithoutGlobalPrefix(CommandSession session, String key) {
-        if (!isMultiScopeMode(session)) {
-            String globalScope = (String) (session != null ? session.get("APPLICATION") : null);
-            if (globalScope != null) {
-                String prefix = globalScope + ":";
-                if (key.startsWith(prefix)) {
-                    // TODO we may only want to do this for single-scope mode when outside of OSGi?
-                    // so we may want to also check for a isMultiScope mode == false
-                    return key.substring(prefix.length());
-                }
-            }
-        }
-        return key;
-    }
-
-    /**
-     * Returns true if the given scope is the global scope so that it can be hidden from help messages
-     */
-    public static boolean isGlobalScope(CommandSession session, String scope) {
-        if (session == null)
-            return false;
-
-        if (!isMultiScopeMode(session)) {
-            String globalScope = (String) session.get("APPLICATION");
-            if (globalScope != null) {
-                return scope.equals(globalScope);
-            }
-        }
-        return false;
-    }
-
-    /**
-     * Returns true if we are in multi-scope mode (the default) or if we are in single scope mode which means we
-     * avoid prefixing commands with their scope
-     */
-    public static boolean isMultiScopeMode(CommandSession session) {
-        if (session == null)
-            return false;
-
-        Object value = session.get(MULTI_SCOPE_MODE_KEY);
-        if (value != null && value.equals("false")) {
-            return false;
-        }
-        return true;
-    }
-}

http://git-wip-us.apache.org/repos/asf/karaf/blob/1ee78df9/shell/console/src/main/java/org/apache/karaf/shell/console/OsgiCommandSupport.java
----------------------------------------------------------------------
diff --git a/shell/console/src/main/java/org/apache/karaf/shell/console/OsgiCommandSupport.java b/shell/console/src/main/java/org/apache/karaf/shell/console/OsgiCommandSupport.java
deleted file mode 100644
index d737865..0000000
--- a/shell/console/src/main/java/org/apache/karaf/shell/console/OsgiCommandSupport.java
+++ /dev/null
@@ -1,103 +0,0 @@
-/*
- * 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.console;
-
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.List;
-
-import org.apache.felix.service.command.CommandSession;
-import org.apache.karaf.shell.commands.Action;
-import org.osgi.framework.Bundle;
-import org.osgi.framework.BundleContext;
-import org.osgi.framework.InvalidSyntaxException;
-import org.osgi.framework.ServiceReference;
-
-public abstract class OsgiCommandSupport extends AbstractAction implements Action, BundleContextAware {
-
-    protected BundleContext bundleContext;
-    protected List<ServiceReference<?>> usedReferences;
-
-    @Override
-    public Object execute(CommandSession session) throws Exception {
-        try {
-            return super.execute(session);
-        } finally {
-            ungetServices();
-        }
-    }
-
-    public BundleContext getBundleContext() {
-        Bundle framework = bundleContext.getBundle(0);
-        return framework == null? bundleContext: framework.getBundleContext();
-    }
-
-    public void setBundleContext(BundleContext bundleContext) {
-        this.bundleContext = bundleContext;
-    }
-
-    protected <T> List<T> getAllServices(Class<T> clazz) {
-        try {
-            return getAllServices(clazz, null);
-        } catch (InvalidSyntaxException e) {
-            throw new IllegalStateException(e);
-        }
-    }
-
-    protected <T> List<T> getAllServices(Class<T> clazz, String filter) throws InvalidSyntaxException {
-        Collection<ServiceReference<T>> references = getBundleContext().getServiceReferences(clazz, filter);
-        List<T> services = new ArrayList<T>();
-        if (references != null) {
-            for (ServiceReference<T> ref : references) {
-                T t = getService(clazz, ref);
-                services.add(t);
-            }
-        }
-        return services;
-    }
-
-    protected <T> T getService(Class<T> clazz) {
-        ServiceReference<T> sr = getBundleContext().getServiceReference(clazz);
-        if (sr != null) {
-            return getService(clazz, sr);
-        } else {
-            return null;
-        }
-    }
-
-    protected <T> T getService(Class<T> clazz, ServiceReference<T> reference) {
-        T t = getBundleContext().getService(reference);
-        if (t != null) {
-            if (usedReferences == null) {
-                usedReferences = new ArrayList<ServiceReference<?>>();
-            }
-            usedReferences.add(reference);
-        }
-        return t;
-    }
-
-    protected void ungetServices() {
-        if (usedReferences != null) {
-            for (ServiceReference<?> ref : usedReferences) {
-                getBundleContext().ungetService(ref);
-            }
-        }
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/karaf/blob/1ee78df9/shell/console/src/main/java/org/apache/karaf/shell/console/SessionProperties.java
----------------------------------------------------------------------
diff --git a/shell/console/src/main/java/org/apache/karaf/shell/console/SessionProperties.java b/shell/console/src/main/java/org/apache/karaf/shell/console/SessionProperties.java
deleted file mode 100644
index 30f3433..0000000
--- a/shell/console/src/main/java/org/apache/karaf/shell/console/SessionProperties.java
+++ /dev/null
@@ -1,28 +0,0 @@
-/*
- * 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.console;
-
-public class SessionProperties {
-
-    public static final String PRINT_STACK_TRACES = "karaf.printStackTraces";
-    public static final String LAST_EXCEPTION = "karaf.lastException";
-    public static final String IGNORE_INTERRUPTS = "karaf.ignoreInterrupts";
-    public static final String COMPLETION_MODE = "karaf.completionMode";
-
-}

http://git-wip-us.apache.org/repos/asf/karaf/blob/1ee78df9/shell/console/src/main/java/org/apache/karaf/shell/console/SubShell.java
----------------------------------------------------------------------
diff --git a/shell/console/src/main/java/org/apache/karaf/shell/console/SubShell.java b/shell/console/src/main/java/org/apache/karaf/shell/console/SubShell.java
deleted file mode 100644
index 2de0d20..0000000
--- a/shell/console/src/main/java/org/apache/karaf/shell/console/SubShell.java
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * 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.console;
-
-public interface SubShell {
-
-    /**
-     * Returns the name of the command if used inside a shell
-     */
-    String getName();
-
-    /**
-     * Returns the description of the command which is used to generate command line help
-     */
-    String getDescription();
-
-    /**
-     * Returns a detailed description of the command
-     */
-    String getDetailedDescription();
-
-}

http://git-wip-us.apache.org/repos/asf/karaf/blob/1ee78df9/shell/console/src/main/java/org/apache/karaf/shell/console/SubShellAction.java
----------------------------------------------------------------------
diff --git a/shell/console/src/main/java/org/apache/karaf/shell/console/SubShellAction.java b/shell/console/src/main/java/org/apache/karaf/shell/console/SubShellAction.java
deleted file mode 100644
index 8774927..0000000
--- a/shell/console/src/main/java/org/apache/karaf/shell/console/SubShellAction.java
+++ /dev/null
@@ -1,39 +0,0 @@
-/**
- *
- * 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.console;
-
-
-
-/**
- * Switch to a sub-shell.
- */
-public class SubShellAction extends AbstractAction {
-    
-    private String subShell;
-    
-    public Object doExecute() throws Exception {
-        session.put("SUBSHELL", subShell);
-        session.put("SCOPE", subShell + ":" + session.get("SCOPE"));
-        return null;
-    }
-    
-    public void setSubShell(String subShell) {
-        this.subShell = subShell;
-    }
-    
-}

http://git-wip-us.apache.org/repos/asf/karaf/blob/1ee78df9/shell/console/src/main/java/org/apache/karaf/shell/console/commands/AnnotatedSubShell.java
----------------------------------------------------------------------
diff --git a/shell/console/src/main/java/org/apache/karaf/shell/console/commands/AnnotatedSubShell.java b/shell/console/src/main/java/org/apache/karaf/shell/console/commands/AnnotatedSubShell.java
deleted file mode 100644
index 9cd3dda..0000000
--- a/shell/console/src/main/java/org/apache/karaf/shell/console/commands/AnnotatedSubShell.java
+++ /dev/null
@@ -1,43 +0,0 @@
-/**
- *
- * 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.console.commands;
-
-import org.apache.karaf.shell.console.SubShell;
-
-public class AnnotatedSubShell implements SubShell {
-
-    public String getName() {
-        return getAnnotation().name();
-    }
-
-    public String getDescription() {
-        return getAnnotation().description();
-    }
-
-    public String getDetailedDescription() {
-        return getAnnotation().detailedDescription();
-    }
-    
-    org.apache.felix.gogo.commands.SubShell getAnnotation() {
-        org.apache.felix.gogo.commands.SubShell ann = getClass().getAnnotation(org.apache.felix.gogo.commands.SubShell.class);
-        if (ann == null) {
-            throw new IllegalStateException("The class should be annotated with the org.apache.felix.gogo.commands.SubShell annotation");
-        }
-        return ann;
-    }
-}

http://git-wip-us.apache.org/repos/asf/karaf/blob/1ee78df9/shell/console/src/main/java/org/apache/karaf/shell/console/commands/BasicSubShell.java
----------------------------------------------------------------------
diff --git a/shell/console/src/main/java/org/apache/karaf/shell/console/commands/BasicSubShell.java b/shell/console/src/main/java/org/apache/karaf/shell/console/commands/BasicSubShell.java
deleted file mode 100644
index 3e40c5a..0000000
--- a/shell/console/src/main/java/org/apache/karaf/shell/console/commands/BasicSubShell.java
+++ /dev/null
@@ -1,51 +0,0 @@
-/**
- *
- * 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.console.commands;
-
-import org.apache.karaf.shell.console.SubShell;
-
-public class BasicSubShell implements SubShell {
-    
-    private String name;
-    private String description;
-    private String detailedDescription;
-
-    public String getName() {
-        return name;
-    }
-
-    public void setName(String name) {
-        this.name = name;
-    }
-
-    public String getDescription() {
-        return description;
-    }
-
-    public void setDescription(String description) {
-        this.description = description;
-    }
-
-    public String getDetailedDescription() {
-        return detailedDescription;
-    }
-
-    public void setDetailedDescription(String detailedDescription) {
-        this.detailedDescription = detailedDescription;
-    }
-}

http://git-wip-us.apache.org/repos/asf/karaf/blob/1ee78df9/shell/console/src/main/java/org/apache/karaf/shell/console/commands/BlueprintCommand.java
----------------------------------------------------------------------
diff --git a/shell/console/src/main/java/org/apache/karaf/shell/console/commands/BlueprintCommand.java b/shell/console/src/main/java/org/apache/karaf/shell/console/commands/BlueprintCommand.java
deleted file mode 100644
index 83fa1ec..0000000
--- a/shell/console/src/main/java/org/apache/karaf/shell/console/commands/BlueprintCommand.java
+++ /dev/null
@@ -1,110 +0,0 @@
-/**
- *
- * 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.console.commands;
-
-import java.lang.reflect.Type;
-import java.util.List;
-import java.util.Map;
-
-import org.apache.felix.gogo.commands.Action;
-import org.apache.felix.service.command.CommandSession;
-import org.apache.karaf.shell.commands.basic.AbstractCommand;
-import org.apache.karaf.shell.commands.basic.ActionPreparator;
-import org.apache.karaf.shell.commands.basic.DefaultActionPreparator;
-import org.apache.karaf.shell.console.BlueprintContainerAware;
-import org.apache.karaf.shell.console.BundleContextAware;
-import org.apache.karaf.shell.console.CompletableFunction;
-import org.apache.karaf.shell.console.Completer;
-import org.osgi.framework.BundleContext;
-import org.osgi.service.blueprint.container.BlueprintContainer;
-import org.osgi.service.blueprint.container.Converter;
-
-public class
-        BlueprintCommand extends AbstractCommand implements CompletableFunction
-{
-
-    protected BlueprintContainer blueprintContainer;
-    protected Converter blueprintConverter;
-    protected String actionId;
-    protected String shell;
-    protected List<Completer> completers;
-    protected Map<String,Completer> optionalCompleters;
-
-    public void setBlueprintContainer(BlueprintContainer blueprintContainer) {
-        this.blueprintContainer = blueprintContainer;
-    }
-
-    public void setBlueprintConverter(Converter blueprintConverter) {
-        this.blueprintConverter = blueprintConverter;
-    }
-
-    public void setActionId(String actionId) {
-        this.actionId = actionId;
-    }
-    
-    public void setShell(String shell) {
-        this.shell = shell;
-    }
-
-    public List<Completer> getCompleters() {
-        return completers;
-    }
-
-    public void setCompleters(List<Completer> completers) {
-        this.completers = completers;
-    }
-
-    public Map<String, Completer> getOptionalCompleters() {
-        return optionalCompleters;
-    }
-
-    public void setOptionalCompleters(Map<String, Completer> optionalCompleters) {
-        this.optionalCompleters = optionalCompleters;
-    }
-
-    @Override
-    protected ActionPreparator getPreparator() throws Exception {
-        return new BlueprintActionPreparator();
-    }
-
-    protected class BlueprintActionPreparator extends DefaultActionPreparator {
-
-        @Override
-        protected Object convert(Action action, CommandSession commandSession, Object o, Type type) throws Exception {
-            GenericType t = new GenericType(type);
-            if (t.getRawClass() == String.class) {
-                return o != null ? o.toString() : null;
-            }
-            return blueprintConverter.convert(o, t);
-        }
-
-    }
-
-    public Action createNewAction() {
-        Action action = (Action) blueprintContainer.getComponentInstance(actionId);
-        if (action instanceof BlueprintContainerAware) {
-            ((BlueprintContainerAware) action).setBlueprintContainer(blueprintContainer);
-        }
-        if (action instanceof BundleContextAware) {
-            BundleContext context = (BundleContext) blueprintContainer.getComponentInstance("blueprintBundleContext");
-            ((BundleContextAware) action).setBundleContext(context);
-        }
-        return action;
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/karaf/blob/1ee78df9/shell/console/src/main/java/org/apache/karaf/shell/console/commands/GenericType.java
----------------------------------------------------------------------
diff --git a/shell/console/src/main/java/org/apache/karaf/shell/console/commands/GenericType.java b/shell/console/src/main/java/org/apache/karaf/shell/console/commands/GenericType.java
deleted file mode 100644
index faf7c18..0000000
--- a/shell/console/src/main/java/org/apache/karaf/shell/console/commands/GenericType.java
+++ /dev/null
@@ -1,195 +0,0 @@
-/**
- *
- * 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.console.commands;
-
-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;
-import org.osgi.service.blueprint.container.ReifiedType;
-
-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);
-	}
-
-}

http://git-wip-us.apache.org/repos/asf/karaf/blob/1ee78df9/shell/console/src/main/java/org/apache/karaf/shell/console/commands/NamespaceHandler.java
----------------------------------------------------------------------
diff --git a/shell/console/src/main/java/org/apache/karaf/shell/console/commands/NamespaceHandler.java b/shell/console/src/main/java/org/apache/karaf/shell/console/commands/NamespaceHandler.java
deleted file mode 100644
index 6c3a5b3..0000000
--- a/shell/console/src/main/java/org/apache/karaf/shell/console/commands/NamespaceHandler.java
+++ /dev/null
@@ -1,523 +0,0 @@
-/*
- * 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.console.commands;
-
-import java.lang.reflect.Field;
-import java.lang.reflect.Method;
-import java.net.URL;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-
-import org.apache.aries.blueprint.ParserContext;
-import org.apache.aries.blueprint.PassThroughMetadata;
-import org.apache.aries.blueprint.mutable.MutableBeanMetadata;
-import org.apache.aries.blueprint.mutable.MutableCollectionMetadata;
-import org.apache.aries.blueprint.mutable.MutableIdRefMetadata;
-import org.apache.aries.blueprint.mutable.MutablePassThroughMetadata;
-import org.apache.aries.blueprint.mutable.MutableRefMetadata;
-import org.apache.aries.blueprint.mutable.MutableReferenceMetadata;
-import org.apache.aries.blueprint.mutable.MutableServiceMetadata;
-import org.apache.aries.blueprint.mutable.MutableValueMetadata;
-import org.apache.felix.gogo.commands.Action;
-import org.apache.karaf.shell.console.Completer;
-import org.apache.karaf.shell.console.SubShellAction;
-import org.apache.karaf.shell.commands.Command;
-import org.apache.karaf.shell.inject.Destroy;
-import org.apache.karaf.shell.inject.Init;
-import org.apache.karaf.shell.inject.Reference;
-import org.apache.karaf.shell.inject.Service;
-import org.osgi.framework.Bundle;
-import org.osgi.framework.BundleContext;
-import org.osgi.framework.wiring.BundleWiring;
-import org.osgi.service.blueprint.container.ComponentDefinitionException;
-import org.osgi.service.blueprint.reflect.BeanArgument;
-import org.osgi.service.blueprint.reflect.BeanMetadata;
-import org.osgi.service.blueprint.reflect.BeanProperty;
-import org.osgi.service.blueprint.reflect.ComponentMetadata;
-import org.osgi.service.blueprint.reflect.IdRefMetadata;
-import org.osgi.service.blueprint.reflect.MapMetadata;
-import org.osgi.service.blueprint.reflect.Metadata;
-import org.osgi.service.blueprint.reflect.NullMetadata;
-import org.osgi.service.blueprint.reflect.RefMetadata;
-import org.osgi.service.blueprint.reflect.ReferenceMetadata;
-import org.osgi.service.blueprint.reflect.ServiceMetadata;
-import org.osgi.service.blueprint.reflect.ValueMetadata;
-import org.w3c.dom.Attr;
-import org.w3c.dom.Element;
-import org.w3c.dom.NamedNodeMap;
-import org.w3c.dom.Node;
-import org.w3c.dom.NodeList;
-
-
-public class NamespaceHandler implements org.apache.aries.blueprint.NamespaceHandler {
-
-    public static final String ID = "id";
-    public static final String ACTION = "action";
-    public static final String ACTION_ID = "actionId";
-    public static final String COMMAND_BUNDLE = "command-bundle";
-    public static final String SCAN = "scan";
-    public static final String NAME = "name";
-    public static final String COMMAND = "command";
-    public static final String COMPLETERS = "completers";
-    public static final String OPTIONAL_COMPLETERS = "optional-completers";
-    public static final String OPTIONAL_COMPLETERS_PROPERTY = "optionalCompleters";
-    public static final String BEAN = "bean";
-    public static final String REF = "ref";
-    public static final String NULL = "null";
-    public static final String MAP = "map";
-    public static final String BLUEPRINT_CONTAINER = "blueprintContainer";
-    public static final String BLUEPRINT_CONVERTER = "blueprintConverter";
-
-    public static final String SHELL_NAMESPACE_1_0_0 = "http://karaf.apache.org/xmlns/shell/v1.0.0";
-    public static final String SHELL_NAMESPACE_1_1_0 = "http://karaf.apache.org/xmlns/shell/v1.1.0";
-    public static final String SHELL_NAMESPACE_1_2_0 = "http://karaf.apache.org/xmlns/shell/v1.2.0";
-
-    private int nameCounter = 0;
-
-    public URL getSchemaLocation(String namespace) {
-        if(SHELL_NAMESPACE_1_0_0.equals(namespace)) {
-            return getClass().getResource("karaf-shell-1.0.0.xsd");
-        } else if(SHELL_NAMESPACE_1_1_0.equals(namespace)) {
-            return getClass().getResource("karaf-shell-1.1.0.xsd");
-        } else if(SHELL_NAMESPACE_1_2_0.equals(namespace)) {
-            return getClass().getResource("karaf-shell-1.2.0.xsd");
-        }
-        return getClass().getResource("karaf-shell-1.2.0.xsd");
-    }
-
-	public Set<Class> getManagedClasses() {
-		return new HashSet<Class>(Arrays.asList(
-			BlueprintCommand.class
-		));
-	}
-
-    public ComponentMetadata decorate(Node node, ComponentMetadata component, ParserContext context) {
-        throw new ComponentDefinitionException("Bad xml syntax: node decoration is not supported");
-    }
-
-    public Metadata parse(Element element, ParserContext context) {
-        if (nodeNameEquals(element, COMMAND_BUNDLE)) {
-            NamedNodeMap attrs = element.getAttributes();
-            for (int i = 0; i < attrs.getLength(); i++) {
-                Node child = attrs.item(i);
-                if (child instanceof Attr) {
-                    Attr childAttr = (Attr) child;
-                    parseChildAttr(childAttr, context);
-                }
-            }
-            NodeList children = element.getChildNodes();
-            for (int i = 0; i < children.getLength(); i++) {
-                Node child  = children.item(i);
-                if (child instanceof Element) {
-                    Element childElement = (Element) child;
-                    parseChildElement(childElement, context);
-                }
-            }
-            registerConverters(context);
-            return null;
-        } else {
-            throw new IllegalStateException("Unexpected element " + element.getNodeName());
-        }
-    }
-
-    private void parseChildAttr(Attr attr, ParserContext context) {
-        if (nodeNameEquals(attr, SCAN)) {
-            scan(attr, context);
-        }
-    }
-
-    private void scan(Attr attr, ParserContext context) {
-        try {
-            Bundle bundle = getBundle(context);
-            BundleWiring wiring = bundle.adapt(BundleWiring.class);
-            for (String pkg : attr.getValue().split(" ")) {
-                String name = pkg;
-                int options = BundleWiring.LISTRESOURCES_LOCAL;
-                name = name.replace('.', '/');
-                if (name.endsWith("*")) {
-                    options |= BundleWiring.LISTRESOURCES_RECURSE;
-                    name = name.substring(0, name.length() - 1);
-                }
-                if (!name.startsWith("/")) {
-                    name = "/" + name;
-                }
-                if (name.endsWith("/")) {
-                    name = name.substring(0, name.length() - 1);
-                }
-                Collection<String> classes = wiring.listResources(name, "*.class", options);
-                for (String className : classes) {
-                    className = className.replace('/', '.').replace(".class", "");
-                    inspectClass(context, bundle.loadClass(className));
-                }
-            }
-        } catch (ComponentDefinitionException e) {
-            throw e;
-        } catch (Exception e) {
-            throw new ComponentDefinitionException("Unable to scan commands", e);
-        }
-    }
-
-    private void inspectClass(ParserContext context, Class<?> clazz) throws Exception {
-        Service reg = clazz.getAnnotation(Service.class);
-        if (reg == null) {
-            return;
-        }
-        if (Action.class.isAssignableFrom(clazz)) {
-            final Command cmd = clazz.getAnnotation(Command.class);
-            if (cmd == null) {
-                throw new IllegalArgumentException("Command " + clazz.getName() + " is not annotated with @Command");
-            }
-            String scope = cmd.scope();
-            String function = cmd.name();
-            // Create action
-            MutableBeanMetadata action = context.createMetadata(MutableBeanMetadata.class);
-            action.setId(getName());
-            action.setActivation(MutableBeanMetadata.ACTIVATION_LAZY);
-            action.setScope(MutableBeanMetadata.SCOPE_PROTOTYPE);
-            action.setRuntimeClass(clazz);
-            for (Class<?> cl = clazz; cl != Object.class; cl = cl.getSuperclass()) {
-                for (Field field : cl.getDeclaredFields()) {
-                    if (field.getAnnotation(Reference.class) != null) {
-                        if (field.getType() == BundleContext.class) {
-                            action.addProperty(field.getName(), createRef(context, "blueprintBundleContext"));
-                        } else {
-                            action.addProperty(field.getName(), createRef(context, createServiceRef(context, field.getType()).getId()));
-                        }
-                    }
-                }
-                for (Method method : cl.getDeclaredMethods()) {
-                    if (method.getAnnotation(Init.class) != null) {
-                        if (action.getInitMethod() == null) {
-                            action.setInitMethod(method.getName());
-                        }
-                    }
-                    if (method.getAnnotation(Destroy.class) != null) {
-                        if (action.getDestroyMethod() == null) {
-                            action.setDestroyMethod(method.getName());
-                        }
-                    }
-                }
-            }
-            context.getComponentDefinitionRegistry().registerComponentDefinition(action);
-            // Create command
-            MutableBeanMetadata command = context.createMetadata(MutableBeanMetadata.class);
-            command.setRuntimeClass(BlueprintCommand.class);
-            command.addProperty(BLUEPRINT_CONTAINER, createRef(context, BLUEPRINT_CONTAINER));
-            command.addProperty(BLUEPRINT_CONVERTER, createRef(context, BLUEPRINT_CONVERTER));
-            command.addProperty(ACTION_ID, createIdRef(context, action.getId()));
-            // Create command service
-            MutableServiceMetadata commandService = context.createMetadata(MutableServiceMetadata.class);
-            commandService.setActivation(MutableServiceMetadata.ACTIVATION_LAZY);
-            commandService.setId(getName());
-            commandService.setAutoExport(ServiceMetadata.AUTO_EXPORT_ALL_CLASSES);
-            commandService.setServiceComponent(command);
-            commandService.addServiceProperty(createStringValue(context, "osgi.command.scope"),
-                    createStringValue(context, scope));
-            commandService.addServiceProperty(createStringValue(context, "osgi.command.function"),
-                    createStringValue(context, function));
-            context.getComponentDefinitionRegistry().registerComponentDefinition(commandService);
-
-            // create the sub-shell action
-            createSubShell(context, scope);
-        }
-        if (Completer.class.isAssignableFrom(clazz)) {
-            MutableBeanMetadata completer = context.createMetadata(MutableBeanMetadata.class);
-            completer.setId(getName());
-            completer.setActivation(MutableBeanMetadata.ACTIVATION_LAZY);
-            completer.setScope(MutableBeanMetadata.SCOPE_SINGLETON);
-            completer.setRuntimeClass(clazz);
-            // Create completer
-            for (Class<?> cl = clazz; cl != Object.class; cl = cl.getSuperclass()) {
-                for (Field field : cl.getDeclaredFields()) {
-                    if (field.getAnnotation(Reference.class) != null) {
-                        if (field.getType() == BundleContext.class) {
-                            completer.addProperty(field.getName(), createRef(context, "blueprintBundleContext"));
-                        } else {
-                            completer.addProperty(field.getName(), createRef(context, createServiceRef(context, field.getType()).getId()));
-                        }
-                    }
-                }
-                for (Method method : cl.getDeclaredMethods()) {
-                    if (method.getAnnotation(Init.class) != null) {
-                        if (completer.getInitMethod() == null) {
-                            completer.setInitMethod(method.getName());
-                        }
-                    }
-                    if (method.getAnnotation(Destroy.class) != null) {
-                        if (completer.getDestroyMethod() == null) {
-                            completer.setDestroyMethod(method.getName());
-                        }
-                    }
-                }
-            }
-            context.getComponentDefinitionRegistry().registerComponentDefinition(completer);
-            // Create completer service
-            MutableServiceMetadata completerService = context.createMetadata(MutableServiceMetadata.class);
-            completerService.setActivation(MutableServiceMetadata.ACTIVATION_LAZY);
-            completerService.setId(getName());
-            completerService.setAutoExport(ServiceMetadata.AUTO_EXPORT_ALL_CLASSES);
-            completerService.setServiceComponent(createRef(context, completer.getId()));
-            context.getComponentDefinitionRegistry().registerComponentDefinition(completerService);
-        }
-    }
-
-    private ComponentMetadata createServiceRef(ParserContext context, Class<?> cls) {
-        String id = ".serviceref." + cls.getName();
-        ComponentMetadata metadata = context.getComponentDefinitionRegistry().getComponentDefinition(id);
-        if (metadata == null) {
-            MutableReferenceMetadata m = context.createMetadata(MutableReferenceMetadata.class);
-            m.setRuntimeInterface(cls);
-            m.setInterface(cls.getName());
-            m.setActivation(ReferenceMetadata.ACTIVATION_EAGER);
-            m.setAvailability(ReferenceMetadata.AVAILABILITY_MANDATORY);
-            m.setId(id);
-            context.getComponentDefinitionRegistry().registerComponentDefinition(m);
-            return m;
-        } else {
-            return metadata;
-        }
-    }
-
-    private Bundle getBundle(ParserContext context) {
-        PassThroughMetadata ptm = (PassThroughMetadata) context.getComponentDefinitionRegistry().getComponentDefinition("blueprintBundle");
-        return (Bundle) ptm.getObject();
-    }
-
-    private void parseChildElement(Element element, ParserContext context) {
-        if (nodeNameEquals(element, COMMAND)) {
-            parseCommand(element, context);
-        }
-    }
-
-    private void registerConverters(ParserContext context) {
-        String converterName = "." + NumberToStringConverter.class.getName();
-        if (!context.getComponentDefinitionRegistry().containsComponentDefinition(converterName)) {
-            MutablePassThroughMetadata cnv = context.createMetadata(MutablePassThroughMetadata.class);
-            cnv.setId(converterName);
-            cnv.setObject(new NumberToStringConverter());
-            context.getComponentDefinitionRegistry().registerTypeConverter(cnv);
-        }
-    }
-
-    private void parseCommand(Element element, ParserContext context) {
-        MutableBeanMetadata command = context.createMetadata(MutableBeanMetadata.class);
-        command.setRuntimeClass(BlueprintCommand.class);
-        command.addProperty(BLUEPRINT_CONTAINER, createRef(context, BLUEPRINT_CONTAINER));
-        command.addProperty(BLUEPRINT_CONVERTER, createRef(context, BLUEPRINT_CONVERTER));
-
-        NodeList children = element.getChildNodes();
-        MutableBeanMetadata action = null;
-        for (int i = 0; i < children.getLength(); i++) {
-            Node child  = children.item(i);
-            if (child instanceof Element) {
-                Element childElement = (Element) child;
-                if (nodeNameEquals(childElement, ACTION)) {
-                    action = parseAction(context, command, childElement);
-                    action.setId(getName());
-                    context.getComponentDefinitionRegistry().registerComponentDefinition(action);
-                    command.addProperty(ACTION_ID, createIdRef(context, action.getId()));
-                } else if (nodeNameEquals(childElement, COMPLETERS)) {
-                    command.addProperty(COMPLETERS, parseCompleters(context, command, childElement));
-                } else if (nodeNameEquals(childElement, OPTIONAL_COMPLETERS)) {
-                    command.addProperty(OPTIONAL_COMPLETERS_PROPERTY, parseOptionalCompleters(context, command, childElement));
-                }
-                else {
-                    throw new ComponentDefinitionException("Bad xml syntax: unknown element '" + childElement.getNodeName() + "'");
-                }
-            }
-        }
-
-        MutableServiceMetadata commandService = context.createMetadata(MutableServiceMetadata.class);
-        commandService.setActivation(MutableServiceMetadata.ACTIVATION_LAZY);
-        commandService.setId(getName());
-        commandService.setAutoExport(ServiceMetadata.AUTO_EXPORT_ALL_CLASSES);
-        commandService.setServiceComponent(command);
-
-        String scope;
-        String function;
-        if (SHELL_NAMESPACE_1_0_0.equals(element.getNamespaceURI())) {
-            String location = element.getAttribute(NAME);
-            location = location.replace('/', ':');
-            if (location.lastIndexOf(':') >= 0) {
-                scope = location.substring(0, location.lastIndexOf(':'));
-                function = location.substring(location.lastIndexOf(':') + 1);
-            } else {
-                scope = "";
-                function = location;
-            }
-        } else {
-            try {
-                Class actionClass = getBundle(context).loadClass(action.getClassName());
-                scope = getScope(actionClass);
-                function = getName(actionClass);
-            } catch (Throwable e) {
-                throw new ComponentDefinitionException("Unable to introspect action " + action.getClassName(), e);
-            }
-        }
-        commandService.addServiceProperty(createStringValue(context, "osgi.command.scope"),
-                createStringValue(context, scope));
-        commandService.addServiceProperty(createStringValue(context, "osgi.command.function"),
-                createStringValue(context, function));
-
-        context.getComponentDefinitionRegistry().registerComponentDefinition(commandService);
-
-        // create the sub-shell action
-        createSubShell(context, scope);
-    }
-
-    private void createSubShell(ParserContext context, String scope) {
-        String subShellName = ".subshell." + scope;
-        if (context.getComponentDefinitionRegistry().containsComponentDefinition(subShellName)) {
-            return;
-        }
-        MutableBeanMetadata subShellAction = context.createMetadata(MutableBeanMetadata.class);
-        subShellAction.setRuntimeClass(SubShellAction.class);
-        subShellAction.setActivation(MutableBeanMetadata.ACTIVATION_LAZY);
-        subShellAction.setScope(MutableBeanMetadata.SCOPE_PROTOTYPE);
-        subShellAction.setId(getName());
-        subShellAction.addProperty("subShell", createStringValue(context, scope));
-        context.getComponentDefinitionRegistry().registerComponentDefinition(subShellAction);
-        // generate the sub-shell command
-        MutableBeanMetadata subShellCommand = context.createMetadata(MutableBeanMetadata.class);
-        subShellCommand.setId(getName());
-        subShellCommand.setRuntimeClass(BlueprintCommand.class);
-        subShellCommand.addProperty(BLUEPRINT_CONTAINER, createRef(context, BLUEPRINT_CONTAINER));
-        subShellCommand.addProperty(BLUEPRINT_CONVERTER, createRef(context, BLUEPRINT_CONVERTER));
-        subShellCommand.addProperty(ACTION_ID, createIdRef(context, subShellAction.getId()));
-        context.getComponentDefinitionRegistry().registerComponentDefinition(subShellCommand);
-        // generate the sub-shell OSGi service
-        MutableServiceMetadata subShellCommandService = context.createMetadata(MutableServiceMetadata.class);
-        subShellCommandService.setActivation(MutableServiceMetadata.ACTIVATION_LAZY);
-        subShellCommandService.setId(subShellName);
-        subShellCommandService.setAutoExport(ServiceMetadata.AUTO_EXPORT_ALL_CLASSES);
-        subShellCommandService.setServiceComponent(subShellCommand);
-        subShellCommandService.addServiceProperty(createStringValue(context, "osgi.command.scope"), createStringValue(context, "*"));
-        subShellCommandService.addServiceProperty(createStringValue(context, "osgi.command.function"), createStringValue(context, scope));
-        context.getComponentDefinitionRegistry().registerComponentDefinition(subShellCommandService);
-    }
-
-    private MutableBeanMetadata getInvocationValue(ParserContext context, String method, String className) {
-        MutableBeanMetadata scope = context.createMetadata(MutableBeanMetadata.class);
-        scope.setRuntimeClass(NamespaceHandler.class);
-        scope.setFactoryMethod(method);
-        scope.addArgument(createStringValue(context, className), null, 0);
-        return scope;
-    }
-
-    private MutableBeanMetadata parseAction(ParserContext context, ComponentMetadata enclosingComponent, Element element) {
-        MutableBeanMetadata action = context.createMetadata(MutableBeanMetadata.class);
-        action.setActivation(MutableBeanMetadata.ACTIVATION_LAZY);
-        action.setScope(MutableBeanMetadata.SCOPE_PROTOTYPE);
-        action.setClassName(element.getAttribute("class"));
-        NodeList children = element.getChildNodes();
-        for (int i = 0; i < children.getLength(); i++) {
-            Node child  = children.item(i);
-            if (child instanceof Element) {
-                Element childElement = (Element) child;
-                if (nodeNameEquals(childElement, "argument")) {
-                    action.addArgument(context.parseElement(BeanArgument.class, enclosingComponent, childElement));
-                } else if (nodeNameEquals(childElement, "property")) {
-                    action.addProperty(context.parseElement(BeanProperty.class, enclosingComponent, childElement));
-                }
-            }
-        }
-        return action;
-    }
-
-    private Metadata parseOptionalCompleters(ParserContext context, ComponentMetadata enclosingComponent, Element element) {
-        Metadata metadata = context.parseElement(MapMetadata.class, context.getEnclosingComponent(), (Element) element);
-        return metadata;
-    }
-
-    private Metadata parseCompleters(ParserContext context, ComponentMetadata enclosingComponent, Element element) {
-        MutableCollectionMetadata collection = context.createMetadata(MutableCollectionMetadata.class);
-        collection.setCollectionClass(List.class);
-        NodeList children = element.getChildNodes();
-        for (int i = 0; i < children.getLength(); i++) {
-            Node child = children.item(i);
-            if (child instanceof Element) {
-                Metadata metadata;
-                if (nodeNameEquals(child, REF)) {
-                    metadata = context.parseElement(RefMetadata.class, context.getEnclosingComponent(), (Element) child);
-                } else if (nodeNameEquals(child, NULL)) {
-                    metadata = context.parseElement(NullMetadata.class, context.getEnclosingComponent(), (Element) child);
-                } else if (nodeNameEquals(child, BEAN)) {
-                    metadata = context.parseElement(BeanMetadata.class, enclosingComponent, (Element) child);
-                } else {
-                    throw new IllegalStateException("Unexpected element " + child.getNodeName());
-                }
-                collection.addValue(metadata);
-            }
-        }
-        return collection;
-    }
-
-    private ValueMetadata createStringValue(ParserContext context, String str) {
-        MutableValueMetadata value = context.createMetadata(MutableValueMetadata.class);
-        value.setStringValue(str);
-        return value;
-    }
-
-    private RefMetadata createRef(ParserContext context, String id) {
-        MutableRefMetadata idref = context.createMetadata(MutableRefMetadata.class);
-        idref.setComponentId(id);
-        return idref;
-    }
-
-    private IdRefMetadata createIdRef(ParserContext context, String id) {
-        MutableIdRefMetadata idref = context.createMetadata(MutableIdRefMetadata.class);
-        idref.setComponentId(id);
-        return idref;
-    }
-
-    public synchronized String getName() {
-        return "shell-" + ++nameCounter;
-    }
-    
-    private static boolean nodeNameEquals(Node node, String name) {
-        return (name.equals(node.getNodeName()) || name.equals(node.getLocalName()));
-    }
-
-    public static String getScope(Class<?> action) {
-        Command command = action.getAnnotation(Command.class);
-        if (command != null) {
-            return command.scope();
-        }
-        org.apache.felix.gogo.commands.Command command2 = action.getAnnotation(org.apache.felix.gogo.commands.Command.class);
-        if (command2 != null) {
-            return command2.scope();
-        }
-        return null;
-    }
-
-    public static String getName(Class<?> action) {
-        Command command = action.getAnnotation(Command.class);
-        if (command != null) {
-            return command.name();
-        }
-        org.apache.felix.gogo.commands.Command command2 = action.getAnnotation(org.apache.felix.gogo.commands.Command.class);
-        if (command2 != null) {
-            return command2.name();
-        }
-        return null;
-    }
-}

http://git-wip-us.apache.org/repos/asf/karaf/blob/1ee78df9/shell/console/src/main/java/org/apache/karaf/shell/console/commands/NumberToStringConverter.java
----------------------------------------------------------------------
diff --git a/shell/console/src/main/java/org/apache/karaf/shell/console/commands/NumberToStringConverter.java b/shell/console/src/main/java/org/apache/karaf/shell/console/commands/NumberToStringConverter.java
deleted file mode 100644
index fb68c3a..0000000
--- a/shell/console/src/main/java/org/apache/karaf/shell/console/commands/NumberToStringConverter.java
+++ /dev/null
@@ -1,36 +0,0 @@
-/*
- * 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.console.commands;
-
-import org.osgi.service.blueprint.container.Converter;
-import org.osgi.service.blueprint.container.ReifiedType;
-
-public class NumberToStringConverter implements Converter {
-
-    @Override
-    public boolean canConvert(Object sourceObject, ReifiedType targetType) {
-        return sourceObject != null && sourceObject instanceof Number
-                && targetType.getRawClass() == String.class;
-    }
-
-    @Override
-    public Object convert(Object sourceObject, ReifiedType targetType) throws Exception {
-        return sourceObject.toString();
-    }
-}

http://git-wip-us.apache.org/repos/asf/karaf/blob/1ee78df9/shell/console/src/main/java/org/apache/karaf/shell/console/completer/AggregateCompleter.java
----------------------------------------------------------------------
diff --git a/shell/console/src/main/java/org/apache/karaf/shell/console/completer/AggregateCompleter.java b/shell/console/src/main/java/org/apache/karaf/shell/console/completer/AggregateCompleter.java
deleted file mode 100644
index dbba436..0000000
--- a/shell/console/src/main/java/org/apache/karaf/shell/console/completer/AggregateCompleter.java
+++ /dev/null
@@ -1,91 +0,0 @@
-/*
- * 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.console.completer;
-
-import java.util.ArrayList;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Collection;
-
-import org.apache.karaf.shell.console.Completer;
-
-/**
- * Completer which contains multipule completers and aggregates them together.
- */
-public class AggregateCompleter implements Completer
-{
-    private final Collection<Completer> completers;
-
-    public AggregateCompleter(final Collection<Completer> completers) {
-        assert completers != null;
-        this.completers = completers;
-    }
-
-    @SuppressWarnings({ "unchecked", "rawtypes" })
-    public int complete(final String buffer, final int cursor, final List candidates) {
-        // buffer could be null
-        assert candidates != null;
-
-        List<Completion> completions = new ArrayList<Completion>(completers.size());
-
-        // Run each completer, saving its completion results
-        int max = -1;
-        for (Completer completer : completers) {
-            Completion completion = new Completion(candidates);
-            completion.complete(completer, buffer, cursor);
-
-            // Compute the max cursor position
-            max = Math.max(max, completion.cursor);
-
-            completions.add(completion);
-        }
-
-        // Append candiates from completions which have the same cursor position as max
-        for (Completion completion : completions) {
-            if (completion.cursor == max) {
-                // noinspection unchecked
-                candidates.addAll(completion.candidates);
-            }
-        }
-
-        return max;
-    }
-
-    private class Completion
-    {
-        public final List<String> candidates;
-
-        public int cursor;
-
-        @SuppressWarnings({ "unchecked", "rawtypes" })
-        public Completion(final List candidates) {
-            assert candidates != null;
-
-            // noinspection unchecked
-            this.candidates = new LinkedList<String>(candidates);
-        }
-
-        public void complete(final Completer completer, final String buffer, final int cursor) {
-            assert completer != null;
-
-            this.cursor = completer.complete(buffer, cursor, candidates);
-        }
-    }
-}

http://git-wip-us.apache.org/repos/asf/karaf/blob/1ee78df9/shell/console/src/main/java/org/apache/karaf/shell/console/completer/ArgumentCompleter.java
----------------------------------------------------------------------
diff --git a/shell/console/src/main/java/org/apache/karaf/shell/console/completer/ArgumentCompleter.java b/shell/console/src/main/java/org/apache/karaf/shell/console/completer/ArgumentCompleter.java
deleted file mode 100644
index 745489e..0000000
--- a/shell/console/src/main/java/org/apache/karaf/shell/console/completer/ArgumentCompleter.java
+++ /dev/null
@@ -1,567 +0,0 @@
-/*
- * 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.console.completer;
-
-import java.io.File;
-import java.lang.reflect.Field;
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
-import java.lang.reflect.Modifier;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.EnumSet;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
-import org.apache.felix.gogo.commands.Action;
-import org.apache.felix.service.command.Function;
-import org.apache.karaf.shell.commands.Argument;
-import org.apache.karaf.shell.commands.CommandWithAction;
-import org.apache.karaf.shell.commands.CompleterValues;
-import org.apache.karaf.shell.commands.HelpOption;
-import org.apache.karaf.shell.commands.Option;
-import org.apache.felix.service.command.CommandSession;
-import org.apache.karaf.shell.console.CommandSessionHolder;
-import org.apache.karaf.shell.console.CompletableFunction;
-import org.apache.karaf.shell.console.Completer;
-import org.apache.karaf.shell.console.NameScoping;
-import org.osgi.framework.BundleContext;
-import org.osgi.framework.FrameworkUtil;
-import org.osgi.framework.ServiceReference;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import static org.apache.karaf.shell.console.completer.CommandsCompleter.unProxy;
-
-public class ArgumentCompleter implements Completer {
-
-    private static final Logger LOGGER = LoggerFactory.getLogger(ArgumentCompleter.class);
-
-    public static final String ARGUMENTS_LIST = "ARGUMENTS_LIST";
-
-    public static final String COMMANDS = ".commands";
-
-    final Completer commandCompleter;
-    final Completer optionsCompleter;
-    final List<Completer> argsCompleters;
-    final Map<String, Completer> optionalCompleters;
-    final CommandWithAction function;
-    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;
-
-    @SuppressWarnings({ "unchecked", "rawtypes" })
-    public ArgumentCompleter(CommandSession session, CommandWithAction function, String command) {
-        this.function = function;
-        // Command name completer
-        commandCompleter = new StringsCompleter(getNames(session, command));
-        // Build options completer
-        for (Class<?> type = function.getActionClass(); type != null; type = type.getSuperclass()) {
-            for (Field field : type.getDeclaredFields()) {
-                Option option = field.getAnnotation(Option.class);
-                if (option != null) {
-                    fields.put(option, field);
-                    options.put(option.name(), option);
-                    String[] aliases = option.aliases();
-                    if (aliases != null) {
-                        for (String alias : aliases) {
-                            options.put(alias, option);
-                        }
-                    }
-                }
-                Argument argument = field.getAnnotation(Argument.class);
-                if (argument != null) {
-                    Integer key = argument.index();
-                    if (arguments.containsKey(key)) {
-                        LOGGER.warn("Duplicate @Argument annotations on class " + type.getName() + " for index: " + key + " see: " + field);
-                    } else {
-                        arguments.put(key, field);
-                    }
-                }
-            }
-        }
-        options.put(HelpOption.HELP.name(), HelpOption.HELP);
-        optionsCompleter = new StringsCompleter(options.keySet());
-
-        // Build arguments completers
-        List<Completer> argsCompleters = null;
-        Map<String, Completer> optionalCompleters = null;
-
-        if (function instanceof CompletableFunction) {
-            Map<String, Completer> focl = ((CompletableFunction) function).getOptionalCompleters();
-            List<Completer> fcl = ((CompletableFunction) function).getCompleters();
-            if (focl != null || fcl != null) {
-                argsCompleters = new ArrayList<Completer>();
-                if (fcl != null) {
-                    for (Completer c : fcl) {
-                        argsCompleters.add(c == null ? NullCompleter.INSTANCE : c);
-                    }
-                }
-                optionalCompleters = focl;
-            }
-        }
-        if (argsCompleters == null) {
-            final Map<Integer, Object> values = getCompleterValues(function);
-            argsCompleters = new ArrayList<Completer>();
-            boolean multi = false;
-            for (int key = 0; key < arguments.size(); key++) {
-                Completer completer = null;
-                Field field = arguments.get(key);
-                if (field != null) {
-                    Argument argument = field.getAnnotation(Argument.class);
-                    multi = (argument != null && argument.multiValued());
-                    org.apache.karaf.shell.commands.Completer ann = field.getAnnotation(org.apache.karaf.shell.commands.Completer.class);
-                    if (ann != null) {
-                        Class clazz = ann.value();
-                        String[] value = ann.values();
-                        if (clazz != null) {
-                            if (value.length > 0 && clazz == StringsCompleter.class) {
-                                completer = new StringsCompleter(value, ann.caseSensitive());
-                            } else {
-                                BundleContext context = FrameworkUtil.getBundle(function.getClass()).getBundleContext();
-                                completer = new ProxyServiceCompleter(context, clazz);
-                            }
-                        }
-                    } else if (values.containsKey(key)) {
-                        Object value = values.get(key);
-                        if (value instanceof String[]) {
-                            completer = new StringsCompleter((String[]) value);
-                        } else if (value instanceof Collection) {
-                            completer = new StringsCompleter((Collection<String>) value);
-                        } else {
-                            LOGGER.warn("Could not use value " + value + " as set of completions!");
-                        }
-                    } else {
-                        completer = getDefaultCompleter(session, field);
-                    }
-                }
-                if (completer == null) {
-                    completer = NullCompleter.INSTANCE;
-                }
-                argsCompleters.add(completer);
-            }
-            if (argsCompleters.isEmpty() || !multi) {
-                argsCompleters.add(NullCompleter.INSTANCE);
-            }
-            optionalCompleters = new HashMap<String, Completer>();
-            for (Option option : fields.keySet()) {
-                Completer completer = null;
-                Field field = fields.get(option);
-                if (field != null) {
-                    org.apache.karaf.shell.commands.Completer ann = field.getAnnotation(org.apache.karaf.shell.commands.Completer.class);
-                    if (ann != null) {
-                        Class clazz = ann.value();
-                        String[] value = ann.values();
-                        if (clazz != null) {
-                            if (value.length > 0 && clazz == StringsCompleter.class) {
-                                completer = new StringsCompleter(value, ann.caseSensitive());
-                            } else {
-                                BundleContext context = FrameworkUtil.getBundle(function.getClass()).getBundleContext();
-                                completer = new ProxyServiceCompleter(context, clazz);
-                            }
-                        }
-                    }
-                }
-                if (completer == null) {
-                    completer = NullCompleter.INSTANCE;
-                }
-                optionalCompleters.put(option.name(), completer);
-                if (option.aliases() != null) {
-                    for (String alias : option.aliases()) {
-                        optionalCompleters.put(alias, completer);
-                    }
-                }
-            }
-        }
-        this.argsCompleters = argsCompleters;
-        this.optionalCompleters = optionalCompleters;
-    }
-
-    private Map<Integer, Object> getCompleterValues(CommandWithAction function) {
-        final Map<Integer, Object> values = new HashMap<Integer, Object>();
-        Action action = null;
-        try {
-            for (Class<?> type = function.getActionClass(); type != null; type = type.getSuperclass()) {
-                for (Method method : type.getDeclaredMethods()) {
-                    CompleterValues completerMethod = method.getAnnotation(CompleterValues.class);
-                    if (completerMethod != null) {
-                        int index = completerMethod.index();
-                        Integer key = index;
-                        if (index >= arguments.size() || index < 0) {
-                            LOGGER.warn("Index out of range on @CompleterValues on class " + type.getName() + " for index: " + key + " see: " + method);
-                        } else if (values.containsKey(key)) {
-                            LOGGER.warn("Duplicate @CompleterMethod annotations on class " + type.getName() + " for index: " + key + " see: " + method);
-                        } else {
-                            try {
-                                Object value;
-                                if (Modifier.isStatic(method.getModifiers())) {
-                                    value = method.invoke(null);
-                                } else {
-                                    if (action == null) {
-                                        action = function.createNewAction();
-                                    }
-                                    value = method.invoke(action);
-                                }
-                                values.put(key, value);
-                            } catch (IllegalAccessException e) {
-                                LOGGER.warn("Could not invoke @CompleterMethod on " + function + ". " + e, e);
-                            } catch (InvocationTargetException e) {
-                                Throwable target = e.getTargetException();
-                                if (target == null) {
-                                    target = e;
-                                }
-                                LOGGER.warn("Could not invoke @CompleterMethod on " + function + ". " + target, target);
-                            }
-                        }
-                    }
-                }
-            }
-        } finally {
-            if (action != null) {
-                try {
-                    function.releaseAction(action);
-                } catch (Exception e) {
-                    LOGGER.warn("Failed to release action: " + action + ". " + e, e);
-                }
-            }
-        }
-        return values;
-    }
-
-    private Completer getDefaultCompleter(CommandSession session, Field field) {
-        Completer completer = null;
-        Class<?> type = field.getType();
-        if (type.isAssignableFrom(File.class)) {
-            completer = new FileCompleter(session);
-        } 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>();
-            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;
-    }
-
-    private String[] getNames(CommandSession session, String scopedCommand) {
-        String command = NameScoping.getCommandNameWithoutGlobalPrefix(session, scopedCommand);
-        String[] s = command.split(":");
-        if (s.length == 1) {
-            return s;
-        } else {
-            return new String[] { command, s[1] };
-        }
-    }
-
-    /**
-     *  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(final String buffer, final int cursor,
-                        final List<String> candidates) {
-        ArgumentList list = delimit(buffer, cursor);
-        int argpos = list.getArgumentPosition();
-        int argIndex = list.getCursorArgumentIndex();
-
-        //Store the argument list so that it can be used by completers.
-        CommandSession commandSession = CommandSessionHolder.getSession();
-        if(commandSession != null) {
-            commandSession.put(ARGUMENTS_LIST,list);
-        }
-
-        Completer comp = null;
-        String[] args = list.getArguments();
-        int index = 0;
-        // First argument is command name
-        if (index < argIndex) {
-            // Verify command name
-            if (!verifyCompleter(commandCompleter, args[index])) {
-                return -1;
-            }
-            // Verify scope if
-            // - the command name has no scope
-            // - we have a session
-            if (!args[index].contains(":") && commandSession != null) {
-                Function f1 = unProxy((Function) commandSession.get("*:" + args[index]));
-                Function f2 = unProxy(this.function);
-                if (f1 != null && f1 != f2) {
-                    return -1;
-                }
-            }
-            index++;
-        } else {
-            comp = commandCompleter;
-        }
-        // Now, check options
-        if (comp == null) {
-            while (index < argIndex && args[index].startsWith("-")) {
-                if (!verifyCompleter(optionsCompleter, args[index])) {
-                    return -1;
-                }
-                Option option = options.get(args[index]);
-                if (option == null) {
-                    return -1;
-                }
-                Field field = fields.get(option);
-                if (field != null && field.getType() != boolean.class && field.getType() != Boolean.class) {
-                    if (++index == argIndex) {
-                        comp = NullCompleter.INSTANCE;
-                    }
-                }
-                index++;
-            }
-            if (comp == null && index >= argIndex && index < args.length && args[index].startsWith("-")) {
-                comp = optionsCompleter;
-            }
-        }
-        //Now check for if last Option has a completer
-        int lastAgurmentIndex = argIndex - 1;
-        if (lastAgurmentIndex >= 1) {
-            Option lastOption = options.get(args[lastAgurmentIndex]);
-            if (lastOption != null) {
-
-                Field lastField = fields.get(lastOption);
-                if (lastField != null && lastField.getType() != boolean.class && lastField.getType() != Boolean.class) {
-                    Option option = lastField.getAnnotation(Option.class);
-                    if (option != null) {
-                        Completer optionValueCompleter = null;
-                        String name = option.name();
-                        if (optionalCompleters != null && name != null) {
-                            optionValueCompleter = optionalCompleters.get(name);
-                            if (optionValueCompleter == null) {
-                                String[] aliases = option.aliases();
-                                if (aliases.length > 0) {
-                                    for (int i = 0; i < aliases.length && optionValueCompleter == null; i++) {
-                                        optionValueCompleter = optionalCompleters.get(option.aliases()[i]);
-                                    }
-                                }
-                            }
-                        }
-                        if(optionValueCompleter != null) {
-                            comp = optionValueCompleter;
-                        }
-                    }
-                }
-            }
-        }
-
-        // Check arguments
-        if (comp == null) {
-            int indexArg = 0;
-            while (index < argIndex) {
-                Completer sub = argsCompleters.get(indexArg >= argsCompleters.size() ? argsCompleters.size() - 1 : indexArg);
-                if (!verifyCompleter(sub, args[index])) {
-                    return -1;
-                }
-                index++;
-                indexArg++;
-            }
-            comp = argsCompleters.get(indexArg >= argsCompleters.size() ? argsCompleters.size() - 1 : indexArg);
-        }
-
-        int ret = comp.complete(list.getCursorArgument(), argpos, candidates);
-
-        if (ret == -1) {
-            return -1;
-        }
-
-        int pos = ret + (list.getBufferPosition() - argpos);
-
-        /**
-         *  Special case: when completing in the middle of a line, and the
-         *  area under the cursor is a delimiter, then trim any delimiters
-         *  from the candidates, since we do not need to have an extra
-         *  delimiter.
-         *
-         *  E.g., if we have a completion for "foo", and we
-         *  enter "f bar" into the buffer, and move to after the "f"
-         *  and hit TAB, we want "foo bar" instead of "foo  bar".
-         */
-
-        if ((buffer != null) && (cursor != buffer.length()) && isDelimiter(buffer, cursor)) {
-            for (int i = 0; i < candidates.size(); i++) {
-                String val = candidates.get(i);
-
-                while ((val.length() > 0)
-                    && isDelimiter(val, val.length() - 1)) {
-                    val = val.substring(0, val.length() - 1);
-                }
-
-                candidates.set(i, val);
-            }
-        }
-
-        return pos;
-    }
-
-    protected boolean verifyCompleter(Completer completer, String argument) {
-        List<String> candidates = new ArrayList<String>();
-        return completer.complete(argument, argument.length(), candidates) != -1 && !candidates.isEmpty();
-    }
-
-    public ArgumentList delimit(final String buffer, final int cursor) {
-        Parser parser = new Parser(buffer, cursor);
-        try {
-            List<List<List<String>>> program = parser.program();
-            List<String> pipe = program.get(parser.c0).get(parser.c1);
-            return new ArgumentList(pipe.toArray(new String[pipe.size()]), parser.c2, parser.c3, cursor);
-        } catch (Throwable t) {
-            return new ArgumentList(new String[] { buffer }, 0, cursor, cursor);
-        }
-    }
-
-    /**
-     *  Returns true if the specified character is a whitespace
-     *  parameter. Check to ensure that the character is not
-     *  escaped and returns true from
-     *  {@link #isDelimiterChar}.
-     *
-     *  @param  buffer the complete command buffer
-     *  @param  pos    the index of the character in the buffer
-     *  @return        true if the character should be a delimiter
-     */
-    public boolean isDelimiter(final String buffer, final int pos) {
-        return !isEscaped(buffer, pos) && isDelimiterChar(buffer, pos);
-    }
-
-    public boolean isEscaped(final String buffer, final int pos) {
-        return pos > 0 && buffer.charAt(pos) == '\\' && !isEscaped(buffer, pos - 1);
-    }
-
-    /**
-     *  The character is a delimiter if it is whitespace, and the
-     *  preceeding character is not an escape character.
-     */
-    public boolean isDelimiterChar(String buffer, int pos) {
-        return Character.isWhitespace(buffer.charAt(pos));
-    }
-
-    /**
-     *  The result of a delimited buffer.
-     */
-    public static class ArgumentList {
-        private String[] arguments;
-        private int cursorArgumentIndex;
-        private int argumentPosition;
-        private int bufferPosition;
-
-        /**
-         *  @param  arguments           the array of tokens
-         *  @param  cursorArgumentIndex the token index of the cursor
-         *  @param  argumentPosition    the position of the cursor in the
-         *                              current token
-         *  @param  bufferPosition      the position of the cursor in
-         *                              the whole buffer
-         */
-        public ArgumentList(String[] arguments, int cursorArgumentIndex,
-            int argumentPosition, int bufferPosition) {
-            this.arguments = arguments;
-            this.cursorArgumentIndex = cursorArgumentIndex;
-            this.argumentPosition = argumentPosition;
-            this.bufferPosition = bufferPosition;
-        }
-
-        public void setCursorArgumentIndex(int cursorArgumentIndex) {
-            this.cursorArgumentIndex = cursorArgumentIndex;
-        }
-
-        public int getCursorArgumentIndex() {
-            return this.cursorArgumentIndex;
-        }
-
-        public String getCursorArgument() {
-            if ((cursorArgumentIndex < 0)
-                || (cursorArgumentIndex >= arguments.length)) {
-                return null;
-            }
-
-            return arguments[cursorArgumentIndex];
-        }
-
-        public void setArgumentPosition(int argumentPosition) {
-            this.argumentPosition = argumentPosition;
-        }
-
-        public int getArgumentPosition() {
-            return this.argumentPosition;
-        }
-
-        public void setArguments(String[] arguments) {
-            this.arguments = arguments;
-        }
-
-        public String[] getArguments() {
-            return this.arguments;
-        }
-
-        public void setBufferPosition(int bufferPosition) {
-            this.bufferPosition = bufferPosition;
-        }
-
-        public int getBufferPosition() {
-            return this.bufferPosition;
-        }
-    }
-
-    public static class ProxyServiceCompleter implements Completer {
-        private final BundleContext context;
-        private final Class<? extends Completer> clazz;
-
-        public ProxyServiceCompleter(BundleContext context, Class<? extends Completer> clazz) {
-            this.context = context;
-            this.clazz = clazz;
-        }
-
-        @Override
-        public int complete(String buffer, int cursor, List<String> candidates) {
-            ServiceReference<? extends Completer> ref = context.getServiceReference(clazz);
-            if (ref != null) {
-                Completer completer = context.getService(ref);
-                if (completer != null) {
-                    try {
-                        return completer.complete(buffer, cursor, candidates);
-                    } finally {
-                        context.ungetService(ref);
-                    }
-                }
-            }
-            return -1;
-        }
-    }
-}


[05/11] KARAF-2772 Extracting command-api

Posted by cs...@apache.org.
http://git-wip-us.apache.org/repos/asf/karaf/blob/1ee78df9/shell/console/src/main/java/org/apache/felix/gogo/commands/basic/DefaultActionPreparator.java
----------------------------------------------------------------------
diff --git a/shell/console/src/main/java/org/apache/felix/gogo/commands/basic/DefaultActionPreparator.java b/shell/console/src/main/java/org/apache/felix/gogo/commands/basic/DefaultActionPreparator.java
deleted file mode 100644
index 6b7865e..0000000
--- a/shell/console/src/main/java/org/apache/felix/gogo/commands/basic/DefaultActionPreparator.java
+++ /dev/null
@@ -1,566 +0,0 @@
-/*
- * 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.basic;
-
-import static org.apache.karaf.shell.commands.ansi.SimpleAnsi.COLOR_DEFAULT;
-import static org.apache.karaf.shell.commands.ansi.SimpleAnsi.COLOR_RED;
-import static org.apache.karaf.shell.commands.ansi.SimpleAnsi.INTENSITY_BOLD;
-import static org.apache.karaf.shell.commands.ansi.SimpleAnsi.INTENSITY_NORMAL;
-
-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 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;
-
-@Deprecated
-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(COLOR_RED
-                                                   + "Error executing command "
-                                                   + command.scope() + ":" 
-                                                   + INTENSITY_BOLD + command.name() + INTENSITY_NORMAL
-                                                   + " undefined option "
-                                                   + INTENSITY_BOLD + param + INTENSITY_NORMAL
-                                                   + COLOR_DEFAULT,
-                                                   "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(COLOR_RED
-                                                   + "Error executing command "
-                                                   + command.scope() + ":" 
-                                                   + INTENSITY_BOLD + command.name() + INTENSITY_NORMAL
-                                                   + " missing value for option "
-                                                   + INTENSITY_BOLD + param + INTENSITY_NORMAL
-                                                   + COLOR_DEFAULT,
-                                                   "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(COLOR_RED
-                                                   + "Error executing command "
-                                                   + command.scope() + ":" 
-                                                   + INTENSITY_BOLD + command.name() + INTENSITY_NORMAL
-                                                   + ": too many arguments specified"
-                                                   + INTENSITY_BOLD + param + INTENSITY_NORMAL
-                                                   + COLOR_DEFAULT,
-                                                   "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(COLOR_RED
-                                               + "Error executing command "
-                                               + command.scope() + ":" 
-                                               + INTENSITY_BOLD + command.name() + INTENSITY_NORMAL
-                                               + ": option "
-                                               + INTENSITY_BOLD + option.name() + INTENSITY_NORMAL
-                                               + " is required"
-                                               + COLOR_DEFAULT,
-                            "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(COLOR_RED
-                                               + "Error executing command "
-                                               + command.scope() + ":" 
-                                               + INTENSITY_BOLD + command.name() + INTENSITY_NORMAL
-                                               + ": argument "
-                                               + INTENSITY_BOLD + argument.name() + INTENSITY_NORMAL
-                                               + " is required"
-                                               + COLOR_DEFAULT,
-                                               "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(COLOR_RED
-                                               + "Error executing command "
-                                               + command.scope() + ":" 
-                                               + INTENSITY_BOLD + command.name() + INTENSITY_NORMAL
-                                               + ": unable to convert option "
-                                               + INTENSITY_BOLD + entry.getKey().name() + INTENSITY_NORMAL
-                                               + " with value '" + entry.getValue() + "' to type "
-                                               + new GenericType(field.getGenericType()).toString()
-                                               + COLOR_DEFAULT,
-                            "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(COLOR_RED
-                                               + "Error executing command "
-                                               + command.scope() + ":" 
-                                               + INTENSITY_BOLD + command.name() + INTENSITY_NORMAL
-                                               + ": unable to convert argument "
-                                               + INTENSITY_BOLD + entry.getKey().name() + INTENSITY_NORMAL
-                                               + " with value '" + entry.getValue() + "' to type "
-                                               + new GenericType(field.getGenericType()).toString()
-                                               + COLOR_DEFAULT,
-                            "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) {
-            
-            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(INTENSITY_BOLD + "DESCRIPTION" + INTENSITY_NORMAL);
-                out.print("        ");
-                if (command.name() != null) {
-                    if (globalScope) {
-                        out.println(INTENSITY_BOLD + command.name() + INTENSITY_NORMAL);
-                    } else {
-                        out.println(command.scope() + ":" + INTENSITY_BOLD + command.name() + INTENSITY_NORMAL);
-                    }
-                    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()));
-                    }
-                }
-            }
-            int width = getWidth(session);
-            out.println(INTENSITY_BOLD + "SYNTAX" + INTENSITY_NORMAL);
-            out.print("        ");
-            out.println(syntax.toString());
-            out.println();
-            if (arguments.size() > 0) {
-                out.println(INTENSITY_BOLD + "ARGUMENTS" + INTENSITY_NORMAL);
-                for (Argument argument : arguments) {
-                    out.print("        ");
-                    out.println(INTENSITY_BOLD + argument.name() + INTENSITY_NORMAL);
-                    
-                    printFormatted("                ", argument.description(), width, 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(INTENSITY_BOLD + "OPTIONS" + INTENSITY_NORMAL);
-                for (Option option : options) {
-                    String opt = option.name();
-                    for (String alias : option.aliases()) {
-                        opt += ", " + alias;
-                    }
-                    out.print("        ");
-                    out.println(INTENSITY_BOLD + opt + INTENSITY_NORMAL);
-                    printFormatted("                ", option.description(), width, 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(INTENSITY_BOLD + "DETAILS" + INTENSITY_NORMAL);
-                String desc = loadDescription(action.getClass(), command.detailedDescription());
-                printFormatted("        ", desc, width, 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);
-    }
-
-    private int getWidth(CommandSession session) {
-        Object cols = session.get("COLUMNS");
-        return  (cols != null && cols instanceof Integer) ? (Integer)cols : 80;
-    }
-}

http://git-wip-us.apache.org/repos/asf/karaf/blob/1ee78df9/shell/console/src/main/java/org/apache/felix/gogo/commands/converter/DefaultConverter.java
----------------------------------------------------------------------
diff --git a/shell/console/src/main/java/org/apache/felix/gogo/commands/converter/DefaultConverter.java b/shell/console/src/main/java/org/apache/felix/gogo/commands/converter/DefaultConverter.java
deleted file mode 100644
index e7a4424..0000000
--- a/shell/console/src/main/java/org/apache/felix/gogo/commands/converter/DefaultConverter.java
+++ /dev/null
@@ -1,402 +0,0 @@
-/*
- * 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;
-
-@Deprecated
-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();
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/karaf/blob/1ee78df9/shell/console/src/main/java/org/apache/felix/gogo/commands/converter/GenericType.java
----------------------------------------------------------------------
diff --git a/shell/console/src/main/java/org/apache/felix/gogo/commands/converter/GenericType.java b/shell/console/src/main/java/org/apache/felix/gogo/commands/converter/GenericType.java
deleted file mode 100644
index dae6651..0000000
--- a/shell/console/src/main/java/org/apache/felix/gogo/commands/converter/GenericType.java
+++ /dev/null
@@ -1,196 +0,0 @@
-/*
- * 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;
-
-@Deprecated
-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);
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/karaf/blob/1ee78df9/shell/console/src/main/java/org/apache/felix/gogo/commands/converter/ReifiedType.java
----------------------------------------------------------------------
diff --git a/shell/console/src/main/java/org/apache/felix/gogo/commands/converter/ReifiedType.java b/shell/console/src/main/java/org/apache/felix/gogo/commands/converter/ReifiedType.java
deleted file mode 100644
index bc835d9..0000000
--- a/shell/console/src/main/java/org/apache/felix/gogo/commands/converter/ReifiedType.java
+++ /dev/null
@@ -1,118 +0,0 @@
-/*
- * 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
- */
-@Deprecated
-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;
-    }
-}

http://git-wip-us.apache.org/repos/asf/karaf/blob/1ee78df9/shell/console/src/main/java/org/apache/karaf/shell/commands/Action.java
----------------------------------------------------------------------
diff --git a/shell/console/src/main/java/org/apache/karaf/shell/commands/Action.java b/shell/console/src/main/java/org/apache/karaf/shell/commands/Action.java
deleted file mode 100644
index c3421e3..0000000
--- a/shell/console/src/main/java/org/apache/karaf/shell/commands/Action.java
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * 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.commands;
-
-import org.apache.felix.service.command.CommandSession;
-
-/**
- * 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.
- * 
- * Any class implementing Action must have a no argument constructor. This
- * is necessary so the help generator can instantiate the class and get the 
- * default values. 
- */
-public interface Action extends org.apache.felix.gogo.commands.Action {
-
-    Object execute(CommandSession session) throws Exception;
-
-}

http://git-wip-us.apache.org/repos/asf/karaf/blob/1ee78df9/shell/console/src/main/java/org/apache/karaf/shell/commands/Argument.java
----------------------------------------------------------------------
diff --git a/shell/console/src/main/java/org/apache/karaf/shell/commands/Argument.java b/shell/console/src/main/java/org/apache/karaf/shell/commands/Argument.java
deleted file mode 100644
index eced53d..0000000
--- a/shell/console/src/main/java/org/apache/karaf/shell/commands/Argument.java
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * 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.commands;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.lang.annotation.Target;
-import java.lang.annotation.ElementType;
-
-/**
- * Represents a positional argument on a command line (as opposed to an optional named {@link Option}
- */
-@Retention(RetentionPolicy.RUNTIME)
-@Target({ElementType.FIELD})
-public @interface Argument
-{
-    public static final String DEFAULT_STRING= "DEFAULT";
-
-    String DEFAULT = "##default";
-
-    String name() default DEFAULT;
-
-    String description() default "";
-
-    boolean required() default false;
-
-    int index() default 0;
-
-    boolean multiValued() default false;
-
-    String valueToShowInHelp() default DEFAULT_STRING;
-}

http://git-wip-us.apache.org/repos/asf/karaf/blob/1ee78df9/shell/console/src/main/java/org/apache/karaf/shell/commands/Command.java
----------------------------------------------------------------------
diff --git a/shell/console/src/main/java/org/apache/karaf/shell/commands/Command.java b/shell/console/src/main/java/org/apache/karaf/shell/commands/Command.java
deleted file mode 100644
index 490785d..0000000
--- a/shell/console/src/main/java/org/apache/karaf/shell/commands/Command.java
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * 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.commands;
-
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.lang.annotation.Target;
-import java.lang.annotation.ElementType;
-
-/**
- * Used to denote a class represents a command which is executable within a shell/scope or as a
- * command line process.
- */
-@Retention(RetentionPolicy.RUNTIME)
-@Target({ElementType.TYPE})
-public @interface Command
-{
-    /**
-     * Returns the scope or sub shell of the command
-     */
-    String scope();
-
-    /**
-     * REturns the name of the command if used inside a shell
-     */
-    String name();
-
-    /**
-     * Returns the description of the command which is used to generate command line help
-     */
-    String description() default "";
-
-    /**
-     * Returns a detailed description of the command
-     */
-    String detailedDescription() default "";
-}

http://git-wip-us.apache.org/repos/asf/karaf/blob/1ee78df9/shell/console/src/main/java/org/apache/karaf/shell/commands/CommandException.java
----------------------------------------------------------------------
diff --git a/shell/console/src/main/java/org/apache/karaf/shell/commands/CommandException.java b/shell/console/src/main/java/org/apache/karaf/shell/commands/CommandException.java
deleted file mode 100644
index 1ffa25d..0000000
--- a/shell/console/src/main/java/org/apache/karaf/shell/commands/CommandException.java
+++ /dev/null
@@ -1,64 +0,0 @@
-/*
- * 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.commands;
-
-import org.apache.karaf.shell.commands.ansi.SimpleAnsi;
-
-
-/**
- * Base class for exceptions thrown when executing commands.
- */
-@SuppressWarnings("serial")
-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
-                    : SimpleAnsi.COLOR_RED + "Error executing command: " 
-                    + getMessage() != null ? getMessage() : getClass().getName()
-                    + SimpleAnsi.COLOR_DEFAULT;
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/karaf/blob/1ee78df9/shell/console/src/main/java/org/apache/karaf/shell/commands/CommandWithAction.java
----------------------------------------------------------------------
diff --git a/shell/console/src/main/java/org/apache/karaf/shell/commands/CommandWithAction.java b/shell/console/src/main/java/org/apache/karaf/shell/commands/CommandWithAction.java
deleted file mode 100644
index 9209eb1..0000000
--- a/shell/console/src/main/java/org/apache/karaf/shell/commands/CommandWithAction.java
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * 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.commands;
-
-import org.apache.felix.service.command.Function;
-
-public interface CommandWithAction extends Function {
-
-    Class<? extends org.apache.felix.gogo.commands.Action> getActionClass();
-
-    org.apache.felix.gogo.commands.Action createNewAction();
-
-    void releaseAction(org.apache.felix.gogo.commands.Action action) throws Exception;
-
-}

http://git-wip-us.apache.org/repos/asf/karaf/blob/1ee78df9/shell/console/src/main/java/org/apache/karaf/shell/commands/Completer.java
----------------------------------------------------------------------
diff --git a/shell/console/src/main/java/org/apache/karaf/shell/commands/Completer.java b/shell/console/src/main/java/org/apache/karaf/shell/commands/Completer.java
deleted file mode 100644
index ece3e1d..0000000
--- a/shell/console/src/main/java/org/apache/karaf/shell/commands/Completer.java
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * 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.commands;
-
-import java.lang.annotation.ElementType;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.lang.annotation.Target;
-
-@Retention(RetentionPolicy.RUNTIME)
-@Target({ElementType.FIELD})
-public @interface Completer {
-
-    Class<? extends org.apache.karaf.shell.console.Completer> value();
-
-    String[] values() default { };
-
-    boolean caseSensitive() default false;
-
-}

http://git-wip-us.apache.org/repos/asf/karaf/blob/1ee78df9/shell/console/src/main/java/org/apache/karaf/shell/commands/CompleterValues.java
----------------------------------------------------------------------
diff --git a/shell/console/src/main/java/org/apache/karaf/shell/commands/CompleterValues.java b/shell/console/src/main/java/org/apache/karaf/shell/commands/CompleterValues.java
deleted file mode 100644
index 4463d35..0000000
--- a/shell/console/src/main/java/org/apache/karaf/shell/commands/CompleterValues.java
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * 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.commands;
-
-import java.lang.annotation.ElementType;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.lang.annotation.Target;
-
-/**
- * Represents a method which can return a List or Array of values used for a
- * {@link org.apache.karaf.shell.console.Completer}
- * which is associated with the index of an
- * {@link Argument}
- */
-@Retention(RetentionPolicy.RUNTIME)
-@Target({ElementType.METHOD, ElementType.FIELD})
-public @interface CompleterValues
-{
-    int index() default 0;
-}

http://git-wip-us.apache.org/repos/asf/karaf/blob/1ee78df9/shell/console/src/main/java/org/apache/karaf/shell/commands/HelpOption.java
----------------------------------------------------------------------
diff --git a/shell/console/src/main/java/org/apache/karaf/shell/commands/HelpOption.java b/shell/console/src/main/java/org/apache/karaf/shell/commands/HelpOption.java
deleted file mode 100644
index b5a0fa5..0000000
--- a/shell/console/src/main/java/org/apache/karaf/shell/commands/HelpOption.java
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * 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.commands;
-
-import java.lang.annotation.Annotation;
-
-public class HelpOption {
-
-    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;
-        }
-    };
-}

http://git-wip-us.apache.org/repos/asf/karaf/blob/1ee78df9/shell/console/src/main/java/org/apache/karaf/shell/commands/InfoProvider.java
----------------------------------------------------------------------
diff --git a/shell/console/src/main/java/org/apache/karaf/shell/commands/InfoProvider.java b/shell/console/src/main/java/org/apache/karaf/shell/commands/InfoProvider.java
deleted file mode 100644
index e253af1..0000000
--- a/shell/console/src/main/java/org/apache/karaf/shell/commands/InfoProvider.java
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * 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.commands;
-
-import java.util.Properties;
-
-/**
- * A bundle can publish a service with this interface to offer some informations for the shell:info command
- */
-public interface InfoProvider {
-
-	public String getName();
-
-	public Properties getProperties();
-
-}

http://git-wip-us.apache.org/repos/asf/karaf/blob/1ee78df9/shell/console/src/main/java/org/apache/karaf/shell/commands/Option.java
----------------------------------------------------------------------
diff --git a/shell/console/src/main/java/org/apache/karaf/shell/commands/Option.java b/shell/console/src/main/java/org/apache/karaf/shell/commands/Option.java
deleted file mode 100644
index b0a9e9c..0000000
--- a/shell/console/src/main/java/org/apache/karaf/shell/commands/Option.java
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * 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.commands;
-
-import java.lang.annotation.ElementType;
-import java.lang.annotation.Retention;
-import java.lang.annotation.RetentionPolicy;
-import java.lang.annotation.Target;
-
-/**
- * Used to mark an optional named command line option who's name typically starts with "--"
- */
-@Retention(RetentionPolicy.RUNTIME)
-@Target({ElementType.FIELD})
-public @interface Option
-{
-    public static final String DEFAULT_STRING= "DEFAULT";
-
-    String name();
-
-    String[] aliases() default {};
-
-    String description() default "";
-
-    boolean required() default false;
-
-    boolean multiValued() default false;
-
-    String valueToShowInHelp() default DEFAULT_STRING;
-}

http://git-wip-us.apache.org/repos/asf/karaf/blob/1ee78df9/shell/console/src/main/java/org/apache/karaf/shell/commands/ansi/SimpleAnsi.java
----------------------------------------------------------------------
diff --git a/shell/console/src/main/java/org/apache/karaf/shell/commands/ansi/SimpleAnsi.java b/shell/console/src/main/java/org/apache/karaf/shell/commands/ansi/SimpleAnsi.java
deleted file mode 100644
index 8437949..0000000
--- a/shell/console/src/main/java/org/apache/karaf/shell/commands/ansi/SimpleAnsi.java
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * 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.commands.ansi;
-
-import org.fusesource.jansi.Ansi;
-import org.fusesource.jansi.Ansi.Color;
-
-public class SimpleAnsi {
-    public static String COLOR_RED = Ansi.ansi().fg(Color.RED).toString();
-    public static String COLOR_DEFAULT = Ansi.ansi().fg(Color.DEFAULT).toString();
-    
-    public static String INTENSITY_BOLD = Ansi.ansi().bold().toString();
-    public static String INTENSITY_NORMAL = Ansi.ansi().boldOff().toString();
-}

http://git-wip-us.apache.org/repos/asf/karaf/blob/1ee78df9/shell/console/src/main/java/org/apache/karaf/shell/commands/basic/AbstractCommand.java
----------------------------------------------------------------------
diff --git a/shell/console/src/main/java/org/apache/karaf/shell/commands/basic/AbstractCommand.java b/shell/console/src/main/java/org/apache/karaf/shell/commands/basic/AbstractCommand.java
deleted file mode 100644
index 41d4a51..0000000
--- a/shell/console/src/main/java/org/apache/karaf/shell/commands/basic/AbstractCommand.java
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * 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.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.CommandWithAction;
-
-public abstract class AbstractCommand implements CommandWithAction {
-
-    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();
-    }
-    
-}

http://git-wip-us.apache.org/repos/asf/karaf/blob/1ee78df9/shell/console/src/main/java/org/apache/karaf/shell/commands/basic/ActionPreparator.java
----------------------------------------------------------------------
diff --git a/shell/console/src/main/java/org/apache/karaf/shell/commands/basic/ActionPreparator.java b/shell/console/src/main/java/org/apache/karaf/shell/commands/basic/ActionPreparator.java
deleted file mode 100644
index a6d2f8f..0000000
--- a/shell/console/src/main/java/org/apache/karaf/shell/commands/basic/ActionPreparator.java
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * 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.commands.basic;
-
-import java.util.List;
-
-import org.apache.felix.gogo.commands.Action;
-import org.apache.felix.service.command.CommandSession;
-
-public interface ActionPreparator {
-
-    /**
-     * Check if the arguments are valid for the action and inject the arguments into the fields
-     * of the action
-     * 
-     * Using deprecated Action for compatiblity
-     * 
-     * @param action
-     * @param session
-     * @param arguments
-     * @return
-     * @throws Exception
-     */
-    boolean prepare(@SuppressWarnings("deprecation") Action action, CommandSession session, List<Object> arguments) throws Exception;
-
-}


[08/11] KARAF-2772 Extracting command-api

Posted by cs...@apache.org.
http://git-wip-us.apache.org/repos/asf/karaf/blob/1ee78df9/shell/command-api/src/main/java/org/apache/karaf/shell/console/commands/BasicSubShell.java
----------------------------------------------------------------------
diff --git a/shell/command-api/src/main/java/org/apache/karaf/shell/console/commands/BasicSubShell.java b/shell/command-api/src/main/java/org/apache/karaf/shell/console/commands/BasicSubShell.java
new file mode 100644
index 0000000..3e40c5a
--- /dev/null
+++ b/shell/command-api/src/main/java/org/apache/karaf/shell/console/commands/BasicSubShell.java
@@ -0,0 +1,51 @@
+/**
+ *
+ * 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.console.commands;
+
+import org.apache.karaf.shell.console.SubShell;
+
+public class BasicSubShell implements SubShell {
+    
+    private String name;
+    private String description;
+    private String detailedDescription;
+
+    public String getName() {
+        return name;
+    }
+
+    public void setName(String name) {
+        this.name = name;
+    }
+
+    public String getDescription() {
+        return description;
+    }
+
+    public void setDescription(String description) {
+        this.description = description;
+    }
+
+    public String getDetailedDescription() {
+        return detailedDescription;
+    }
+
+    public void setDetailedDescription(String detailedDescription) {
+        this.detailedDescription = detailedDescription;
+    }
+}

http://git-wip-us.apache.org/repos/asf/karaf/blob/1ee78df9/shell/command-api/src/main/java/org/apache/karaf/shell/console/commands/BlueprintCommand.java
----------------------------------------------------------------------
diff --git a/shell/command-api/src/main/java/org/apache/karaf/shell/console/commands/BlueprintCommand.java b/shell/command-api/src/main/java/org/apache/karaf/shell/console/commands/BlueprintCommand.java
new file mode 100644
index 0000000..83fa1ec
--- /dev/null
+++ b/shell/command-api/src/main/java/org/apache/karaf/shell/console/commands/BlueprintCommand.java
@@ -0,0 +1,110 @@
+/**
+ *
+ * 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.console.commands;
+
+import java.lang.reflect.Type;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.felix.gogo.commands.Action;
+import org.apache.felix.service.command.CommandSession;
+import org.apache.karaf.shell.commands.basic.AbstractCommand;
+import org.apache.karaf.shell.commands.basic.ActionPreparator;
+import org.apache.karaf.shell.commands.basic.DefaultActionPreparator;
+import org.apache.karaf.shell.console.BlueprintContainerAware;
+import org.apache.karaf.shell.console.BundleContextAware;
+import org.apache.karaf.shell.console.CompletableFunction;
+import org.apache.karaf.shell.console.Completer;
+import org.osgi.framework.BundleContext;
+import org.osgi.service.blueprint.container.BlueprintContainer;
+import org.osgi.service.blueprint.container.Converter;
+
+public class
+        BlueprintCommand extends AbstractCommand implements CompletableFunction
+{
+
+    protected BlueprintContainer blueprintContainer;
+    protected Converter blueprintConverter;
+    protected String actionId;
+    protected String shell;
+    protected List<Completer> completers;
+    protected Map<String,Completer> optionalCompleters;
+
+    public void setBlueprintContainer(BlueprintContainer blueprintContainer) {
+        this.blueprintContainer = blueprintContainer;
+    }
+
+    public void setBlueprintConverter(Converter blueprintConverter) {
+        this.blueprintConverter = blueprintConverter;
+    }
+
+    public void setActionId(String actionId) {
+        this.actionId = actionId;
+    }
+    
+    public void setShell(String shell) {
+        this.shell = shell;
+    }
+
+    public List<Completer> getCompleters() {
+        return completers;
+    }
+
+    public void setCompleters(List<Completer> completers) {
+        this.completers = completers;
+    }
+
+    public Map<String, Completer> getOptionalCompleters() {
+        return optionalCompleters;
+    }
+
+    public void setOptionalCompleters(Map<String, Completer> optionalCompleters) {
+        this.optionalCompleters = optionalCompleters;
+    }
+
+    @Override
+    protected ActionPreparator getPreparator() throws Exception {
+        return new BlueprintActionPreparator();
+    }
+
+    protected class BlueprintActionPreparator extends DefaultActionPreparator {
+
+        @Override
+        protected Object convert(Action action, CommandSession commandSession, Object o, Type type) throws Exception {
+            GenericType t = new GenericType(type);
+            if (t.getRawClass() == String.class) {
+                return o != null ? o.toString() : null;
+            }
+            return blueprintConverter.convert(o, t);
+        }
+
+    }
+
+    public Action createNewAction() {
+        Action action = (Action) blueprintContainer.getComponentInstance(actionId);
+        if (action instanceof BlueprintContainerAware) {
+            ((BlueprintContainerAware) action).setBlueprintContainer(blueprintContainer);
+        }
+        if (action instanceof BundleContextAware) {
+            BundleContext context = (BundleContext) blueprintContainer.getComponentInstance("blueprintBundleContext");
+            ((BundleContextAware) action).setBundleContext(context);
+        }
+        return action;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/karaf/blob/1ee78df9/shell/command-api/src/main/java/org/apache/karaf/shell/console/commands/GenericType.java
----------------------------------------------------------------------
diff --git a/shell/command-api/src/main/java/org/apache/karaf/shell/console/commands/GenericType.java b/shell/command-api/src/main/java/org/apache/karaf/shell/console/commands/GenericType.java
new file mode 100644
index 0000000..faf7c18
--- /dev/null
+++ b/shell/command-api/src/main/java/org/apache/karaf/shell/console/commands/GenericType.java
@@ -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.karaf.shell.console.commands;
+
+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;
+import org.osgi.service.blueprint.container.ReifiedType;
+
+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);
+	}
+
+}

http://git-wip-us.apache.org/repos/asf/karaf/blob/1ee78df9/shell/command-api/src/main/java/org/apache/karaf/shell/console/commands/NamespaceHandler.java
----------------------------------------------------------------------
diff --git a/shell/command-api/src/main/java/org/apache/karaf/shell/console/commands/NamespaceHandler.java b/shell/command-api/src/main/java/org/apache/karaf/shell/console/commands/NamespaceHandler.java
new file mode 100644
index 0000000..6c3a5b3
--- /dev/null
+++ b/shell/command-api/src/main/java/org/apache/karaf/shell/console/commands/NamespaceHandler.java
@@ -0,0 +1,523 @@
+/*
+ * 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.console.commands;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.net.URL;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import org.apache.aries.blueprint.ParserContext;
+import org.apache.aries.blueprint.PassThroughMetadata;
+import org.apache.aries.blueprint.mutable.MutableBeanMetadata;
+import org.apache.aries.blueprint.mutable.MutableCollectionMetadata;
+import org.apache.aries.blueprint.mutable.MutableIdRefMetadata;
+import org.apache.aries.blueprint.mutable.MutablePassThroughMetadata;
+import org.apache.aries.blueprint.mutable.MutableRefMetadata;
+import org.apache.aries.blueprint.mutable.MutableReferenceMetadata;
+import org.apache.aries.blueprint.mutable.MutableServiceMetadata;
+import org.apache.aries.blueprint.mutable.MutableValueMetadata;
+import org.apache.felix.gogo.commands.Action;
+import org.apache.karaf.shell.console.Completer;
+import org.apache.karaf.shell.console.SubShellAction;
+import org.apache.karaf.shell.commands.Command;
+import org.apache.karaf.shell.inject.Destroy;
+import org.apache.karaf.shell.inject.Init;
+import org.apache.karaf.shell.inject.Reference;
+import org.apache.karaf.shell.inject.Service;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.wiring.BundleWiring;
+import org.osgi.service.blueprint.container.ComponentDefinitionException;
+import org.osgi.service.blueprint.reflect.BeanArgument;
+import org.osgi.service.blueprint.reflect.BeanMetadata;
+import org.osgi.service.blueprint.reflect.BeanProperty;
+import org.osgi.service.blueprint.reflect.ComponentMetadata;
+import org.osgi.service.blueprint.reflect.IdRefMetadata;
+import org.osgi.service.blueprint.reflect.MapMetadata;
+import org.osgi.service.blueprint.reflect.Metadata;
+import org.osgi.service.blueprint.reflect.NullMetadata;
+import org.osgi.service.blueprint.reflect.RefMetadata;
+import org.osgi.service.blueprint.reflect.ReferenceMetadata;
+import org.osgi.service.blueprint.reflect.ServiceMetadata;
+import org.osgi.service.blueprint.reflect.ValueMetadata;
+import org.w3c.dom.Attr;
+import org.w3c.dom.Element;
+import org.w3c.dom.NamedNodeMap;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+
+
+public class NamespaceHandler implements org.apache.aries.blueprint.NamespaceHandler {
+
+    public static final String ID = "id";
+    public static final String ACTION = "action";
+    public static final String ACTION_ID = "actionId";
+    public static final String COMMAND_BUNDLE = "command-bundle";
+    public static final String SCAN = "scan";
+    public static final String NAME = "name";
+    public static final String COMMAND = "command";
+    public static final String COMPLETERS = "completers";
+    public static final String OPTIONAL_COMPLETERS = "optional-completers";
+    public static final String OPTIONAL_COMPLETERS_PROPERTY = "optionalCompleters";
+    public static final String BEAN = "bean";
+    public static final String REF = "ref";
+    public static final String NULL = "null";
+    public static final String MAP = "map";
+    public static final String BLUEPRINT_CONTAINER = "blueprintContainer";
+    public static final String BLUEPRINT_CONVERTER = "blueprintConverter";
+
+    public static final String SHELL_NAMESPACE_1_0_0 = "http://karaf.apache.org/xmlns/shell/v1.0.0";
+    public static final String SHELL_NAMESPACE_1_1_0 = "http://karaf.apache.org/xmlns/shell/v1.1.0";
+    public static final String SHELL_NAMESPACE_1_2_0 = "http://karaf.apache.org/xmlns/shell/v1.2.0";
+
+    private int nameCounter = 0;
+
+    public URL getSchemaLocation(String namespace) {
+        if(SHELL_NAMESPACE_1_0_0.equals(namespace)) {
+            return getClass().getResource("karaf-shell-1.0.0.xsd");
+        } else if(SHELL_NAMESPACE_1_1_0.equals(namespace)) {
+            return getClass().getResource("karaf-shell-1.1.0.xsd");
+        } else if(SHELL_NAMESPACE_1_2_0.equals(namespace)) {
+            return getClass().getResource("karaf-shell-1.2.0.xsd");
+        }
+        return getClass().getResource("karaf-shell-1.2.0.xsd");
+    }
+
+	public Set<Class> getManagedClasses() {
+		return new HashSet<Class>(Arrays.asList(
+			BlueprintCommand.class
+		));
+	}
+
+    public ComponentMetadata decorate(Node node, ComponentMetadata component, ParserContext context) {
+        throw new ComponentDefinitionException("Bad xml syntax: node decoration is not supported");
+    }
+
+    public Metadata parse(Element element, ParserContext context) {
+        if (nodeNameEquals(element, COMMAND_BUNDLE)) {
+            NamedNodeMap attrs = element.getAttributes();
+            for (int i = 0; i < attrs.getLength(); i++) {
+                Node child = attrs.item(i);
+                if (child instanceof Attr) {
+                    Attr childAttr = (Attr) child;
+                    parseChildAttr(childAttr, context);
+                }
+            }
+            NodeList children = element.getChildNodes();
+            for (int i = 0; i < children.getLength(); i++) {
+                Node child  = children.item(i);
+                if (child instanceof Element) {
+                    Element childElement = (Element) child;
+                    parseChildElement(childElement, context);
+                }
+            }
+            registerConverters(context);
+            return null;
+        } else {
+            throw new IllegalStateException("Unexpected element " + element.getNodeName());
+        }
+    }
+
+    private void parseChildAttr(Attr attr, ParserContext context) {
+        if (nodeNameEquals(attr, SCAN)) {
+            scan(attr, context);
+        }
+    }
+
+    private void scan(Attr attr, ParserContext context) {
+        try {
+            Bundle bundle = getBundle(context);
+            BundleWiring wiring = bundle.adapt(BundleWiring.class);
+            for (String pkg : attr.getValue().split(" ")) {
+                String name = pkg;
+                int options = BundleWiring.LISTRESOURCES_LOCAL;
+                name = name.replace('.', '/');
+                if (name.endsWith("*")) {
+                    options |= BundleWiring.LISTRESOURCES_RECURSE;
+                    name = name.substring(0, name.length() - 1);
+                }
+                if (!name.startsWith("/")) {
+                    name = "/" + name;
+                }
+                if (name.endsWith("/")) {
+                    name = name.substring(0, name.length() - 1);
+                }
+                Collection<String> classes = wiring.listResources(name, "*.class", options);
+                for (String className : classes) {
+                    className = className.replace('/', '.').replace(".class", "");
+                    inspectClass(context, bundle.loadClass(className));
+                }
+            }
+        } catch (ComponentDefinitionException e) {
+            throw e;
+        } catch (Exception e) {
+            throw new ComponentDefinitionException("Unable to scan commands", e);
+        }
+    }
+
+    private void inspectClass(ParserContext context, Class<?> clazz) throws Exception {
+        Service reg = clazz.getAnnotation(Service.class);
+        if (reg == null) {
+            return;
+        }
+        if (Action.class.isAssignableFrom(clazz)) {
+            final Command cmd = clazz.getAnnotation(Command.class);
+            if (cmd == null) {
+                throw new IllegalArgumentException("Command " + clazz.getName() + " is not annotated with @Command");
+            }
+            String scope = cmd.scope();
+            String function = cmd.name();
+            // Create action
+            MutableBeanMetadata action = context.createMetadata(MutableBeanMetadata.class);
+            action.setId(getName());
+            action.setActivation(MutableBeanMetadata.ACTIVATION_LAZY);
+            action.setScope(MutableBeanMetadata.SCOPE_PROTOTYPE);
+            action.setRuntimeClass(clazz);
+            for (Class<?> cl = clazz; cl != Object.class; cl = cl.getSuperclass()) {
+                for (Field field : cl.getDeclaredFields()) {
+                    if (field.getAnnotation(Reference.class) != null) {
+                        if (field.getType() == BundleContext.class) {
+                            action.addProperty(field.getName(), createRef(context, "blueprintBundleContext"));
+                        } else {
+                            action.addProperty(field.getName(), createRef(context, createServiceRef(context, field.getType()).getId()));
+                        }
+                    }
+                }
+                for (Method method : cl.getDeclaredMethods()) {
+                    if (method.getAnnotation(Init.class) != null) {
+                        if (action.getInitMethod() == null) {
+                            action.setInitMethod(method.getName());
+                        }
+                    }
+                    if (method.getAnnotation(Destroy.class) != null) {
+                        if (action.getDestroyMethod() == null) {
+                            action.setDestroyMethod(method.getName());
+                        }
+                    }
+                }
+            }
+            context.getComponentDefinitionRegistry().registerComponentDefinition(action);
+            // Create command
+            MutableBeanMetadata command = context.createMetadata(MutableBeanMetadata.class);
+            command.setRuntimeClass(BlueprintCommand.class);
+            command.addProperty(BLUEPRINT_CONTAINER, createRef(context, BLUEPRINT_CONTAINER));
+            command.addProperty(BLUEPRINT_CONVERTER, createRef(context, BLUEPRINT_CONVERTER));
+            command.addProperty(ACTION_ID, createIdRef(context, action.getId()));
+            // Create command service
+            MutableServiceMetadata commandService = context.createMetadata(MutableServiceMetadata.class);
+            commandService.setActivation(MutableServiceMetadata.ACTIVATION_LAZY);
+            commandService.setId(getName());
+            commandService.setAutoExport(ServiceMetadata.AUTO_EXPORT_ALL_CLASSES);
+            commandService.setServiceComponent(command);
+            commandService.addServiceProperty(createStringValue(context, "osgi.command.scope"),
+                    createStringValue(context, scope));
+            commandService.addServiceProperty(createStringValue(context, "osgi.command.function"),
+                    createStringValue(context, function));
+            context.getComponentDefinitionRegistry().registerComponentDefinition(commandService);
+
+            // create the sub-shell action
+            createSubShell(context, scope);
+        }
+        if (Completer.class.isAssignableFrom(clazz)) {
+            MutableBeanMetadata completer = context.createMetadata(MutableBeanMetadata.class);
+            completer.setId(getName());
+            completer.setActivation(MutableBeanMetadata.ACTIVATION_LAZY);
+            completer.setScope(MutableBeanMetadata.SCOPE_SINGLETON);
+            completer.setRuntimeClass(clazz);
+            // Create completer
+            for (Class<?> cl = clazz; cl != Object.class; cl = cl.getSuperclass()) {
+                for (Field field : cl.getDeclaredFields()) {
+                    if (field.getAnnotation(Reference.class) != null) {
+                        if (field.getType() == BundleContext.class) {
+                            completer.addProperty(field.getName(), createRef(context, "blueprintBundleContext"));
+                        } else {
+                            completer.addProperty(field.getName(), createRef(context, createServiceRef(context, field.getType()).getId()));
+                        }
+                    }
+                }
+                for (Method method : cl.getDeclaredMethods()) {
+                    if (method.getAnnotation(Init.class) != null) {
+                        if (completer.getInitMethod() == null) {
+                            completer.setInitMethod(method.getName());
+                        }
+                    }
+                    if (method.getAnnotation(Destroy.class) != null) {
+                        if (completer.getDestroyMethod() == null) {
+                            completer.setDestroyMethod(method.getName());
+                        }
+                    }
+                }
+            }
+            context.getComponentDefinitionRegistry().registerComponentDefinition(completer);
+            // Create completer service
+            MutableServiceMetadata completerService = context.createMetadata(MutableServiceMetadata.class);
+            completerService.setActivation(MutableServiceMetadata.ACTIVATION_LAZY);
+            completerService.setId(getName());
+            completerService.setAutoExport(ServiceMetadata.AUTO_EXPORT_ALL_CLASSES);
+            completerService.setServiceComponent(createRef(context, completer.getId()));
+            context.getComponentDefinitionRegistry().registerComponentDefinition(completerService);
+        }
+    }
+
+    private ComponentMetadata createServiceRef(ParserContext context, Class<?> cls) {
+        String id = ".serviceref." + cls.getName();
+        ComponentMetadata metadata = context.getComponentDefinitionRegistry().getComponentDefinition(id);
+        if (metadata == null) {
+            MutableReferenceMetadata m = context.createMetadata(MutableReferenceMetadata.class);
+            m.setRuntimeInterface(cls);
+            m.setInterface(cls.getName());
+            m.setActivation(ReferenceMetadata.ACTIVATION_EAGER);
+            m.setAvailability(ReferenceMetadata.AVAILABILITY_MANDATORY);
+            m.setId(id);
+            context.getComponentDefinitionRegistry().registerComponentDefinition(m);
+            return m;
+        } else {
+            return metadata;
+        }
+    }
+
+    private Bundle getBundle(ParserContext context) {
+        PassThroughMetadata ptm = (PassThroughMetadata) context.getComponentDefinitionRegistry().getComponentDefinition("blueprintBundle");
+        return (Bundle) ptm.getObject();
+    }
+
+    private void parseChildElement(Element element, ParserContext context) {
+        if (nodeNameEquals(element, COMMAND)) {
+            parseCommand(element, context);
+        }
+    }
+
+    private void registerConverters(ParserContext context) {
+        String converterName = "." + NumberToStringConverter.class.getName();
+        if (!context.getComponentDefinitionRegistry().containsComponentDefinition(converterName)) {
+            MutablePassThroughMetadata cnv = context.createMetadata(MutablePassThroughMetadata.class);
+            cnv.setId(converterName);
+            cnv.setObject(new NumberToStringConverter());
+            context.getComponentDefinitionRegistry().registerTypeConverter(cnv);
+        }
+    }
+
+    private void parseCommand(Element element, ParserContext context) {
+        MutableBeanMetadata command = context.createMetadata(MutableBeanMetadata.class);
+        command.setRuntimeClass(BlueprintCommand.class);
+        command.addProperty(BLUEPRINT_CONTAINER, createRef(context, BLUEPRINT_CONTAINER));
+        command.addProperty(BLUEPRINT_CONVERTER, createRef(context, BLUEPRINT_CONVERTER));
+
+        NodeList children = element.getChildNodes();
+        MutableBeanMetadata action = null;
+        for (int i = 0; i < children.getLength(); i++) {
+            Node child  = children.item(i);
+            if (child instanceof Element) {
+                Element childElement = (Element) child;
+                if (nodeNameEquals(childElement, ACTION)) {
+                    action = parseAction(context, command, childElement);
+                    action.setId(getName());
+                    context.getComponentDefinitionRegistry().registerComponentDefinition(action);
+                    command.addProperty(ACTION_ID, createIdRef(context, action.getId()));
+                } else if (nodeNameEquals(childElement, COMPLETERS)) {
+                    command.addProperty(COMPLETERS, parseCompleters(context, command, childElement));
+                } else if (nodeNameEquals(childElement, OPTIONAL_COMPLETERS)) {
+                    command.addProperty(OPTIONAL_COMPLETERS_PROPERTY, parseOptionalCompleters(context, command, childElement));
+                }
+                else {
+                    throw new ComponentDefinitionException("Bad xml syntax: unknown element '" + childElement.getNodeName() + "'");
+                }
+            }
+        }
+
+        MutableServiceMetadata commandService = context.createMetadata(MutableServiceMetadata.class);
+        commandService.setActivation(MutableServiceMetadata.ACTIVATION_LAZY);
+        commandService.setId(getName());
+        commandService.setAutoExport(ServiceMetadata.AUTO_EXPORT_ALL_CLASSES);
+        commandService.setServiceComponent(command);
+
+        String scope;
+        String function;
+        if (SHELL_NAMESPACE_1_0_0.equals(element.getNamespaceURI())) {
+            String location = element.getAttribute(NAME);
+            location = location.replace('/', ':');
+            if (location.lastIndexOf(':') >= 0) {
+                scope = location.substring(0, location.lastIndexOf(':'));
+                function = location.substring(location.lastIndexOf(':') + 1);
+            } else {
+                scope = "";
+                function = location;
+            }
+        } else {
+            try {
+                Class actionClass = getBundle(context).loadClass(action.getClassName());
+                scope = getScope(actionClass);
+                function = getName(actionClass);
+            } catch (Throwable e) {
+                throw new ComponentDefinitionException("Unable to introspect action " + action.getClassName(), e);
+            }
+        }
+        commandService.addServiceProperty(createStringValue(context, "osgi.command.scope"),
+                createStringValue(context, scope));
+        commandService.addServiceProperty(createStringValue(context, "osgi.command.function"),
+                createStringValue(context, function));
+
+        context.getComponentDefinitionRegistry().registerComponentDefinition(commandService);
+
+        // create the sub-shell action
+        createSubShell(context, scope);
+    }
+
+    private void createSubShell(ParserContext context, String scope) {
+        String subShellName = ".subshell." + scope;
+        if (context.getComponentDefinitionRegistry().containsComponentDefinition(subShellName)) {
+            return;
+        }
+        MutableBeanMetadata subShellAction = context.createMetadata(MutableBeanMetadata.class);
+        subShellAction.setRuntimeClass(SubShellAction.class);
+        subShellAction.setActivation(MutableBeanMetadata.ACTIVATION_LAZY);
+        subShellAction.setScope(MutableBeanMetadata.SCOPE_PROTOTYPE);
+        subShellAction.setId(getName());
+        subShellAction.addProperty("subShell", createStringValue(context, scope));
+        context.getComponentDefinitionRegistry().registerComponentDefinition(subShellAction);
+        // generate the sub-shell command
+        MutableBeanMetadata subShellCommand = context.createMetadata(MutableBeanMetadata.class);
+        subShellCommand.setId(getName());
+        subShellCommand.setRuntimeClass(BlueprintCommand.class);
+        subShellCommand.addProperty(BLUEPRINT_CONTAINER, createRef(context, BLUEPRINT_CONTAINER));
+        subShellCommand.addProperty(BLUEPRINT_CONVERTER, createRef(context, BLUEPRINT_CONVERTER));
+        subShellCommand.addProperty(ACTION_ID, createIdRef(context, subShellAction.getId()));
+        context.getComponentDefinitionRegistry().registerComponentDefinition(subShellCommand);
+        // generate the sub-shell OSGi service
+        MutableServiceMetadata subShellCommandService = context.createMetadata(MutableServiceMetadata.class);
+        subShellCommandService.setActivation(MutableServiceMetadata.ACTIVATION_LAZY);
+        subShellCommandService.setId(subShellName);
+        subShellCommandService.setAutoExport(ServiceMetadata.AUTO_EXPORT_ALL_CLASSES);
+        subShellCommandService.setServiceComponent(subShellCommand);
+        subShellCommandService.addServiceProperty(createStringValue(context, "osgi.command.scope"), createStringValue(context, "*"));
+        subShellCommandService.addServiceProperty(createStringValue(context, "osgi.command.function"), createStringValue(context, scope));
+        context.getComponentDefinitionRegistry().registerComponentDefinition(subShellCommandService);
+    }
+
+    private MutableBeanMetadata getInvocationValue(ParserContext context, String method, String className) {
+        MutableBeanMetadata scope = context.createMetadata(MutableBeanMetadata.class);
+        scope.setRuntimeClass(NamespaceHandler.class);
+        scope.setFactoryMethod(method);
+        scope.addArgument(createStringValue(context, className), null, 0);
+        return scope;
+    }
+
+    private MutableBeanMetadata parseAction(ParserContext context, ComponentMetadata enclosingComponent, Element element) {
+        MutableBeanMetadata action = context.createMetadata(MutableBeanMetadata.class);
+        action.setActivation(MutableBeanMetadata.ACTIVATION_LAZY);
+        action.setScope(MutableBeanMetadata.SCOPE_PROTOTYPE);
+        action.setClassName(element.getAttribute("class"));
+        NodeList children = element.getChildNodes();
+        for (int i = 0; i < children.getLength(); i++) {
+            Node child  = children.item(i);
+            if (child instanceof Element) {
+                Element childElement = (Element) child;
+                if (nodeNameEquals(childElement, "argument")) {
+                    action.addArgument(context.parseElement(BeanArgument.class, enclosingComponent, childElement));
+                } else if (nodeNameEquals(childElement, "property")) {
+                    action.addProperty(context.parseElement(BeanProperty.class, enclosingComponent, childElement));
+                }
+            }
+        }
+        return action;
+    }
+
+    private Metadata parseOptionalCompleters(ParserContext context, ComponentMetadata enclosingComponent, Element element) {
+        Metadata metadata = context.parseElement(MapMetadata.class, context.getEnclosingComponent(), (Element) element);
+        return metadata;
+    }
+
+    private Metadata parseCompleters(ParserContext context, ComponentMetadata enclosingComponent, Element element) {
+        MutableCollectionMetadata collection = context.createMetadata(MutableCollectionMetadata.class);
+        collection.setCollectionClass(List.class);
+        NodeList children = element.getChildNodes();
+        for (int i = 0; i < children.getLength(); i++) {
+            Node child = children.item(i);
+            if (child instanceof Element) {
+                Metadata metadata;
+                if (nodeNameEquals(child, REF)) {
+                    metadata = context.parseElement(RefMetadata.class, context.getEnclosingComponent(), (Element) child);
+                } else if (nodeNameEquals(child, NULL)) {
+                    metadata = context.parseElement(NullMetadata.class, context.getEnclosingComponent(), (Element) child);
+                } else if (nodeNameEquals(child, BEAN)) {
+                    metadata = context.parseElement(BeanMetadata.class, enclosingComponent, (Element) child);
+                } else {
+                    throw new IllegalStateException("Unexpected element " + child.getNodeName());
+                }
+                collection.addValue(metadata);
+            }
+        }
+        return collection;
+    }
+
+    private ValueMetadata createStringValue(ParserContext context, String str) {
+        MutableValueMetadata value = context.createMetadata(MutableValueMetadata.class);
+        value.setStringValue(str);
+        return value;
+    }
+
+    private RefMetadata createRef(ParserContext context, String id) {
+        MutableRefMetadata idref = context.createMetadata(MutableRefMetadata.class);
+        idref.setComponentId(id);
+        return idref;
+    }
+
+    private IdRefMetadata createIdRef(ParserContext context, String id) {
+        MutableIdRefMetadata idref = context.createMetadata(MutableIdRefMetadata.class);
+        idref.setComponentId(id);
+        return idref;
+    }
+
+    public synchronized String getName() {
+        return "shell-" + ++nameCounter;
+    }
+    
+    private static boolean nodeNameEquals(Node node, String name) {
+        return (name.equals(node.getNodeName()) || name.equals(node.getLocalName()));
+    }
+
+    public static String getScope(Class<?> action) {
+        Command command = action.getAnnotation(Command.class);
+        if (command != null) {
+            return command.scope();
+        }
+        org.apache.felix.gogo.commands.Command command2 = action.getAnnotation(org.apache.felix.gogo.commands.Command.class);
+        if (command2 != null) {
+            return command2.scope();
+        }
+        return null;
+    }
+
+    public static String getName(Class<?> action) {
+        Command command = action.getAnnotation(Command.class);
+        if (command != null) {
+            return command.name();
+        }
+        org.apache.felix.gogo.commands.Command command2 = action.getAnnotation(org.apache.felix.gogo.commands.Command.class);
+        if (command2 != null) {
+            return command2.name();
+        }
+        return null;
+    }
+}

http://git-wip-us.apache.org/repos/asf/karaf/blob/1ee78df9/shell/command-api/src/main/java/org/apache/karaf/shell/console/commands/NumberToStringConverter.java
----------------------------------------------------------------------
diff --git a/shell/command-api/src/main/java/org/apache/karaf/shell/console/commands/NumberToStringConverter.java b/shell/command-api/src/main/java/org/apache/karaf/shell/console/commands/NumberToStringConverter.java
new file mode 100644
index 0000000..fb68c3a
--- /dev/null
+++ b/shell/command-api/src/main/java/org/apache/karaf/shell/console/commands/NumberToStringConverter.java
@@ -0,0 +1,36 @@
+/*
+ * 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.console.commands;
+
+import org.osgi.service.blueprint.container.Converter;
+import org.osgi.service.blueprint.container.ReifiedType;
+
+public class NumberToStringConverter implements Converter {
+
+    @Override
+    public boolean canConvert(Object sourceObject, ReifiedType targetType) {
+        return sourceObject != null && sourceObject instanceof Number
+                && targetType.getRawClass() == String.class;
+    }
+
+    @Override
+    public Object convert(Object sourceObject, ReifiedType targetType) throws Exception {
+        return sourceObject.toString();
+    }
+}

http://git-wip-us.apache.org/repos/asf/karaf/blob/1ee78df9/shell/command-api/src/main/java/org/apache/karaf/shell/console/completer/AggregateCompleter.java
----------------------------------------------------------------------
diff --git a/shell/command-api/src/main/java/org/apache/karaf/shell/console/completer/AggregateCompleter.java b/shell/command-api/src/main/java/org/apache/karaf/shell/console/completer/AggregateCompleter.java
new file mode 100644
index 0000000..dbba436
--- /dev/null
+++ b/shell/command-api/src/main/java/org/apache/karaf/shell/console/completer/AggregateCompleter.java
@@ -0,0 +1,91 @@
+/*
+ * 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.console.completer;
+
+import java.util.ArrayList;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Collection;
+
+import org.apache.karaf.shell.console.Completer;
+
+/**
+ * Completer which contains multipule completers and aggregates them together.
+ */
+public class AggregateCompleter implements Completer
+{
+    private final Collection<Completer> completers;
+
+    public AggregateCompleter(final Collection<Completer> completers) {
+        assert completers != null;
+        this.completers = completers;
+    }
+
+    @SuppressWarnings({ "unchecked", "rawtypes" })
+    public int complete(final String buffer, final int cursor, final List candidates) {
+        // buffer could be null
+        assert candidates != null;
+
+        List<Completion> completions = new ArrayList<Completion>(completers.size());
+
+        // Run each completer, saving its completion results
+        int max = -1;
+        for (Completer completer : completers) {
+            Completion completion = new Completion(candidates);
+            completion.complete(completer, buffer, cursor);
+
+            // Compute the max cursor position
+            max = Math.max(max, completion.cursor);
+
+            completions.add(completion);
+        }
+
+        // Append candiates from completions which have the same cursor position as max
+        for (Completion completion : completions) {
+            if (completion.cursor == max) {
+                // noinspection unchecked
+                candidates.addAll(completion.candidates);
+            }
+        }
+
+        return max;
+    }
+
+    private class Completion
+    {
+        public final List<String> candidates;
+
+        public int cursor;
+
+        @SuppressWarnings({ "unchecked", "rawtypes" })
+        public Completion(final List candidates) {
+            assert candidates != null;
+
+            // noinspection unchecked
+            this.candidates = new LinkedList<String>(candidates);
+        }
+
+        public void complete(final Completer completer, final String buffer, final int cursor) {
+            assert completer != null;
+
+            this.cursor = completer.complete(buffer, cursor, candidates);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/karaf/blob/1ee78df9/shell/command-api/src/main/java/org/apache/karaf/shell/console/completer/ArgumentCompleter.java
----------------------------------------------------------------------
diff --git a/shell/command-api/src/main/java/org/apache/karaf/shell/console/completer/ArgumentCompleter.java b/shell/command-api/src/main/java/org/apache/karaf/shell/console/completer/ArgumentCompleter.java
new file mode 100644
index 0000000..745489e
--- /dev/null
+++ b/shell/command-api/src/main/java/org/apache/karaf/shell/console/completer/ArgumentCompleter.java
@@ -0,0 +1,567 @@
+/*
+ * 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.console.completer;
+
+import java.io.File;
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.EnumSet;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.felix.gogo.commands.Action;
+import org.apache.felix.service.command.Function;
+import org.apache.karaf.shell.commands.Argument;
+import org.apache.karaf.shell.commands.CommandWithAction;
+import org.apache.karaf.shell.commands.CompleterValues;
+import org.apache.karaf.shell.commands.HelpOption;
+import org.apache.karaf.shell.commands.Option;
+import org.apache.felix.service.command.CommandSession;
+import org.apache.karaf.shell.console.CommandSessionHolder;
+import org.apache.karaf.shell.console.CompletableFunction;
+import org.apache.karaf.shell.console.Completer;
+import org.apache.karaf.shell.console.NameScoping;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.FrameworkUtil;
+import org.osgi.framework.ServiceReference;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import static org.apache.karaf.shell.console.completer.CommandsCompleter.unProxy;
+
+public class ArgumentCompleter implements Completer {
+
+    private static final Logger LOGGER = LoggerFactory.getLogger(ArgumentCompleter.class);
+
+    public static final String ARGUMENTS_LIST = "ARGUMENTS_LIST";
+
+    public static final String COMMANDS = ".commands";
+
+    final Completer commandCompleter;
+    final Completer optionsCompleter;
+    final List<Completer> argsCompleters;
+    final Map<String, Completer> optionalCompleters;
+    final CommandWithAction function;
+    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;
+
+    @SuppressWarnings({ "unchecked", "rawtypes" })
+    public ArgumentCompleter(CommandSession session, CommandWithAction function, String command) {
+        this.function = function;
+        // Command name completer
+        commandCompleter = new StringsCompleter(getNames(session, command));
+        // Build options completer
+        for (Class<?> type = function.getActionClass(); type != null; type = type.getSuperclass()) {
+            for (Field field : type.getDeclaredFields()) {
+                Option option = field.getAnnotation(Option.class);
+                if (option != null) {
+                    fields.put(option, field);
+                    options.put(option.name(), option);
+                    String[] aliases = option.aliases();
+                    if (aliases != null) {
+                        for (String alias : aliases) {
+                            options.put(alias, option);
+                        }
+                    }
+                }
+                Argument argument = field.getAnnotation(Argument.class);
+                if (argument != null) {
+                    Integer key = argument.index();
+                    if (arguments.containsKey(key)) {
+                        LOGGER.warn("Duplicate @Argument annotations on class " + type.getName() + " for index: " + key + " see: " + field);
+                    } else {
+                        arguments.put(key, field);
+                    }
+                }
+            }
+        }
+        options.put(HelpOption.HELP.name(), HelpOption.HELP);
+        optionsCompleter = new StringsCompleter(options.keySet());
+
+        // Build arguments completers
+        List<Completer> argsCompleters = null;
+        Map<String, Completer> optionalCompleters = null;
+
+        if (function instanceof CompletableFunction) {
+            Map<String, Completer> focl = ((CompletableFunction) function).getOptionalCompleters();
+            List<Completer> fcl = ((CompletableFunction) function).getCompleters();
+            if (focl != null || fcl != null) {
+                argsCompleters = new ArrayList<Completer>();
+                if (fcl != null) {
+                    for (Completer c : fcl) {
+                        argsCompleters.add(c == null ? NullCompleter.INSTANCE : c);
+                    }
+                }
+                optionalCompleters = focl;
+            }
+        }
+        if (argsCompleters == null) {
+            final Map<Integer, Object> values = getCompleterValues(function);
+            argsCompleters = new ArrayList<Completer>();
+            boolean multi = false;
+            for (int key = 0; key < arguments.size(); key++) {
+                Completer completer = null;
+                Field field = arguments.get(key);
+                if (field != null) {
+                    Argument argument = field.getAnnotation(Argument.class);
+                    multi = (argument != null && argument.multiValued());
+                    org.apache.karaf.shell.commands.Completer ann = field.getAnnotation(org.apache.karaf.shell.commands.Completer.class);
+                    if (ann != null) {
+                        Class clazz = ann.value();
+                        String[] value = ann.values();
+                        if (clazz != null) {
+                            if (value.length > 0 && clazz == StringsCompleter.class) {
+                                completer = new StringsCompleter(value, ann.caseSensitive());
+                            } else {
+                                BundleContext context = FrameworkUtil.getBundle(function.getClass()).getBundleContext();
+                                completer = new ProxyServiceCompleter(context, clazz);
+                            }
+                        }
+                    } else if (values.containsKey(key)) {
+                        Object value = values.get(key);
+                        if (value instanceof String[]) {
+                            completer = new StringsCompleter((String[]) value);
+                        } else if (value instanceof Collection) {
+                            completer = new StringsCompleter((Collection<String>) value);
+                        } else {
+                            LOGGER.warn("Could not use value " + value + " as set of completions!");
+                        }
+                    } else {
+                        completer = getDefaultCompleter(session, field);
+                    }
+                }
+                if (completer == null) {
+                    completer = NullCompleter.INSTANCE;
+                }
+                argsCompleters.add(completer);
+            }
+            if (argsCompleters.isEmpty() || !multi) {
+                argsCompleters.add(NullCompleter.INSTANCE);
+            }
+            optionalCompleters = new HashMap<String, Completer>();
+            for (Option option : fields.keySet()) {
+                Completer completer = null;
+                Field field = fields.get(option);
+                if (field != null) {
+                    org.apache.karaf.shell.commands.Completer ann = field.getAnnotation(org.apache.karaf.shell.commands.Completer.class);
+                    if (ann != null) {
+                        Class clazz = ann.value();
+                        String[] value = ann.values();
+                        if (clazz != null) {
+                            if (value.length > 0 && clazz == StringsCompleter.class) {
+                                completer = new StringsCompleter(value, ann.caseSensitive());
+                            } else {
+                                BundleContext context = FrameworkUtil.getBundle(function.getClass()).getBundleContext();
+                                completer = new ProxyServiceCompleter(context, clazz);
+                            }
+                        }
+                    }
+                }
+                if (completer == null) {
+                    completer = NullCompleter.INSTANCE;
+                }
+                optionalCompleters.put(option.name(), completer);
+                if (option.aliases() != null) {
+                    for (String alias : option.aliases()) {
+                        optionalCompleters.put(alias, completer);
+                    }
+                }
+            }
+        }
+        this.argsCompleters = argsCompleters;
+        this.optionalCompleters = optionalCompleters;
+    }
+
+    private Map<Integer, Object> getCompleterValues(CommandWithAction function) {
+        final Map<Integer, Object> values = new HashMap<Integer, Object>();
+        Action action = null;
+        try {
+            for (Class<?> type = function.getActionClass(); type != null; type = type.getSuperclass()) {
+                for (Method method : type.getDeclaredMethods()) {
+                    CompleterValues completerMethod = method.getAnnotation(CompleterValues.class);
+                    if (completerMethod != null) {
+                        int index = completerMethod.index();
+                        Integer key = index;
+                        if (index >= arguments.size() || index < 0) {
+                            LOGGER.warn("Index out of range on @CompleterValues on class " + type.getName() + " for index: " + key + " see: " + method);
+                        } else if (values.containsKey(key)) {
+                            LOGGER.warn("Duplicate @CompleterMethod annotations on class " + type.getName() + " for index: " + key + " see: " + method);
+                        } else {
+                            try {
+                                Object value;
+                                if (Modifier.isStatic(method.getModifiers())) {
+                                    value = method.invoke(null);
+                                } else {
+                                    if (action == null) {
+                                        action = function.createNewAction();
+                                    }
+                                    value = method.invoke(action);
+                                }
+                                values.put(key, value);
+                            } catch (IllegalAccessException e) {
+                                LOGGER.warn("Could not invoke @CompleterMethod on " + function + ". " + e, e);
+                            } catch (InvocationTargetException e) {
+                                Throwable target = e.getTargetException();
+                                if (target == null) {
+                                    target = e;
+                                }
+                                LOGGER.warn("Could not invoke @CompleterMethod on " + function + ". " + target, target);
+                            }
+                        }
+                    }
+                }
+            }
+        } finally {
+            if (action != null) {
+                try {
+                    function.releaseAction(action);
+                } catch (Exception e) {
+                    LOGGER.warn("Failed to release action: " + action + ". " + e, e);
+                }
+            }
+        }
+        return values;
+    }
+
+    private Completer getDefaultCompleter(CommandSession session, Field field) {
+        Completer completer = null;
+        Class<?> type = field.getType();
+        if (type.isAssignableFrom(File.class)) {
+            completer = new FileCompleter(session);
+        } 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>();
+            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;
+    }
+
+    private String[] getNames(CommandSession session, String scopedCommand) {
+        String command = NameScoping.getCommandNameWithoutGlobalPrefix(session, scopedCommand);
+        String[] s = command.split(":");
+        if (s.length == 1) {
+            return s;
+        } else {
+            return new String[] { command, s[1] };
+        }
+    }
+
+    /**
+     *  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(final String buffer, final int cursor,
+                        final List<String> candidates) {
+        ArgumentList list = delimit(buffer, cursor);
+        int argpos = list.getArgumentPosition();
+        int argIndex = list.getCursorArgumentIndex();
+
+        //Store the argument list so that it can be used by completers.
+        CommandSession commandSession = CommandSessionHolder.getSession();
+        if(commandSession != null) {
+            commandSession.put(ARGUMENTS_LIST,list);
+        }
+
+        Completer comp = null;
+        String[] args = list.getArguments();
+        int index = 0;
+        // First argument is command name
+        if (index < argIndex) {
+            // Verify command name
+            if (!verifyCompleter(commandCompleter, args[index])) {
+                return -1;
+            }
+            // Verify scope if
+            // - the command name has no scope
+            // - we have a session
+            if (!args[index].contains(":") && commandSession != null) {
+                Function f1 = unProxy((Function) commandSession.get("*:" + args[index]));
+                Function f2 = unProxy(this.function);
+                if (f1 != null && f1 != f2) {
+                    return -1;
+                }
+            }
+            index++;
+        } else {
+            comp = commandCompleter;
+        }
+        // Now, check options
+        if (comp == null) {
+            while (index < argIndex && args[index].startsWith("-")) {
+                if (!verifyCompleter(optionsCompleter, args[index])) {
+                    return -1;
+                }
+                Option option = options.get(args[index]);
+                if (option == null) {
+                    return -1;
+                }
+                Field field = fields.get(option);
+                if (field != null && field.getType() != boolean.class && field.getType() != Boolean.class) {
+                    if (++index == argIndex) {
+                        comp = NullCompleter.INSTANCE;
+                    }
+                }
+                index++;
+            }
+            if (comp == null && index >= argIndex && index < args.length && args[index].startsWith("-")) {
+                comp = optionsCompleter;
+            }
+        }
+        //Now check for if last Option has a completer
+        int lastAgurmentIndex = argIndex - 1;
+        if (lastAgurmentIndex >= 1) {
+            Option lastOption = options.get(args[lastAgurmentIndex]);
+            if (lastOption != null) {
+
+                Field lastField = fields.get(lastOption);
+                if (lastField != null && lastField.getType() != boolean.class && lastField.getType() != Boolean.class) {
+                    Option option = lastField.getAnnotation(Option.class);
+                    if (option != null) {
+                        Completer optionValueCompleter = null;
+                        String name = option.name();
+                        if (optionalCompleters != null && name != null) {
+                            optionValueCompleter = optionalCompleters.get(name);
+                            if (optionValueCompleter == null) {
+                                String[] aliases = option.aliases();
+                                if (aliases.length > 0) {
+                                    for (int i = 0; i < aliases.length && optionValueCompleter == null; i++) {
+                                        optionValueCompleter = optionalCompleters.get(option.aliases()[i]);
+                                    }
+                                }
+                            }
+                        }
+                        if(optionValueCompleter != null) {
+                            comp = optionValueCompleter;
+                        }
+                    }
+                }
+            }
+        }
+
+        // Check arguments
+        if (comp == null) {
+            int indexArg = 0;
+            while (index < argIndex) {
+                Completer sub = argsCompleters.get(indexArg >= argsCompleters.size() ? argsCompleters.size() - 1 : indexArg);
+                if (!verifyCompleter(sub, args[index])) {
+                    return -1;
+                }
+                index++;
+                indexArg++;
+            }
+            comp = argsCompleters.get(indexArg >= argsCompleters.size() ? argsCompleters.size() - 1 : indexArg);
+        }
+
+        int ret = comp.complete(list.getCursorArgument(), argpos, candidates);
+
+        if (ret == -1) {
+            return -1;
+        }
+
+        int pos = ret + (list.getBufferPosition() - argpos);
+
+        /**
+         *  Special case: when completing in the middle of a line, and the
+         *  area under the cursor is a delimiter, then trim any delimiters
+         *  from the candidates, since we do not need to have an extra
+         *  delimiter.
+         *
+         *  E.g., if we have a completion for "foo", and we
+         *  enter "f bar" into the buffer, and move to after the "f"
+         *  and hit TAB, we want "foo bar" instead of "foo  bar".
+         */
+
+        if ((buffer != null) && (cursor != buffer.length()) && isDelimiter(buffer, cursor)) {
+            for (int i = 0; i < candidates.size(); i++) {
+                String val = candidates.get(i);
+
+                while ((val.length() > 0)
+                    && isDelimiter(val, val.length() - 1)) {
+                    val = val.substring(0, val.length() - 1);
+                }
+
+                candidates.set(i, val);
+            }
+        }
+
+        return pos;
+    }
+
+    protected boolean verifyCompleter(Completer completer, String argument) {
+        List<String> candidates = new ArrayList<String>();
+        return completer.complete(argument, argument.length(), candidates) != -1 && !candidates.isEmpty();
+    }
+
+    public ArgumentList delimit(final String buffer, final int cursor) {
+        Parser parser = new Parser(buffer, cursor);
+        try {
+            List<List<List<String>>> program = parser.program();
+            List<String> pipe = program.get(parser.c0).get(parser.c1);
+            return new ArgumentList(pipe.toArray(new String[pipe.size()]), parser.c2, parser.c3, cursor);
+        } catch (Throwable t) {
+            return new ArgumentList(new String[] { buffer }, 0, cursor, cursor);
+        }
+    }
+
+    /**
+     *  Returns true if the specified character is a whitespace
+     *  parameter. Check to ensure that the character is not
+     *  escaped and returns true from
+     *  {@link #isDelimiterChar}.
+     *
+     *  @param  buffer the complete command buffer
+     *  @param  pos    the index of the character in the buffer
+     *  @return        true if the character should be a delimiter
+     */
+    public boolean isDelimiter(final String buffer, final int pos) {
+        return !isEscaped(buffer, pos) && isDelimiterChar(buffer, pos);
+    }
+
+    public boolean isEscaped(final String buffer, final int pos) {
+        return pos > 0 && buffer.charAt(pos) == '\\' && !isEscaped(buffer, pos - 1);
+    }
+
+    /**
+     *  The character is a delimiter if it is whitespace, and the
+     *  preceeding character is not an escape character.
+     */
+    public boolean isDelimiterChar(String buffer, int pos) {
+        return Character.isWhitespace(buffer.charAt(pos));
+    }
+
+    /**
+     *  The result of a delimited buffer.
+     */
+    public static class ArgumentList {
+        private String[] arguments;
+        private int cursorArgumentIndex;
+        private int argumentPosition;
+        private int bufferPosition;
+
+        /**
+         *  @param  arguments           the array of tokens
+         *  @param  cursorArgumentIndex the token index of the cursor
+         *  @param  argumentPosition    the position of the cursor in the
+         *                              current token
+         *  @param  bufferPosition      the position of the cursor in
+         *                              the whole buffer
+         */
+        public ArgumentList(String[] arguments, int cursorArgumentIndex,
+            int argumentPosition, int bufferPosition) {
+            this.arguments = arguments;
+            this.cursorArgumentIndex = cursorArgumentIndex;
+            this.argumentPosition = argumentPosition;
+            this.bufferPosition = bufferPosition;
+        }
+
+        public void setCursorArgumentIndex(int cursorArgumentIndex) {
+            this.cursorArgumentIndex = cursorArgumentIndex;
+        }
+
+        public int getCursorArgumentIndex() {
+            return this.cursorArgumentIndex;
+        }
+
+        public String getCursorArgument() {
+            if ((cursorArgumentIndex < 0)
+                || (cursorArgumentIndex >= arguments.length)) {
+                return null;
+            }
+
+            return arguments[cursorArgumentIndex];
+        }
+
+        public void setArgumentPosition(int argumentPosition) {
+            this.argumentPosition = argumentPosition;
+        }
+
+        public int getArgumentPosition() {
+            return this.argumentPosition;
+        }
+
+        public void setArguments(String[] arguments) {
+            this.arguments = arguments;
+        }
+
+        public String[] getArguments() {
+            return this.arguments;
+        }
+
+        public void setBufferPosition(int bufferPosition) {
+            this.bufferPosition = bufferPosition;
+        }
+
+        public int getBufferPosition() {
+            return this.bufferPosition;
+        }
+    }
+
+    public static class ProxyServiceCompleter implements Completer {
+        private final BundleContext context;
+        private final Class<? extends Completer> clazz;
+
+        public ProxyServiceCompleter(BundleContext context, Class<? extends Completer> clazz) {
+            this.context = context;
+            this.clazz = clazz;
+        }
+
+        @Override
+        public int complete(String buffer, int cursor, List<String> candidates) {
+            ServiceReference<? extends Completer> ref = context.getServiceReference(clazz);
+            if (ref != null) {
+                Completer completer = context.getService(ref);
+                if (completer != null) {
+                    try {
+                        return completer.complete(buffer, cursor, candidates);
+                    } finally {
+                        context.ungetService(ref);
+                    }
+                }
+            }
+            return -1;
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/karaf/blob/1ee78df9/shell/command-api/src/main/java/org/apache/karaf/shell/console/completer/CommandNamesCompleter.java
----------------------------------------------------------------------
diff --git a/shell/command-api/src/main/java/org/apache/karaf/shell/console/completer/CommandNamesCompleter.java b/shell/command-api/src/main/java/org/apache/karaf/shell/console/completer/CommandNamesCompleter.java
new file mode 100644
index 0000000..23f5447
--- /dev/null
+++ b/shell/command-api/src/main/java/org/apache/karaf/shell/console/completer/CommandNamesCompleter.java
@@ -0,0 +1,103 @@
+/*
+ * 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.console.completer;
+
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.concurrent.CopyOnWriteArraySet;
+
+import org.apache.felix.service.command.CommandProcessor;
+import org.apache.felix.service.command.CommandSession;
+import org.apache.karaf.shell.console.CommandSessionHolder;
+import org.apache.karaf.shell.console.Completer;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.FrameworkUtil;
+import org.osgi.framework.ServiceEvent;
+import org.osgi.framework.ServiceListener;
+
+/**
+ * Completes command names
+ */
+public class CommandNamesCompleter implements Completer {
+
+    public static final String COMMANDS = ".commands";
+
+    private CommandSession session;
+    private final Set<String> commands = new CopyOnWriteArraySet<String>();
+
+    public CommandNamesCompleter() {
+        this(CommandSessionHolder.getSession());
+    }
+
+    public CommandNamesCompleter(CommandSession session) {
+        this.session = session;
+
+        try {
+            new CommandTracker();
+        } catch (Throwable t) {
+            // Ignore in case we're not in OSGi
+        }
+    }
+
+
+    public int complete(String buffer, int cursor, List<String> candidates) {
+        if (session == null) {
+            session = CommandSessionHolder.getSession();
+        }
+        checkData();
+        int res = new StringsCompleter(commands).complete(buffer, cursor, candidates);
+        Collections.sort(candidates);
+        return res;
+    }
+
+    @SuppressWarnings("unchecked")
+    protected void checkData() {
+        if (commands.isEmpty()) {
+            Set<String> names = new HashSet<String>((Set<String>) session.get(COMMANDS));
+            for (String name : names) {
+                commands.add(name);
+                if (name.indexOf(':') > 0) {
+                    commands.add(name.substring(0, name.indexOf(':')));
+                }
+            }
+        }
+    }
+
+    private class CommandTracker {
+        public CommandTracker() throws Exception {
+            BundleContext context = FrameworkUtil.getBundle(getClass()).getBundleContext();
+            if (context == null) {
+                throw new IllegalStateException("Bundle is stopped");
+            }
+            ServiceListener listener = new ServiceListener() {
+                public void serviceChanged(ServiceEvent event) {
+                    commands.clear();
+                }
+            };
+            context.addServiceListener(listener,
+                    String.format("(&(%s=*)(%s=*))",
+                            CommandProcessor.COMMAND_SCOPE,
+                            CommandProcessor.COMMAND_FUNCTION));
+        }
+    }
+
+}
+


[09/11] KARAF-2772 Extracting command-api

Posted by cs...@apache.org.
http://git-wip-us.apache.org/repos/asf/karaf/blob/1ee78df9/shell/command-api/src/main/java/org/apache/karaf/shell/commands/converter/DefaultConverter.java
----------------------------------------------------------------------
diff --git a/shell/command-api/src/main/java/org/apache/karaf/shell/commands/converter/DefaultConverter.java b/shell/command-api/src/main/java/org/apache/karaf/shell/commands/converter/DefaultConverter.java
new file mode 100644
index 0000000..54fe81a
--- /dev/null
+++ b/shell/command-api/src/main/java/org/apache/karaf/shell/commands/converter/DefaultConverter.java
@@ -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.karaf.shell.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();
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/karaf/blob/1ee78df9/shell/command-api/src/main/java/org/apache/karaf/shell/commands/converter/GenericType.java
----------------------------------------------------------------------
diff --git a/shell/command-api/src/main/java/org/apache/karaf/shell/commands/converter/GenericType.java b/shell/command-api/src/main/java/org/apache/karaf/shell/commands/converter/GenericType.java
new file mode 100644
index 0000000..2f2b5e0
--- /dev/null
+++ b/shell/command-api/src/main/java/org/apache/karaf/shell/commands/converter/GenericType.java
@@ -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.karaf.shell.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);
+	}
+
+}

http://git-wip-us.apache.org/repos/asf/karaf/blob/1ee78df9/shell/command-api/src/main/java/org/apache/karaf/shell/commands/converter/ReifiedType.java
----------------------------------------------------------------------
diff --git a/shell/command-api/src/main/java/org/apache/karaf/shell/commands/converter/ReifiedType.java b/shell/command-api/src/main/java/org/apache/karaf/shell/commands/converter/ReifiedType.java
new file mode 100644
index 0000000..dda4477
--- /dev/null
+++ b/shell/command-api/src/main/java/org/apache/karaf/shell/commands/converter/ReifiedType.java
@@ -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.karaf.shell.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;
+	}
+}

http://git-wip-us.apache.org/repos/asf/karaf/blob/1ee78df9/shell/command-api/src/main/java/org/apache/karaf/shell/commands/meta/ActionMetaData.java
----------------------------------------------------------------------
diff --git a/shell/command-api/src/main/java/org/apache/karaf/shell/commands/meta/ActionMetaData.java b/shell/command-api/src/main/java/org/apache/karaf/shell/commands/meta/ActionMetaData.java
new file mode 100644
index 0000000..e0bd11a
--- /dev/null
+++ b/shell/command-api/src/main/java/org/apache/karaf/shell/commands/meta/ActionMetaData.java
@@ -0,0 +1,296 @@
+/*
+ * 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.commands.meta;
+
+import static org.apache.karaf.shell.commands.ansi.SimpleAnsi.INTENSITY_BOLD;
+import static org.apache.karaf.shell.commands.ansi.SimpleAnsi.INTENSITY_NORMAL;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.PrintStream;
+import java.io.Reader;
+import java.io.StringWriter;
+import java.lang.reflect.Field;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.apache.felix.gogo.commands.Action;
+import org.apache.karaf.shell.commands.Argument;
+import org.apache.karaf.shell.commands.Command;
+import org.apache.karaf.shell.commands.HelpOption;
+import org.apache.karaf.shell.commands.Option;
+import org.apache.karaf.shell.commands.ansi.SimpleAnsi;
+import org.apache.karaf.shell.console.Completer;
+
+public class ActionMetaData {
+
+    private final Class<? extends Action> actionClass;
+    private final Command command;
+    private final Map<Option, Field> options;
+    private final Map<Argument, Field> arguments;
+    List<Argument> orderedArguments;
+    private final Completer[] completers;
+
+    public ActionMetaData(Class<? extends Action> actionClass, Command command, Map<Option, Field> options, Map<Argument, Field> args,
+            List<Argument> orderedArguments, Completer[] completers) {
+        super();
+        this.actionClass = actionClass;
+        this.command = command;
+        this.options = options;
+        this.arguments = args;
+        this.orderedArguments = orderedArguments;
+        this.completers = completers;
+    }
+
+    public Class<? extends Action> getActionClass() {
+        return actionClass;
+    }
+    
+    public Command getCommand() {
+        return command;
+    }
+
+    public Map<Option, Field> getOptions() {
+        return options;
+    }
+
+    public Map<Argument, Field> getArguments() {
+        return arguments;
+    }
+
+    public Completer[] getCompleters() {
+        return completers;
+    }
+
+    public List<Argument> getOrderedArguments() {
+        return orderedArguments;
+    }
+
+    public void printUsage(Action action, PrintStream out, boolean globalScope, int termWidth) {
+        if (command != null) {
+            List<Argument> argumentsSet = new ArrayList<Argument>(arguments.keySet());
+            Collections.sort(argumentsSet, new Comparator<Argument>() {
+                public int compare(Argument o1, Argument o2) {
+                    return Integer.valueOf(o1.index()).compareTo(Integer.valueOf(o2.index()));
+                }
+            });
+            Set<Option> optionsSet = new HashSet<Option>(options.keySet());
+            optionsSet.add(HelpOption.HELP);
+            if (command != null && (command.description() != null || command.name() != null)) {
+                out.println(INTENSITY_BOLD + "DESCRIPTION" + INTENSITY_NORMAL);
+                out.print("        ");
+                if (command.name() != null) {
+                    if (globalScope) {
+                        out.println(INTENSITY_BOLD + command.name() + INTENSITY_NORMAL);
+                    } else {
+                        out.println(command.scope() + ":" + INTENSITY_BOLD + command.name() + INTENSITY_NORMAL);
+                    }
+                    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 : argumentsSet) {
+                    if (!argument.required()) {
+                        syntax.append(String.format("[%s] ", argument.name()));
+                    } else {
+                        syntax.append(String.format("%s ", argument.name()));
+                    }
+                }
+            }
+
+            out.println(INTENSITY_BOLD + "SYNTAX" + INTENSITY_NORMAL);
+            out.print("        ");
+            out.println(syntax.toString());
+            out.println();
+            if (arguments.size() > 0) {
+                out.println(INTENSITY_BOLD + "ARGUMENTS" + INTENSITY_NORMAL);
+                for (Argument argument : argumentsSet) {
+                    out.print("        ");
+                    out.println(INTENSITY_BOLD + argument.name() + INTENSITY_NORMAL);
+                    ActionMetaData.printFormatted("                ", argument.description(), termWidth, out, true);
+                    if (!argument.required()) {
+                        if (argument.valueToShowInHelp() != null && argument.valueToShowInHelp().length() != 0) {
+                            if (Argument.DEFAULT_STRING.equals(argument.valueToShowInHelp())) {
+                                Object o = getDefaultValue(action, argument);
+                                String defaultValue = getDefaultValueString(o);
+                                if (defaultValue != null) {
+                                    printDefaultsTo(out, defaultValue);
+                                }
+                            } else {
+                                printDefaultsTo(out, argument.valueToShowInHelp());
+                            }
+                        }
+                    }
+                }
+                out.println();
+            }
+            if (options.size() > 0) {
+                out.println(INTENSITY_BOLD + "OPTIONS" + INTENSITY_NORMAL);
+                for (Option option : optionsSet) {
+                    String opt = option.name();
+                    for (String alias : option.aliases()) {
+                        opt += ", " + alias;
+                    }
+                    out.print("        ");
+                    out.println(INTENSITY_BOLD + opt + INTENSITY_NORMAL);
+                    ActionMetaData.printFormatted("                ", option.description(), termWidth, out, true);
+                    if (option.valueToShowInHelp() != null && option.valueToShowInHelp().length() != 0) {
+                        if (Option.DEFAULT_STRING.equals(option.valueToShowInHelp())) {
+                            Object o = getDefaultValue(action, option);
+                            String defaultValue = getDefaultValueString(o);
+                            if (defaultValue != null) {
+                                printDefaultsTo(out, defaultValue);
+                            }
+                        } else {
+                            printDefaultsTo(out, option.valueToShowInHelp());
+                        }
+                    }
+                }
+                out.println();
+            }
+            if (command.detailedDescription().length() > 0) {
+                out.println(INTENSITY_BOLD + "DETAILS" + INTENSITY_NORMAL);
+                String desc = getDetailedDescription();
+                ActionMetaData.printFormatted("        ", desc, termWidth, out, true);
+            }
+        }
+    }
+    
+    public Object getDefaultValue(Action action, Argument argument) {
+        try {
+            arguments.get(argument).setAccessible(true);
+            return arguments.get(argument).get(action);
+        } catch (Exception e) {
+            return null;
+        }
+    }
+    
+    public Object getDefaultValue(Action action, Option option) {
+        try {
+            options.get(option).setAccessible(true);
+            return options.get(option).get(action);
+        } catch (Exception e) {
+            return null;
+        }
+    }
+    
+    public String getDetailedDescription() {
+        String desc = command.detailedDescription();
+        return loadDescription(actionClass, desc);
+    }
+    
+    private String loadDescription(Class<?> clazz, String desc) {
+        if (desc != null && desc.startsWith("classpath:")) {
+            desc = loadClassPathResource(clazz, desc.substring("classpath:".length()));
+        }
+        return desc;
+    }
+    
+    public String getDefaultValueString(Object o) {
+        if (o != null && (!(o instanceof Boolean) || ((Boolean)o))
+            && (!(o instanceof Number) || ((Number)o).doubleValue() != 0.0)) {
+            return o.toString();
+        } else {
+            return null;
+        }
+    }
+
+    private void printDefaultsTo(PrintStream out, String value) {
+        out.println("                (defaults to " + value + ")");
+    }
+
+    static void printFormatted(String prefix, String str, int termWidth, PrintStream out, boolean prefixFirstLine) {
+        int pfxLen = prefix.length();
+        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;
+            }
+        }
+    }
+
+    private String loadClassPathResource(Class<?> clazz, String path) {
+        InputStream is = clazz.getResourceAsStream(path);
+        if (is == null) {
+            is = clazz.getClassLoader().getResourceAsStream(path);
+        }
+        if (is == null) {
+            return "Unable to load description from " + path;
+        }
+    
+        try {
+            Reader r = new InputStreamReader(is);
+            StringWriter sw = new StringWriter();
+            int c;
+            while ((c = r.read()) != -1) {
+                sw.append((char) c);
+            }
+            return sw.toString();
+        } catch (IOException e) {
+            return "Unable to load description from " + path;
+        } finally {
+            try {
+                is.close();
+            } catch (IOException e) {
+                // Ignore
+            }
+        }
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/karaf/blob/1ee78df9/shell/command-api/src/main/java/org/apache/karaf/shell/commands/meta/ActionMetaDataFactory.java
----------------------------------------------------------------------
diff --git a/shell/command-api/src/main/java/org/apache/karaf/shell/commands/meta/ActionMetaDataFactory.java b/shell/command-api/src/main/java/org/apache/karaf/shell/commands/meta/ActionMetaDataFactory.java
new file mode 100644
index 0000000..91bc48f
--- /dev/null
+++ b/shell/command-api/src/main/java/org/apache/karaf/shell/commands/meta/ActionMetaDataFactory.java
@@ -0,0 +1,251 @@
+/*
+ * 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.commands.meta;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Field;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.felix.gogo.commands.Action;
+import org.apache.karaf.shell.commands.Argument;
+import org.apache.karaf.shell.commands.Command;
+import org.apache.karaf.shell.commands.Option;
+
+public class ActionMetaDataFactory {
+
+    public ActionMetaData create(Class<? extends Action> actionClass) {
+        Command command = getCommand(actionClass);
+        Map<Option, Field> options = new HashMap<Option, Field>();
+        Map<Argument, Field> arguments = new HashMap<Argument, Field>();
+        List<Argument> orderedArguments = new ArrayList<Argument>();
+
+        for (Class<?> type = actionClass; type != null; type = type.getSuperclass()) {
+            for (Field field : type.getDeclaredFields()) {
+                Option option = field.getAnnotation(Option.class);
+                if (option == null) {
+                    option = getAndConvertDeprecatedOption(field);
+                }
+                if (option != null) {
+                    options.put(option, field);
+                }
+
+                Argument argument = field.getAnnotation(Argument.class);
+                if (argument == null) {
+                    argument = getAndConvertDeprecatedArgument(field);
+                }
+                if (argument != null) {
+                    argument = replaceDefaultArgument(field, argument);
+                    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 + " on Action " + actionClass.getName());
+                    }
+                    orderedArguments.set(index, argument);
+                }
+            }
+        }
+        assertIndexesAreCorrect(actionClass, orderedArguments);
+
+        return new ActionMetaData(actionClass, command, options, arguments, orderedArguments, null);
+    }
+
+    public Command getCommand(Class<? extends Action> actionClass) {
+        Command command = actionClass.getAnnotation(Command.class);
+        if (command == null) {
+            command = getAndConvertDeprecatedCommand(actionClass);
+        }
+        return command;
+    }
+
+    @SuppressWarnings("deprecation")
+    public Command getAndConvertDeprecatedCommand(Class<? extends Action> actionClass) {
+        final org.apache.felix.gogo.commands.Command oldCommand = actionClass.getAnnotation(org.apache.felix.gogo.commands.Command.class);
+        if (oldCommand == null) {
+            return null;
+        }
+        return new Command() {
+            
+            @Override
+            public Class<? extends Annotation> annotationType() {
+                return Command.class;
+            }
+            
+            @Override
+            public String scope() {
+                return oldCommand.scope();
+            }
+            
+            @Override
+            public String name() {
+                return oldCommand.name();
+            }
+            
+            @Override
+            public String detailedDescription() {
+                return oldCommand.detailedDescription();
+            }
+
+            @Override
+            public String description() {
+                return oldCommand.description();
+            }
+        };
+    }
+
+    @SuppressWarnings("deprecation")
+    private Option getAndConvertDeprecatedOption(Field field) {
+        final org.apache.felix.gogo.commands.Option oldOption = field.getAnnotation(org.apache.felix.gogo.commands.Option.class);
+        if (oldOption == null) {
+            return null;
+        }
+        return new Option() {
+            
+            @Override
+            public Class<? extends Annotation> annotationType() {
+                return Option.class;
+            }
+            
+            @Override
+            public String valueToShowInHelp() {
+                return oldOption.valueToShowInHelp();
+            }
+            
+            @Override
+            public boolean required() {
+                return oldOption.required();
+            }
+            
+            @Override
+            public String name() {
+                return oldOption.name();
+            }
+            
+            @Override
+            public boolean multiValued() {
+                return oldOption.multiValued();
+            }
+            
+            @Override
+            public String description() {
+                return oldOption.description();
+            }
+            
+            @Override
+            public String[] aliases() {
+                return oldOption.aliases();
+            }
+        };
+    }
+    
+    @SuppressWarnings("deprecation")
+    private Argument getAndConvertDeprecatedArgument(Field field) {
+        final org.apache.felix.gogo.commands.Argument oldArgument = field.getAnnotation(org.apache.felix.gogo.commands.Argument.class);
+        if (oldArgument == null) {
+            return null;
+        }
+        return new Argument() {
+            
+            @Override
+            public Class<? extends Annotation> annotationType() {
+                return Argument.class;
+            }
+            
+            @Override
+            public String valueToShowInHelp() {
+                return oldArgument.valueToShowInHelp();
+            }
+            
+            @Override
+            public boolean required() {
+                return oldArgument.required();
+            }
+            
+            @Override
+            public String name() {
+                return oldArgument.name();
+            }
+            
+            @Override
+            public boolean multiValued() {
+                return oldArgument.multiValued();
+            }
+            
+            @Override
+            public int index() {
+                return oldArgument.index();
+            }
+            
+            @Override
+            public String description() {
+                return oldArgument.description();
+            }
+        };
+    }
+
+    private Argument replaceDefaultArgument(Field field, Argument argument) {
+        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();
+                }
+            };
+        }
+        return argument;
+    }
+    
+    private void assertIndexesAreCorrect(Class<? extends Action> actionClass, List<Argument> orderedArguments) {
+        for (int i = 0; i < orderedArguments.size(); i++) {
+            if (orderedArguments.get(i) == null) {
+                throw new IllegalArgumentException("Missing argument for index: " + i + " on Action " + actionClass.getName());
+            }
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/karaf/blob/1ee78df9/shell/command-api/src/main/java/org/apache/karaf/shell/console/AbstractAction.java
----------------------------------------------------------------------
diff --git a/shell/command-api/src/main/java/org/apache/karaf/shell/console/AbstractAction.java b/shell/command-api/src/main/java/org/apache/karaf/shell/console/AbstractAction.java
new file mode 100644
index 0000000..f9990c5
--- /dev/null
+++ b/shell/command-api/src/main/java/org/apache/karaf/shell/console/AbstractAction.java
@@ -0,0 +1,50 @@
+/*
+ * 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.console;
+
+import org.apache.karaf.shell.commands.Action;
+import org.apache.felix.service.command.CommandSession;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public abstract class AbstractAction implements Action {
+
+    protected final Logger log = LoggerFactory.getLogger(getClass());
+    protected CommandSession session;
+
+    public Object execute(CommandSession session) throws Exception {
+        this.session = session;
+        return doExecute();
+    }
+
+    protected abstract Object doExecute() throws Exception;
+    
+    /**
+     * This is for long running commands to be interrupted by ctrl-c
+     * 
+     * @throws InterruptedException
+     */
+    public static void checkInterrupted() throws InterruptedException {
+        Thread.yield(); 
+        if (Thread.currentThread().isInterrupted()) {
+            throw new InterruptedException();
+        }
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/karaf/blob/1ee78df9/shell/command-api/src/main/java/org/apache/karaf/shell/console/BlueprintContainerAware.java
----------------------------------------------------------------------
diff --git a/shell/command-api/src/main/java/org/apache/karaf/shell/console/BlueprintContainerAware.java b/shell/command-api/src/main/java/org/apache/karaf/shell/console/BlueprintContainerAware.java
new file mode 100644
index 0000000..d566f48
--- /dev/null
+++ b/shell/command-api/src/main/java/org/apache/karaf/shell/console/BlueprintContainerAware.java
@@ -0,0 +1,27 @@
+/*
+ * 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.console;
+
+import org.osgi.service.blueprint.container.BlueprintContainer;
+
+public interface BlueprintContainerAware {
+
+    void setBlueprintContainer(BlueprintContainer blueprintContainer);
+
+}

http://git-wip-us.apache.org/repos/asf/karaf/blob/1ee78df9/shell/command-api/src/main/java/org/apache/karaf/shell/console/BundleContextAware.java
----------------------------------------------------------------------
diff --git a/shell/command-api/src/main/java/org/apache/karaf/shell/console/BundleContextAware.java b/shell/command-api/src/main/java/org/apache/karaf/shell/console/BundleContextAware.java
new file mode 100644
index 0000000..bbd03c5
--- /dev/null
+++ b/shell/command-api/src/main/java/org/apache/karaf/shell/console/BundleContextAware.java
@@ -0,0 +1,27 @@
+/*
+ * 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.console;
+
+import org.osgi.framework.BundleContext;
+
+public interface BundleContextAware {
+
+    void setBundleContext(BundleContext bundleContext);
+
+}

http://git-wip-us.apache.org/repos/asf/karaf/blob/1ee78df9/shell/command-api/src/main/java/org/apache/karaf/shell/console/CloseShellException.java
----------------------------------------------------------------------
diff --git a/shell/command-api/src/main/java/org/apache/karaf/shell/console/CloseShellException.java b/shell/command-api/src/main/java/org/apache/karaf/shell/console/CloseShellException.java
new file mode 100644
index 0000000..81cee88
--- /dev/null
+++ b/shell/command-api/src/main/java/org/apache/karaf/shell/console/CloseShellException.java
@@ -0,0 +1,27 @@
+/*
+ * 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.console;
+
+/**
+ * Exception thrown to indicate the console to close.
+ */
+@SuppressWarnings("serial")
+public class CloseShellException extends Exception {
+
+}

http://git-wip-us.apache.org/repos/asf/karaf/blob/1ee78df9/shell/command-api/src/main/java/org/apache/karaf/shell/console/CommandSessionHolder.java
----------------------------------------------------------------------
diff --git a/shell/command-api/src/main/java/org/apache/karaf/shell/console/CommandSessionHolder.java b/shell/command-api/src/main/java/org/apache/karaf/shell/console/CommandSessionHolder.java
new file mode 100644
index 0000000..f07554f
--- /dev/null
+++ b/shell/command-api/src/main/java/org/apache/karaf/shell/console/CommandSessionHolder.java
@@ -0,0 +1,38 @@
+/*
+ * 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.console;
+
+import org.apache.felix.service.command.CommandSession;
+
+public class CommandSessionHolder {
+
+    private static final ThreadLocal<CommandSession> session = new ThreadLocal<CommandSession>();
+
+    public static CommandSession getSession() {
+        return session.get();
+    }
+
+    public static void setSession(CommandSession commandSession) {
+        session.set(commandSession);
+    }
+
+    public static void unset() {
+        session.remove();
+    }
+}

http://git-wip-us.apache.org/repos/asf/karaf/blob/1ee78df9/shell/command-api/src/main/java/org/apache/karaf/shell/console/CompletableFunction.java
----------------------------------------------------------------------
diff --git a/shell/command-api/src/main/java/org/apache/karaf/shell/console/CompletableFunction.java b/shell/command-api/src/main/java/org/apache/karaf/shell/console/CompletableFunction.java
new file mode 100644
index 0000000..fcb96da
--- /dev/null
+++ b/shell/command-api/src/main/java/org/apache/karaf/shell/console/CompletableFunction.java
@@ -0,0 +1,31 @@
+/*
+ * 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.console;
+
+import java.util.List;
+import java.util.Map;
+
+import org.apache.felix.service.command.Function;
+
+public interface CompletableFunction extends Function {
+
+    List<Completer> getCompleters();
+    Map<String, Completer> getOptionalCompleters();
+
+}

http://git-wip-us.apache.org/repos/asf/karaf/blob/1ee78df9/shell/command-api/src/main/java/org/apache/karaf/shell/console/Completer.java
----------------------------------------------------------------------
diff --git a/shell/command-api/src/main/java/org/apache/karaf/shell/console/Completer.java b/shell/command-api/src/main/java/org/apache/karaf/shell/console/Completer.java
new file mode 100644
index 0000000..59f7897
--- /dev/null
+++ b/shell/command-api/src/main/java/org/apache/karaf/shell/console/Completer.java
@@ -0,0 +1,27 @@
+/*
+ * 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.console;
+
+import java.util.List;
+
+public interface Completer {
+
+    int complete(String buffer, int cursor, List<String> candidates);
+
+}

http://git-wip-us.apache.org/repos/asf/karaf/blob/1ee78df9/shell/command-api/src/main/java/org/apache/karaf/shell/console/Console.java
----------------------------------------------------------------------
diff --git a/shell/command-api/src/main/java/org/apache/karaf/shell/console/Console.java b/shell/command-api/src/main/java/org/apache/karaf/shell/console/Console.java
new file mode 100644
index 0000000..fcca7ad
--- /dev/null
+++ b/shell/command-api/src/main/java/org/apache/karaf/shell/console/Console.java
@@ -0,0 +1,29 @@
+/*
+ * 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.console;
+
+import org.apache.felix.service.command.CommandSession;
+
+public interface Console extends Runnable {
+
+    CommandSession getSession();
+
+    void close(boolean closedByUser);
+
+}

http://git-wip-us.apache.org/repos/asf/karaf/blob/1ee78df9/shell/command-api/src/main/java/org/apache/karaf/shell/console/ExitAction.java
----------------------------------------------------------------------
diff --git a/shell/command-api/src/main/java/org/apache/karaf/shell/console/ExitAction.java b/shell/command-api/src/main/java/org/apache/karaf/shell/console/ExitAction.java
new file mode 100644
index 0000000..1449315
--- /dev/null
+++ b/shell/command-api/src/main/java/org/apache/karaf/shell/console/ExitAction.java
@@ -0,0 +1,46 @@
+/*
+ * 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.console;
+
+import org.apache.karaf.shell.commands.Command;
+
+/**
+ * Exit from the current sub-shell and get back to the previous one.
+ */
+@Command(scope = "*", name = "exit", description = "Exit from the current shell")
+public class ExitAction extends AbstractAction {
+    
+    public Object doExecute() throws Exception {
+        // get the current sub-shell
+        String currentSubShell = (String) session.get("SUBSHELL");
+        if (!currentSubShell.isEmpty()) {
+            if (currentSubShell.contains(":")) {
+                int index = currentSubShell.lastIndexOf(":");
+                session.put("SUBSHELL", currentSubShell.substring(0, index));
+            } else {
+                session.put("SUBSHELL", "");
+            }
+            String currentScope = (String) session.get("SCOPE");
+            int index = currentScope.indexOf(":");
+            session.put("SCOPE", currentScope.substring(index + 1));
+        }
+        return null;
+    }
+    
+}

http://git-wip-us.apache.org/repos/asf/karaf/blob/1ee78df9/shell/command-api/src/main/java/org/apache/karaf/shell/console/HelpProvider.java
----------------------------------------------------------------------
diff --git a/shell/command-api/src/main/java/org/apache/karaf/shell/console/HelpProvider.java b/shell/command-api/src/main/java/org/apache/karaf/shell/console/HelpProvider.java
new file mode 100644
index 0000000..7e3bca7
--- /dev/null
+++ b/shell/command-api/src/main/java/org/apache/karaf/shell/console/HelpProvider.java
@@ -0,0 +1,26 @@
+/**
+ *
+ * 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.console;
+
+import org.apache.felix.service.command.CommandSession;
+
+public interface HelpProvider {
+    
+    String getHelp(CommandSession session, String path);
+    
+}

http://git-wip-us.apache.org/repos/asf/karaf/blob/1ee78df9/shell/command-api/src/main/java/org/apache/karaf/shell/console/MultiException.java
----------------------------------------------------------------------
diff --git a/shell/command-api/src/main/java/org/apache/karaf/shell/console/MultiException.java b/shell/command-api/src/main/java/org/apache/karaf/shell/console/MultiException.java
new file mode 100644
index 0000000..6e6185c
--- /dev/null
+++ b/shell/command-api/src/main/java/org/apache/karaf/shell/console/MultiException.java
@@ -0,0 +1,95 @@
+/*
+ * 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.console;
+
+import java.io.PrintStream;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.List;
+
+@SuppressWarnings("serial")
+public class MultiException extends Exception {
+
+    private List<Exception> exceptions = new ArrayList<Exception>();
+
+    public MultiException(String message) {
+        super(message);
+    }
+
+    public MultiException(String message, List<Exception> exceptions) {
+        super(message);
+        this.exceptions = exceptions;
+    }
+
+    public void addException(Exception e) {
+        exceptions.add(e);
+    }
+
+    public void throwIfExceptions() throws MultiException {
+        if (!exceptions.isEmpty()) {
+            throw this;
+        }
+    }
+    
+    public Throwable[] getCauses() {
+        return exceptions.toArray(new Throwable[exceptions.size()]);
+    }
+
+    @Override
+    public void printStackTrace()
+    {
+        super.printStackTrace();
+        for (Exception e : exceptions) {
+            e.printStackTrace();
+        }
+    }
+
+
+    /* ------------------------------------------------------------------------------- */
+    /**
+     * @see java.lang.Throwable#printStackTrace(java.io.PrintStream)
+     */
+    @Override
+    public void printStackTrace(PrintStream out)
+    {
+        super.printStackTrace(out);
+        for (Exception e : exceptions) {
+            e.printStackTrace(out);
+        }
+    }
+
+    @Override
+    public void printStackTrace(PrintWriter out)
+    {
+        super.printStackTrace(out);
+        for (Exception e : exceptions) {
+            e.printStackTrace(out);
+        }
+    }
+
+    public static void throwIf(String message, List<Exception> exceptions) throws MultiException {
+        if (exceptions != null && !exceptions.isEmpty()) {
+            StringBuilder sb = new StringBuilder(message);
+            sb.append(":");
+            for (Exception e : exceptions) {
+                sb.append("\n\t");
+                sb.append(e.getMessage());
+            }
+            throw new MultiException(sb.toString(), exceptions);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/karaf/blob/1ee78df9/shell/command-api/src/main/java/org/apache/karaf/shell/console/NameScoping.java
----------------------------------------------------------------------
diff --git a/shell/command-api/src/main/java/org/apache/karaf/shell/console/NameScoping.java b/shell/command-api/src/main/java/org/apache/karaf/shell/console/NameScoping.java
new file mode 100644
index 0000000..355464d
--- /dev/null
+++ b/shell/command-api/src/main/java/org/apache/karaf/shell/console/NameScoping.java
@@ -0,0 +1,79 @@
+/**
+ *
+ * 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.console;
+
+import org.apache.felix.service.command.CommandSession;
+
+
+/**
+ * A helper class for name scoping
+ */
+public class NameScoping {
+
+    public static final String MULTI_SCOPE_MODE_KEY = "MULTI_SCOPE_MODE";
+
+    /**
+     * Returns the name of the command which can omit the global scope prefix if the command starts with the
+     * same prefix as the current application
+     */
+    public static String getCommandNameWithoutGlobalPrefix(CommandSession session, String key) {
+        if (!isMultiScopeMode(session)) {
+            String globalScope = (String) (session != null ? session.get("APPLICATION") : null);
+            if (globalScope != null) {
+                String prefix = globalScope + ":";
+                if (key.startsWith(prefix)) {
+                    // TODO we may only want to do this for single-scope mode when outside of OSGi?
+                    // so we may want to also check for a isMultiScope mode == false
+                    return key.substring(prefix.length());
+                }
+            }
+        }
+        return key;
+    }
+
+    /**
+     * Returns true if the given scope is the global scope so that it can be hidden from help messages
+     */
+    public static boolean isGlobalScope(CommandSession session, String scope) {
+        if (session == null)
+            return false;
+
+        if (!isMultiScopeMode(session)) {
+            String globalScope = (String) session.get("APPLICATION");
+            if (globalScope != null) {
+                return scope.equals(globalScope);
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Returns true if we are in multi-scope mode (the default) or if we are in single scope mode which means we
+     * avoid prefixing commands with their scope
+     */
+    public static boolean isMultiScopeMode(CommandSession session) {
+        if (session == null)
+            return false;
+
+        Object value = session.get(MULTI_SCOPE_MODE_KEY);
+        if (value != null && value.equals("false")) {
+            return false;
+        }
+        return true;
+    }
+}

http://git-wip-us.apache.org/repos/asf/karaf/blob/1ee78df9/shell/command-api/src/main/java/org/apache/karaf/shell/console/OsgiCommandSupport.java
----------------------------------------------------------------------
diff --git a/shell/command-api/src/main/java/org/apache/karaf/shell/console/OsgiCommandSupport.java b/shell/command-api/src/main/java/org/apache/karaf/shell/console/OsgiCommandSupport.java
new file mode 100644
index 0000000..d737865
--- /dev/null
+++ b/shell/command-api/src/main/java/org/apache/karaf/shell/console/OsgiCommandSupport.java
@@ -0,0 +1,103 @@
+/*
+ * 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.console;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+import org.apache.felix.service.command.CommandSession;
+import org.apache.karaf.shell.commands.Action;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.InvalidSyntaxException;
+import org.osgi.framework.ServiceReference;
+
+public abstract class OsgiCommandSupport extends AbstractAction implements Action, BundleContextAware {
+
+    protected BundleContext bundleContext;
+    protected List<ServiceReference<?>> usedReferences;
+
+    @Override
+    public Object execute(CommandSession session) throws Exception {
+        try {
+            return super.execute(session);
+        } finally {
+            ungetServices();
+        }
+    }
+
+    public BundleContext getBundleContext() {
+        Bundle framework = bundleContext.getBundle(0);
+        return framework == null? bundleContext: framework.getBundleContext();
+    }
+
+    public void setBundleContext(BundleContext bundleContext) {
+        this.bundleContext = bundleContext;
+    }
+
+    protected <T> List<T> getAllServices(Class<T> clazz) {
+        try {
+            return getAllServices(clazz, null);
+        } catch (InvalidSyntaxException e) {
+            throw new IllegalStateException(e);
+        }
+    }
+
+    protected <T> List<T> getAllServices(Class<T> clazz, String filter) throws InvalidSyntaxException {
+        Collection<ServiceReference<T>> references = getBundleContext().getServiceReferences(clazz, filter);
+        List<T> services = new ArrayList<T>();
+        if (references != null) {
+            for (ServiceReference<T> ref : references) {
+                T t = getService(clazz, ref);
+                services.add(t);
+            }
+        }
+        return services;
+    }
+
+    protected <T> T getService(Class<T> clazz) {
+        ServiceReference<T> sr = getBundleContext().getServiceReference(clazz);
+        if (sr != null) {
+            return getService(clazz, sr);
+        } else {
+            return null;
+        }
+    }
+
+    protected <T> T getService(Class<T> clazz, ServiceReference<T> reference) {
+        T t = getBundleContext().getService(reference);
+        if (t != null) {
+            if (usedReferences == null) {
+                usedReferences = new ArrayList<ServiceReference<?>>();
+            }
+            usedReferences.add(reference);
+        }
+        return t;
+    }
+
+    protected void ungetServices() {
+        if (usedReferences != null) {
+            for (ServiceReference<?> ref : usedReferences) {
+                getBundleContext().ungetService(ref);
+            }
+        }
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/karaf/blob/1ee78df9/shell/command-api/src/main/java/org/apache/karaf/shell/console/SessionProperties.java
----------------------------------------------------------------------
diff --git a/shell/command-api/src/main/java/org/apache/karaf/shell/console/SessionProperties.java b/shell/command-api/src/main/java/org/apache/karaf/shell/console/SessionProperties.java
new file mode 100644
index 0000000..30f3433
--- /dev/null
+++ b/shell/command-api/src/main/java/org/apache/karaf/shell/console/SessionProperties.java
@@ -0,0 +1,28 @@
+/*
+ * 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.console;
+
+public class SessionProperties {
+
+    public static final String PRINT_STACK_TRACES = "karaf.printStackTraces";
+    public static final String LAST_EXCEPTION = "karaf.lastException";
+    public static final String IGNORE_INTERRUPTS = "karaf.ignoreInterrupts";
+    public static final String COMPLETION_MODE = "karaf.completionMode";
+
+}

http://git-wip-us.apache.org/repos/asf/karaf/blob/1ee78df9/shell/command-api/src/main/java/org/apache/karaf/shell/console/SubShell.java
----------------------------------------------------------------------
diff --git a/shell/command-api/src/main/java/org/apache/karaf/shell/console/SubShell.java b/shell/command-api/src/main/java/org/apache/karaf/shell/console/SubShell.java
new file mode 100644
index 0000000..2de0d20
--- /dev/null
+++ b/shell/command-api/src/main/java/org/apache/karaf/shell/console/SubShell.java
@@ -0,0 +1,38 @@
+/*
+ * 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.console;
+
+public interface SubShell {
+
+    /**
+     * Returns the name of the command if used inside a shell
+     */
+    String getName();
+
+    /**
+     * Returns the description of the command which is used to generate command line help
+     */
+    String getDescription();
+
+    /**
+     * Returns a detailed description of the command
+     */
+    String getDetailedDescription();
+
+}

http://git-wip-us.apache.org/repos/asf/karaf/blob/1ee78df9/shell/command-api/src/main/java/org/apache/karaf/shell/console/SubShellAction.java
----------------------------------------------------------------------
diff --git a/shell/command-api/src/main/java/org/apache/karaf/shell/console/SubShellAction.java b/shell/command-api/src/main/java/org/apache/karaf/shell/console/SubShellAction.java
new file mode 100644
index 0000000..8774927
--- /dev/null
+++ b/shell/command-api/src/main/java/org/apache/karaf/shell/console/SubShellAction.java
@@ -0,0 +1,39 @@
+/**
+ *
+ * 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.console;
+
+
+
+/**
+ * Switch to a sub-shell.
+ */
+public class SubShellAction extends AbstractAction {
+    
+    private String subShell;
+    
+    public Object doExecute() throws Exception {
+        session.put("SUBSHELL", subShell);
+        session.put("SCOPE", subShell + ":" + session.get("SCOPE"));
+        return null;
+    }
+    
+    public void setSubShell(String subShell) {
+        this.subShell = subShell;
+    }
+    
+}

http://git-wip-us.apache.org/repos/asf/karaf/blob/1ee78df9/shell/command-api/src/main/java/org/apache/karaf/shell/console/commands/AnnotatedSubShell.java
----------------------------------------------------------------------
diff --git a/shell/command-api/src/main/java/org/apache/karaf/shell/console/commands/AnnotatedSubShell.java b/shell/command-api/src/main/java/org/apache/karaf/shell/console/commands/AnnotatedSubShell.java
new file mode 100644
index 0000000..9cd3dda
--- /dev/null
+++ b/shell/command-api/src/main/java/org/apache/karaf/shell/console/commands/AnnotatedSubShell.java
@@ -0,0 +1,43 @@
+/**
+ *
+ * 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.console.commands;
+
+import org.apache.karaf.shell.console.SubShell;
+
+public class AnnotatedSubShell implements SubShell {
+
+    public String getName() {
+        return getAnnotation().name();
+    }
+
+    public String getDescription() {
+        return getAnnotation().description();
+    }
+
+    public String getDetailedDescription() {
+        return getAnnotation().detailedDescription();
+    }
+    
+    org.apache.felix.gogo.commands.SubShell getAnnotation() {
+        org.apache.felix.gogo.commands.SubShell ann = getClass().getAnnotation(org.apache.felix.gogo.commands.SubShell.class);
+        if (ann == null) {
+            throw new IllegalStateException("The class should be annotated with the org.apache.felix.gogo.commands.SubShell annotation");
+        }
+        return ann;
+    }
+}


[10/11] KARAF-2772 Extracting command-api

Posted by cs...@apache.org.
http://git-wip-us.apache.org/repos/asf/karaf/blob/1ee78df9/shell/command-api/src/main/java/org/apache/felix/gogo/commands/converter/DefaultConverter.java
----------------------------------------------------------------------
diff --git a/shell/command-api/src/main/java/org/apache/felix/gogo/commands/converter/DefaultConverter.java b/shell/command-api/src/main/java/org/apache/felix/gogo/commands/converter/DefaultConverter.java
new file mode 100644
index 0000000..e7a4424
--- /dev/null
+++ b/shell/command-api/src/main/java/org/apache/felix/gogo/commands/converter/DefaultConverter.java
@@ -0,0 +1,402 @@
+/*
+ * 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;
+
+@Deprecated
+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();
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/karaf/blob/1ee78df9/shell/command-api/src/main/java/org/apache/felix/gogo/commands/converter/GenericType.java
----------------------------------------------------------------------
diff --git a/shell/command-api/src/main/java/org/apache/felix/gogo/commands/converter/GenericType.java b/shell/command-api/src/main/java/org/apache/felix/gogo/commands/converter/GenericType.java
new file mode 100644
index 0000000..dae6651
--- /dev/null
+++ b/shell/command-api/src/main/java/org/apache/felix/gogo/commands/converter/GenericType.java
@@ -0,0 +1,196 @@
+/*
+ * 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;
+
+@Deprecated
+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);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/karaf/blob/1ee78df9/shell/command-api/src/main/java/org/apache/felix/gogo/commands/converter/ReifiedType.java
----------------------------------------------------------------------
diff --git a/shell/command-api/src/main/java/org/apache/felix/gogo/commands/converter/ReifiedType.java b/shell/command-api/src/main/java/org/apache/felix/gogo/commands/converter/ReifiedType.java
new file mode 100644
index 0000000..bc835d9
--- /dev/null
+++ b/shell/command-api/src/main/java/org/apache/felix/gogo/commands/converter/ReifiedType.java
@@ -0,0 +1,118 @@
+/*
+ * 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
+ */
+@Deprecated
+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;
+    }
+}

http://git-wip-us.apache.org/repos/asf/karaf/blob/1ee78df9/shell/command-api/src/main/java/org/apache/karaf/shell/commands/Action.java
----------------------------------------------------------------------
diff --git a/shell/command-api/src/main/java/org/apache/karaf/shell/commands/Action.java b/shell/command-api/src/main/java/org/apache/karaf/shell/commands/Action.java
new file mode 100644
index 0000000..c3421e3
--- /dev/null
+++ b/shell/command-api/src/main/java/org/apache/karaf/shell/commands/Action.java
@@ -0,0 +1,40 @@
+/*
+ * 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.commands;
+
+import org.apache.felix.service.command.CommandSession;
+
+/**
+ * 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.
+ * 
+ * Any class implementing Action must have a no argument constructor. This
+ * is necessary so the help generator can instantiate the class and get the 
+ * default values. 
+ */
+public interface Action extends org.apache.felix.gogo.commands.Action {
+
+    Object execute(CommandSession session) throws Exception;
+
+}

http://git-wip-us.apache.org/repos/asf/karaf/blob/1ee78df9/shell/command-api/src/main/java/org/apache/karaf/shell/commands/Argument.java
----------------------------------------------------------------------
diff --git a/shell/command-api/src/main/java/org/apache/karaf/shell/commands/Argument.java b/shell/command-api/src/main/java/org/apache/karaf/shell/commands/Argument.java
new file mode 100644
index 0000000..eced53d
--- /dev/null
+++ b/shell/command-api/src/main/java/org/apache/karaf/shell/commands/Argument.java
@@ -0,0 +1,48 @@
+/*
+ * 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.commands;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import java.lang.annotation.ElementType;
+
+/**
+ * Represents a positional argument on a command line (as opposed to an optional named {@link Option}
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.FIELD})
+public @interface Argument
+{
+    public static final String DEFAULT_STRING= "DEFAULT";
+
+    String DEFAULT = "##default";
+
+    String name() default DEFAULT;
+
+    String description() default "";
+
+    boolean required() default false;
+
+    int index() default 0;
+
+    boolean multiValued() default false;
+
+    String valueToShowInHelp() default DEFAULT_STRING;
+}

http://git-wip-us.apache.org/repos/asf/karaf/blob/1ee78df9/shell/command-api/src/main/java/org/apache/karaf/shell/commands/Command.java
----------------------------------------------------------------------
diff --git a/shell/command-api/src/main/java/org/apache/karaf/shell/commands/Command.java b/shell/command-api/src/main/java/org/apache/karaf/shell/commands/Command.java
new file mode 100644
index 0000000..490785d
--- /dev/null
+++ b/shell/command-api/src/main/java/org/apache/karaf/shell/commands/Command.java
@@ -0,0 +1,53 @@
+/*
+ * 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.commands;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import java.lang.annotation.ElementType;
+
+/**
+ * Used to denote a class represents a command which is executable within a shell/scope or as a
+ * command line process.
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.TYPE})
+public @interface Command
+{
+    /**
+     * Returns the scope or sub shell of the command
+     */
+    String scope();
+
+    /**
+     * REturns the name of the command if used inside a shell
+     */
+    String name();
+
+    /**
+     * Returns the description of the command which is used to generate command line help
+     */
+    String description() default "";
+
+    /**
+     * Returns a detailed description of the command
+     */
+    String detailedDescription() default "";
+}

http://git-wip-us.apache.org/repos/asf/karaf/blob/1ee78df9/shell/command-api/src/main/java/org/apache/karaf/shell/commands/CommandException.java
----------------------------------------------------------------------
diff --git a/shell/command-api/src/main/java/org/apache/karaf/shell/commands/CommandException.java b/shell/command-api/src/main/java/org/apache/karaf/shell/commands/CommandException.java
new file mode 100644
index 0000000..1ffa25d
--- /dev/null
+++ b/shell/command-api/src/main/java/org/apache/karaf/shell/commands/CommandException.java
@@ -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.karaf.shell.commands;
+
+import org.apache.karaf.shell.commands.ansi.SimpleAnsi;
+
+
+/**
+ * Base class for exceptions thrown when executing commands.
+ */
+@SuppressWarnings("serial")
+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
+                    : SimpleAnsi.COLOR_RED + "Error executing command: " 
+                    + getMessage() != null ? getMessage() : getClass().getName()
+                    + SimpleAnsi.COLOR_DEFAULT;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/karaf/blob/1ee78df9/shell/command-api/src/main/java/org/apache/karaf/shell/commands/CommandWithAction.java
----------------------------------------------------------------------
diff --git a/shell/command-api/src/main/java/org/apache/karaf/shell/commands/CommandWithAction.java b/shell/command-api/src/main/java/org/apache/karaf/shell/commands/CommandWithAction.java
new file mode 100644
index 0000000..9209eb1
--- /dev/null
+++ b/shell/command-api/src/main/java/org/apache/karaf/shell/commands/CommandWithAction.java
@@ -0,0 +1,31 @@
+/*
+ * 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.commands;
+
+import org.apache.felix.service.command.Function;
+
+public interface CommandWithAction extends Function {
+
+    Class<? extends org.apache.felix.gogo.commands.Action> getActionClass();
+
+    org.apache.felix.gogo.commands.Action createNewAction();
+
+    void releaseAction(org.apache.felix.gogo.commands.Action action) throws Exception;
+
+}

http://git-wip-us.apache.org/repos/asf/karaf/blob/1ee78df9/shell/command-api/src/main/java/org/apache/karaf/shell/commands/Completer.java
----------------------------------------------------------------------
diff --git a/shell/command-api/src/main/java/org/apache/karaf/shell/commands/Completer.java b/shell/command-api/src/main/java/org/apache/karaf/shell/commands/Completer.java
new file mode 100644
index 0000000..ece3e1d
--- /dev/null
+++ b/shell/command-api/src/main/java/org/apache/karaf/shell/commands/Completer.java
@@ -0,0 +1,34 @@
+/*
+ * 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.commands;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.FIELD})
+public @interface Completer {
+
+    Class<? extends org.apache.karaf.shell.console.Completer> value();
+
+    String[] values() default { };
+
+    boolean caseSensitive() default false;
+
+}

http://git-wip-us.apache.org/repos/asf/karaf/blob/1ee78df9/shell/command-api/src/main/java/org/apache/karaf/shell/commands/CompleterValues.java
----------------------------------------------------------------------
diff --git a/shell/command-api/src/main/java/org/apache/karaf/shell/commands/CompleterValues.java b/shell/command-api/src/main/java/org/apache/karaf/shell/commands/CompleterValues.java
new file mode 100644
index 0000000..4463d35
--- /dev/null
+++ b/shell/command-api/src/main/java/org/apache/karaf/shell/commands/CompleterValues.java
@@ -0,0 +1,37 @@
+/*
+ * 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.commands;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Represents a method which can return a List or Array of values used for a
+ * {@link org.apache.karaf.shell.console.Completer}
+ * which is associated with the index of an
+ * {@link Argument}
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.METHOD, ElementType.FIELD})
+public @interface CompleterValues
+{
+    int index() default 0;
+}

http://git-wip-us.apache.org/repos/asf/karaf/blob/1ee78df9/shell/command-api/src/main/java/org/apache/karaf/shell/commands/HelpOption.java
----------------------------------------------------------------------
diff --git a/shell/command-api/src/main/java/org/apache/karaf/shell/commands/HelpOption.java b/shell/command-api/src/main/java/org/apache/karaf/shell/commands/HelpOption.java
new file mode 100644
index 0000000..b5a0fa5
--- /dev/null
+++ b/shell/command-api/src/main/java/org/apache/karaf/shell/commands/HelpOption.java
@@ -0,0 +1,54 @@
+/*
+ * 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.commands;
+
+import java.lang.annotation.Annotation;
+
+public class HelpOption {
+
+    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;
+        }
+    };
+}

http://git-wip-us.apache.org/repos/asf/karaf/blob/1ee78df9/shell/command-api/src/main/java/org/apache/karaf/shell/commands/InfoProvider.java
----------------------------------------------------------------------
diff --git a/shell/command-api/src/main/java/org/apache/karaf/shell/commands/InfoProvider.java b/shell/command-api/src/main/java/org/apache/karaf/shell/commands/InfoProvider.java
new file mode 100644
index 0000000..e253af1
--- /dev/null
+++ b/shell/command-api/src/main/java/org/apache/karaf/shell/commands/InfoProvider.java
@@ -0,0 +1,30 @@
+/*
+ * 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.commands;
+
+import java.util.Properties;
+
+/**
+ * A bundle can publish a service with this interface to offer some informations for the shell:info command
+ */
+public interface InfoProvider {
+
+	public String getName();
+
+	public Properties getProperties();
+
+}

http://git-wip-us.apache.org/repos/asf/karaf/blob/1ee78df9/shell/command-api/src/main/java/org/apache/karaf/shell/commands/Option.java
----------------------------------------------------------------------
diff --git a/shell/command-api/src/main/java/org/apache/karaf/shell/commands/Option.java b/shell/command-api/src/main/java/org/apache/karaf/shell/commands/Option.java
new file mode 100644
index 0000000..b0a9e9c
--- /dev/null
+++ b/shell/command-api/src/main/java/org/apache/karaf/shell/commands/Option.java
@@ -0,0 +1,46 @@
+/*
+ * 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.commands;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Used to mark an optional named command line option who's name typically starts with "--"
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.FIELD})
+public @interface Option
+{
+    public static final String DEFAULT_STRING= "DEFAULT";
+
+    String name();
+
+    String[] aliases() default {};
+
+    String description() default "";
+
+    boolean required() default false;
+
+    boolean multiValued() default false;
+
+    String valueToShowInHelp() default DEFAULT_STRING;
+}

http://git-wip-us.apache.org/repos/asf/karaf/blob/1ee78df9/shell/command-api/src/main/java/org/apache/karaf/shell/commands/ansi/SimpleAnsi.java
----------------------------------------------------------------------
diff --git a/shell/command-api/src/main/java/org/apache/karaf/shell/commands/ansi/SimpleAnsi.java b/shell/command-api/src/main/java/org/apache/karaf/shell/commands/ansi/SimpleAnsi.java
new file mode 100644
index 0000000..1ada311
--- /dev/null
+++ b/shell/command-api/src/main/java/org/apache/karaf/shell/commands/ansi/SimpleAnsi.java
@@ -0,0 +1,28 @@
+/*
+ * 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.commands.ansi;
+
+
+public class SimpleAnsi {
+    public static String COLOR_RED = "\u001B[31m";
+    public static String COLOR_DEFAULT = "\u001B[39m";
+    
+    public static String INTENSITY_BOLD = "\u001B[1m";
+    public static String INTENSITY_NORMAL = "\u001B[0m";
+}

http://git-wip-us.apache.org/repos/asf/karaf/blob/1ee78df9/shell/command-api/src/main/java/org/apache/karaf/shell/commands/basic/AbstractCommand.java
----------------------------------------------------------------------
diff --git a/shell/command-api/src/main/java/org/apache/karaf/shell/commands/basic/AbstractCommand.java b/shell/command-api/src/main/java/org/apache/karaf/shell/commands/basic/AbstractCommand.java
new file mode 100644
index 0000000..41d4a51
--- /dev/null
+++ b/shell/command-api/src/main/java/org/apache/karaf/shell/commands/basic/AbstractCommand.java
@@ -0,0 +1,62 @@
+/*
+ * 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.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.CommandWithAction;
+
+public abstract class AbstractCommand implements CommandWithAction {
+
+    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();
+    }
+    
+}

http://git-wip-us.apache.org/repos/asf/karaf/blob/1ee78df9/shell/command-api/src/main/java/org/apache/karaf/shell/commands/basic/ActionPreparator.java
----------------------------------------------------------------------
diff --git a/shell/command-api/src/main/java/org/apache/karaf/shell/commands/basic/ActionPreparator.java b/shell/command-api/src/main/java/org/apache/karaf/shell/commands/basic/ActionPreparator.java
new file mode 100644
index 0000000..a6d2f8f
--- /dev/null
+++ b/shell/command-api/src/main/java/org/apache/karaf/shell/commands/basic/ActionPreparator.java
@@ -0,0 +1,42 @@
+/*
+ * 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.commands.basic;
+
+import java.util.List;
+
+import org.apache.felix.gogo.commands.Action;
+import org.apache.felix.service.command.CommandSession;
+
+public interface ActionPreparator {
+
+    /**
+     * Check if the arguments are valid for the action and inject the arguments into the fields
+     * of the action
+     * 
+     * Using deprecated Action for compatiblity
+     * 
+     * @param action
+     * @param session
+     * @param arguments
+     * @return
+     * @throws Exception
+     */
+    boolean prepare(@SuppressWarnings("deprecation") Action action, CommandSession session, List<Object> arguments) throws Exception;
+
+}

http://git-wip-us.apache.org/repos/asf/karaf/blob/1ee78df9/shell/command-api/src/main/java/org/apache/karaf/shell/commands/basic/DefaultActionPreparator.java
----------------------------------------------------------------------
diff --git a/shell/command-api/src/main/java/org/apache/karaf/shell/commands/basic/DefaultActionPreparator.java b/shell/command-api/src/main/java/org/apache/karaf/shell/commands/basic/DefaultActionPreparator.java
new file mode 100644
index 0000000..3ad8d29
--- /dev/null
+++ b/shell/command-api/src/main/java/org/apache/karaf/shell/commands/basic/DefaultActionPreparator.java
@@ -0,0 +1,223 @@
+/*
+ * 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.commands.basic;
+
+import static org.apache.karaf.shell.commands.ansi.SimpleAnsi.COLOR_DEFAULT;
+import static org.apache.karaf.shell.commands.ansi.SimpleAnsi.COLOR_RED;
+import static org.apache.karaf.shell.commands.ansi.SimpleAnsi.INTENSITY_BOLD;
+import static org.apache.karaf.shell.commands.ansi.SimpleAnsi.INTENSITY_NORMAL;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Type;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.felix.gogo.commands.Action;
+import org.apache.felix.service.command.CommandSession;
+import org.apache.karaf.shell.commands.Argument;
+import org.apache.karaf.shell.commands.Command;
+import org.apache.karaf.shell.commands.CommandException;
+import org.apache.karaf.shell.commands.HelpOption;
+import org.apache.karaf.shell.commands.Option;
+import org.apache.karaf.shell.commands.converter.DefaultConverter;
+import org.apache.karaf.shell.commands.converter.GenericType;
+import org.apache.karaf.shell.commands.meta.ActionMetaData;
+import org.apache.karaf.shell.commands.meta.ActionMetaDataFactory;
+import org.apache.karaf.shell.console.NameScoping;
+
+public class DefaultActionPreparator implements ActionPreparator {
+
+    public boolean prepare(Action action, CommandSession session, List<Object> params) throws Exception {
+        ActionMetaData actionMetaData = new ActionMetaDataFactory().create(action.getClass());
+        Map<Option, Field> options = actionMetaData.getOptions();
+        Map<Argument, Field> arguments = actionMetaData.getArguments();
+        List<Argument> orderedArguments = actionMetaData.getOrderedArguments();
+        Command command2 = actionMetaData.getCommand();
+
+        if (command2 == null) {
+            // to avoid NPE with subshell
+            return true;
+        }
+
+        String commandErrorSt = (command2 != null) ? COLOR_RED
+                + "Error executing command " + command2.scope() + ":" 
+                + INTENSITY_BOLD + command2.name() + INTENSITY_NORMAL
+                + COLOR_DEFAULT + ": " : "";
+        for (Iterator<Object> it = params.iterator(); it.hasNext(); ) {
+            Object param = it.next();
+            if (HelpOption.HELP.name().equals(param)) {
+                int termWidth = getWidth(session);
+                boolean globalScope = NameScoping.isGlobalScope(session, actionMetaData.getCommand().scope());
+                actionMetaData.printUsage(action, System.out, globalScope, termWidth);
+                return false;
+            }
+        }
+        
+        // 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();
+
+            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) {
+                    throw new CommandException(commandErrorSt 
+                                + "undefined option " + INTENSITY_BOLD + param + INTENSITY_NORMAL + "\n"
+                                + "Try <command> --help' for more information.",
+                                        "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) {
+                        throw new CommandException(commandErrorSt
+                                + "missing value for option " + INTENSITY_BOLD + param + INTENSITY_NORMAL,
+                                "Missing value for option: " + param
+                        );
+                }
+                if (option.multiValued()) {
+                    @SuppressWarnings("unchecked")
+                    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()) {
+                        throw new CommandException(commandErrorSt +
+                                "too many arguments specified",
+                                "Too many arguments specified"
+                        );
+                }
+                Argument argument = orderedArguments.get(argIndex);
+                if (!argument.multiValued()) {
+                    argIndex++;
+                }
+                if (argument.multiValued()) {
+                    @SuppressWarnings("unchecked")
+                    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) {
+                    throw new CommandException(commandErrorSt +
+                            "option " + INTENSITY_BOLD + option.name() + INTENSITY_NORMAL + " is required",
+                            "Option " + option.name() + " is required"
+                    );
+            }
+        }
+        for (Argument argument : orderedArguments) {
+            if (argument.required() && argumentValues.get(argument) == null) {
+                    throw new CommandException(commandErrorSt +
+                            "argument " + INTENSITY_BOLD + argument.name() + INTENSITY_NORMAL + " is required",
+                            "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) {
+                    throw new CommandException(commandErrorSt +
+                            "unable to convert option " + INTENSITY_BOLD + entry.getKey().name() + INTENSITY_NORMAL + " with value '"
+                            + entry.getValue() + "' to type " + new GenericType(field.getGenericType()).toString(),
+                            "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) {
+                    throw new CommandException(commandErrorSt +
+                            "unable to convert argument " + INTENSITY_BOLD + entry.getKey().name() + INTENSITY_NORMAL + " with value '"
+                            + entry.getValue() + "' to type " + new GenericType(field.getGenericType()).toString(),
+                            "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 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);
+    }
+
+    private int getWidth(CommandSession session) {
+        Object cols = session.get("COLUMNS");
+        return  (cols != null && cols instanceof Integer) ? (Integer)cols : 80;
+    }
+}

http://git-wip-us.apache.org/repos/asf/karaf/blob/1ee78df9/shell/command-api/src/main/java/org/apache/karaf/shell/commands/basic/SimpleCommand.java
----------------------------------------------------------------------
diff --git a/shell/command-api/src/main/java/org/apache/karaf/shell/commands/basic/SimpleCommand.java b/shell/command-api/src/main/java/org/apache/karaf/shell/commands/basic/SimpleCommand.java
new file mode 100644
index 0000000..4ea5dfc
--- /dev/null
+++ b/shell/command-api/src/main/java/org/apache/karaf/shell/commands/basic/SimpleCommand.java
@@ -0,0 +1,82 @@
+/*
+ * 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.commands.basic;
+
+import java.util.Hashtable;
+
+import org.apache.felix.service.command.Function;
+import org.apache.karaf.shell.commands.Action;
+import org.apache.karaf.shell.commands.Command;
+import org.apache.karaf.shell.commands.CommandWithAction;
+import org.osgi.framework.ServiceRegistration;
+import org.osgi.framework.BundleContext;
+
+/**
+ * A very simple {@link Function} which creates {@link Action} based on a class name.
+ */
+public class SimpleCommand extends AbstractCommand {
+
+    private Class<? extends Action> actionClass;
+
+    public SimpleCommand()
+    {
+    }
+
+    public SimpleCommand(Class<? extends Action> actionClass)
+    {
+        this.actionClass = actionClass;
+    }
+
+    public Class<? extends Action> getActionClass()
+    {
+        return actionClass;
+    }
+
+    public void setActionClass(Class<? extends Action> actionClass)
+    {
+        this.actionClass = actionClass;
+    }
+
+    public Action createNewAction() {
+        try {
+            return actionClass.newInstance();
+        } catch (InstantiationException e) {
+            throw new RuntimeException(e);
+        } catch (IllegalAccessException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    public static ServiceRegistration export(BundleContext context, Class<? extends Action> actionClass)
+    {
+        Command cmd = actionClass.getAnnotation(Command.class);
+        if (cmd == null)
+        {
+            throw new IllegalArgumentException("Action class is not annotated with @Command");
+        }
+        Hashtable<String, String> props = new Hashtable<String, String>();
+        props.put("osgi.command.scope", cmd.scope());
+        props.put("osgi.command.function", cmd.name());
+        SimpleCommand command = new SimpleCommand(actionClass);
+        return context.registerService(
+                new String[] { Function.class.getName(), CommandWithAction.class.getName() },
+                command, props);
+    }
+
+}


[07/11] KARAF-2772 Extracting command-api

Posted by cs...@apache.org.
http://git-wip-us.apache.org/repos/asf/karaf/blob/1ee78df9/shell/command-api/src/main/java/org/apache/karaf/shell/console/completer/CommandsCompleter.java
----------------------------------------------------------------------
diff --git a/shell/command-api/src/main/java/org/apache/karaf/shell/console/completer/CommandsCompleter.java b/shell/command-api/src/main/java/org/apache/karaf/shell/console/completer/CommandsCompleter.java
new file mode 100644
index 0000000..ba95b0c
--- /dev/null
+++ b/shell/command-api/src/main/java/org/apache/karaf/shell/console/completer/CommandsCompleter.java
@@ -0,0 +1,321 @@
+/*
+ * 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.console.completer;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeMap;
+
+import org.apache.felix.gogo.runtime.CommandProxy;
+import org.apache.felix.service.command.CommandProcessor;
+import org.apache.felix.service.command.CommandSession;
+import org.apache.felix.service.command.Function;
+import org.apache.karaf.shell.commands.CommandWithAction;
+import org.apache.karaf.shell.console.CommandSessionHolder;
+import org.apache.karaf.shell.console.Completer;
+import org.apache.karaf.shell.console.SessionProperties;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.FrameworkUtil;
+import org.osgi.framework.ServiceEvent;
+import org.osgi.framework.ServiceListener;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Like the {@link org.apache.karaf.shell.console.completer.CommandsCompleter} but does not use OSGi but is
+ * instead used from the non-OSGi {@link org.apache.karaf.shell.console.impl.Main}
+ */
+public class CommandsCompleter implements Completer {
+
+    public static final String COMMANDS = ".commands";
+
+    private static final Logger LOGGER = LoggerFactory.getLogger(CommandsCompleter.class);
+
+    private CommandSession session;
+    private final Map<String, Completer> globalCompleters = new HashMap<String, Completer>();
+    private final Map<String, Completer> localCompleters = new HashMap<String, Completer>();
+    private final Set<String> commands = new HashSet<String>();
+
+    public CommandsCompleter() {
+        this(CommandSessionHolder.getSession());
+    }
+
+    public CommandsCompleter(CommandSession session) {
+        this.session = session;
+        try {
+            new CommandTracker();
+        } catch (Throwable t) {
+            // Ignore in case we're not in OSGi
+        }
+    }
+
+
+    public int complete(String buffer, int cursor, List<String> candidates) {
+        if (session == null) {
+            session = CommandSessionHolder.getSession();
+        }
+
+        List<String> scopes = getCurrentScopes();
+        Map<String, Completer>[] allCompleters = checkData();
+        sort(allCompleters, scopes);
+
+        String subShell = getCurrentSubShell();
+        String completion = getCompletionType();
+
+        // SUBSHELL mode
+        if ("SUBSHELL".equalsIgnoreCase(completion)) {
+            if (subShell.isEmpty()) {
+                subShell = "*";
+            }
+            List<Completer> completers = new ArrayList<Completer>();
+            for (String name : allCompleters[1].keySet()) {
+                if (name.startsWith(subShell)) {
+                    completers.add(allCompleters[1].get(name));
+                }
+            }
+            if (!subShell.equals("*")) {
+                completers.add(new StringsCompleter(new String[] { "exit" }));
+            }
+            int res = new AggregateCompleter(completers).complete(buffer, cursor, candidates);
+            Collections.sort(candidates);
+            return res;
+        }
+
+        if ("FIRST".equalsIgnoreCase(completion)) {
+            if (!subShell.isEmpty()) {
+                List<Completer> completers = new ArrayList<Completer>();
+                for (String name : allCompleters[1].keySet()) {
+                    if (name.startsWith(subShell)) {
+                        completers.add(allCompleters[1].get(name));
+                    }
+                }
+                int res = new AggregateCompleter(completers).complete(buffer, cursor, candidates);
+                if (!candidates.isEmpty()) {
+                    Collections.sort(candidates);
+                    return res;
+                }
+            }
+            List<Completer> compl = new ArrayList<Completer>();
+            compl.add(new StringsCompleter(getAliases()));
+            compl.addAll(allCompleters[0].values());
+            int res = new AggregateCompleter(compl).complete(buffer, cursor, candidates);
+            Collections.sort(candidates);
+            return res;
+        }
+
+        List<Completer> compl = new ArrayList<Completer>();
+        compl.add(new StringsCompleter(getAliases()));
+        compl.addAll(allCompleters[0].values());
+        int res = new AggregateCompleter(compl).complete(buffer, cursor, candidates);
+        Collections.sort(candidates);
+        return res;
+    }
+
+    protected void sort(Map<String, Completer>[] completers, List<String> scopes) {
+        ScopeComparator comparator = new ScopeComparator(scopes);
+        for (int i = 0; i < completers.length; i++) {
+            Map<String, Completer> map = new TreeMap<String, Completer>(comparator);
+            map.putAll(completers[i]);
+            completers[i] = map;
+        }
+    }
+
+    protected static class ScopeComparator implements Comparator<String> {
+        private final List<String> scopes;
+        public ScopeComparator(List<String> scopes) {
+            this.scopes = scopes;
+        }
+        @Override
+        public int compare(String o1, String o2) {
+            String[] p1 = o1.split(":");
+            String[] p2 = o2.split(":");
+            int p = 0;
+            while (p < p1.length && p < p2.length) {
+                int i1 = scopes.indexOf(p1[p]);
+                int i2 = scopes.indexOf(p2[p]);
+                if (i1 < 0) {
+                    if (i2 < 0) {
+                        int c = p1[p].compareTo(p2[p]);
+                        if (c != 0) {
+                            return c;
+                        } else {
+                            p++;
+                        }
+                    } else {
+                        return +1;
+                    }
+                } else if (i2 < 0) {
+                    return -1;
+                } else if (i1 < i2) {
+                    return -1;
+                } else if (i1 > i2) {
+                    return +1;
+                } else {
+                    p++;
+                }
+            }
+            return 0;
+        }
+    }
+
+    protected List<String> getCurrentScopes() {
+        String scopes = (String) session.get("SCOPE");
+        return Arrays.asList(scopes.split(":"));
+    }
+
+    protected String getCurrentSubShell() {
+        String s = (String) session.get("SUBSHELL");
+        if (s == null) {
+            s = "";
+        }
+        return s;
+    }
+
+    protected String getCompletionType() {
+        String completion = (String) session.get(SessionProperties.COMPLETION_MODE);
+        if (completion == null) {
+            completion = "GLOBAL";
+        }
+        return completion;
+    }
+
+    protected String stripScope(String name) {
+        int index = name.indexOf(":");
+        return index > 0 ? name.substring(index + 1) : name;
+    }
+
+    @SuppressWarnings({
+        "unchecked", "deprecation"
+    })
+    protected Map<String, Completer>[] checkData() {
+        // Copy the set to avoid concurrent modification exceptions
+        // TODO: fix that in gogo instead
+        Set<String> names;
+        boolean update;
+        synchronized (this) {
+            names = new HashSet<String>((Set<String>) session.get(COMMANDS));
+            update = !names.equals(commands);
+        }
+        if (update) {
+            // get command aliases
+            Set<String> commands = new HashSet<String>();
+            Map<String, Completer> global = new HashMap<String, Completer>();
+            Map<String, Completer> local = new HashMap<String, Completer>();
+
+            // add argument completers for each command
+            for (String command : names) {
+                String rawCommand = stripScope(command);
+                Function function = (Function) session.get(command);
+                function = unProxy(function);
+                if (function instanceof CommandWithAction) {
+                    try {
+                        global.put(command, new ArgumentCompleter(session, (CommandWithAction) function, command));
+                        local.put(command, new ArgumentCompleter(session, (CommandWithAction) function, rawCommand));
+                    } catch (Throwable t) {
+                        LOGGER.debug("Unable to create completers for command '" + command + "'", t);
+                    }
+                }
+                else if (function instanceof org.apache.felix.gogo.commands.CommandWithAction) {
+                    try {
+                        global.put(command, new OldArgumentCompleter(session, (org.apache.felix.gogo.commands.CommandWithAction) function, command));
+                        local.put(command, new OldArgumentCompleter(session, (org.apache.felix.gogo.commands.CommandWithAction) function, rawCommand));
+                    } catch (Throwable t) {
+                        LOGGER.debug("Unable to create completers for command '" + command + "'", t);
+                    }
+                }
+                commands.add(command);
+            }
+
+            synchronized (this) {
+                this.commands.clear();
+                this.globalCompleters.clear();
+                this.localCompleters.clear();
+                this.commands.addAll(commands);
+                this.globalCompleters.putAll(global);
+                this.localCompleters.putAll(local);
+            }
+        }
+        synchronized (this) {
+            return new Map[] {
+                    new HashMap<String, Completer>(this.globalCompleters),
+                    new HashMap<String, Completer>(this.localCompleters)
+            };
+        }
+    }
+
+    /**
+     * Get the aliases defined in the console session.
+     *
+     * @return the aliases set
+     */
+    @SuppressWarnings("unchecked")
+    private Set<String> getAliases() {
+        Set<String> vars = ((Set<String>) session.get(null));
+        Set<String> aliases = new HashSet<String>();
+        for (String var : vars) {
+            Object content = session.get(var);
+            if (content != null && "org.apache.felix.gogo.runtime.Closure".equals(content.getClass().getName())) {
+                aliases.add(var);
+            }
+        }
+        return aliases;
+    }
+
+    public static Function unProxy(Function function) {
+        if (function == null || function.getClass() != CommandProxy.class) {
+            return function;
+        }
+        CommandProxy proxy = (CommandProxy)function;
+        Object target = proxy.getTarget();
+        try {
+            return target instanceof Function ? (Function)target : function;
+        } finally {
+            proxy.ungetTarget();
+        }
+    }
+
+    private class CommandTracker {
+        public CommandTracker() throws Exception {
+            BundleContext context = FrameworkUtil.getBundle(getClass()).getBundleContext();
+            if (context == null) {
+                throw new IllegalStateException("Bundle is stopped");
+            }
+            ServiceListener listener = new ServiceListener() {
+                public void serviceChanged(ServiceEvent event) {
+                    synchronized (CommandsCompleter.this) {
+                        commands.clear();
+                    }
+                }
+            };
+            context.addServiceListener(listener,
+                    String.format("(&(%s=*)(%s=*))",
+                            CommandProcessor.COMMAND_SCOPE,
+                            CommandProcessor.COMMAND_FUNCTION));
+        }
+    }
+
+}
+

http://git-wip-us.apache.org/repos/asf/karaf/blob/1ee78df9/shell/command-api/src/main/java/org/apache/karaf/shell/console/completer/FileCompleter.java
----------------------------------------------------------------------
diff --git a/shell/command-api/src/main/java/org/apache/karaf/shell/console/completer/FileCompleter.java b/shell/command-api/src/main/java/org/apache/karaf/shell/console/completer/FileCompleter.java
new file mode 100644
index 0000000..dbb3034
--- /dev/null
+++ b/shell/command-api/src/main/java/org/apache/karaf/shell/console/completer/FileCompleter.java
@@ -0,0 +1,147 @@
+/**
+ *
+ * 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.console.completer;
+
+import java.io.File;
+import java.util.List;
+
+import org.apache.felix.service.command.CommandSession;
+import org.apache.karaf.shell.console.Completer;
+
+/**
+ * A file name completer takes the buffer and issues a list of
+ * potential completions.
+ * <p/>
+ * This completer tries to behave as similar as possible to
+ * <i>bash</i>'s file name completion (using GNU readline)
+ * with the following exceptions:
+ * <p/>
+ * <ul>
+ * <li>Candidates that are directories will end with "/"</li>
+ * <li>Wildcard regular expressions are not evaluated or replaced</li>
+ * <li>The "~" character can be used to represent the user's home,
+ * but it cannot complete to other users' homes, since java does
+ * not provide any way of determining that easily</li>
+ * </ul>
+ *
+ * @author <a href="mailto:mwp1@cornell.edu">Marc Prud'hommeaux</a>
+ * @author <a href="mailto:jason@planet57.com">Jason Dillon</a>
+ * @since 2.3
+ */
+public class FileCompleter implements Completer
+{
+    private static String OS = System.getProperty("os.name").toLowerCase();
+
+    // TODO: Handle files with spaces in them
+
+    private static final boolean OS_IS_WINDOWS = isWindows();
+    
+    public FileCompleter(CommandSession session) {
+    }
+
+    public static boolean isWindows() {
+        return (OS.indexOf("win") >= 0);
+
+    }
+
+    public int complete(String buffer, final int cursor, final List<String> candidates) {
+        // buffer can be null
+        if (candidates == null) {
+            return 0;
+        }
+
+        if (buffer == null) {
+            buffer = "";
+        }
+
+        if (OS_IS_WINDOWS) {
+            buffer = buffer.replace('/', '\\');
+        }
+
+        String translated = buffer;
+
+        File homeDir = getUserHome();
+
+        // Special character: ~ maps to the user's home directory
+        if (translated.startsWith("~" + separator())) {
+            translated = homeDir.getPath() + translated.substring(1);
+        }
+        else if (translated.startsWith("~")) {
+            translated = homeDir.getParentFile().getAbsolutePath();
+        }
+        else if (!(translated.startsWith(separator()))) {
+            String cwd = getUserDir().getAbsolutePath();
+            translated = cwd + separator() + translated;
+        }
+
+        File file = new File(translated);
+        final File dir;
+
+        if (translated.endsWith(separator())) {
+            dir = file;
+        }
+        else {
+            dir = file.getParentFile();
+        }
+
+        File[] entries = dir == null ? new File[0] : dir.listFiles();
+
+        return matchFiles(buffer, translated, entries, candidates);
+    }
+
+    protected String separator() {
+        return File.separator;
+    }
+
+    protected File getUserHome() {
+        return new File(System.getProperty("user.home"));
+    }
+
+    protected File getUserDir() {
+        return new File(".");
+    }
+
+    protected int matchFiles(final String buffer, final String translated, final File[] files, final List<String> candidates) {
+        if (files == null) {
+            return -1;
+        }
+
+        int matches = 0;
+
+        // first pass: just count the matches
+        for (File file : files) {
+            if (file.getAbsolutePath().startsWith(translated)) {
+                matches++;
+            }
+        }
+        for (File file : files) {
+            if (file.getAbsolutePath().startsWith(translated)) {
+                CharSequence name = file.getName() + (matches == 1 && file.isDirectory() ? separator() : " ");
+                candidates.add(render(file, name).toString());
+            }
+        }
+
+        final int index = buffer.lastIndexOf(separator());
+
+        return index + separator().length();
+    }
+
+    protected CharSequence render(final File file, final CharSequence name) {
+        return name;
+    }
+}

http://git-wip-us.apache.org/repos/asf/karaf/blob/1ee78df9/shell/command-api/src/main/java/org/apache/karaf/shell/console/completer/NullCompleter.java
----------------------------------------------------------------------
diff --git a/shell/command-api/src/main/java/org/apache/karaf/shell/console/completer/NullCompleter.java b/shell/command-api/src/main/java/org/apache/karaf/shell/console/completer/NullCompleter.java
new file mode 100644
index 0000000..13da2df
--- /dev/null
+++ b/shell/command-api/src/main/java/org/apache/karaf/shell/console/completer/NullCompleter.java
@@ -0,0 +1,32 @@
+/*
+ * 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.console.completer;
+
+import java.util.List;
+
+import org.apache.karaf.shell.console.Completer;
+
+public class NullCompleter implements Completer {
+
+    public static final NullCompleter INSTANCE = new NullCompleter();
+
+    public int complete(String buffer, int cursor, List<String> candidates) {
+        return -1;
+    }
+}

http://git-wip-us.apache.org/repos/asf/karaf/blob/1ee78df9/shell/command-api/src/main/java/org/apache/karaf/shell/console/completer/OldArgumentCompleter.java
----------------------------------------------------------------------
diff --git a/shell/command-api/src/main/java/org/apache/karaf/shell/console/completer/OldArgumentCompleter.java b/shell/command-api/src/main/java/org/apache/karaf/shell/console/completer/OldArgumentCompleter.java
new file mode 100644
index 0000000..583d6f8
--- /dev/null
+++ b/shell/command-api/src/main/java/org/apache/karaf/shell/console/completer/OldArgumentCompleter.java
@@ -0,0 +1,448 @@
+/*
+ * 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.console.completer;
+
+import java.io.File;
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.EnumSet;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.felix.gogo.commands.Action;
+import org.apache.felix.gogo.commands.Argument;
+import org.apache.felix.gogo.commands.CommandWithAction;
+import org.apache.felix.gogo.commands.CompleterValues;
+import org.apache.felix.gogo.commands.Option;
+import org.apache.felix.service.command.CommandSession;
+import org.apache.karaf.shell.console.CommandSessionHolder;
+import org.apache.karaf.shell.console.CompletableFunction;
+import org.apache.karaf.shell.console.Completer;
+import org.apache.karaf.shell.console.NameScoping;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class OldArgumentCompleter implements Completer {
+
+    private static final Logger LOGGER = LoggerFactory.getLogger(OldArgumentCompleter.class);
+
+    public static final String ARGUMENTS_LIST = "ARGUMENTS_LIST";
+
+    final Completer commandCompleter;
+    final Completer optionsCompleter;
+    final List<Completer> argsCompleters;
+    final Map<String, Completer> optionalCompleters;
+    final CommandWithAction function;
+    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;
+
+    @SuppressWarnings({ "unchecked", "rawtypes" })
+    public OldArgumentCompleter(CommandSession session, CommandWithAction function, String command) {
+        this.function = function;
+        // Command name completer
+        commandCompleter = new StringsCompleter(getNames(session, command));
+        // Build options completer
+        for (Class<?> type = function.getActionClass(); type != null; type = type.getSuperclass()) {
+            for (Field field : type.getDeclaredFields()) {
+                Option option = field.getAnnotation(Option.class);
+                if (option != null) {
+                    fields.put(option, field);
+                    options.put(option.name(), option);
+                    String[] aliases = option.aliases();
+                    if (aliases != null) {
+                        for (String alias : aliases) {
+                            options.put(alias, option);
+                        }
+                    }
+                }
+                Argument argument = field.getAnnotation(Argument.class);
+                if (argument != null) {
+                    Integer key = argument.index();
+                    if (arguments.containsKey(key)) {
+                        LOGGER.warn("Duplicate @Argument annotations on class " + type.getName() + " for index: " + key + " see: " + field);
+                    } else {
+                        arguments.put(key, field);
+                    }
+                }
+            }
+        }
+//        options.put(HelpOption.HELP.name(), HelpOption.HELP);
+        optionsCompleter = new StringsCompleter(options.keySet());
+        // Build arguments completers
+        argsCompleters = new ArrayList<Completer>();
+
+        if (function instanceof CompletableFunction) {
+            Map<String, Completer> opt;
+            try {
+                //
+                opt = ((CompletableFunction) function).getOptionalCompleters();
+            } catch (Throwable t) {
+                opt = new HashMap<String, Completer>();
+            }
+            optionalCompleters = opt;
+            List<Completer> fcl = ((CompletableFunction) function).getCompleters();
+            if (fcl != null) {
+                for (Completer c : fcl) {
+                    argsCompleters.add(c == null ? NullCompleter.INSTANCE : c);
+                }
+            } else {
+                argsCompleters.add(NullCompleter.INSTANCE);
+            }
+        } else {
+            optionalCompleters = new HashMap<String, Completer>();
+            final Map<Integer, Method> methods = new HashMap<Integer, Method>();
+            for (Class<?> type = function.getActionClass(); type != null; type = type.getSuperclass()) {
+                for (Method method : type.getDeclaredMethods()) {
+                    CompleterValues completerMethod = method.getAnnotation(CompleterValues.class);
+                    if (completerMethod != null) {
+                        int index = completerMethod.index();
+                        Integer key = index;
+                        if (index >= arguments.size() || index < 0) {
+                            LOGGER.warn("Index out of range on @CompleterValues on class " + type.getName() + " for index: " + key + " see: " + method);
+                        }
+                        if (methods.containsKey(key)) {
+                            LOGGER.warn("Duplicate @CompleterMethod annotations on class " + type.getName() + " for index: " + key + " see: " + method);
+                        } else {
+                            methods.put(key, method);
+                        }
+                    }
+                }
+            }
+            for (int i = 0, size = arguments.size(); i < size; i++) {
+                Completer argCompleter = NullCompleter.INSTANCE;
+                Method method = methods.get(i);
+                if (method != null) {
+                    // lets invoke the method
+                    Action action = function.createNewAction();
+                    try {
+                        Object value = method.invoke(action);
+                        if (value instanceof String[]) {
+                            argCompleter = new StringsCompleter((String[]) value);
+                        } else if (value instanceof Collection) {
+                            argCompleter = new StringsCompleter((Collection<String>) value);
+                        } else {
+                            LOGGER.warn("Could not use value " + value + " as set of completions!");
+                        }
+                    } catch (IllegalAccessException e) {
+                        LOGGER.warn("Could not invoke @CompleterMethod on " + function + ". " + e, e);
+                    } catch (InvocationTargetException e) {
+                        Throwable target = e.getTargetException();
+                        if (target == null) {
+                            target = e;
+                        }
+                        LOGGER.warn("Could not invoke @CompleterMethod on " + function + ". " + target, target);
+                    } finally {
+                        try {
+                            function.releaseAction(action);
+                        } catch (Exception e) {
+                            LOGGER.warn("Failed to release action: " + action + ". " + e, e);
+                        }
+                    }
+                } else {
+                    Field field = arguments.get(i);
+                    Class<?> type = field.getType();
+                    if (type.isAssignableFrom(File.class)) {
+                        argCompleter = new FileCompleter(session);
+                    } else if (type.isAssignableFrom(Boolean.class) || type.isAssignableFrom(boolean.class)) {
+                        argCompleter = new StringsCompleter(new String[] {"false", "true"}, false);
+                    } else if (type.isAssignableFrom(Enum.class)) {
+                        Set<String> values = new HashSet<String>();
+                        for (Object o : EnumSet.allOf((Class<Enum>) type)) {
+                            values.add(o.toString());
+                        }
+                        argCompleter = new StringsCompleter(values, false);
+                    } else {
+                        // TODO any other completers we can add?
+                    }
+                }
+                argsCompleters.add(argCompleter);
+            }
+        }
+    }
+
+    private String[] getNames(CommandSession session, String scopedCommand) {
+        String command = NameScoping.getCommandNameWithoutGlobalPrefix(session, scopedCommand);
+        String[] s = command.split(":");
+        if (s.length == 1) {
+            return s;
+        } else {
+            return new String[] { command, s[1] };
+        }
+    }
+
+    /**
+     *  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(final String buffer, final int cursor,
+                        final List<String> candidates) {
+        ArgumentList list = delimit(buffer, cursor);
+        int argpos = list.getArgumentPosition();
+        int argIndex = list.getCursorArgumentIndex();
+
+        //Store the argument list so that it can be used by completers.
+        CommandSession commandSession = CommandSessionHolder.getSession();
+        if(commandSession != null) {
+            commandSession.put(ARGUMENTS_LIST,list);
+        }
+
+        Completer comp = null;
+        String[] args = list.getArguments();
+        int index = 0;
+        // First argument is command name
+        if (index < argIndex) {
+            // Verify command name
+            if (!verifyCompleter(commandCompleter, args[index])) {
+                return -1;
+            }
+            index++;
+        } else {
+            comp = commandCompleter;
+        }
+        // Now, check options
+        if (comp == null) {
+            while (index < argIndex && args[index].startsWith("-")) {
+                if (!verifyCompleter(optionsCompleter, args[index])) {
+                    return -1;
+                }
+                Option option = options.get(args[index]);
+                if (option == null) {
+                    return -1;
+                }
+                Field field = fields.get(option);
+                if (field != null && field.getType() != boolean.class && field.getType() != Boolean.class) {
+                    if (++index == argIndex) {
+                        comp = NullCompleter.INSTANCE;
+                    }
+                }
+                index++;
+            }
+            if (comp == null && index >= argIndex && index < args.length && args[index].startsWith("-")) {
+                comp = optionsCompleter;
+            }
+        }
+        //Now check for if last Option has a completer
+        int lastAgurmentIndex = argIndex - 1;
+        if (lastAgurmentIndex >= 1) {
+            Option lastOption = options.get(args[lastAgurmentIndex]);
+            if (lastOption != null) {
+
+                Field lastField = fields.get(lastOption);
+                if (lastField != null && lastField.getType() != boolean.class && lastField.getType() != Boolean.class) {
+                    Option option = lastField.getAnnotation(Option.class);
+                    if (option != null) {
+                        Completer optionValueCompleter = null;
+                        String name = option.name();
+                        if (optionalCompleters != null && name != null) {
+                            optionValueCompleter = optionalCompleters.get(name);
+                            if (optionValueCompleter == null) {
+                                String[] aliases = option.aliases();
+                                if (aliases.length > 0) {
+                                    for (int i = 0; i < aliases.length && optionValueCompleter == null; i++) {
+                                        optionValueCompleter = optionalCompleters.get(option.aliases()[i]);
+                                    }
+                                }
+                            }
+                        }
+                        if(optionValueCompleter != null) {
+                            comp = optionValueCompleter;
+                        }
+                    }
+                }
+            }
+        }
+
+        // Check arguments
+        if (comp == null) {
+            int indexArg = 0;
+            while (index < argIndex) {
+                Completer sub = argsCompleters.get(indexArg >= argsCompleters.size() ? argsCompleters.size() - 1 : indexArg);
+                if (!verifyCompleter(sub, args[index])) {
+                    return -1;
+                }
+                index++;
+                indexArg++;
+            }
+            comp = argsCompleters.get(indexArg >= argsCompleters.size() ? argsCompleters.size() - 1 : indexArg);
+        }
+
+        int ret = comp.complete(list.getCursorArgument(), argpos, candidates);
+
+        if (ret == -1) {
+            return -1;
+        }
+
+        int pos = ret + (list.getBufferPosition() - argpos);
+
+        /**
+         *  Special case: when completing in the middle of a line, and the
+         *  area under the cursor is a delimiter, then trim any delimiters
+         *  from the candidates, since we do not need to have an extra
+         *  delimiter.
+         *
+         *  E.g., if we have a completion for "foo", and we
+         *  enter "f bar" into the buffer, and move to after the "f"
+         *  and hit TAB, we want "foo bar" instead of "foo  bar".
+         */
+
+        if ((buffer != null) && (cursor != buffer.length()) && isDelimiter(buffer, cursor)) {
+            for (int i = 0; i < candidates.size(); i++) {
+                String val = candidates.get(i);
+
+                while ((val.length() > 0)
+                    && isDelimiter(val, val.length() - 1)) {
+                    val = val.substring(0, val.length() - 1);
+                }
+
+                candidates.set(i, val);
+            }
+        }
+
+        return pos;
+    }
+
+    protected boolean verifyCompleter(Completer completer, String argument) {
+        List<String> candidates = new ArrayList<String>();
+        return completer.complete(argument, argument.length(), candidates) != -1 && !candidates.isEmpty();
+    }
+
+    public ArgumentList delimit(final String buffer, final int cursor) {
+        Parser parser = new Parser(buffer, cursor);
+        try {
+            List<List<List<String>>> program = parser.program();
+            List<String> pipe = program.get(parser.c0).get(parser.c1);
+            return new ArgumentList(pipe.toArray(new String[pipe.size()]), parser.c2, parser.c3, cursor);
+        } catch (Throwable t) {
+            return new ArgumentList(new String[] { buffer }, 0, cursor, cursor);
+        }
+    }
+
+    /**
+     *  Returns true if the specified character is a whitespace
+     *  parameter. Check to ensure that the character is not
+     *  escaped and returns true from
+     *  {@link #isDelimiterChar}.
+     *
+     *  @param  buffer the complete command buffer
+     *  @param  pos    the index of the character in the buffer
+     *  @return        true if the character should be a delimiter
+     */
+    public boolean isDelimiter(final String buffer, final int pos) {
+        return !isEscaped(buffer, pos) && isDelimiterChar(buffer, pos);
+    }
+
+    public boolean isEscaped(final String buffer, final int pos) {
+        return pos > 0 && buffer.charAt(pos) == '\\' && !isEscaped(buffer, pos - 1);
+    }
+
+    /**
+     *  The character is a delimiter if it is whitespace, and the
+     *  preceeding character is not an escape character.
+     */
+    public boolean isDelimiterChar(String buffer, int pos) {
+        return Character.isWhitespace(buffer.charAt(pos));
+    }
+
+    /**
+     *  The result of a delimited buffer.
+     */
+    public static class ArgumentList {
+        private String[] arguments;
+        private int cursorArgumentIndex;
+        private int argumentPosition;
+        private int bufferPosition;
+
+        /**
+         *  @param  arguments           the array of tokens
+         *  @param  cursorArgumentIndex the token index of the cursor
+         *  @param  argumentPosition    the position of the cursor in the
+         *                              current token
+         *  @param  bufferPosition      the position of the cursor in
+         *                              the whole buffer
+         */
+        public ArgumentList(String[] arguments, int cursorArgumentIndex,
+            int argumentPosition, int bufferPosition) {
+            this.arguments = arguments;
+            this.cursorArgumentIndex = cursorArgumentIndex;
+            this.argumentPosition = argumentPosition;
+            this.bufferPosition = bufferPosition;
+        }
+
+        public void setCursorArgumentIndex(int cursorArgumentIndex) {
+            this.cursorArgumentIndex = cursorArgumentIndex;
+        }
+
+        public int getCursorArgumentIndex() {
+            return this.cursorArgumentIndex;
+        }
+
+        public String getCursorArgument() {
+            if ((cursorArgumentIndex < 0)
+                || (cursorArgumentIndex >= arguments.length)) {
+                return null;
+            }
+
+            return arguments[cursorArgumentIndex];
+        }
+
+        public void setArgumentPosition(int argumentPosition) {
+            this.argumentPosition = argumentPosition;
+        }
+
+        public int getArgumentPosition() {
+            return this.argumentPosition;
+        }
+
+        public void setArguments(String[] arguments) {
+            this.arguments = arguments;
+        }
+
+        public String[] getArguments() {
+            return this.arguments;
+        }
+
+        public void setBufferPosition(int bufferPosition) {
+            this.bufferPosition = bufferPosition;
+        }
+
+        public int getBufferPosition() {
+            return this.bufferPosition;
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/karaf/blob/1ee78df9/shell/command-api/src/main/java/org/apache/karaf/shell/console/completer/Parser.java
----------------------------------------------------------------------
diff --git a/shell/command-api/src/main/java/org/apache/karaf/shell/console/completer/Parser.java b/shell/command-api/src/main/java/org/apache/karaf/shell/console/completer/Parser.java
new file mode 100644
index 0000000..dcdab7d
--- /dev/null
+++ b/shell/command-api/src/main/java/org/apache/karaf/shell/console/completer/Parser.java
@@ -0,0 +1,396 @@
+/*
+ * 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.
+ */
+// DWB14: parser loops if // comment at start of program
+// DWB15: allow program to have trailing ';'
+package org.apache.karaf.shell.console.completer;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class Parser {
+
+    int current = 0;
+    String text;
+    boolean escaped;
+    static final String SPECIAL = "<;|{[\"'$`(=";
+
+    List<List<List<String>>> program;
+    List<List<String>> statements;
+    List<String> statement;
+    int cursor;
+    int start = -1;
+    int c0;
+    int c1;
+    int c2;
+    int c3;
+
+    public Parser(String text, int cursor) {
+        this.text = text;
+        this.cursor = cursor;
+    }
+
+    void ws() {
+        // derek: BUGFIX: loop if comment  at beginning of input
+        //while (!eof() && Character.isWhitespace(peek())) {
+        while (!eof() && (!escaped && Character.isWhitespace(peek()) || current == 0)) {
+            if (current != 0 || !escaped && Character.isWhitespace(peek())) {
+                current++;
+            }
+            if (peek() == '/' && current < text.length() - 2
+                && text.charAt(current + 1) == '/') {
+                comment();
+            }
+            if (current == 0) {
+                break;
+            }
+        }
+    }
+
+    private void comment() {
+        while (!eof() && peek() != '\n' && peek() != '\r') {
+            next();
+        }
+    }
+
+    boolean eof() {
+        return current >= text.length();
+    }
+
+    char peek() {
+        return peek(false);
+    }
+
+    char peek(boolean increment) {
+        escaped = false;
+        if (eof()) {
+            return 0;
+        }
+
+        int last = current;
+        char c = text.charAt(current++);
+
+        if (c == '\\') {
+            escaped = true;
+            if (eof()) {
+                throw new RuntimeException("Eof found after \\");
+            }
+
+            c = text.charAt(current++);
+
+            switch (c) {
+                case 't':
+                    c = '\t';
+                    break;
+                case '\r':
+                case '\n':
+                    c = ' ';
+                    break;
+                case 'b':
+                    c = '\b';
+                    break;
+                case 'f':
+                    c = '\f';
+                    break;
+                case 'n':
+                    c = '\n';
+                    break;
+                case 'r':
+                    c = '\r';
+                    break;
+                case 'u':
+                    c = unicode();
+                    current += 4;
+                    break;
+                default:
+                    // We just take the next character literally
+                    // but have the escaped flag set, important for {},[] etc
+            }
+        }
+        if (cursor > last && cursor <= current) {
+            c0 = program != null ? program.size() : 0;
+            c1 = statements != null ? statements.size() : 0;
+            c2 = statement != null ? statement.size() : 0;
+            c3 = (start >= 0) ? current - start : 0;
+        }
+        if (!increment) {
+            current = last;
+        }
+        return c;
+    }
+
+    public List<List<List<String>>> program() {
+        program = new ArrayList<List<List<String>>>();
+        ws();
+        if (!eof()) {
+            program.add(pipeline());
+            while (peek() == ';') {
+                current++;
+                List<List<String>> pipeline = pipeline();
+                program.add(pipeline);
+            }
+        }
+        if (!eof()) {
+            throw new RuntimeException("Program has trailing text: " + context(current));
+        }
+
+        List<List<List<String>>> p = program;
+        program = null;
+        return p;
+    }
+
+    CharSequence context(int around) {
+        return text.subSequence(Math.max(0, current - 20), Math.min(text.length(),
+            current + 4));
+    }
+
+    public List<List<String>> pipeline() {
+        statements = new ArrayList<List<String>>();
+        statements.add(statement());
+        while (peek() == '|') {
+            current++;
+            ws();
+            if (!eof()) {
+                statements.add(statement());
+            }
+            else {
+                statements.add(new ArrayList<String>());
+                break;
+            }
+        }
+        List<List<String>> s = statements;
+        statements = null;
+        return s;
+    }
+
+    public List<String> statement() {
+        statement = new ArrayList<String>();
+        statement.add(value());
+        while (!eof()) {
+            ws();
+            if (peek() == '|' || peek() == ';') {
+                break;
+            }
+
+            if (!eof()) {
+                statement.add(messy());
+            }
+        }
+        List<String> s = statement;
+        statement = null;
+        return s;
+    }
+
+    public String messy()
+    {
+        start = current;
+        char c = peek();
+        if (c > 0 && SPECIAL.indexOf(c) < 0) {
+            current++;
+            try {
+                while (!eof()) {
+                    c = peek();
+                    if (!escaped && (c == ';' || c == '|' || Character.isWhitespace(c))) {
+                        break;
+                    }
+                    next();
+                }
+                return text.substring(start, current);
+            } finally {
+                start = -1;
+            }
+        }
+        else {
+            return value();
+        }
+    }
+
+    String value() {
+        ws();
+
+        start = current;
+        try {
+            char c = next();
+            if (!escaped) {
+                switch (c) {
+                    case '{':
+                        return text.substring(start, find('}', '{'));
+                    case '(':
+                        return text.substring(start, find(')', '('));
+                    case '[':
+                        return text.substring(start, find(']', '['));
+                    case '<':
+                        return text.substring(start, find('>', '<'));
+                    case '=':
+                        return text.substring(start, current);
+                    case '"':
+                    case '\'':
+                        quote(c);
+                        break;
+                }
+            }
+
+            // Some identifier or number
+            while (!eof()) {
+                c = peek();
+                if (!escaped) {
+                    if (Character.isWhitespace(c) || c == ';' || c == '|' || c == '=') {
+                        break;
+                    }
+                    else if (c == '{') {
+                        next();
+                        find('}', '{');
+                    }
+                    else if (c == '(') {
+                        next();
+                        find(')', '(');
+                    }
+                    else if (c == '<') {
+                        next();
+                        find('>', '<');
+                    }
+                    else if (c == '[') {
+                        next();
+                        find(']', '[');
+                    }
+                    else if (c == '\'' || c == '"') {
+                        next();
+                        quote(c);
+                        next();
+                    }
+                    else {
+                        next();
+                    }
+                }
+                else {
+                    next();
+                }
+            }
+            return text.substring(start, current);
+        } finally {
+            start = -1;
+        }
+    }
+
+    boolean escaped() {
+        return escaped;
+    }
+
+    char next() {
+        return peek(true);
+    }
+
+    char unicode() {
+        if (current + 4 > text.length()) {
+            throw new IllegalArgumentException("Unicode \\u escape at eof at pos ..."
+                + context(current) + "...");
+        }
+
+        String s = text.subSequence(current, current + 4).toString();
+        int n = Integer.parseInt(s, 16);
+        return (char) n;
+    }
+
+    int find(char target, char deeper) {
+        int start = current;
+        int level = 1;
+
+        while (level != 0) {
+            if (eof()) {
+                throw new RuntimeException("Eof found in the middle of a compound for '"
+                    + target + deeper + "', begins at " + context(start));
+            }
+
+            char c = next();
+            if (!escaped) {
+                if (c == target) {
+                    level--;
+                } else {
+                    if (c == deeper) {
+                        level++;
+                    } else {
+                        if (c == '"') {
+                            quote('"');
+                        } else {
+                            if (c == '\'') {
+                                quote('\'');
+                            }
+                            else {
+                                if (c == '`') {
+                                    quote('`');
+                                }
+                            }
+                        }
+                    }
+                }
+            }
+        }
+        return current;
+    }
+
+    int quote(char which) {
+        while (!eof() && (peek() != which || escaped)) {
+            next();
+        }
+
+        return current++;
+    }
+
+    CharSequence findVar() {
+        int start = current;
+        char c = peek();
+
+        if (c == '{') {
+            next();
+            int end = find('}', '{');
+            return text.subSequence(start, end);
+        }
+        if (c == '(') {
+            next();
+            int end = find(')', '(');
+            return text.subSequence(start, end);
+        }
+
+        if (Character.isJavaIdentifierPart(c)) {
+            while (c == '$') {
+                c = next();
+            }
+            while (!eof() && (Character.isJavaIdentifierPart(c) || c == '.') && c != '$') {
+                next();
+                c = peek();
+            }
+            return text.subSequence(start, current);
+        }
+        throw new IllegalArgumentException(
+            "Reference to variable does not match syntax of a variable: "
+                + context(start));
+    }
+
+    public String toString() {
+        return "..." + context(current) + "...";
+    }
+
+    public String unescape() {
+        StringBuilder sb = new StringBuilder();
+        while (!eof()) {
+            sb.append(next());
+        }
+        return sb.toString();
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/karaf/blob/1ee78df9/shell/command-api/src/main/java/org/apache/karaf/shell/console/completer/StringsCompleter.java
----------------------------------------------------------------------
diff --git a/shell/command-api/src/main/java/org/apache/karaf/shell/console/completer/StringsCompleter.java b/shell/command-api/src/main/java/org/apache/karaf/shell/console/completer/StringsCompleter.java
new file mode 100644
index 0000000..a3f7c6d
--- /dev/null
+++ b/shell/command-api/src/main/java/org/apache/karaf/shell/console/completer/StringsCompleter.java
@@ -0,0 +1,103 @@
+/*
+ * 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.console.completer;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+import java.util.SortedSet;
+import java.util.TreeSet;
+
+import org.apache.karaf.shell.console.Completer;
+
+/**
+ * Completer for a set of strings.
+ */
+public class StringsCompleter
+    implements Completer
+{
+    private final SortedSet<String> strings = new TreeSet<String>();
+    private final boolean caseSensitive;
+
+    public StringsCompleter() {
+        this(true);
+    }
+
+
+    public StringsCompleter(final boolean caseSensitive) {
+        this.caseSensitive = caseSensitive;
+    }
+
+    public StringsCompleter(final Collection<String> strings) {
+        this();
+        assert strings != null;
+        getStrings().addAll(strings);
+    }
+
+    public StringsCompleter(final String[] strings, boolean caseSensitive) {
+        this(Arrays.asList(strings), caseSensitive);
+    }
+
+    public StringsCompleter(final Collection<String> strings, boolean caseSensitive) {
+        this(caseSensitive);
+        assert strings != null;
+        getStrings().addAll(strings);
+    }
+
+    public StringsCompleter(final String[] strings) {
+        this(Arrays.asList(strings));
+    }
+
+    public SortedSet<String> getStrings() {
+        return strings;
+    }
+
+    @SuppressWarnings({ "rawtypes", "unchecked" })
+    public int complete(String buffer, final int cursor, final List candidates) {
+        // buffer could be null
+        assert candidates != null;
+
+        if (buffer == null) {
+            buffer = "";
+        }
+        if (!caseSensitive) {
+            buffer = buffer.toLowerCase();
+        }
+
+        // KARAF-421, use getStrings() instead strings field.
+        SortedSet<String> matches = getStrings().tailSet(buffer);
+
+        for (String match : matches) {
+            String s = caseSensitive ? match : match.toLowerCase();
+            if (!s.startsWith(buffer)) {
+                break;
+            }
+
+            // noinspection unchecked
+            candidates.add(match);
+        }
+
+        if (candidates.size() == 1) {
+            // noinspection unchecked
+            candidates.set(0, candidates.get(0) + " ");
+        }
+
+        return candidates.isEmpty() ? -1 : 0;
+    }
+}

http://git-wip-us.apache.org/repos/asf/karaf/blob/1ee78df9/shell/command-api/src/main/java/org/apache/karaf/shell/inject/Destroy.java
----------------------------------------------------------------------
diff --git a/shell/command-api/src/main/java/org/apache/karaf/shell/inject/Destroy.java b/shell/command-api/src/main/java/org/apache/karaf/shell/inject/Destroy.java
new file mode 100644
index 0000000..4df9fe3
--- /dev/null
+++ b/shell/command-api/src/main/java/org/apache/karaf/shell/inject/Destroy.java
@@ -0,0 +1,29 @@
+/*
+ * 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.inject;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.METHOD})
+public @interface Destroy {
+}

http://git-wip-us.apache.org/repos/asf/karaf/blob/1ee78df9/shell/command-api/src/main/java/org/apache/karaf/shell/inject/Init.java
----------------------------------------------------------------------
diff --git a/shell/command-api/src/main/java/org/apache/karaf/shell/inject/Init.java b/shell/command-api/src/main/java/org/apache/karaf/shell/inject/Init.java
new file mode 100644
index 0000000..154b883
--- /dev/null
+++ b/shell/command-api/src/main/java/org/apache/karaf/shell/inject/Init.java
@@ -0,0 +1,29 @@
+/*
+ * 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.inject;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.METHOD})
+public @interface Init {
+}

http://git-wip-us.apache.org/repos/asf/karaf/blob/1ee78df9/shell/command-api/src/main/java/org/apache/karaf/shell/inject/Reference.java
----------------------------------------------------------------------
diff --git a/shell/command-api/src/main/java/org/apache/karaf/shell/inject/Reference.java b/shell/command-api/src/main/java/org/apache/karaf/shell/inject/Reference.java
new file mode 100644
index 0000000..b74d609
--- /dev/null
+++ b/shell/command-api/src/main/java/org/apache/karaf/shell/inject/Reference.java
@@ -0,0 +1,29 @@
+/*
+ * 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.inject;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.FIELD})
+public @interface Reference {
+}

http://git-wip-us.apache.org/repos/asf/karaf/blob/1ee78df9/shell/command-api/src/main/java/org/apache/karaf/shell/inject/Service.java
----------------------------------------------------------------------
diff --git a/shell/command-api/src/main/java/org/apache/karaf/shell/inject/Service.java b/shell/command-api/src/main/java/org/apache/karaf/shell/inject/Service.java
new file mode 100644
index 0000000..ff3f3a3
--- /dev/null
+++ b/shell/command-api/src/main/java/org/apache/karaf/shell/inject/Service.java
@@ -0,0 +1,29 @@
+/*
+ * 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.inject;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.TYPE})
+public @interface Service {
+}

http://git-wip-us.apache.org/repos/asf/karaf/blob/1ee78df9/shell/command-api/src/main/resources/OSGI-INF/blueprint/shell-namespacehandler.xml
----------------------------------------------------------------------
diff --git a/shell/command-api/src/main/resources/OSGI-INF/blueprint/shell-namespacehandler.xml b/shell/command-api/src/main/resources/OSGI-INF/blueprint/shell-namespacehandler.xml
new file mode 100644
index 0000000..b2b5566
--- /dev/null
+++ b/shell/command-api/src/main/resources/OSGI-INF/blueprint/shell-namespacehandler.xml
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+
+    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.
+
+-->
+<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0">
+
+    <bean id="namespaceHandler" class="org.apache.karaf.shell.console.commands.NamespaceHandler" />
+
+    <service ref="namespaceHandler" interface="org.apache.aries.blueprint.NamespaceHandler">
+        <service-properties>
+            <entry key="osgi.service.blueprint.namespace" value="http://karaf.apache.org/xmlns/shell/v1.0.0" />
+        </service-properties>
+    </service>
+
+    <service ref="namespaceHandler" interface="org.apache.aries.blueprint.NamespaceHandler">
+        <service-properties>
+            <entry key="osgi.service.blueprint.namespace" value="http://karaf.apache.org/xmlns/shell/v1.1.0" />
+        </service-properties>
+    </service>
+
+    <service ref="namespaceHandler" interface="org.apache.aries.blueprint.NamespaceHandler">
+        <service-properties>
+            <entry key="osgi.service.blueprint.namespace" value="http://karaf.apache.org/xmlns/shell/v1.2.0" />
+        </service-properties>
+    </service>
+
+</blueprint>

http://git-wip-us.apache.org/repos/asf/karaf/blob/1ee78df9/shell/command-api/src/main/resources/OSGI-INF/bundle.info
----------------------------------------------------------------------
diff --git a/shell/command-api/src/main/resources/OSGI-INF/bundle.info b/shell/command-api/src/main/resources/OSGI-INF/bundle.info
new file mode 100644
index 0000000..95214e0
--- /dev/null
+++ b/shell/command-api/src/main/resources/OSGI-INF/bundle.info
@@ -0,0 +1,14 @@
+h1. Synopsis
+
+${project.name}
+
+${project.description}
+
+Maven URL:
+[mvn:${project.groupId}/${project.artifactId}/${project.version}]
+
+h1. Description
+
+
+
+h1. See also

http://git-wip-us.apache.org/repos/asf/karaf/blob/1ee78df9/shell/command-api/src/main/resources/org/apache/karaf/shell/console/commands/karaf-shell-1.0.0.xsd
----------------------------------------------------------------------
diff --git a/shell/command-api/src/main/resources/org/apache/karaf/shell/console/commands/karaf-shell-1.0.0.xsd b/shell/command-api/src/main/resources/org/apache/karaf/shell/console/commands/karaf-shell-1.0.0.xsd
new file mode 100644
index 0000000..0e0dafd
--- /dev/null
+++ b/shell/command-api/src/main/resources/org/apache/karaf/shell/console/commands/karaf-shell-1.0.0.xsd
@@ -0,0 +1,217 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+    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.
+-->
+
+<!-- $Rev: 699828 $ $Date: 2008-09-28 16:35:27 +0200 (Sun, 28 Sep 2008) $ -->
+
+<xsd:schema xmlns="http://karaf.apache.org/xmlns/shell/v1.0.0"
+        xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+        xmlns:bp="http://www.osgi.org/xmlns/blueprint/v1.0.0"
+        targetNamespace="http://karaf.apache.org/xmlns/shell/v1.0.0"
+        elementFormDefault="qualified"
+        attributeFormDefault="unqualified">
+
+    <xsd:import namespace="http://www.osgi.org/xmlns/blueprint/v1.0.0"/>
+
+    <xsd:annotation>
+        <xsd:documentation>
+            Defines the configuration elements for Apache Karaf commands support.
+        </xsd:documentation>
+    </xsd:annotation>
+
+    <xsd:element name="command-bundle">
+        <xsd:complexType>
+            <xsd:annotation>
+                <xsd:documentation>
+                    Defines a command bundle.
+                </xsd:documentation>
+            </xsd:annotation>
+            <xsd:sequence>
+                <xsd:choice minOccurs="0" maxOccurs="unbounded">
+					<xsd:element ref="command"/>
+					<xsd:element ref="alias"/>
+                    <xsd:element ref="link"/>
+				</xsd:choice>
+            </xsd:sequence>
+        </xsd:complexType>
+    </xsd:element>
+
+    <xsd:element name="command">
+        <xsd:complexType>
+            <xsd:annotation>
+                <xsd:documentation>
+                    Defines a command.
+                </xsd:documentation>
+            </xsd:annotation>
+            <xsd:sequence>
+                <!--
+                NOTE: Not using an xsd:choice here, as I can't seem to figure out how to get it to properly
+                      validate the min/max of the containted elements.  W/o the xsd:choice the validation
+                      works, though have to define elements in order :-(
+                -->
+                <xsd:element ref="action" minOccurs="1" maxOccurs="1"/>
+                <xsd:element ref="documenter" minOccurs="0" maxOccurs="1"/>
+                <xsd:choice minOccurs="0" maxOccurs="1">
+                    <xsd:element ref="completer"/>
+                    <xsd:element ref="completers"/>
+                </xsd:choice>
+                <xsd:element ref="message-source" minOccurs="0" maxOccurs="1"/>
+            </xsd:sequence>
+            <xsd:attribute name="name" type="xsd:string" use="required"/>
+        </xsd:complexType>
+    </xsd:element>
+
+    <xsd:element name="link">
+        <xsd:complexType>
+            <xsd:annotation>
+                <xsd:documentation>
+                    Defines a link command.
+                </xsd:documentation>
+            </xsd:annotation>
+            <xsd:attribute name="name" type="xsd:string" use="required"/>
+            <xsd:attribute name="target" type="xsd:string" use="required"/>
+        </xsd:complexType>
+    </xsd:element>
+
+    <xsd:group name="commandComponentElements">
+        <xsd:annotation>
+            <xsd:documentation>
+                Defines the valid elements for command components.  This is based on beans:beanElements,
+                stripping off the bits which are not valid in the command component context.
+            </xsd:documentation>
+        </xsd:annotation>
+		<xsd:sequence>
+			<xsd:choice minOccurs="0" maxOccurs="unbounded">
+                <xsd:element name="argument" type="bp:Targument"/>
+                <xsd:element name="property" type="bp:Tproperty"/>
+				<!--
+				NOTE: This seems to cause schema validation problems... not really sure why
+				<xsd:any namespace="##other" processContents="strict" minOccurs="0" maxOccurs="unbounded"/>
+				-->
+			</xsd:choice>
+		</xsd:sequence>
+	</xsd:group>
+
+    <xsd:attributeGroup name="commandComponentAttributes">
+        <xsd:annotation>
+            <xsd:documentation>
+                Defines the valid attributes for command components.  This is based on beans:beanAttributes,
+                stripping off the bits which are not valid in the command component context.
+            </xsd:documentation>
+        </xsd:annotation>
+		<xsd:attribute name="class" type="xsd:string"/>
+		<xsd:attribute name="depends-on" type="xsd:string"/>
+		<xsd:attribute name="init-method" type="xsd:string"/>
+		<xsd:attribute name="factory-method" type="xsd:string"/>
+		<xsd:attribute name="factory-bean" type="xsd:string"/>
+		<xsd:anyAttribute namespace="##other" processContents="lax"/>
+	</xsd:attributeGroup>
+
+    <xsd:complexType name="commandComponent" abstract="true">
+        <xsd:annotation>
+            <xsd:documentation>
+                Support for command component elements, which are all basically just beans.
+            </xsd:documentation>
+        </xsd:annotation>
+        <xsd:group ref="commandComponentElements"/>
+        <xsd:attributeGroup ref="commandComponentAttributes"/>
+    </xsd:complexType>
+
+    <xsd:element name="action">
+        <xsd:complexType>
+            <xsd:annotation>
+                <xsd:documentation>
+                    Defines a command action.
+                </xsd:documentation>
+            </xsd:annotation>
+            <xsd:complexContent>
+                <xsd:extension base="commandComponent"/>
+            </xsd:complexContent>
+        </xsd:complexType>
+    </xsd:element>
+
+    <xsd:element name="documenter">
+        <xsd:complexType>
+            <xsd:annotation>
+                <xsd:documentation>
+                    Defines a command documenter.
+                </xsd:documentation>
+            </xsd:annotation>
+            <xsd:complexContent>
+                <xsd:extension base="commandComponent"/>
+            </xsd:complexContent>
+        </xsd:complexType>
+    </xsd:element>
+
+    <xsd:element name="completer">
+        <xsd:complexType>
+            <xsd:annotation>
+                <xsd:documentation>
+                    Defines a command completer.
+                </xsd:documentation>
+            </xsd:annotation>
+            <xsd:complexContent>
+                <xsd:extension base="commandComponent"/>
+            </xsd:complexContent>
+        </xsd:complexType>
+    </xsd:element>
+
+    <xsd:element name="completers">
+        <xsd:complexType>
+            <xsd:annotation>
+                <xsd:documentation>
+                    Defines a configurable command completer with a set of completers.
+                </xsd:documentation>
+            </xsd:annotation>
+            <xsd:sequence>
+                <xsd:choice minOccurs="1" maxOccurs="unbounded">
+                    <xsd:element name="bean" type="bp:Tbean"/>
+                    <xsd:element name="ref" type="bp:Tref"/>
+                    <xsd:element name="null" type="bp:Tnull"/>
+                </xsd:choice>
+            </xsd:sequence>
+        </xsd:complexType>
+    </xsd:element>
+
+    <xsd:element name="message-source">
+        <xsd:complexType>
+            <xsd:annotation>
+                <xsd:documentation>
+                    Defines a command message source.
+                </xsd:documentation>
+            </xsd:annotation>
+            <xsd:complexContent>
+                <xsd:extension base="commandComponent"/>
+            </xsd:complexContent>
+        </xsd:complexType>
+    </xsd:element>
+
+    <xsd:element name="alias">
+        <xsd:complexType>
+            <xsd:annotation>
+                <xsd:documentation>
+                    Defines a command alias.
+                </xsd:documentation>
+            </xsd:annotation>
+            <xsd:attribute name="name" type="xsd:string" use="required"/>
+            <xsd:attribute name="alias" type="xsd:string" use="required"/>
+        </xsd:complexType>
+    </xsd:element>
+
+</xsd:schema>

http://git-wip-us.apache.org/repos/asf/karaf/blob/1ee78df9/shell/command-api/src/main/resources/org/apache/karaf/shell/console/commands/karaf-shell-1.1.0.xsd
----------------------------------------------------------------------
diff --git a/shell/command-api/src/main/resources/org/apache/karaf/shell/console/commands/karaf-shell-1.1.0.xsd b/shell/command-api/src/main/resources/org/apache/karaf/shell/console/commands/karaf-shell-1.1.0.xsd
new file mode 100644
index 0000000..929cbd2
--- /dev/null
+++ b/shell/command-api/src/main/resources/org/apache/karaf/shell/console/commands/karaf-shell-1.1.0.xsd
@@ -0,0 +1,209 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+    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.
+-->
+
+<!-- $Rev: 699828 $ $Date: 2008-09-28 16:35:27 +0200 (Sun, 28 Sep 2008) $ -->
+
+<xsd:schema xmlns="http://karaf.apache.org/xmlns/shell/v1.1.0"
+        xmlns:xsd="http://www.w3.org/2001/XMLSchema"
+        xmlns:bp="http://www.osgi.org/xmlns/blueprint/v1.0.0"
+        targetNamespace="http://karaf.apache.org/xmlns/shell/v1.1.0"
+        elementFormDefault="qualified"
+        attributeFormDefault="unqualified">
+
+    <xsd:import namespace="http://www.osgi.org/xmlns/blueprint/v1.0.0"/>
+
+    <xsd:annotation>
+        <xsd:documentation>
+            Defines the configuration elements for Apache Karaf commands support.
+        </xsd:documentation>
+    </xsd:annotation>
+
+    <xsd:element name="command-bundle">
+        <xsd:complexType>
+            <xsd:annotation>
+                <xsd:documentation>
+                    Defines a command bundle.
+                </xsd:documentation>
+            </xsd:annotation>
+            <xsd:sequence>
+                <xsd:element ref="command" minOccurs="0" maxOccurs="unbounded"/>
+            </xsd:sequence>
+        </xsd:complexType>
+    </xsd:element>
+
+    <xsd:element name="command">
+        <xsd:complexType>
+            <xsd:annotation>
+                <xsd:documentation>
+                    Defines a command.
+                </xsd:documentation>
+            </xsd:annotation>
+            <xsd:sequence>
+                <!--
+                NOTE: Not using an xsd:choice here, as I can't seem to figure out how to get it to properly
+                      validate the min/max of the containted elements.  W/o the xsd:choice the validation
+                      works, though have to define elements in order :-(
+                -->
+                <xsd:element ref="action" minOccurs="1" maxOccurs="1"/>
+                <xsd:element ref="documenter" minOccurs="0" maxOccurs="1"/>
+                <xsd:choice minOccurs="0" maxOccurs="1">
+                    <xsd:element ref="completer"/>
+                    <xsd:element ref="completers"/>
+                </xsd:choice>
+                <xsd:element ref="optional-completers" minOccurs="0" maxOccurs="1"/>
+                <xsd:element ref="message-source" minOccurs="0" maxOccurs="1"/>
+            </xsd:sequence>
+        </xsd:complexType>
+    </xsd:element>
+
+    <xsd:group name="commandComponentElements">
+        <xsd:annotation>
+            <xsd:documentation>
+                Defines the valid elements for command components.  This is based on beans:beanElements,
+                stripping off the bits which are not valid in the command component context.
+            </xsd:documentation>
+        </xsd:annotation>
+		<xsd:sequence>
+			<xsd:choice minOccurs="0" maxOccurs="unbounded">
+                <xsd:element name="argument" type="bp:Targument"/>
+                <xsd:element name="property" type="bp:Tproperty"/>
+				<!--
+				NOTE: This seems to cause schema validation problems... not really sure why
+				<xsd:any namespace="##other" processContents="strict" minOccurs="0" maxOccurs="unbounded"/>
+				-->
+			</xsd:choice>
+		</xsd:sequence>
+	</xsd:group>
+
+    <xsd:attributeGroup name="commandComponentAttributes">
+        <xsd:annotation>
+            <xsd:documentation>
+                Defines the valid attributes for command components.  This is based on beans:beanAttributes,
+                stripping off the bits which are not valid in the command component context.
+            </xsd:documentation>
+        </xsd:annotation>
+		<xsd:attribute name="class" type="xsd:string"/>
+		<xsd:attribute name="depends-on" type="xsd:string"/>
+		<xsd:attribute name="init-method" type="xsd:string"/>
+		<xsd:attribute name="factory-method" type="xsd:string"/>
+		<xsd:attribute name="factory-bean" type="xsd:string"/>
+		<xsd:anyAttribute namespace="##other" processContents="lax"/>
+	</xsd:attributeGroup>
+
+    <xsd:complexType name="commandComponent" abstract="true">
+        <xsd:annotation>
+            <xsd:documentation>
+                Support for command component elements, which are all basically just beans.
+            </xsd:documentation>
+        </xsd:annotation>
+        <xsd:group ref="commandComponentElements"/>
+        <xsd:attributeGroup ref="commandComponentAttributes"/>
+    </xsd:complexType>
+
+    <xsd:element name="action">
+        <xsd:complexType>
+            <xsd:annotation>
+                <xsd:documentation>
+                    Defines a command action.
+                </xsd:documentation>
+            </xsd:annotation>
+            <xsd:complexContent>
+                <xsd:extension base="commandComponent"/>
+            </xsd:complexContent>
+        </xsd:complexType>
+    </xsd:element>
+
+    <xsd:element name="documenter">
+        <xsd:complexType>
+            <xsd:annotation>
+                <xsd:documentation>
+                    Defines a command documenter.
+                </xsd:documentation>
+            </xsd:annotation>
+            <xsd:complexContent>
+                <xsd:extension base="commandComponent"/>
+            </xsd:complexContent>
+        </xsd:complexType>
+    </xsd:element>
+
+    <xsd:element name="completer">
+        <xsd:complexType>
+            <xsd:annotation>
+                <xsd:documentation>
+                    Defines a command completer.
+                </xsd:documentation>
+            </xsd:annotation>
+            <xsd:complexContent>
+                <xsd:extension base="commandComponent"/>
+            </xsd:complexContent>
+        </xsd:complexType>
+    </xsd:element>
+
+    <xsd:element name="completers">
+        <xsd:complexType>
+            <xsd:annotation>
+                <xsd:documentation>
+                    Defines a configurable command completer with a set of completers.
+                </xsd:documentation>
+            </xsd:annotation>
+            <xsd:sequence>
+                <xsd:choice minOccurs="1" maxOccurs="unbounded">
+                    <xsd:element name="bean" type="bp:Tbean"/>
+                    <xsd:element name="ref" type="bp:Tref"/>
+                    <xsd:element name="null" type="bp:Tnull"/>
+                </xsd:choice>
+            </xsd:sequence>
+        </xsd:complexType>
+    </xsd:element>
+
+        <xsd:element name="optional-completers">
+        <xsd:complexType>
+            <xsd:annotation>
+                <xsd:documentation>
+                    Defines a configurable command completer with a set of completers.
+                    These completers are used for option value completion.
+                </xsd:documentation>
+            </xsd:annotation>
+            <xsd:complexContent>
+			<xsd:extension base="bp:TtypedCollection">
+				<xsd:sequence>
+					<xsd:element name="entry" type="bp:TmapEntry" minOccurs="0"
+						maxOccurs="unbounded" />
+				</xsd:sequence>
+				<xsd:attribute name="key-type" type="bp:Ttype" />
+			</xsd:extension>
+		</xsd:complexContent>
+        </xsd:complexType>
+    </xsd:element>
+
+    <xsd:element name="message-source">
+        <xsd:complexType>
+            <xsd:annotation>
+                <xsd:documentation>
+                    Defines a command message source.
+                </xsd:documentation>
+            </xsd:annotation>
+            <xsd:complexContent>
+                <xsd:extension base="commandComponent"/>
+            </xsd:complexContent>
+        </xsd:complexType>
+    </xsd:element>
+
+</xsd:schema>