Add commands, fix parser, remove DS dependency, remove equinox support

git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@790958 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/gogo/gogo.commands/pom.xml b/gogo/gogo.commands/pom.xml
new file mode 100644
index 0000000..9b78ac6
--- /dev/null
+++ b/gogo/gogo.commands/pom.xml
@@ -0,0 +1,73 @@
+<!--
+ 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.
+-->
+<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/maven-v4_0_0.xsd">
+    <parent>
+        <groupId>org.apache.felix.gogo</groupId>
+        <artifactId>gogo</artifactId>
+        <version>1.0.0-SNAPSHOT</version>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+    <packaging>bundle</packaging>
+    <name>Apache Felix Gogo Shell Commands</name>
+    <artifactId>org.apache.felix.gogo.commands</artifactId>
+    <version>1.0.0-SNAPSHOT</version>
+    <dependencies>
+        <dependency>
+            <groupId>org.apache.felix</groupId>
+            <artifactId>org.osgi.core</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.felix</groupId>
+            <artifactId>org.osgi.compendium</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.felix.gogo</groupId>
+            <artifactId>org.apache.felix.gogo.runtime</artifactId>
+        </dependency>
+    </dependencies>
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.felix</groupId>
+                <artifactId>maven-bundle-plugin</artifactId>
+                <version>2.0.0</version>
+                <extensions>true</extensions>
+                <configuration>
+                    <instructions>
+                        <Export-Package>
+                            org.apache.felix.gogo.commands*;version=${pom.version},
+                        </Export-Package>
+                        <Import-Package>
+                            !org.apache.felix.gogo.commands.basic,
+                            !org.apache.felix.gogo.commands.converter,
+                            *
+                        </Import-Package>
+                        <Bundle-SymbolicName>${pom.artifactId}</Bundle-SymbolicName>
+                        <Bundle-Vendor>The Apache Software Foundation</Bundle-Vendor>
+                        <_versionpolicy>[$(version;==;$(@)),$(version;+;$(@)))</_versionpolicy>
+                        <_removeheaders>Private-Package,Ignore-Package</_removeheaders>
+                    </instructions>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+</project>
diff --git a/gogo/gogo.commands/src/main/java/org/apache/felix/gogo/commands/Action.java b/gogo/gogo.commands/src/main/java/org/apache/felix/gogo/commands/Action.java
new file mode 100644
index 0000000..7979a41
--- /dev/null
+++ b/gogo/gogo.commands/src/main/java/org/apache/felix/gogo/commands/Action.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.felix.gogo.commands;
+
+import org.osgi.service.command.CommandSession;
+
+public interface Action
+{
+
+    Object execute(CommandSession session) throws Exception;
+
+}
diff --git a/gogo/gogo.commands/src/main/java/org/apache/felix/gogo/commands/Argument.java b/gogo/gogo.commands/src/main/java/org/apache/felix/gogo/commands/Argument.java
new file mode 100644
index 0000000..e1b8f7f
--- /dev/null
+++ b/gogo/gogo.commands/src/main/java/org/apache/felix/gogo/commands/Argument.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.felix.gogo.commands;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import java.lang.annotation.ElementType;
+
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.FIELD})
+public @interface Argument
+{
+    String name() default "VAL";
+
+    String description() default "";
+
+    boolean required() default false;
+
+    int index() default 0;
+
+    boolean multiValued() default false;
+}
diff --git a/gogo/gogo.commands/src/main/java/org/apache/felix/gogo/commands/Command.java b/gogo/gogo.commands/src/main/java/org/apache/felix/gogo/commands/Command.java
new file mode 100644
index 0000000..9f94e1f
--- /dev/null
+++ b/gogo/gogo.commands/src/main/java/org/apache/felix/gogo/commands/Command.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.felix.gogo.commands;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import java.lang.annotation.ElementType;
+
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.TYPE})
+public @interface Command
+{
+    String scope();
+
+    String name();
+
+    String description() default "";
+}
diff --git a/gogo/gogo.commands/src/main/java/org/apache/felix/gogo/commands/Option.java b/gogo/gogo.commands/src/main/java/org/apache/felix/gogo/commands/Option.java
new file mode 100644
index 0000000..0197ff5
--- /dev/null
+++ b/gogo/gogo.commands/src/main/java/org/apache/felix/gogo/commands/Option.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.felix.gogo.commands;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import java.lang.annotation.ElementType;
+
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.FIELD})
+public @interface Option
+{
+    String name();
+
+    String[] aliases() default {};
+
+    String description() default "";
+
+    boolean required() default false;
+
+    boolean multiValued() default false;
+
+}
diff --git a/gogo/gogo.commands/src/main/java/org/apache/felix/gogo/commands/basic/AbstractCommand.java b/gogo/gogo.commands/src/main/java/org/apache/felix/gogo/commands/basic/AbstractCommand.java
new file mode 100644
index 0000000..39c7cf3
--- /dev/null
+++ b/gogo/gogo.commands/src/main/java/org/apache/felix/gogo/commands/basic/AbstractCommand.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.felix.gogo.commands.basic;
+
+import java.util.List;
+
+import org.apache.felix.gogo.commands.basic.DefaultActionPreparator;
+import org.apache.felix.gogo.commands.basic.ActionPreparator;
+import org.apache.felix.gogo.commands.Action;
+import org.osgi.service.command.CommandSession;
+import org.osgi.service.command.Function;
+
+public abstract class AbstractCommand implements Function {
+
+    public Object execute(CommandSession session, List<Object> arguments) throws Exception {
+        Action action = createNewAction();
+        getPreparator().prepare(action, session, arguments);
+        return action.execute(session);
+    }
+
+    protected abstract Action createNewAction() throws Exception;
+
+    protected ActionPreparator getPreparator() throws Exception {
+        return new DefaultActionPreparator();
+    }
+
+}
diff --git a/gogo/gogo.commands/src/main/java/org/apache/felix/gogo/commands/basic/ActionPreparator.java b/gogo/gogo.commands/src/main/java/org/apache/felix/gogo/commands/basic/ActionPreparator.java
new file mode 100644
index 0000000..b10303a
--- /dev/null
+++ b/gogo/gogo.commands/src/main/java/org/apache/felix/gogo/commands/basic/ActionPreparator.java
@@ -0,0 +1,12 @@
+package org.apache.felix.gogo.commands.basic;
+
+import java.util.List;
+
+import org.osgi.service.command.CommandSession;
+import org.apache.felix.gogo.commands.Action;
+
+public interface ActionPreparator {
+
+    void prepare(Action action, CommandSession session, List<Object> arguments) throws Exception;
+
+}
diff --git a/gogo/gogo.commands/src/main/java/org/apache/felix/gogo/commands/basic/DefaultActionPreparator.java b/gogo/gogo.commands/src/main/java/org/apache/felix/gogo/commands/basic/DefaultActionPreparator.java
new file mode 100644
index 0000000..a7d650c
--- /dev/null
+++ b/gogo/gogo.commands/src/main/java/org/apache/felix/gogo/commands/basic/DefaultActionPreparator.java
@@ -0,0 +1,269 @@
+/**
+ *
+ * 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.lang.annotation.Annotation;
+import java.lang.reflect.Field;
+import java.lang.reflect.Type;
+import java.util.List;
+import java.util.Map;
+import java.util.HashMap;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.Arrays;
+import java.util.Set;
+import java.util.HashSet;
+import java.io.PrintStream;
+
+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.basic.ActionPreparator;
+import org.apache.felix.gogo.commands.converter.DefaultConverter;
+import org.osgi.service.command.CommandSession;
+
+public class DefaultActionPreparator implements ActionPreparator {
+
+    protected 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 Class<? extends Annotation> annotationType()
+        {
+            return Option.class;
+        }
+    };
+
+    public void 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) {
+                    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(action.getClass().getAnnotation(Command.class), options.keySet(), arguments.keySet(), System.out);
+                return;
+            }
+            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.binarySearch(opt.aliases(), name) >= 0) {
+                        option = opt;
+                        break;
+                    }
+                }
+                if (option == null) {
+                    throw new IllegalArgumentException("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 IllegalArgumentException("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()) {
+                    throw new IllegalArgumentException("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) {
+                throw new IllegalArgumentException("Option " + option.name() + " is required");
+            }
+        }
+        for (Argument argument : arguments.keySet()) {
+            if (argument.required() && argumentValues.get(argument) == null) {
+                throw new IllegalArgumentException("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 = convert(action, session, entry.getValue(), field.getGenericType());
+            field.setAccessible(true);
+            field.set(action, value);
+        }
+        for (Map.Entry<Argument, Object> entry : argumentValues.entrySet()) {
+            Field field = arguments.get(entry.getKey());
+            Object value = convert(action, session, entry.getValue(), field.getGenericType());
+            field.setAccessible(true);
+            field.set(action, value);
+        }
+    }
+
+    protected void printUsage(Command command, Set<Option> options, Set<Argument> arguments, PrintStream out)
+    {
+        options = new HashSet<Option>(options);
+        options.add(HELP);
+        if (command != null && command.description() != null && command.description().length() > 0)
+        {
+            out.println(command.description());
+            out.println();
+        }
+        String syntax = "syntax: ";
+        if (command != null)
+        {
+            syntax += command.scope() + ":" + command.name();
+        }
+        if (options.size() > 0)
+        {
+            syntax += " [options]";
+        }
+        if (arguments.size() > 0)
+        {
+            syntax += " [arguments]";
+        }
+        out.println(syntax);
+        out.println();
+        if (arguments.size() > 0)
+        {
+            out.println("arguments:");
+            for (Argument argument : arguments)
+            {
+                out.print("  ");
+                out.print(argument.name());
+                out.print("  ");
+                out.print(argument.description());
+                out.println();
+            }
+            out.println();
+        }
+        if (options.size() > 0)
+        {
+            out.println("options:");
+            for (Option option : options)
+            {
+                out.print("  ");
+                out.print(option.name());
+                out.print("  ");
+                if (option.aliases().length > 0)
+                {
+                    out.print("(");
+                    out.print(Arrays.toString(option.aliases()));
+                    out.print(")  ");
+                }
+                out.print(option.description());
+                out.println();
+            }
+            out.println();
+        }
+    }
+
+    protected Object convert(Action action, CommandSession session, Object value, Type toType) throws Exception
+    {
+        return new DefaultConverter(action.getClass().getClassLoader()).convert(value, toType);
+    }
+
+}
diff --git a/gogo/gogo.commands/src/main/java/org/apache/felix/gogo/commands/basic/SimpleCommand.java b/gogo/gogo.commands/src/main/java/org/apache/felix/gogo/commands/basic/SimpleCommand.java
new file mode 100644
index 0000000..8d7ba46
--- /dev/null
+++ b/gogo/gogo.commands/src/main/java/org/apache/felix/gogo/commands/basic/SimpleCommand.java
@@ -0,0 +1,71 @@
+/**
+ *
+ * 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.Hashtable;
+
+import org.osgi.framework.ServiceRegistration;
+import org.osgi.framework.BundleContext;
+import org.osgi.service.command.Function;
+import org.apache.felix.gogo.commands.basic.AbstractCommand;
+import org.apache.felix.gogo.commands.Action;
+import org.apache.felix.gogo.commands.Command;
+
+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;
+    }
+
+    protected Action createNewAction() throws Exception {
+        return actionClass.newInstance();
+    }
+
+
+    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 props = new Hashtable();
+        props.put("osgi.command.scope", cmd.scope());
+        props.put("osgi.command.function", cmd.name());
+        SimpleCommand command = new SimpleCommand(actionClass);
+        return context.registerService(Function.class.getName(), command, props);
+    }
+
+}
diff --git a/gogo/gogo.commands/src/main/java/org/apache/felix/gogo/commands/converter/DefaultConverter.java b/gogo/gogo.commands/src/main/java/org/apache/felix/gogo/commands/converter/DefaultConverter.java
new file mode 100644
index 0000000..277f22e
--- /dev/null
+++ b/gogo/gogo.commands/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;
+
+import org.apache.felix.gogo.commands.converter.ReifiedType;
+
+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();
+    }
+
+}
diff --git a/gogo/gogo.commands/src/main/java/org/apache/felix/gogo/commands/converter/GenericType.java b/gogo/gogo.commands/src/main/java/org/apache/felix/gogo/commands/converter/GenericType.java
new file mode 100644
index 0000000..bb9d192
--- /dev/null
+++ b/gogo/gogo.commands/src/main/java/org/apache/felix/gogo/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.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;
+import org.apache.felix.gogo.commands.converter.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);
+	}
+
+}
diff --git a/gogo/gogo.commands/src/main/java/org/apache/felix/gogo/commands/converter/ReifiedType.java b/gogo/gogo.commands/src/main/java/org/apache/felix/gogo/commands/converter/ReifiedType.java
new file mode 100644
index 0000000..239616e
--- /dev/null
+++ b/gogo/gogo.commands/src/main/java/org/apache/felix/gogo/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.felix.gogo.commands.converter;
+
+/**
+ * Provides access to a concrete type and its optional generic type arguments.
+ *
+ * Java 5 and later support generic types. These types consist of a raw class
+ * with type arguments. This class models such a <code>Type</code> class but
+ * ensures that the type is <em>reified</em>. Reification means that the Type
+ * graph associated with a Java 5 <code>Type</code> instance is traversed
+ * until the type becomes a concrete class. In Java 1.4 a class has no
+ * arguments. This concrete class implements the Reified Type for Java 1.4.
+ *
+ * In Java 1.4, this class works with non-generic types. In that cases, a
+ * Reified Type provides access to the class and has zero type arguments, though
+ * a subclass that provide type arguments should be respected. Blueprint
+ * extender implementations can subclass this class and provide access to the
+ * generics type graph if used in a conversion. Such a subclass must
+ * <em>reify<em> the different Java 5 <code>Type</code> instances into the
+ * reified form. That is, a form where the raw Class is available with its optional type arguments as Reified Types.
+ *
+ * @Immutable
+ */
+public class ReifiedType {
+	final static ReifiedType ALL = new ReifiedType(Object.class);
+
+	private final Class clazz;
+
+	/**
+	 * Create a Reified Type for a raw Java class without any generic arguments.
+	 * Subclasses can provide the optional generic argument information. Without
+	 * subclassing, this instance has no type arguments.
+	 *
+	 * @param clazz
+	 *            The raw class of the Reified Type.
+	 */
+	public ReifiedType(Class clazz) {
+		this.clazz = clazz;
+	}
+
+	/**
+	 * Access to the raw class.
+	 *
+	 * The raw class represents the concrete class that is associated with a
+	 * type declaration. This class could have been deduced from the generics
+	 * type graph of the declaration. For example, in the following example:
+	 *
+	 * <pre>
+	 * Map&lt;String, Object&gt; map;
+	 * </pre>
+	 *
+	 * The raw class is the Map class.
+	 *
+	 * @return the collapsed raw class that represents this type.
+	 */
+	public Class getRawClass() {
+		return clazz;
+	}
+
+	/**
+	 * Access to a type argument.
+	 *
+	 * The type argument refers to a argument in a generic type declaration
+	 * given by index <code>i</code>. This method returns a Reified Type that
+	 * has Object as class when no generic type information is available. Any
+	 * object is assignable to Object and therefore no conversion is then
+	 * necessary, this is compatible with older Javas than 5. For this reason,
+	 * the implementation in this class always returns the
+	 * <code>Object<code> class, regardless of the given index.
+	 *
+	 * This method should be overridden by a subclass that provides access to
+	 * the generic information.
+	 *
+	 * For example, in the following example:
+	 *
+	 * <pre>
+	 * Map&lt;String, Object&gt; map;
+	 * </pre>
+	 *
+	 * The type argument 0 is <code>String</code>, and type argument 1 is
+	 * <code>Object</code>.
+	 *
+	 * @param i
+	 *            The index of the type argument
+	 * @return <code>ReifiedType(Object.class)<code>, subclasses must override this and return the generic argument at index <code>i</code>
+	 */
+	public ReifiedType getActualTypeArgument(int i) {
+		return ALL;
+	}
+
+	/**
+	 * Return the number of type arguments.
+	 *
+	 * This method should be overridden by a subclass to support Java 5 types.
+	 *
+	 * @return 0, subclasses must override this and return the number of generic
+	 *         arguments
+	 */
+	public int size() {
+		return 0;
+	}
+}
diff --git a/gogo/gogo.commands/src/test/java/org/apache/felix/gogo/commands/TestCommands.java b/gogo/gogo.commands/src/test/java/org/apache/felix/gogo/commands/TestCommands.java
new file mode 100644
index 0000000..1b3fc22
--- /dev/null
+++ b/gogo/gogo.commands/src/test/java/org/apache/felix/gogo/commands/TestCommands.java
@@ -0,0 +1,87 @@
+/*
+ * 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.util.List;
+import java.util.Collections;
+import java.util.Arrays;
+
+import junit.framework.TestCase;
+import org.osgi.service.command.CommandSession;
+import org.apache.felix.gogo.runtime.shell.Context;
+import org.apache.felix.gogo.commands.basic.SimpleCommand;
+
+public class TestCommands extends TestCase {
+
+
+    public void testCommand() throws Exception {
+        Context c= new Context();
+        c.addCommand("my-action", new SimpleCommand(MyAction.class));
+
+        // Test help
+        c.execute("my-action --help");
+
+        // Test required argument
+        try
+        {
+            c.execute("my-action");
+            fail("Action should have thrown an exception because of a missing argument");
+        }
+        catch (IllegalArgumentException e)
+        {
+        }
+
+        // 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"));
+    }
+
+    @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;
+        }
+    }
+}
diff --git a/gogo/gogo.console/src/main/java/org/apache/felix/gogo/console/stdio/StdioConsole.java b/gogo/gogo.console/src/main/java/org/apache/felix/gogo/console/stdio/StdioConsole.java
index 15c1ed1..e83c1b4 100644
--- a/gogo/gogo.console/src/main/java/org/apache/felix/gogo/console/stdio/StdioConsole.java
+++ b/gogo/gogo.console/src/main/java/org/apache/felix/gogo/console/stdio/StdioConsole.java
@@ -19,7 +19,6 @@
 package org.apache.felix.gogo.console.stdio;
 
 import org.osgi.service.command.CommandProcessor;
-import org.osgi.service.component.ComponentContext;
 
 public class StdioConsole extends Thread
 {
diff --git a/gogo/gogo.runtime/pom.xml b/gogo/gogo.runtime/pom.xml
index 22c6dfb..b2f0362 100644
--- a/gogo/gogo.runtime/pom.xml
+++ b/gogo/gogo.runtime/pom.xml
@@ -59,7 +59,6 @@
                             org.osgi.service.threadio; version=1.0.0
                         </Export-Package>
                         <Import-Package>
-                            org.osgi.service.component*; resolution:=optional,
                             org.osgi.service.log*; resolution:=optional,
                             org.osgi.service.packageadmin*; resolution:=optional,
                             org.osgi.service.startlevel*; resolution:=optional,
@@ -69,6 +68,7 @@
                         <Bundle-SymbolicName>${pom.artifactId}</Bundle-SymbolicName>
                         <Bundle-Vendor>The Apache Software Foundation</Bundle-Vendor>
                         <Bundle-Activator>org.apache.felix.gogo.runtime.Activator</Bundle-Activator>
+                        <_versionpolicy>[$(version;==;$(@)),$(version;+;$(@)))</_versionpolicy>
                     </instructions>
                 </configuration>
             </plugin>
diff --git a/gogo/gogo.runtime/src/main/java/org/apache/felix/gogo/runtime/equinox/Equinox.java b/gogo/gogo.runtime/src/main/java/org/apache/felix/gogo/runtime/equinox/Equinox.java
deleted file mode 100644
index 8187496..0000000
--- a/gogo/gogo.runtime/src/main/java/org/apache/felix/gogo/runtime/equinox/Equinox.java
+++ /dev/null
@@ -1,476 +0,0 @@
-package org.apache.felix.gogo.runtime.equinox;
-
-import org.osgi.framework.Bundle;
-import org.osgi.framework.BundleContext;
-import org.osgi.framework.BundleException;
-import org.osgi.framework.ServiceReference;
-import org.osgi.service.command.CommandProcessor;
-import org.osgi.service.command.CommandSession;
-import org.osgi.service.command.Converter;
-import org.osgi.service.component.ComponentContext;
-import org.osgi.service.log.LogEntry;
-import org.osgi.service.log.LogReaderService;
-import org.osgi.service.log.LogService;
-import org.osgi.service.packageadmin.ExportedPackage;
-import org.osgi.service.packageadmin.PackageAdmin;
-import org.osgi.service.startlevel.StartLevel;
-
-import java.io.IOException;
-import java.net.URL;
-import java.util.*;
-
-public class Equinox implements Converter
-{
-    BundleContext context;
-    PackageAdmin pka;
-    LogReaderService lrs;
-    StartLevel sls;
-    final static String[] functions = {"active", "bundles", "close", "diag", "exec", "exit", "fork", "gc", "getprop", "headers", "init", "install", "launch", "log", "packages", "packages", "refresh", "services", "setbsl", "setfwsl", "setibsl", "setprop", "shutdown", "sl", "ss", "start", "status", "stop", "uninstall", "update"};
-
-    protected void activate(ComponentContext context)
-    {
-        this.context = context.getBundleContext();
-        Dictionary<String, Object> dict = new Hashtable<String, Object>();
-        dict.put(CommandProcessor.COMMAND_SCOPE, "eqx");
-        dict.put(CommandProcessor.COMMAND_FUNCTION, functions);
-        this.context.registerService(Converter.class.getName(), this, dict);
-    }
-
-    BundleContext getContext()
-    {
-        return context;
-    }
-
-    public void setPka(PackageAdmin pka)
-    {
-        this.pka = pka;
-    }
-
-    public void setLrs(LogReaderService lrs)
-    {
-        this.lrs = lrs;
-    }
-
-    public void setSls(StartLevel sls)
-    {
-        this.sls = sls;
-    }
-
-    /**
-     * - Displays unsatisfied constraints for the specified bundle(s).
-     */
-    public void diag()
-    {
-    }
-
-    /*
-     * active - Displays a list of all bundles currently in the ACTIVE state.
-     */
-    public List<Bundle> active()
-    {
-        List<Bundle> result = new ArrayList<Bundle>();
-        Bundle[] bundles = getContext().getBundles();
-        for (Bundle b : bundles)
-        {
-            if (b.getState() == Bundle.ACTIVE)
-            {
-                result.add(b);
-            }
-        }
-        return result;
-    }
-
-    /*
-     * getprop { name } - Displays the system properties with the given name, or
-     * all of them.
-     */
-
-    public Object getprop(CharSequence name)
-    {
-        if (name == null)
-        {
-            return System.getProperties();
-        }
-        else
-        {
-            return System.getProperty(name.toString());
-        }
-    }
-
-    /**
-     * launch - start the OSGi Framework
-     */
-
-    public void launch()
-    {
-        throw new IllegalStateException("Already running");
-    }
-
-    /**
-     * shutdown - shutdown the OSGi Framework
-     */
-    public void shutdown() throws BundleException
-    {
-        getContext().getBundle().stop();
-    }
-
-    /**
-     * close - shutdown and exit
-     */
-    public void close(CommandSession session)
-    {
-        session.close();
-    }
-
-    /**
-     * exit - exit immediately (System.exit)
-     */
-
-    public void exit(int exitValue)
-    {
-        exit(exitValue);
-    }
-
-    /**
-     * gc - perform a garbage collection
-     */
-    public long gc()
-    {
-        Runtime.getRuntime().gc();
-        return Runtime.getRuntime().freeMemory();
-    }
-
-    /**
-     * init - uninstall all bundles
-     */
-
-    public void init() throws Exception
-    {
-        Bundle bundles[] = getContext().getBundles();
-        for (Bundle b : bundles)
-        {
-            if (b.getBundleId() != 0)
-            {
-                b.uninstall();
-            }
-        }
-    }
-
-    /**
-     * setprop <key>=<value> - set the OSGi property
-     */
-    public void setprop(CommandSession session, String key, String value)
-    {
-        session.put(key, value);
-    }
-
-    /**
-     * install - install and optionally start bundle from the given URL
-     *
-     * @throws BundleException
-     */
-
-    public Bundle install(URL url) throws BundleException
-    {
-        return getContext().installBundle(url.toExternalForm());
-    }
-
-    /**
-     * uninstall - uninstall the specified bundle(s)
-     *
-     * @throws BundleException
-     */
-    public void uninstall(Bundle[] bundles) throws BundleException
-    {
-        for (Bundle b : bundles)
-        {
-            b.uninstall();
-        }
-    }
-
-    /**
-     * start - start the specified bundle(s)
-     */
-    public void start(Bundle[] bundles) throws BundleException
-    {
-        for (Bundle b : bundles)
-        {
-            b.start();
-        }
-    }
-
-    /**
-     * stop - stop the specified bundle(s)
-     */
-    public void stop(Bundle[] bundles) throws BundleException
-    {
-        for (Bundle b : bundles)
-        {
-            b.stop();
-        }
-    }
-
-    /**
-     * refresh - refresh the packages of the specified bundles
-     */
-    public void refresh(Bundle[] bundles) throws Exception
-    {
-        if (pka != null)
-        {
-            pka.refreshPackages(bundles);
-        }
-        else
-        {
-            throw new RuntimeException("No PackageAdmin service registered");
-        }
-    }
-
-    /**
-     * update - update the specified bundle(s)
-     */
-    public void update(Bundle[] bundles) throws BundleException
-    {
-        for (Bundle b : bundles)
-        {
-            b.update();
-        }
-    }
-
-    /**
-     * status - display installed bundles and registered services
-     */
-    public List<Object> status() throws Exception
-    {
-        List<Object> status = new ArrayList<Object>();
-        status.addAll(Arrays.asList(getContext().getBundles()));
-        status.addAll(Arrays.asList(getContext().getServiceReferences(null, null)));
-        return status;
-    }
-
-    /**
-     * ss - display installed bundles (short status)
-     */
-    public Bundle[] ss()
-    {
-        return getContext().getBundles();
-    }
-
-    /**
-     * services {filter} - display registered service details
-     */
-    public ServiceReference[] services(String filter) throws Exception
-    {
-        return getContext().getServiceReferences(null, filter);
-    }
-
-    /**
-     * packages {<pkgname>|<id>|<location>} - display imported/exported
-     * package details
-     */
-    public ExportedPackage[] packages(Bundle bundle) throws Exception
-    {
-        if (pka != null)
-        {
-            return pka.getExportedPackages(bundle);
-        }
-        else
-        {
-            throw new RuntimeException("No PackageAdmin service registered");
-        }
-    }
-
-    public ExportedPackage[] packages(String packageName) throws Exception
-    {
-        if (pka != null)
-        {
-            return pka.getExportedPackages(packageName);
-        }
-        return null;
-    }
-
-    /**
-     * bundles - display details for all installed bundles
-     */
-    public Bundle[] bundles()
-    {
-        return ss();
-    }
-
-    /**
-     * bundle (<id>|<location>) - display details for the specified bundle(s)
-     */
-
-    /**
-     * headers (<id>|<location>) - print bundle headers
-     */
-
-    @SuppressWarnings("unchecked")
-    public Dictionary headers(Bundle b, String locale)
-    {
-        return b.getHeaders(locale);
-    }
-
-    /**
-     * log (<id>|<location>) - display log entries
-     */
-
-    @SuppressWarnings("unchecked")
-    public Collection<LogEntry> log(Bundle bundle) throws Exception
-    {
-        if (lrs != null)
-        {
-            return Collections.list((Enumeration<LogEntry>) lrs.getLog());
-        }
-        else
-        {
-            throw new RuntimeException("LogReader not available");
-        }
-    }
-
-    /**
-     * exec <command> - execute a command in a separate process and wait
-     *
-     * @throws IOException
-     */
-
-    public int exec(Object[] args, boolean fork) throws IOException
-    {
-        StringBuffer sb = new StringBuffer();
-        String del = "";
-        for (Object arg : args)
-        {
-            sb.append(del);
-            sb.append(arg);
-            del = " ";
-        }
-        Process p = Runtime.getRuntime().exec(sb.toString());
-        if (fork)
-        {
-            int c;
-            while ((c = p.getInputStream().read()) > 0)
-            {
-                System.out.print(c);
-            }
-        }
-        return p.exitValue();
-    }
-
-    /**
-     * fork <command> - execute a command in a separate process
-     */
-
-    public void fork(Object args[]) throws Exception
-    {
-        exec(args, true);
-    }
-
-    /**
-     * sl {(<id>|<location>)} - display the start level for the specified
-     * bundle, or for the framework if no bundle specified
-     */
-    public int sl(Bundle b) throws Exception
-    {
-        if (sls == null)
-        {
-            throw new RuntimeException("No StartLevel service registered");
-        }
-        if (b == null)
-        {
-            return sls.getStartLevel();
-        }
-        else
-        {
-            return sls.getBundleStartLevel(b);
-        }
-    }
-
-    /**
-     * setfwsl <start level> - set the framework start level
-     */
-    public int setfwsl(int n) throws Exception
-    {
-        if (sls == null)
-        {
-            throw new RuntimeException("No StartLevel service registered");
-        }
-        int old = sls.getStartLevel();
-        sls.setStartLevel(n);
-        return old;
-    }
-
-    /**
-     * setbsl <start level> (<id>|<location>) - set the start level for the
-     * bundle(s)
-     */
-    public int setbsl(Bundle b, int n) throws Exception
-    {
-        if (sls == null)
-        {
-            throw new RuntimeException("No StartLevel service registered");
-        }
-        int old = sls.getBundleStartLevel(b);
-        sls.setBundleStartLevel(b, n);
-        return old;
-    }
-
-    /**
-     * setibsl <start level> - set the initial bundle start level
-     */
-    public int setibsl(int n) throws Exception
-    {
-        if (sls == null)
-        {
-            throw new RuntimeException("No StartLevel service registered");
-        }
-        int old = sls.getInitialBundleStartLevel();
-        sls.setInitialBundleStartLevel(n);
-        return old;
-    }
-
-    public Object convert(Class<?> desiredType, Object in) throws Exception
-    {
-        return null;
-    }
-
-    String getLevel(int index)
-    {
-        switch (index)
-        {
-            case LogService.LOG_DEBUG:
-                return "DEBUG";
-            case LogService.LOG_INFO:
-                return "INFO ";
-            case LogService.LOG_WARNING:
-                return "WARNI";
-            case LogService.LOG_ERROR:
-                return "ERROR";
-            default:
-                return "<" + index + ">";
-        }
-    }
-
-    public CharSequence format(Object target, int level, Converter escape)
-    {
-        if (target instanceof LogEntry)
-        {
-            LogEntry entry = (LogEntry) target;
-            switch (level)
-            {
-                case LINE:
-                    Formatter f = new Formatter();
-                    f.format("%tT %04d %s %s", entry.getTime(), entry.getBundle().getBundleId(), getLevel(entry.getLevel()) + "", entry.getMessage() + "");
-                    return f.toString();
-
-                case PART:
-                    Formatter f2 = new Formatter();
-                    f2.format("%tT %s", entry.getTime(), entry.getMessage());
-                    return f2.toString();
-            }
-        }
-        return null;
-    }
-    /**
-     * profilelog - Display & flush the profile log messages
-     */
-
-}
diff --git a/gogo/gogo.runtime/src/main/java/org/apache/felix/gogo/runtime/osgi/OSGiShell.java b/gogo/gogo.runtime/src/main/java/org/apache/felix/gogo/runtime/osgi/OSGiShell.java
index 75bdcf6..e968740 100644
--- a/gogo/gogo.runtime/src/main/java/org/apache/felix/gogo/runtime/osgi/OSGiShell.java
+++ b/gogo/gogo.runtime/src/main/java/org/apache/felix/gogo/runtime/osgi/OSGiShell.java
@@ -26,7 +26,6 @@
 import org.osgi.framework.InvalidSyntaxException;
 import org.osgi.framework.ServiceReference;
 import org.osgi.service.command.Converter;
-import org.osgi.service.component.ComponentContext;
 import org.osgi.service.packageadmin.PackageAdmin;
 import org.osgi.service.threadio.ThreadIO;
 
@@ -35,16 +34,6 @@
     Bundle bundle;
     OSGiCommands commands;
 
-    protected void activate(ComponentContext context) throws Exception
-    {
-        this.bundle = context.getBundleContext().getBundle();
-        if (threadIO == null)
-        {
-            threadIO = (ThreadIO) context.locateService("x");
-        }
-        start();
-    }
-
     public void start() throws Exception
     {
         commands = new OSGiCommands(bundle);
@@ -84,11 +73,6 @@
         }
     }
 
-    protected void deactivate(ComponentContext context)
-    {
-        System.out.println("Deactivating");
-    }
-
     public Object get(String name)
     {
         if (bundle.getBundleContext() != null)
diff --git a/gogo/gogo.runtime/src/main/java/org/apache/felix/gogo/runtime/shell/Closure.java b/gogo/gogo.runtime/src/main/java/org/apache/felix/gogo/runtime/shell/Closure.java
index e479bac..3bc61a5 100644
--- a/gogo/gogo.runtime/src/main/java/org/apache/felix/gogo/runtime/shell/Closure.java
+++ b/gogo/gogo.runtime/src/main/java/org/apache/felix/gogo/runtime/shell/Closure.java
@@ -235,7 +235,7 @@
                 {
                     return false;
                 }
-                return seq;
+                return result;
         }
     }
 
diff --git a/gogo/gogo.runtime/src/main/java/org/apache/felix/gogo/runtime/shell/Parser.java b/gogo/gogo.runtime/src/main/java/org/apache/felix/gogo/runtime/shell/Parser.java
index 6d6e7cd..44a5153 100644
--- a/gogo/gogo.runtime/src/main/java/org/apache/felix/gogo/runtime/shell/Parser.java
+++ b/gogo/gogo.runtime/src/main/java/org/apache/felix/gogo/runtime/shell/Parser.java
@@ -28,7 +28,7 @@
     int current = 0;
     CharSequence text;
     boolean escaped;
-    static final String SPECIAL = "<;|{[\"'$'`(=";
+    static final String SPECIAL = "<;|{[\"'$`(=";
 
     public Parser(CharSequence program)
     {
@@ -71,24 +71,29 @@
 
     char peek()
     {
+        return peek(false);
+    }
+
+    char peek(boolean increment)
+    {
         escaped = false;
         if (eof())
         {
             return 0;
         }
 
-        char c = text.charAt(current);
+        int last = current;
+        char c = text.charAt(current++);
 
         if (c == '\\')
         {
             escaped = true;
-            ++current;
             if (eof())
             {
                 throw new RuntimeException("Eof found after \\"); // derek
             }
 
-            c = text.charAt(current);
+            c = text.charAt(current++);
 
             switch (c)
             {
@@ -113,12 +118,16 @@
                     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 (!increment) {
+            current = last;
+        }
         return c;
     }
 
@@ -200,7 +209,6 @@
                 }
                 next();
             }
-
             return text.subSequence(start, current);
         }
         else
@@ -215,59 +223,45 @@
 
         int start = current;
         char c = next();
-        switch (c)
-        {
-            case '{':
-                return text.subSequence(start, find('}', '{'));
-            case '(':
-                return text.subSequence(start, find(')', '('));
-            case '[':
-                return text.subSequence(start, find(']', '['));
-            case '"':
-                return text.subSequence(start + 1, quote('"'));
-            case '\'':
-                return text.subSequence(start + 1, quote('\''));
-            case '<':
-                return text.subSequence(start, find('>', '<'));
-            case '$':
-                value();
-                return text.subSequence(start, current);
-        }
-
-        if (Character.isJavaIdentifierPart(c))
-        {
-            // Some identifier or number
-            while (!eof())
+        if (!escaped) {
+            switch (c)
             {
-                c = peek();
-                if (c != ':' && !Character.isJavaIdentifierPart(c) && c != '.')
-                {
-                    break;
-                }
-                next();
-            }
-        }
-        else
-        {
-            // Operator, repeat while in operator class
-            while (!eof())
-            {
-                c = peek();
-                if (Character.isWhitespace(c) || Character.isJavaIdentifierPart(c))
-                {
-                    break;
-                }
+                case '{':
+                    return text.subSequence(start, find('}', '{'));
+                case '(':
+                    return text.subSequence(start, find(')', '('));
+                case '[':
+                    return text.subSequence(start, find(']', '['));
+                case '"':
+                    return text.subSequence(start + 1, quote('"'));
+                case '\'':
+                    return text.subSequence(start + 1, quote('\''));
+                case '<':
+                    return text.subSequence(start, find('>', '<'));
+                case '$':
+                    value();
+                    return text.subSequence(start, current);
+                case '=':
+                    return text.subSequence(start, current);
             }
         }
 
+        // Some identifier or number
+        while (!eof())
+        {
+            c = peek();
+            if ((!escaped && SPECIAL.indexOf(c) >= 0) || Character.isWhitespace(c))
+            {
+                break;
+            }
+            next();
+        }
         return text.subSequence(start, current);
     }
 
     char next()
     {
-        char c = peek();
-        current++;
-        return c;
+        return peek(true);
     }
 
     char unicode()
diff --git a/gogo/gogo.runtime/src/main/java/org/apache/felix/gogo/runtime/threadio/ThreadIOImpl.java b/gogo/gogo.runtime/src/main/java/org/apache/felix/gogo/runtime/threadio/ThreadIOImpl.java
index b9cab7a..2f15984 100644
--- a/gogo/gogo.runtime/src/main/java/org/apache/felix/gogo/runtime/threadio/ThreadIOImpl.java
+++ b/gogo/gogo.runtime/src/main/java/org/apache/felix/gogo/runtime/threadio/ThreadIOImpl.java
@@ -19,7 +19,6 @@
 // DWB20: ThreadIO should check and reset IO if something (e.g. jetty) overrides
 package org.apache.felix.gogo.runtime.threadio;
 
-import org.osgi.service.component.ComponentContext;
 import org.osgi.service.threadio.ThreadIO;
 
 import java.io.InputStream;
@@ -34,23 +33,6 @@
     ThreadInputStream in = new ThreadInputStream(System.in);
     ThreadLocal<Marker> current = new ThreadLocal<Marker>();
 
-    protected void activate(ComponentContext context)
-    {
-        start();
-    }
-
-    protected void deactivate()
-    {
-        stop();
-    }
-
-    public void stop()
-    {
-        System.setErr(err.dflt);
-        System.setOut(out.dflt);
-        System.setIn(in.dflt);
-    }
-
     public void start()
     {
         if (System.out instanceof ThreadPrintStream)
@@ -62,6 +44,13 @@
         System.setErr(err);
     }
 
+    public void stop()
+    {
+        System.setErr(err.dflt);
+        System.setOut(out.dflt);
+        System.setIn(in.dflt);
+    }
+
     private void checkIO()
     {    // derek
         if (System.in != in)
diff --git a/gogo/gogo.runtime/src/test/java/org/apache/felix/gogo/runtime/shell/TestParser.java b/gogo/gogo.runtime/src/test/java/org/apache/felix/gogo/runtime/shell/TestParser.java
index aea28f1..0aa294c 100644
--- a/gogo/gogo.runtime/src/test/java/org/apache/felix/gogo/runtime/shell/TestParser.java
+++ b/gogo/gogo.runtime/src/test/java/org/apache/felix/gogo/runtime/shell/TestParser.java
@@ -35,6 +35,15 @@
 {
     int beentheredonethat = 0;
 
+    public void testScope() throws Exception
+    {
+        Context c= new Context();
+        c.addCommand("echo", this);
+        c.addCommand("capture", this);
+        assertEquals("$a", c.execute("test:echo \\$a | capture"));
+        assertEquals("file://poo", c.execute("test:echo file://poo|capture"));
+    }
+
     public void testPipe() throws Exception
     {
         Context c = new Context();
@@ -98,6 +107,7 @@
         CharSequence cs = parser.messy();
         assertEquals("a|b;c", cs.toString());
         assertEquals("a|b;c", new Parser(cs).unescape());
+        assertEquals("$a", new Parser("\\$a").unescape());
     }
 
 
diff --git a/gogo/pom.xml b/gogo/pom.xml
index be4ab33..bb875f1 100644
--- a/gogo/pom.xml
+++ b/gogo/pom.xml
@@ -27,6 +27,7 @@
     <modelVersion>4.0.0</modelVersion>
     <packaging>pom</packaging>
     <name>Apache Felix Gogo Shell</name>
+    <description>Apache Felix Gogo Shell</description>
     <groupId>org.apache.felix.gogo</groupId>
     <artifactId>gogo</artifactId>
     <version>1.0.0-SNAPSHOT</version>
@@ -35,6 +36,7 @@
         <module>gogo.runtime</module>
         <module>gogo.launcher</module>
         <module>gogo.console</module>
+        <module>gogo.commands</module>
     </modules>
 
     <dependencyManagement>
diff --git a/gogo/src/main/java/org/osgi/framework/boot/SystemBundle.java b/gogo/src/main/java/org/osgi/framework/boot/SystemBundle.java
deleted file mode 100644
index 25cd373..0000000
--- a/gogo/src/main/java/org/osgi/framework/boot/SystemBundle.java
+++ /dev/null
@@ -1,90 +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.osgi.framework.boot;
-
-import java.util.Properties;
-
-/**
- * This interface should be implemented by framework implementations when their
- * main object is created. It allows a configurator to set the properties and
- * launch the framework.
- *
- * @author aqute
- */
-public interface SystemBundle
-{
-    /**
-     * The name of a Security Manager class with public empty constructor. A
-     * valid value is also true, this means that the framework should
-     * instantiate its own security manager. If not set, security could be
-     * defined by a parent framework or there is no security. This can be
-     * detected by looking if there is a security manager set
-     */
-    String SECURITY = "org.osgi.framework.security";
-
-    /**
-     * A valid file path in the file system to a directory that exists. The
-     * framework is free to use this directory as it sees fit. This area can not
-     * be shared with anything else. If this property is not set, the framework
-     * should use a file area from the parent bundle. If it is not embedded, it
-     * must use a reasonable platform default.
-     */
-    String STORAGE = "org.osgi.framework.storage";
-
-    /*
-     * A list of paths (separated by path separator) that point to additional
-     * directories to search for platform specific libraries
-     */ String LIBRARIES = "org.osgi.framework.libraries";
-    /*
-     * The command to give a file executable permission. This is necessary in
-     * some environments for running shared libraries.
-     */ String EXECPERMISSION = "org.osgi.framework.command.execpermission";
-
-    /*
-     * Points to a directory with certificates. ###??? Keystore? Certificate
-     * format?
-     */ String ROOT_CERTIFICATES = "org.osgi.framework.root.certificates";
-
-    /*
-     * Set by the configurator but the framework should provide a reasonable
-     * default.
-     */ String WINDOWSYSTEM = "org.osgi.framework.windowsystem";
-
-    /**
-     * Configure this framework with the given properties. These properties can
-     * contain framework specific properties or of the general kind defined in
-     * the specification or in this interface.
-     *
-     * @param properties The properties. This properties can be backed by another
-     *                   properties, it can there not be assumed that it contains all
-     *                   keys. Use it only through the getProperty methods. This parameter may be null.
-     */
-    void init(Properties configuration);
-
-    /**
-     * Wait until the framework is completely finished.
-     * <p/>
-     * This method will return if the framework is stopped and has cleaned up
-     * all the framework resources.
-     *
-     * @param timeout Maximum number of milliseconds to wait until the framework is finished. Specifying a zero will wait indefinitely.
-     */
-
-    void join(long timeout) throws InterruptedException;
-}