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

[2/2] karaf git commit: [KARAF-3455] Support for custom command parsers

[KARAF-3455] Support for custom command parsers


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

Branch: refs/heads/master
Commit: cbbed8ac4d992756d1da1f6aff2de60943032169
Parents: cb75183
Author: Guillaume Nodet <gn...@gmail.com>
Authored: Wed Jan 28 12:06:34 2015 +0100
Committer: Guillaume Nodet <gn...@gmail.com>
Committed: Wed Jan 28 12:08:25 2015 +0100

----------------------------------------------------------------------
 jdbc/pom.xml                                    |   1 +
 .../karaf/jdbc/command/ExecuteCommand.java      |   5 +
 .../apache/karaf/jdbc/command/QueryCommand.java |   5 +
 .../jdbc/command/completers/SqlCompleter.java   |  52 ++
 .../karaf/jdbc/command/parsing/JdbcParser.java  |  75 ++
 .../karaf/shell/compat/CommandTracker.java      |  11 +
 .../org/apache/felix/gogo/runtime/Closure.java  | 686 ----------------
 .../felix/gogo/runtime/CommandSessionImpl.java  | 404 ---------
 .../org/apache/felix/gogo/runtime/Parser.java   | 181 -----
 .../apache/felix/gogo/runtime/Tokenizer.java    | 813 -------------------
 .../apache/karaf/shell/api/action/Parsing.java  |  39 +
 .../apache/karaf/shell/api/console/Command.java |   7 +
 .../apache/karaf/shell/api/console/Parser.java  |  28 +
 .../impl/action/command/ActionCommand.java      |  28 +
 .../shell/impl/action/command/ManagerImpl.java  |   5 +-
 .../impl/console/CompleterAsCompletor.java      |  14 +-
 .../shell/impl/console/ConsoleSessionImpl.java  |  17 +-
 .../shell/impl/console/HeadlessSessionImpl.java |   4 +-
 .../impl/console/commands/TopLevelCommand.java  |   6 +
 .../impl/console/commands/help/HelpCommand.java |   6 +
 .../console/osgi/secured/SecuredCommand.java    |   6 +
 .../impl/console/parsing/CommandLineImpl.java   |  95 ---
 .../impl/console/parsing/CommandLineParser.java | 113 +++
 .../shell/impl/console/parsing/Parser.java      | 404 ---------
 .../shell/support/parsing/CommandLineImpl.java  |  80 ++
 .../shell/support/parsing/DefaultParser.java    |  43 +
 .../karaf/shell/support/parsing/GogoParser.java | 408 ++++++++++
 .../shell/impl/console/parsing/ParsingTest.java | 122 +++
 28 files changed, 1050 insertions(+), 2608 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/karaf/blob/cbbed8ac/jdbc/pom.xml
----------------------------------------------------------------------
diff --git a/jdbc/pom.xml b/jdbc/pom.xml
index 41bc234..d98ef28 100644
--- a/jdbc/pom.xml
+++ b/jdbc/pom.xml
@@ -87,6 +87,7 @@
                         <Private-Package>
                             org.apache.karaf.jdbc.command,
                             org.apache.karaf.jdbc.command.completers,
+                            org.apache.karaf.jdbc.command.parsing,
                             org.apache.karaf.jdbc.internal,
                             org.apache.karaf.jdbc.internal.osgi,
                             org.apache.karaf.util,

http://git-wip-us.apache.org/repos/asf/karaf/blob/cbbed8ac/jdbc/src/main/java/org/apache/karaf/jdbc/command/ExecuteCommand.java
----------------------------------------------------------------------
diff --git a/jdbc/src/main/java/org/apache/karaf/jdbc/command/ExecuteCommand.java b/jdbc/src/main/java/org/apache/karaf/jdbc/command/ExecuteCommand.java
index 480ab51..137f085 100644
--- a/jdbc/src/main/java/org/apache/karaf/jdbc/command/ExecuteCommand.java
+++ b/jdbc/src/main/java/org/apache/karaf/jdbc/command/ExecuteCommand.java
@@ -17,12 +17,16 @@
 package org.apache.karaf.jdbc.command;
 
 import org.apache.karaf.jdbc.command.completers.DataSourcesNameCompleter;
+import org.apache.karaf.jdbc.command.completers.SqlCompleter;
+import org.apache.karaf.jdbc.command.parsing.JdbcParser;
 import org.apache.karaf.shell.api.action.Argument;
 import org.apache.karaf.shell.api.action.Command;
 import org.apache.karaf.shell.api.action.Completion;
+import org.apache.karaf.shell.api.action.Parsing;
 import org.apache.karaf.shell.api.action.lifecycle.Service;
 
 @Command(scope = "jdbc", name = "execute", description = "Execute a SQL command on a given JDBC datasource")
+@Parsing(JdbcParser.class)
 @Service
 public class ExecuteCommand extends JdbcCommandSupport {
 
@@ -31,6 +35,7 @@ public class ExecuteCommand extends JdbcCommandSupport {
     String datasource;
 
     @Argument(index = 1, name = "command", description = "The SQL command to execute", required = true, multiValued = false)
+    @Completion(SqlCompleter.class)
     String command;
 
     @Override

http://git-wip-us.apache.org/repos/asf/karaf/blob/cbbed8ac/jdbc/src/main/java/org/apache/karaf/jdbc/command/QueryCommand.java
----------------------------------------------------------------------
diff --git a/jdbc/src/main/java/org/apache/karaf/jdbc/command/QueryCommand.java b/jdbc/src/main/java/org/apache/karaf/jdbc/command/QueryCommand.java
index 297288b..34089b3 100644
--- a/jdbc/src/main/java/org/apache/karaf/jdbc/command/QueryCommand.java
+++ b/jdbc/src/main/java/org/apache/karaf/jdbc/command/QueryCommand.java
@@ -20,14 +20,18 @@ import java.util.List;
 import java.util.Map;
 
 import org.apache.karaf.jdbc.command.completers.DataSourcesNameCompleter;
+import org.apache.karaf.jdbc.command.completers.SqlCompleter;
+import org.apache.karaf.jdbc.command.parsing.JdbcParser;
 import org.apache.karaf.shell.api.action.Argument;
 import org.apache.karaf.shell.api.action.Command;
 import org.apache.karaf.shell.api.action.Completion;
+import org.apache.karaf.shell.api.action.Parsing;
 import org.apache.karaf.shell.api.action.lifecycle.Service;
 import org.apache.karaf.shell.support.table.Row;
 import org.apache.karaf.shell.support.table.ShellTable;
 
 @Command(scope = "jdbc", name = "query", description = "Execute a SQL query on a JDBC datasource")
+@Parsing(JdbcParser.class)
 @Service
 public class QueryCommand extends JdbcCommandSupport {
 
@@ -36,6 +40,7 @@ public class QueryCommand extends JdbcCommandSupport {
     String datasource;
 
     @Argument(index = 1, name = "query", description = "The SQL query to execute", required = true, multiValued = false)
+    @Completion(SqlCompleter.class)
     String query;
 
     @Override

http://git-wip-us.apache.org/repos/asf/karaf/blob/cbbed8ac/jdbc/src/main/java/org/apache/karaf/jdbc/command/completers/SqlCompleter.java
----------------------------------------------------------------------
diff --git a/jdbc/src/main/java/org/apache/karaf/jdbc/command/completers/SqlCompleter.java b/jdbc/src/main/java/org/apache/karaf/jdbc/command/completers/SqlCompleter.java
new file mode 100644
index 0000000..eb3015d
--- /dev/null
+++ b/jdbc/src/main/java/org/apache/karaf/jdbc/command/completers/SqlCompleter.java
@@ -0,0 +1,52 @@
+/*
+ * 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.jdbc.command.completers;
+
+import java.util.List;
+
+import org.apache.karaf.jdbc.JdbcService;
+import org.apache.karaf.shell.api.action.lifecycle.Reference;
+import org.apache.karaf.shell.api.action.lifecycle.Service;
+import org.apache.karaf.shell.api.console.CommandLine;
+import org.apache.karaf.shell.api.console.Completer;
+import org.apache.karaf.shell.api.console.Session;
+import org.apache.karaf.shell.support.completers.StringsCompleter;
+
+/**
+ * Completer SQL
+ */
+@Service
+public class SqlCompleter implements Completer {
+
+    @Reference
+    private JdbcService jdbcService;
+
+    @Override
+    public int complete(Session session, CommandLine commandLine, List<String> candidates) {
+        // TODO: implement SQL completion
+        return -1;
+    }
+
+    public JdbcService getJdbcService() {
+        return jdbcService;
+    }
+
+    public void setJdbcService(JdbcService jdbcService) {
+        this.jdbcService = jdbcService;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/karaf/blob/cbbed8ac/jdbc/src/main/java/org/apache/karaf/jdbc/command/parsing/JdbcParser.java
----------------------------------------------------------------------
diff --git a/jdbc/src/main/java/org/apache/karaf/jdbc/command/parsing/JdbcParser.java b/jdbc/src/main/java/org/apache/karaf/jdbc/command/parsing/JdbcParser.java
new file mode 100644
index 0000000..90238c3
--- /dev/null
+++ b/jdbc/src/main/java/org/apache/karaf/jdbc/command/parsing/JdbcParser.java
@@ -0,0 +1,75 @@
+package org.apache.karaf.jdbc.command.parsing;
+
+/*
+ * 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.
+ */
+import java.util.ArrayList;
+import java.util.List;
+
+import org.apache.karaf.shell.api.action.lifecycle.Service;
+import org.apache.karaf.shell.api.console.CommandLine;
+import org.apache.karaf.shell.api.console.Parser;
+import org.apache.karaf.shell.api.console.Session;
+import org.apache.karaf.shell.support.parsing.CommandLineImpl;
+import org.apache.karaf.shell.support.parsing.GogoParser;
+
+/**
+ * A parser for JDBC commands using SQL.
+ */
+@Service
+public class JdbcParser implements Parser {
+
+    @Override
+    public CommandLine parse(Session session, String command, int cursor) {
+        GogoParser parser = new GogoParser(command, cursor);
+        List<String> args = parser.statement();
+        List<String> nargs = new ArrayList<>();
+        int state = 0;
+        for (String arg : args) {
+            switch (state) {
+            // command
+            case 0:
+                nargs.add(arg);
+                state = 1;
+                break;
+            // option or target
+            case 1:
+                nargs.add(arg);
+                if (!arg.startsWith("-")) {
+                    state = 2;
+                }
+                break;
+            // first sql fragment
+            case 2:
+                nargs.add(arg);
+                state = 3;
+                break;
+            // addtional sql
+            case 3:
+                nargs.set(nargs.size() - 1, nargs.get(nargs.size() - 1) + " " + arg);
+                break;
+            }
+        }
+        nargs.set(nargs.size() - 1, "\"" + nargs.get(nargs.size() - 1) + "\"");
+        return new CommandLineImpl(
+                nargs.toArray(new String[nargs.size()]),
+                parser.cursorArgumentIndex(),
+                Math.max(parser.argumentPosition(), nargs.size()),
+                cursor,
+                command.substring(0, parser.position()));
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/karaf/blob/cbbed8ac/shell/console/src/main/java/org/apache/karaf/shell/compat/CommandTracker.java
----------------------------------------------------------------------
diff --git a/shell/console/src/main/java/org/apache/karaf/shell/compat/CommandTracker.java b/shell/console/src/main/java/org/apache/karaf/shell/compat/CommandTracker.java
index 191735c..a923cb7 100644
--- a/shell/console/src/main/java/org/apache/karaf/shell/compat/CommandTracker.java
+++ b/shell/console/src/main/java/org/apache/karaf/shell/compat/CommandTracker.java
@@ -22,6 +22,7 @@ import java.util.List;
 
 import org.apache.felix.service.command.CommandProcessor;
 import org.apache.felix.service.command.CommandSession;
+import org.apache.karaf.shell.api.console.Parser;
 import org.apache.karaf.shell.commands.Command;
 import org.apache.karaf.shell.commands.CommandWithAction;
 import org.apache.karaf.shell.api.console.CommandLine;
@@ -97,6 +98,11 @@ public class CommandTracker implements ServiceTrackerCustomizer<Object, Object>
                 }
 
                 @Override
+                public Parser getParser() {
+                    return null;
+                }
+
+                @Override
                 public Object execute(Session session, List<Object> arguments) throws Exception {
                     // TODO: remove not really nice cast
                     CommandSession commandSession = (CommandSession) session.get(".commandSession");
@@ -140,6 +146,11 @@ public class CommandTracker implements ServiceTrackerCustomizer<Object, Object>
                 }
 
                 @Override
+                public Parser getParser() {
+                    return null;
+                }
+
+                @Override
                 public Object execute(Session session, List<Object> arguments) throws Exception {
                     // TODO: remove not really nice cast
                     CommandSession commandSession = (CommandSession) session.get(".commandSession");

http://git-wip-us.apache.org/repos/asf/karaf/blob/cbbed8ac/shell/core/src/main/java/org/apache/felix/gogo/runtime/Closure.java
----------------------------------------------------------------------
diff --git a/shell/core/src/main/java/org/apache/felix/gogo/runtime/Closure.java b/shell/core/src/main/java/org/apache/felix/gogo/runtime/Closure.java
deleted file mode 100644
index 4994ec3..0000000
--- a/shell/core/src/main/java/org/apache/felix/gogo/runtime/Closure.java
+++ /dev/null
@@ -1,686 +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.runtime;
-
-import java.io.EOFException;
-import java.util.*;
-import java.util.Map.Entry;
-
-import org.apache.felix.gogo.runtime.Tokenizer.Type;
-import org.apache.felix.service.command.CommandSession;
-import org.apache.felix.service.command.Function;
-
-public class Closure implements Function, Evaluate
-{
-    public static final String LOCATION = ".location";
-    private static final String DEFAULT_LOCK = ".defaultLock";
-
-    private static final long serialVersionUID = 1L;
-    private static final ThreadLocal<String> location = new ThreadLocal<String>();
-
-    private final CommandSessionImpl session;
-    private final Closure parent;
-    private final CharSequence source;
-    private final List<List<List<Token>>> program;
-    private final Object script;
-
-    private Token errTok;
-    private Token errTok2;
-    private List<Object> parms = null;
-    private List<Object> parmv = null;
-
-    public Closure(CommandSessionImpl session, Closure parent, CharSequence source) throws Exception
-    {
-        this.session = session;
-        this.parent = parent;
-        this.source = source;
-        script = session.get("0"); // by convention, $0 is script name
-
-        try
-        {
-            program = new Parser(source, isExpansionEnabled()).program(); //CQL-Handling
-        }
-        catch (Exception e)
-        {
-            throw setLocation(e);
-        }
-    }
-
-    public CommandSessionImpl session()
-    {
-        return session;
-    }
-
-    private Exception setLocation(Exception e)
-    {
-        if (session.get(DEFAULT_LOCK) == null)
-        {
-            String loc = location.get();
-            if (null == loc)
-            {
-                loc = (null == script ? "" : script + ":");
-
-                if (e instanceof SyntaxError)
-                {
-                    SyntaxError se = (SyntaxError) e;
-                    loc += se.line() + "." + se.column();
-                }
-                else if (null != errTok)
-                {
-                    loc += errTok.line + "." + errTok.column;
-                }
-
-                location.set(loc);
-            }
-            else if (null != script && !loc.contains(":"))
-            {
-                location.set(script + ":" + loc);
-            }
-
-            session.put(LOCATION, location.get());
-        }
-
-        if (e instanceof EOFError)
-        { // map to public exception, so interactive clients can provide more input
-            EOFException eofe = new EOFException(e.getMessage());
-            eofe.initCause(e);
-            return eofe;
-        }
-
-        return e;
-    }
-
-    // implements Function interface
-    public Object execute(CommandSession x, List<Object> values) throws Exception
-    {
-        try
-        {
-            location.remove();
-            session.variables.remove(LOCATION);
-            return execute(values);
-        }
-        catch (Exception e)
-        {
-            throw setLocation(e);
-        }
-    }
-
-    @SuppressWarnings("unchecked")
-    private Object execute(List<Object> values) throws Exception
-    {
-        if (null != values)
-        {
-            parmv = values;
-            parms = new ArgList(parmv);
-        }
-        else if (null != parent)
-        {
-            // inherit parent closure parameters
-            parms = parent.parms;
-            parmv = parent.parmv;
-        }
-        else
-        {
-            // inherit session parameters
-            Object args = session.get("args");
-            if (null != args && args instanceof List<?>)
-            {
-                parmv = (List<Object>) args;
-                parms = new ArgList(parmv);
-            }
-        }
-
-        Pipe last = null;
-        Object[] mark = Pipe.mark();
-
-        for (List<List<Token>> pipeline : program)
-        {
-            ArrayList<Pipe> pipes = new ArrayList<Pipe>();
-
-            for (List<Token> statement : pipeline)
-            {
-                Pipe current = new Pipe(this, statement);
-
-                if (pipes.isEmpty())
-                {
-                    if (current.out == null)
-                    {
-                        current.setIn(session.in);
-                        current.setOut(session.out);
-                        current.setErr(session.err);
-                    }
-                }
-                else
-                {
-                    Pipe previous = pipes.get(pipes.size() - 1);
-                    previous.connect(current);
-                }
-                pipes.add(current);
-            }
-
-            if (pipes.size() == 1)
-            {
-                pipes.get(0).run();
-            }
-            else if (pipes.size() > 1)
-            {
-                for (Pipe pipe : pipes)
-                {
-                    pipe.start();
-                }
-                try
-                {
-                    for (Pipe pipe : pipes)
-                    {
-                        pipe.join();
-                    }
-                }
-                catch (InterruptedException e)
-                {
-                    for (Pipe pipe : pipes)
-                    {
-                        pipe.interrupt();
-                    }
-                    throw e;
-                }
-            }
-
-            last = pipes.remove(pipes.size() - 1);
-
-            for (Pipe pipe : pipes)
-            {
-                if (pipe.exception != null)
-                {
-                    // can't throw exception, as result is defined by last pipe
-                    Object oloc = session.get(LOCATION);
-                    String loc = (String.valueOf(oloc).contains(":") ? oloc + ": "
-                        : "pipe: ");
-                    session.err.println(loc + pipe.exception);
-                    session.put("pipe-exception", pipe.exception);
-                }
-            }
-
-            if (last.exception != null)
-            {
-                Pipe.reset(mark);
-                throw last.exception;
-            }
-        }
-
-        Pipe.reset(mark); // reset IO in case same thread used for new client
-
-        return last == null ? null : last.result;
-    }
-
-    private Object eval(Object v)
-    {
-        String s = v.toString();
-        if ("null".equals(s))
-        {
-            v = null;
-        }
-        else if ("false".equals(s))
-        {
-            v = false;
-        }
-        else if ("true".equals(s))
-        {
-            v = true;
-        }
-        else
-        {
-            try
-            {
-                v = s;
-                v = Double.parseDouble(s);    // if it parses as double
-                v = Long.parseLong(s);        // see whether it is integral
-            }
-            catch (NumberFormatException e)
-            {
-            }
-        }
-        return v;
-    }
-
-    public Object eval(final Token t) throws Exception
-    {
-        Object v = null;
-
-        switch (t.type)
-        {
-            case WORD:
-                v = Tokenizer.expand(t, this);
-
-                if (t == v)
-                {
-                    v = eval(v);
-                }
-                break;
-
-            case CLOSURE:
-                v = new Closure(session, this, t);
-                break;
-
-            case EXECUTION:
-                v = new Closure(session, this, t).execute(session, parms);
-                break;
-
-            case ARRAY:
-                v = array(t);
-                break;
-
-            case ASSIGN:
-                v = t.type;
-                break;
-
-            case EXPR:
-                v = expr(t.value);
-                break;
-
-            default:
-                throw new SyntaxError(t.line, t.column, "unexpected token: " + t.type);
-        }
-
-        return v;
-    }
-
-    //extra handling for CQL-Shell
-    private boolean isExpansionEnabled() {
-        Object v = session.get("org.apache.felix.gogo.expansion");
-        if (v != null) {
-            return Boolean.parseBoolean(v.toString());
-        }
-        return true;
-    }
-
-    /*
-     * executeStatement handles the following cases:
-     *    <string> '=' word // simple assignment
-     *    <string> '=' word word.. // complex assignment
-     *    <bareword> word.. // command invocation
-     *    <object> // value of <object>
-     *    <object> word.. // method call
-     */
-    public Object executeStatement(List<Token> statement) throws Exception
-    {
-        Object echo = session.get("echo");
-        String xtrace = null;
-
-        if (echo != null && !"false".equals(echo.toString()))
-        {
-            // set -x execution trace
-            StringBuilder buf = new StringBuilder("+");
-            for (Token token : statement)
-            {
-                buf.append(' ');
-                buf.append(token.source());
-            }
-            xtrace = buf.toString();
-            session.err.println(xtrace);
-        }
-
-        List<Object> values = new ArrayList<Object>();
-        errTok = statement.get(0);
-
-        if ((statement.size() > 3) && Type.ASSIGN.equals(statement.get(1).type))
-        {
-            errTok2 = statement.get(2);
-        }
-
-        for (Token t : statement)
-        {
-            Object v = isExpansionEnabled() ? eval(t) : t.toString();
-
-            if ((Type.EXECUTION == t.type) && (statement.size() == 1))
-            {
-                return v;
-            }
-
-            if (parms == v && parms != null)
-            {
-                values.addAll(parms); // explode $args array
-            }
-            else
-            {
-                values.add(v);
-            }
-        }
-
-        Object cmd = values.remove(0);
-        if (cmd == null)
-        {
-            if (values.isEmpty())
-            {
-                return null;
-            }
-
-            throw new RuntimeException("Command name evaluates to null: " + errTok);
-        }
-
-        if (cmd instanceof CharSequence && values.size() > 0
-            && Type.ASSIGN.equals(values.get(0)))
-        {
-            values.remove(0);
-            String scmd = cmd.toString();
-            Object value;
-
-            if (values.size() == 0)
-            {
-                return session.variables.remove(scmd);
-            }
-
-            if (values.size() == 1)
-            {
-                value = values.get(0);
-            }
-            else
-            {
-                cmd = values.remove(0);
-                if (null == cmd)
-                {
-                    throw new RuntimeException("Command name evaluates to null: "
-                        + errTok2);
-                }
-
-                trace2(xtrace, cmd, values);
-
-                value = bareword(statement.get(2)) ? executeCmd(cmd.toString(), values)
-                    : executeMethod(cmd, values);
-            }
-
-            return assignment(scmd, value);
-        }
-
-        trace2(xtrace, cmd, values);
-
-        return bareword(statement.get(0)) ? executeCmd(cmd.toString(), values)
-            : executeMethod(cmd, values);
-    }
-
-    // second level expanded execution trace
-    private void trace2(String trace1, Object cmd, List<Object> values)
-    {
-        if ("verbose".equals(session.get("echo")))
-        {
-            StringBuilder buf = new StringBuilder("+ " + cmd);
-
-            for (Object value : values)
-            {
-                buf.append(' ');
-                buf.append(value);
-            }
-
-            String trace2 = buf.toString();
-
-            if (!trace2.equals(trace1))
-            {
-                session.err.println("+" + trace2);
-            }
-        }
-    }
-
-    private boolean bareword(Token t) throws Exception
-    {
-        return ((t.type == Type.WORD) && (t == Tokenizer.expand(t, this)) && (eval((Object) t) instanceof String));
-    }
-
-    private Object executeCmd(String scmd, List<Object> values) throws Exception
-    {
-        String scopedFunction = scmd;
-        Object x = get(scmd);
-
-        if (!(x instanceof Function))
-        {
-            if (scmd.indexOf(':') < 0)
-            {
-                scopedFunction = "*:" + scmd;
-            }
-
-            x = get(scopedFunction);
-
-            if (x == null || !(x instanceof Function))
-            {
-                // try default command handler
-                if (session.get(DEFAULT_LOCK) == null)
-                {
-                    x = get("default");
-                    if (x == null)
-                    {
-                        x = get("*:default");
-                    }
-
-                    if (x instanceof Function)
-                    {
-                        try
-                        {
-                            session.put(DEFAULT_LOCK, true);
-                            values.add(0, scmd);
-                            return ((Function) x).execute(session, values);
-                        }
-                        finally
-                        {
-                            session.variables.remove(DEFAULT_LOCK);
-                        }
-                    }
-                }
-
-                throw new CommandNotFoundException(scmd);
-            }
-        }
-        return ((Function) x).execute(session, values);
-    }
-
-    private Object executeMethod(Object cmd, List<Object> values) throws Exception
-    {
-        if (values.isEmpty())
-        {
-            return cmd;
-        }
-
-        boolean dot = values.size() > 1 && ".".equals(String.valueOf(values.get(0)));
-
-        // FELIX-1473 - allow method chaining using dot pseudo-operator, e.g.
-        //  (bundle 0) . loadClass java.net.InetAddress . localhost . hostname
-        //  (((bundle 0) loadClass java.net.InetAddress ) localhost ) hostname
-        if (dot)
-        {
-            Object target = cmd;
-            ArrayList<Object> args = new ArrayList<Object>();
-            values.remove(0);
-
-            for (Object arg : values)
-            {
-                if (".".equals(arg))
-                {
-                    target = Reflective.invoke(session, target,
-                        args.remove(0).toString(), args);
-                    args.clear();
-                }
-                else
-                {
-                    args.add(arg);
-                }
-            }
-
-            if (args.size() == 0)
-            {
-                return target;
-            }
-
-            return Reflective.invoke(session, target, args.remove(0).toString(), args);
-        }
-        else if (cmd.getClass().isArray() && values.size() == 1)
-        {
-            Object[] cmdv = (Object[]) cmd;
-            String index = values.get(0).toString();
-            return "length".equals(index) ? cmdv.length : cmdv[Integer.parseInt(index)];
-        }
-        else
-        {
-            return Reflective.invoke(session, cmd, values.remove(0).toString(), values);
-        }
-    }
-
-    private Object assignment(String name, Object value)
-    {
-        session.variables.put(name, value);
-        return value;
-    }
-
-    private Object expr(CharSequence expr) throws Exception
-    {
-        return session.expr(expr);
-    }
-
-    private Object array(Token array) throws Exception
-    {
-        List<Token> list = new ArrayList<Token>();
-        Map<Token, Token> map = new LinkedHashMap<Token, Token>();
-        (new Parser(array, isExpansionEnabled())).array(list, map); //CQL-Handling
-
-        if (map.isEmpty())
-        {
-            List<Object> olist = new ArrayList<Object>();
-            for (Token t : list)
-            {
-                Object oval = eval(t);
-                if (oval.getClass().isArray())
-                {
-                    for (Object o : (Object[]) oval)
-                    {
-                        olist.add(o);
-                    }
-                }
-                else
-                {
-                    olist.add(oval);
-                }
-            }
-            return olist;
-        }
-        else
-        {
-            Map<Object, Object> omap = new LinkedHashMap<Object, Object>();
-            for (Entry<Token, Token> e : map.entrySet())
-            {
-                Token key = e.getKey();
-                Object k = eval(key);
-                if (!(k instanceof String))
-                {
-                    throw new SyntaxError(key.line, key.column,
-                        "map key null or not String: " + key);
-                }
-                omap.put(k, eval(e.getValue()));
-            }
-            return omap;
-        }
-    }
-
-    public Object get(String name)
-    {
-        if (parms != null)
-        {
-            if ("args".equals(name))
-            {
-                return parms;
-            }
-
-            if ("argv".equals(name))
-            {
-                return parmv;
-            }
-
-            if ("it".equals(name))
-            {
-                return parms.get(0);
-            }
-
-            if (name.length() == 1 && Character.isDigit(name.charAt(0)))
-            {
-                int i = name.charAt(0) - '0';
-                if (i > 0)
-                {
-                    return parms.get(i - 1);
-                }
-            }
-        }
-
-        return session.get(name);
-    }
-
-    public Object put(String key, Object value)
-    {
-        return session.variables.put(key, value);
-    }
-
-    @Override
-    public String toString()
-    {
-        return source.toString().trim().replaceAll("\n+", "\n").replaceAll(
-            "([^\\\\{(\\[])\n", "\\1;").replaceAll("[ \\\\\t\n]+", " ");
-    }
-
-    /**
-     * List that overrides toString() for implicit $args expansion.
-     * Also checks for index out of bounds, so that $1 evaluates to null
-     * rather than throwing IndexOutOfBoundsException.
-     * e.g. x = { a$args }; x 1 2 => a1 2 and not a[1, 2]
-     */
-    class ArgList extends AbstractList<Object>
-    {
-        private List<Object> list;
-
-        public ArgList(List<Object> args)
-        {
-            this.list = args;
-        }
-
-        @Override
-        public String toString()
-        {
-            StringBuilder buf = new StringBuilder();
-            for (Object o : list)
-            {
-                if (buf.length() > 0)
-                    buf.append(' ');
-                buf.append(o);
-            }
-            return buf.toString();
-        }
-
-        @Override
-        public Object get(int index)
-        {
-            return index < list.size() ? list.get(index) : null;
-        }
-
-        @Override
-        public Object remove(int index)
-        {
-            return list.remove(index);
-        }
-
-        @Override
-        public int size()
-        {
-            return list.size();
-        }
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/karaf/blob/cbbed8ac/shell/core/src/main/java/org/apache/felix/gogo/runtime/CommandSessionImpl.java
----------------------------------------------------------------------
diff --git a/shell/core/src/main/java/org/apache/felix/gogo/runtime/CommandSessionImpl.java b/shell/core/src/main/java/org/apache/felix/gogo/runtime/CommandSessionImpl.java
deleted file mode 100644
index ee3c09b..0000000
--- a/shell/core/src/main/java/org/apache/felix/gogo/runtime/CommandSessionImpl.java
+++ /dev/null
@@ -1,404 +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.
- */
-// DWB8: throw IllegatlStateException if session used after closed (as per rfc132)
-// DWB9: there is no API to list all variables: https://www.osgi.org/bugzilla/show_bug.cgi?id=49
-// DWB10: add SCOPE support: https://www.osgi.org/bugzilla/show_bug.cgi?id=51
-package org.apache.felix.gogo.runtime;
-
-import java.io.InputStream;
-import java.io.PrintStream;
-import java.lang.reflect.Method;
-import java.lang.reflect.Modifier;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Dictionary;
-import java.util.Enumeration;
-import java.util.Formatter;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Map;
-import java.util.concurrent.ConcurrentHashMap;
-
-import org.apache.felix.service.command.CommandSession;
-import org.apache.felix.service.command.Converter;
-import org.apache.felix.service.command.Function;
-import org.apache.felix.service.threadio.ThreadIO;
-
-public class CommandSessionImpl implements CommandSession, Converter
-{
-    public static final String SESSION_CLOSED = "session is closed";
-    public static final String VARIABLES = ".variables";
-    public static final String COMMANDS = ".commands";
-    private static final String COLUMN = "%-20s %s\n";
-    
-    protected InputStream in;
-    protected PrintStream out;
-    PrintStream err;
-    
-    private final CommandProcessorImpl processor;
-    protected final Map<String, Object> variables = new HashMap<String, Object>();
-    private boolean closed;
-
-    protected CommandSessionImpl(CommandProcessorImpl shell, InputStream in, PrintStream out, PrintStream err)
-    {
-        this.processor = shell;
-        this.in = in;
-        this.out = out;
-        this.err = err;
-    }
-    
-    ThreadIO threadIO()
-    {
-        return processor.threadIO;
-    }
-
-    public void close()
-    {
-        this.closed = true;
-    }
-
-    public Object execute(CharSequence commandline) throws Exception
-    {
-        assert processor != null;
-        assert processor.threadIO != null;
-
-        if (closed)
-        {
-            throw new IllegalStateException(SESSION_CLOSED);
-        }
-
-        processor.beforeExecute(this, commandline);
-
-        try
-        {
-            Closure impl = new Closure(this, null, commandline);
-            Object result = impl.execute(this, null);
-            processor.afterExecute(this, commandline, result);
-            return result;
-        }
-        catch (Exception e)
-        {
-            processor.afterExecute(this, commandline, e);
-            throw e;
-        }
-    }
-
-    public InputStream getKeyboard()
-    {
-        return in;
-    }
-
-    public Object get(String name)
-    {
-        // there is no API to list all variables, so overload name == null
-        if (name == null || VARIABLES.equals(name))
-        {
-            return Collections.unmodifiableSet(variables.keySet());
-        }
-
-        if (COMMANDS.equals(name))
-        {
-            return processor.getCommands();
-        }
-
-        Object val = processor.constants.get(name);
-        if( val != null )
-        {
-            return val;
-        }
-
-        val = variables.get("#" + name);
-        if (val instanceof Function)
-        {
-            try
-            {
-                val = ((Function) val).execute(this, null);
-            }
-            catch (Exception e)
-            {
-                // Ignore
-            }
-            return val;
-        }
-        else if( val != null )
-        {
-            return val;
-        }
-
-        val = variables.get(name);
-        if( val != null )
-        {
-            return val;
-        }
-
-        return processor.getCommand(name, variables.get("SCOPE"));
-    }
-
-    public void put(String name, Object value)
-    {
-        synchronized (variables)
-        {
-            variables.put(name, value);
-        }
-    }
-
-    public PrintStream getConsole()
-    {
-        return out;
-    }
-
-    @SuppressWarnings("unchecked")
-    public CharSequence format(Object target, int level, Converter escape)
-        throws Exception
-    {
-        if (target == null)
-        {
-            return "null";
-        }
-
-        if (target instanceof CharSequence)
-        {
-            return (CharSequence) target;
-        }
-
-        for (Converter c : processor.converters)
-        {
-            CharSequence s = c.format(target, level, this);
-            if (s != null)
-            {
-                return s;
-            }
-        }
-
-        if (target.getClass().isArray())
-        {
-            if (target.getClass().getComponentType().isPrimitive())
-            {
-                if (target.getClass().getComponentType() == boolean.class)
-                {
-                    return Arrays.toString((boolean[]) target);
-                }
-                else
-                {
-                    if (target.getClass().getComponentType() == byte.class)
-                    {
-                        return Arrays.toString((byte[]) target);
-                    }
-                    else
-                    {
-                        if (target.getClass().getComponentType() == short.class)
-                        {
-                            return Arrays.toString((short[]) target);
-                        }
-                        else
-                        {
-                            if (target.getClass().getComponentType() == int.class)
-                            {
-                                return Arrays.toString((int[]) target);
-                            }
-                            else
-                            {
-                                if (target.getClass().getComponentType() == long.class)
-                                {
-                                    return Arrays.toString((long[]) target);
-                                }
-                                else
-                                {
-                                    if (target.getClass().getComponentType() == float.class)
-                                    {
-                                        return Arrays.toString((float[]) target);
-                                    }
-                                    else
-                                    {
-                                        if (target.getClass().getComponentType() == double.class)
-                                        {
-                                            return Arrays.toString((double[]) target);
-                                        }
-                                        else
-                                        {
-                                            if (target.getClass().getComponentType() == char.class)
-                                            {
-                                                return Arrays.toString((char[]) target);
-                                            }
-                                        }
-                                    }
-                                }
-                            }
-                        }
-                    }
-                }
-            }
-            target = Arrays.asList((Object[]) target);
-        }
-        if (target instanceof Collection)
-        {
-            if (level == Converter.INSPECT)
-            {
-                StringBuilder sb = new StringBuilder();
-                Collection<?> c = (Collection<?>) target;
-                for (Object o : c)
-                {
-                    sb.append(format(o, level + 1, this));
-                    sb.append("\n");
-                }
-                return sb;
-            }
-            else
-            {
-                if (level == Converter.LINE)
-                {
-                    StringBuilder sb = new StringBuilder();
-                    Collection<?> c = (Collection<?>) target;
-                    sb.append("[");
-                    for (Object o : c)
-                    {
-                        if (sb.length() > 1)
-                        {
-                            sb.append(", ");
-                        }
-                        sb.append(format(o, level + 1, this));
-                    }
-                    sb.append("]");
-                    return sb;
-                }
-            }
-        }
-        if (target instanceof Dictionary)
-        {
-            Map<Object, Object> result = new HashMap<Object, Object>();
-            for (Enumeration e = ((Dictionary) target).keys(); e.hasMoreElements();)
-            {
-                Object key = e.nextElement();
-                result.put(key, ((Dictionary) target).get(key));
-            }
-            target = result;
-        }
-        if (target instanceof Map)
-        {
-            if (level == Converter.INSPECT)
-            {
-                StringBuilder sb = new StringBuilder();
-                Map<?, ?> c = (Map<?, ?>) target;
-                for (Map.Entry<?, ?> entry : c.entrySet())
-                {
-                    CharSequence key = format(entry.getKey(), level + 1, this);
-                    sb.append(key);
-                    for (int i = key.length(); i < 20; i++)
-                    {
-                        sb.append(' ');
-                    }
-                    sb.append(format(entry.getValue(), level + 1, this));
-                    sb.append("\n");
-                }
-                return sb;
-            }
-            else
-            {
-                if (level == Converter.LINE)
-                {
-                    StringBuilder sb = new StringBuilder();
-                    Map<?, ?> c = (Map<?, ?>) target;
-                    sb.append("[");
-                    for (Map.Entry<?, ?> entry : c.entrySet())
-                    {
-                        if (sb.length() > 1)
-                        {
-                            sb.append(", ");
-                        }
-                        sb.append(format(entry, level + 1, this));
-                    }
-                    sb.append("]");
-                    return sb;
-                }
-            }
-        }
-        if (level == Converter.INSPECT)
-        {
-            return inspect(target);
-        }
-        else
-        {
-            return target.toString();
-        }
-    }
-
-    CharSequence inspect(Object b)
-    {
-        boolean found = false;
-        Formatter f = new Formatter();
-        Method methods[] = b.getClass().getMethods();
-        for (Method m : methods)
-        {
-            try
-            {
-                String name = m.getName();
-                if (m.getName().startsWith("get") && !m.getName().equals("getClass")
-                    && m.getParameterTypes().length == 0
-                    && Modifier.isPublic(m.getModifiers()))
-                {
-                    found = true;
-                    name = name.substring(3);
-                    m.setAccessible(true);
-                    Object value = m.invoke(b, (Object[]) null);
-                    f.format(COLUMN, name, format(value, Converter.LINE, this));
-                }
-            }
-            catch (IllegalAccessException e)
-            {
-                // Ignore
-            }
-            catch (Exception e)
-            {
-                e.printStackTrace();
-            }
-        }
-        if (found)
-        {
-            return (StringBuilder) f.out();
-        }
-        else
-        {
-            return b.toString();
-        }
-    }
-
-    public Object convert(Class<?> desiredType, Object in)
-    {
-        return processor.convert(desiredType, in);
-    }
-
-    public CharSequence format(Object result, int inspect)
-    {
-        try
-        {
-            return format(result, inspect, this);
-        }
-        catch (Exception e)
-        {
-            return "<can not format " + result + ":" + e;
-        }
-    }
-
-    public Object expr(CharSequence expr)
-    {
-        return processor.expr(this, expr);
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/karaf/blob/cbbed8ac/shell/core/src/main/java/org/apache/felix/gogo/runtime/Parser.java
----------------------------------------------------------------------
diff --git a/shell/core/src/main/java/org/apache/felix/gogo/runtime/Parser.java b/shell/core/src/main/java/org/apache/felix/gogo/runtime/Parser.java
deleted file mode 100644
index b036830..0000000
--- a/shell/core/src/main/java/org/apache/felix/gogo/runtime/Parser.java
+++ /dev/null
@@ -1,181 +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.felix.gogo.runtime;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Map;
-
-import org.apache.felix.gogo.runtime.Tokenizer.Type;
-
-public class Parser
-{
-    private final Tokenizer tz;
-
-    public Parser(CharSequence program) {
-    	tz = new Tokenizer(program, null, false, true);
-    }
-    
-    public Parser(CharSequence program, boolean isExpansionEnabled)
-    {
-        tz = new Tokenizer(program, null, false, isExpansionEnabled);
-    }
-
-    public List<List<List<Token>>> program()
-    {
-        List<List<List<Token>>> program = new ArrayList<List<List<Token>>>();
-        
-        while (tz.next() != Type.EOT)
-        {
-            program.add(pipeline());
-            
-            switch (tz.type())
-            {
-                case SEMICOLON:
-                case NEWLINE:
-                    continue;
-            }
-            
-            break;
-        }
-
-        if (tz.next() != Type.EOT)
-            throw new RuntimeException("Program has trailing text: " + tz.value());
-
-        return program;
-    }
-
-    private List<List<Token>> pipeline()
-    {
-        List<List<Token>> pipeline = new ArrayList<List<Token>>();
-        
-        while (true)
-        {
-            pipeline.add(command());
-            switch (tz.type())
-            {
-                case PIPE:
-                    if (tz.next() == Type.EOT)
-                    {
-                        Token t = tz.token();
-                        throw new EOFError(t.line, t.column, "unexpected EOT after pipe '|'");
-                    }
-                    break;
-
-                default:
-                    return pipeline;
-            }
-        }
-    }
-
-    private List<Token> command()
-    {
-        List<Token> command = new ArrayList<Token>();
-
-        while (true)
-        {
-            Token t = tz.token();
-            
-            switch (t.type)
-            {
-                case WORD:
-                case CLOSURE:
-                case EXECUTION:
-                case ARRAY:
-                case ASSIGN:
-                case EXPR:
-                    break;
-                    
-                default:
-                    throw new SyntaxError(t.line, t.column, "unexpected token: " + t.type);
-            }
-            
-            command.add(t);
-            
-            switch (tz.next())
-            {
-                case PIPE:
-                case SEMICOLON:
-                case NEWLINE:
-                case EOT:
-                    return command;
-            }
-        }
-    }
-    
-    public void array(List<Token> list, Map<Token, Token> map) throws Exception
-    {
-        Token lt = null;
-        boolean isMap = false;
-
-        while (tz.next() != Type.EOT)
-        {
-            if (isMap)
-            {
-                Token key = lt;
-                lt = null;
-                if (null == key)
-                {
-                    key = tz.token();
-
-                    if (tz.next() != Type.ASSIGN)
-                    {
-                        Token t = tz.token();
-                        throw new SyntaxError(t.line, t.column,
-                            "map expected '=', found: " + t);
-                    }
-
-                    tz.next();
-                }
-
-                Token k = (list.isEmpty() ? key : list.remove(0));
-                Token v = tz.token();
-                map.put(k, v);
-            }
-            else
-            {
-                switch (tz.type())
-                {
-                    case WORD:
-                    case CLOSURE:
-                    case EXECUTION:
-                    case ARRAY:
-                        lt = tz.token();
-                        list.add(lt);
-                        break;
-
-                    case ASSIGN:
-                        if (list.size() == 1)
-                        {
-                            isMap = true;
-                            break;
-                        }
-                        // fall through
-                    default:
-                        lt = tz.token();
-                        throw new SyntaxError(lt.line, lt.column,
-                            "unexpected token in list: " + lt);
-                }
-            }
-        }
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/karaf/blob/cbbed8ac/shell/core/src/main/java/org/apache/felix/gogo/runtime/Tokenizer.java
----------------------------------------------------------------------
diff --git a/shell/core/src/main/java/org/apache/felix/gogo/runtime/Tokenizer.java b/shell/core/src/main/java/org/apache/felix/gogo/runtime/Tokenizer.java
deleted file mode 100644
index 866db9e..0000000
--- a/shell/core/src/main/java/org/apache/felix/gogo/runtime/Tokenizer.java
+++ /dev/null
@@ -1,813 +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.runtime;
-
-/**
- * Bash-like tokenizer.
- * 
- * Single and double quotes are just like Bash - single quotes escape everything
- * (including backslashes), newlines are allowed in quotes.
- * backslash-newline indicates a line continuation and is removed.
- * 
- * Variable expansion is just like Bash: $NAME or ${NAME[[:][-+=?WORD]},
- * except it can yield any Object. Variables expanded within double-quotes,
- * or adjacent to a String are converted to String.
- * 
- * Unlike bash, indirect variable expansion is supported using ${$NAME}.
- * 
- * Only a single variable assignment is recognized, with '=' being the second token.
- * (Bash allows name1=value1 name2=value2 ... command args)
- * 
- * Comments can only start where white space is allowed:
- * # or // starts a line comment, /* starts a block comment.
- * The following common uses do NOT start comments:
- *    ls http://example.com#anchor
- *    ls $dir/*.java
- * 
- * @see http://wiki.bash-hackers.org/syntax/basicgrammar
- */
-public class Tokenizer
-{
-    public enum Type
-    {
-        ASSIGN('='), PIPE('|'), SEMICOLON(';'), NEWLINE, ARRAY, CLOSURE, EXPR, EXECUTION, WORD, EOT;
-
-        private char c;
-
-        Type()
-        {
-        }
-
-        Type(char c)
-        {
-            this.c = c;
-        }
-
-        @Override
-        public String toString()
-        {
-            return (c == 0 ? super.toString() : "'" + c + "'");
-        }
-    }
-
-    private static final boolean DEBUG = false;
-    private static final char EOT = (char) -1;
-
-    private final CharSequence text;
-    private final Evaluate evaluate;
-    private final boolean inArray;
-    private final boolean inQuote;
-    //extra to disable some special handling of brackets etc. needed for CQL-Completion
-    private final boolean isExpansionEnabled;
-
-    private Type type = Type.NEWLINE;
-    private CharSequence value;
-    private Token token;
-
-    private short line;
-    private short column;
-    private char ch;
-    private int index;
-    private boolean firstWord;
-
-    public Tokenizer(CharSequence text)
-    {
-        this(text, null, false, true);
-    }
-    
-    public Tokenizer(CharSequence text, Evaluate evaluate, boolean inQuote)
-    {
-    	this(text, evaluate, inQuote, true);
-    }
-
-    //This constructor gets the isExpansionEnabled flag for extra handling of a CQL-Like shell
-    public Tokenizer(CharSequence text, Evaluate evaluate, boolean inQuote, boolean isExpansionEnabled)
-    {
-        this.text = text;
-        this.evaluate = evaluate;
-        this.inQuote = inQuote;
-        this.isExpansionEnabled  = isExpansionEnabled;
-        index = 0;
-        line = column = 1;
-
-        boolean array = false;
-
-        if (text instanceof Token)
-        {
-            Token t = (Token) text;
-            line = t.line;
-            column = t.column;
-            array = (Type.ARRAY == t.type);
-        }
-
-        inArray = array;
-        getch();
-
-        if (DEBUG)
-        {
-            if (inArray)
-                System.err.println("Tokenizer[" + text + "]");
-            else
-                System.err.println("Tokenizer<" + text + ">");
-        }
-    }
-
-    public Type type()
-    {
-        return type;
-    }
-
-    public CharSequence value()
-    {
-        return value;
-    }
-
-    public Token token()
-    {
-        return token;
-    }
-
-    public Type next()
-    {
-        final Type prevType = type;
-        token = null;
-        value = null;
-
-        short tLine;
-        short tColumn;
-
-        while (true)
-        {
-            skipSpace();
-            tLine = line;
-            tColumn = column;
-
-            switch (ch)
-            {
-                case EOT:
-                    type = Type.EOT;
-                    break;
-
-                case '\n':
-                    getch();
-                    if (inArray)
-                        continue;
-                    // only return NEWLINE once and not if not preceded by ; or |
-                    switch (prevType)
-                    {
-                        case PIPE:
-                        case SEMICOLON:
-                        case NEWLINE:
-                            continue;
-
-                        default:
-                            type = Type.NEWLINE;
-                            break;
-                    }
-                    break;
-
-                case '{':
-                case '(':
-                case '[':
-                	if (isExpansionEnabled) { //Disabled for CQL
-	                    value = group();
-	                    getch();
-                	} else {
-                		//treat it as normal word
-                		value = word();
-                        type = Type.WORD;
-                	}
-                    break;
-
-                case ';':
-                    getch();
-                    type = Type.SEMICOLON;
-                    break;
-
-                case '|':
-                    getch();
-                    type = Type.PIPE;
-                    break;
-
-                case '=':
-                    if (firstWord || inArray)
-                    {
-                        getch();
-                        type = Type.ASSIGN;
-                        break;
-                    }
-                    // fall through
-                default:
-                    value = word();
-                    type = Type.WORD;
-            }
-
-            firstWord = (Type.WORD == type && (Type.WORD != prevType && Type.ASSIGN != prevType));
-            token = new Token(type, value, tLine, tColumn);
-
-            if (DEBUG)
-            {
-                System.err.print("<" + type + ">");
-                if (Type.EOT == type)
-                {
-                    System.err.println();
-                }
-            }
-
-            return type;
-        }
-    }
-
-    private CharSequence word()
-    {
-        int start = index - 1;
-        int skipCR = 0;
-
-        do
-        {
-            switch (ch)
-            {
-                case '\n':
-                    if (index >= 2 && text.charAt(index - 2) == '\r')
-                        skipCR = 1;
-                    // fall through
-                case '=':
-                    if ((Type.WORD == type || Type.ASSIGN == type) && '=' == ch
-                        && !inArray)
-                        continue;
-                    // fall through
-                case ' ':
-                case '\t':
-                case '|':
-                case ';':
-                    return text.subSequence(start, index - 1 - skipCR);
-
-                case '{':
-                    group();
-                    break;
-
-                case '\\':
-                    escape();
-                    break;
-
-                case '\'':
-                case '"':
-                    skipQuote();
-                    break;
-            }
-        }
-        while (getch() != EOT);
-
-        return text.subSequence(start, index - 1);
-    }
-
-    private CharSequence group()
-    {
-        final char push = ch;
-        final char push2;
-        final char pop;
-        final char pop2;
-
-        switch (ch)
-        {
-            case '{':
-                type = Type.CLOSURE;
-                push2 = 0;
-                pop = '}';
-                pop2 = 0;
-                break;
-            case '(':
-                if (peek() == '(') {
-                    getch();
-                    push2 = '(';
-                    type = Type.EXPR;
-                    pop = ')';
-                    pop2 = ')';
-                } else {
-                    type = Type.EXECUTION;
-                    push2 = 0;
-                    pop = ')';
-                    pop2 = 0;
-                }
-                break;
-            case '[':
-                type = Type.ARRAY;
-                push2 = 0;
-                pop = ']';
-                pop2 = 0;
-                break;
-            default:
-                assert false;
-                push2 = 0;
-                pop = 0;
-                pop2 = 0;
-        }
-
-        short sLine = line;
-        short sCol = column;
-        int start = index;
-        int depth = 1;
-
-        while (true)
-        {
-            boolean comment = false;
-
-            switch (ch)
-            {
-                case '{':
-                case '(':
-                case '[':
-                case '\n':
-                    comment = true;
-                    break;
-            }
-
-            if (getch() == EOT)
-            {
-                throw new EOFError(sLine, sCol, "unexpected EOT looking for matching '"
-                    + pop + "'");
-            }
-
-            // don't recognize comments that start within a word
-            if (comment || isBlank(ch))
-                skipSpace();
-
-            switch (ch)
-            {
-                case '"':
-                case '\'':
-                    skipQuote();
-                    break;
-
-                case '\\':
-                    ch = escape();
-                    break;
-
-                default:
-                    if (push == ch) {
-                        depth++;
-                    }
-                    else if (pop == ch && --depth == 0) {
-                        if (pop2 == 0)
-                            return text.subSequence(start, index - 1);
-                        else if (pop2 == peek()) {
-                            getch();
-                            return text.subSequence(start, index - 2);
-                        }
-                    }
-            }
-        }
-
-    }
-
-    private char escape()
-    {
-        assert '\\' == ch;
-
-        switch (getch())
-        {
-            case 'u':
-                getch();
-                getch();
-                getch();
-                getch();
-
-                if (EOT == ch)
-                {
-                    throw new EOFError(line, column, "unexpected EOT in \\u escape");
-                }
-
-                String u = text.subSequence(index - 4, index).toString();
-
-                try
-                {
-                    return (char) Integer.parseInt(u, 16);
-                }
-                catch (NumberFormatException e)
-                {
-                    throw new SyntaxError(line, column, "bad unicode escape: \\u" + u);
-                }
-
-            case EOT:
-                throw new EOFError(line, column, "unexpected EOT in \\ escape");
-
-            case '\n':
-                return '\0'; // line continuation
-
-            case '\\':
-            case '\'':
-            case '"':
-            case '$':
-                return ch;
-
-            default:
-                return ch;
-        }
-    }
-
-    private void skipQuote()
-    {
-        assert '\'' == ch || '"' == ch;
-        final char quote = ch;
-        final short sLine = line;
-        final short sCol = column;
-
-        while (getch() != EOT)
-        {
-            if (quote == ch)
-                return;
-
-            if ((quote == '"') && ('\\' == ch))
-                escape();
-        }
-
-        throw new EOFError(sLine, sCol, "unexpected EOT looking for matching quote: "
-            + quote);
-    }
-
-    private void skipSpace()
-    {
-        while (true)
-        {
-            while (isBlank(ch))
-            {
-                getch();
-            }
-
-            // skip continuation lines, but not other escapes
-            if (('\\' == ch) && (peek() == '\n'))
-            {
-                getch();
-                getch();
-                continue;
-            }
-
-            // skip comments
-            if (('/' == ch) || ('#' == ch))
-            {
-                if (('#' == ch) || (peek() == '/'))
-                {
-                    while ((getch() != EOT) && ('\n' != ch))
-                    {
-                    }
-                    continue;
-                }
-                else if ('*' == peek())
-                {
-                    short sLine = line;
-                    short sCol = column;
-                    getch();
-
-                    while ((getch() != EOT) && !(('*' == ch) && (peek() == '/')))
-                    {
-                    }
-
-                    if (EOT == ch)
-                    {
-                        throw new EOFError(sLine, sCol,
-                            "unexpected EOT looking for closing comment: */");
-                    }
-
-                    getch();
-                    getch();
-                    continue;
-                }
-            }
-
-            break;
-        }
-    }
-
-    private boolean isBlank(char ch)
-    {
-        return ' ' == ch || '\t' == ch;
-    }
-
-    private boolean isName(char ch)
-    {
-        return Character.isJavaIdentifierPart(ch) && (ch != '$') || ('.' == ch);
-    }
-
-    /**
-     * expand variables, quotes and escapes in word.
-     * @param vars
-     * @return
-     * @throws Exception 
-     */
-    public static Object expand(CharSequence word, Evaluate eval) throws Exception
-    {
-        return expand(word, eval, false);
-    }
-
-    private static Object expand(CharSequence word, Evaluate eval, boolean inQuote) throws Exception
-    {
-        final String special = "$\\\"'";
-        int i = word.length();
-
-        while ((--i >= 0) && (special.indexOf(word.charAt(i)) == -1))
-        {
-        }
-
-        // shortcut if word doesn't contain any special characters
-        if (i < 0)
-            return word;
-
-        return new Tokenizer(word, eval, inQuote).expand();
-    }
-
-    public Object expand(CharSequence word, short line, short column) throws Exception
-    {
-        return expand(new Token(Type.WORD, word, line, column), evaluate, inQuote);
-    }
-
-    private Token word(CharSequence value)
-    {
-        return new Token(Type.WORD, value, line, column);
-    }
-
-    private Object expand() throws Exception
-    {
-        StringBuilder buf = new StringBuilder();
-
-        while (ch != EOT)
-        {
-            int start = index;
-
-            switch (ch)
-            {
-                case '$':
-                    Object val = expandVar();
-
-                    if (EOT == ch && buf.length() == 0)
-                    {
-                        return val;
-                    }
-
-                    if (null != val)
-                    {
-                        buf.append(val);
-                    }
-
-                    continue; // expandVar() has already read next char
-
-                case '\\':
-                    ch = (inQuote && ("u$\\\n\"".indexOf(peek()) == -1)) ? '\\'
-                        : escape();
-
-                    if (ch != '\0') // ignore line continuation
-                    {
-                        buf.append(ch);
-                    }
-
-                    break;
-
-                case '"':
-                    Token ww = word(null);
-                    skipQuote();
-                    ww.value = text.subSequence(start, index - 1);
-                    value = ww;
-                    Object expand = expand(value, evaluate, true);
-
-                    if (eot() && buf.length() == 0 && value == expand)
-                    {
-                        // FELIX-2468 avoid returning CharSequence implementation
-                        return ww.value.toString();
-                    }
-
-                    if (null != expand)
-                    {
-                        buf.append(expand.toString());
-                    }
-                    break;
-
-                case '\'':
-                    if (!inQuote)
-                    {
-                        skipQuote();
-                        value = text.subSequence(start, index - 1);
-
-                        if (eot() && buf.length() == 0)
-                        {
-                            return value;
-                        }
-
-                        buf.append(value);
-                        break;
-                    }
-                    // else fall through
-                default:
-                    buf.append(ch);
-            }
-
-            getch();
-        }
-
-        return buf.toString();
-    }
-
-    private Object expandVar() throws Exception
-    {
-        assert '$' == ch;
-        Object val;
-
-        if (getch() != '{')
-        {
-            if ('(' == ch)
-            {
-                short sLine = line;
-                short sCol = column;
-                if ('(' == peek())
-                {
-                    val = evaluate.eval(new Token(Type.EXPR, group(), sLine, sCol));
-                    getch();
-                }
-                else
-                {
-                    // support $(...) FELIX-2433
-                    val = evaluate.eval(new Token(Type.EXECUTION, group(), sLine, sCol));
-                    getch();
-                }
-            }
-            else
-            {
-                int start = index - 1;
-                while (isName(ch))
-                {
-                    getch();
-                }
-
-                if (index - 1 == start)
-                {
-                    val = "$";
-                }
-                else
-                {
-                    String name = text.subSequence(start, index - 1).toString();
-                    val = evaluate.get(name);
-                }
-            }
-        }
-        else
-        {
-            // ${NAME[[:]-+=?]WORD}
-            short sLine = line;
-            short sCol = column;
-            CharSequence group = group();
-            char c;
-            int i = 0;
-
-            while (i < group.length())
-            {
-                switch (group.charAt(i))
-                {
-                    case ':':
-                    case '-':
-                    case '+':
-                    case '=':
-                    case '?':
-                        break;
-
-                    default:
-                        ++i;
-                        continue;
-                }
-                break;
-            }
-
-            sCol += i;
-
-            String name = String.valueOf(expand(group.subSequence(0, i), sLine, sCol));
-
-            for (int j = 0; j < name.length(); ++j)
-            {
-                if (!isName(name.charAt(j)))
-                {
-                    throw new SyntaxError(sLine, sCol, "bad name: ${" + group + "}");
-                }
-            }
-
-            val = evaluate.get(name);
-
-            if (i < group.length())
-            {
-                c = group.charAt(i++);
-                if (':' == c)
-                {
-                    c = (i < group.length() ? group.charAt(i++) : EOT);
-                }
-
-                CharSequence word = group.subSequence(i, group.length());
-
-                switch (c)
-                {
-                    case '-':
-                    case '=':
-                        if (null == val)
-                        {
-                            val = expand(word, evaluate, false);
-                            if ('=' == c)
-                            {
-                                evaluate.put(name, val);
-                            }
-                        }
-                        break;
-
-                    case '+':
-                        if (null != val)
-                        {
-                            val = expand(word, evaluate, false);
-                        }
-                        break;
-
-                    case '?':
-                        if (null == val)
-                        {
-                            val = expand(word, evaluate, false);
-                            if (null == val || val.toString().length() == 0)
-                            {
-                                val = "parameter not set";
-                            }
-                            throw new IllegalArgumentException(name + ": " + val);
-                        }
-                        break;
-
-                    default:
-                        throw new SyntaxError(sLine, sCol, "bad substitution: ${" + group
-                            + "}");
-                }
-            }
-            getch();
-        }
-
-        return val;
-    }
-
-    /**
-     * returns true if getch() will return EOT
-     * @return
-     */
-    private boolean eot()
-    {
-        return index >= text.length();
-    }
-
-    private char getch()
-    {
-        return ch = getch(false);
-    }
-
-    private char peek()
-    {
-        return getch(true);
-    }
-
-    private char getch(boolean peek)
-    {
-        if (eot())
-        {
-            if (!peek)
-            {
-                ++index;
-                ch = EOT;
-            }
-            return EOT;
-        }
-
-        int current = index;
-        char c = text.charAt(index++);
-
-        if (('\r' == c) && !eot() && (text.charAt(index) == '\n'))
-            c = text.charAt(index++);
-
-        if (peek)
-        {
-            index = current;
-        }
-        else if ('\n' == c)
-        {
-            ++line;
-            column = 0;
-        }
-        else
-            ++column;
-
-        return c;
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/karaf/blob/cbbed8ac/shell/core/src/main/java/org/apache/karaf/shell/api/action/Parsing.java
----------------------------------------------------------------------
diff --git a/shell/core/src/main/java/org/apache/karaf/shell/api/action/Parsing.java b/shell/core/src/main/java/org/apache/karaf/shell/api/action/Parsing.java
new file mode 100644
index 0000000..d9808c5
--- /dev/null
+++ b/shell/core/src/main/java/org/apache/karaf/shell/api/action/Parsing.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.api.action;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * The @Parsing annotation can be used on a {@link org.apache.karaf.shell.api.action.Command}
+ * to specify a custom {@link org.apache.karaf.shell.api.console.Parser} to use for the command.
+ *
+ * @see org.apache.karaf.shell.api.console.Parser
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.TYPE})
+public @interface Parsing {
+
+    /**
+     * The parser class to use for this command.
+     */
+    Class<?> value();
+
+}

http://git-wip-us.apache.org/repos/asf/karaf/blob/cbbed8ac/shell/core/src/main/java/org/apache/karaf/shell/api/console/Command.java
----------------------------------------------------------------------
diff --git a/shell/core/src/main/java/org/apache/karaf/shell/api/console/Command.java b/shell/core/src/main/java/org/apache/karaf/shell/api/console/Command.java
index 4bb1db0..8ac47fc 100644
--- a/shell/core/src/main/java/org/apache/karaf/shell/api/console/Command.java
+++ b/shell/core/src/main/java/org/apache/karaf/shell/api/console/Command.java
@@ -48,4 +48,11 @@ public interface Command extends Function {
      */
     Completer getCompleter(boolean scoped);
 
+    /**
+     * Retrieve the parser associated with this command or null for the default one.
+     *
+     * @return
+     */
+    Parser getParser();
+
 }

http://git-wip-us.apache.org/repos/asf/karaf/blob/cbbed8ac/shell/core/src/main/java/org/apache/karaf/shell/api/console/Parser.java
----------------------------------------------------------------------
diff --git a/shell/core/src/main/java/org/apache/karaf/shell/api/console/Parser.java b/shell/core/src/main/java/org/apache/karaf/shell/api/console/Parser.java
new file mode 100644
index 0000000..c653c27
--- /dev/null
+++ b/shell/core/src/main/java/org/apache/karaf/shell/api/console/Parser.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.api.console;
+
+/**
+ * A <code>Parser</code> is used by the console to parse a command line.
+ */
+public interface Parser {
+
+    CommandLine parse(Session session, String command, int cursor);
+
+}

http://git-wip-us.apache.org/repos/asf/karaf/blob/cbbed8ac/shell/core/src/main/java/org/apache/karaf/shell/impl/action/command/ActionCommand.java
----------------------------------------------------------------------
diff --git a/shell/core/src/main/java/org/apache/karaf/shell/impl/action/command/ActionCommand.java b/shell/core/src/main/java/org/apache/karaf/shell/impl/action/command/ActionCommand.java
index 4cd58ca..9c33d34 100644
--- a/shell/core/src/main/java/org/apache/karaf/shell/impl/action/command/ActionCommand.java
+++ b/shell/core/src/main/java/org/apache/karaf/shell/impl/action/command/ActionCommand.java
@@ -22,8 +22,10 @@ import java.util.List;
 
 import org.apache.karaf.shell.api.action.Action;
 import org.apache.karaf.shell.api.action.Command;
+import org.apache.karaf.shell.api.action.Parsing;
 import org.apache.karaf.shell.api.console.CommandLine;
 import org.apache.karaf.shell.api.console.Completer;
+import org.apache.karaf.shell.api.console.Parser;
 import org.apache.karaf.shell.api.console.Session;
 
 public class ActionCommand implements org.apache.karaf.shell.api.console.Command {
@@ -60,6 +62,15 @@ public class ActionCommand implements org.apache.karaf.shell.api.console.Command
         return new ArgumentCompleter(this, scoped);
     }
 
+    @Override
+    public Parser getParser() {
+        Parsing parsing = actionClass.getAnnotation(Parsing.class);
+        if (parsing != null) {
+            return new DelayedParser(parsing.value());
+        }
+        return null;
+    }
+
     protected Completer getCompleter(Class<?> clazz) {
         return new DelayedCompleter(clazz);
     }
@@ -106,4 +117,21 @@ public class ActionCommand implements org.apache.karaf.shell.api.console.Command
         }
     }
 
+    public static class DelayedParser implements Parser {
+        private final Class<?> clazz;
+
+        public DelayedParser(Class<?> clazz) {
+            this.clazz = clazz;
+        }
+
+        @Override
+        public CommandLine parse(Session session, String command, int cursor) {
+            Object service = session.getRegistry().getService(clazz);
+            if (service instanceof Parser) {
+                return ((Parser) service).parse(session, command, cursor);
+            }
+            throw new IllegalStateException("Could not find specified parser");
+        }
+    }
+
 }

http://git-wip-us.apache.org/repos/asf/karaf/blob/cbbed8ac/shell/core/src/main/java/org/apache/karaf/shell/impl/action/command/ManagerImpl.java
----------------------------------------------------------------------
diff --git a/shell/core/src/main/java/org/apache/karaf/shell/impl/action/command/ManagerImpl.java b/shell/core/src/main/java/org/apache/karaf/shell/impl/action/command/ManagerImpl.java
index 470b4f5..79ee43c 100644
--- a/shell/core/src/main/java/org/apache/karaf/shell/impl/action/command/ManagerImpl.java
+++ b/shell/core/src/main/java/org/apache/karaf/shell/impl/action/command/ManagerImpl.java
@@ -35,6 +35,7 @@ import org.apache.karaf.shell.api.action.lifecycle.Manager;
 import org.apache.karaf.shell.api.action.lifecycle.Reference;
 import org.apache.karaf.shell.api.action.lifecycle.Service;
 import org.apache.karaf.shell.api.console.Completer;
+import org.apache.karaf.shell.api.console.Parser;
 import org.apache.karaf.shell.api.console.Registry;
 import org.apache.karaf.shell.support.converter.GenericType;
 
@@ -140,7 +141,9 @@ public class ManagerImpl implements Manager {
             }
             registrations.register(command);
         }
-        if (allowCustomServices || Completer.class.isAssignableFrom(clazz)) {
+        if (allowCustomServices
+                || Completer.class.isAssignableFrom(clazz)
+                || Parser.class.isAssignableFrom(clazz)) {
             try {
                 // Create completer
                 Object completer = instantiate(clazz);

http://git-wip-us.apache.org/repos/asf/karaf/blob/cbbed8ac/shell/core/src/main/java/org/apache/karaf/shell/impl/console/CompleterAsCompletor.java
----------------------------------------------------------------------
diff --git a/shell/core/src/main/java/org/apache/karaf/shell/impl/console/CompleterAsCompletor.java b/shell/core/src/main/java/org/apache/karaf/shell/impl/console/CompleterAsCompletor.java
index e732dfa..49a4420 100644
--- a/shell/core/src/main/java/org/apache/karaf/shell/impl/console/CompleterAsCompletor.java
+++ b/shell/core/src/main/java/org/apache/karaf/shell/impl/console/CompleterAsCompletor.java
@@ -20,9 +20,10 @@ package org.apache.karaf.shell.impl.console;
 
 import java.util.List;
 
+import org.apache.karaf.shell.api.console.CommandLine;
 import org.apache.karaf.shell.api.console.Completer;
 import org.apache.karaf.shell.api.console.Session;
-import org.apache.karaf.shell.impl.console.parsing.CommandLineImpl;
+import org.apache.karaf.shell.impl.console.parsing.CommandLineParser;
 
 public class CompleterAsCompletor implements jline.console.completer.Completer {
 
@@ -36,15 +37,8 @@ public class CompleterAsCompletor implements jline.console.completer.Completer {
 
     @SuppressWarnings("unchecked")
 	public int complete(String buffer, int cursor, @SuppressWarnings("rawtypes") List candidates) {
-        return completer.complete(session, CommandLineImpl.build(buffer, cursor, isExpansionEnabled()), candidates); //CQL-Handling
+        CommandLine cmdLine = CommandLineParser.buildCommandLine(session, buffer, cursor);
+        return completer.complete(session, cmdLine, candidates);
     }
 
-    //special handling for CQL-Shell, disables the brackets from completion
-    private boolean isExpansionEnabled() {
-        Object v = session.get("org.apache.felix.gogo.expansion");
-        if (v != null) {
-            return Boolean.parseBoolean(v.toString());
-        }
-        return true;
-    }
 }

http://git-wip-us.apache.org/repos/asf/karaf/blob/cbbed8ac/shell/core/src/main/java/org/apache/karaf/shell/impl/console/ConsoleSessionImpl.java
----------------------------------------------------------------------
diff --git a/shell/core/src/main/java/org/apache/karaf/shell/impl/console/ConsoleSessionImpl.java b/shell/core/src/main/java/org/apache/karaf/shell/impl/console/ConsoleSessionImpl.java
index 3d63123..4ff0c96 100644
--- a/shell/core/src/main/java/org/apache/karaf/shell/impl/console/ConsoleSessionImpl.java
+++ b/shell/core/src/main/java/org/apache/karaf/shell/impl/console/ConsoleSessionImpl.java
@@ -53,6 +53,7 @@ import org.apache.karaf.shell.api.console.Registry;
 import org.apache.karaf.shell.api.console.Session;
 import org.apache.karaf.shell.api.console.SessionFactory;
 import org.apache.karaf.shell.api.console.Terminal;
+import org.apache.karaf.shell.impl.console.parsing.CommandLineParser;
 import org.apache.karaf.shell.support.ShellUtil;
 import org.apache.karaf.shell.support.completers.FileCompleter;
 import org.apache.karaf.shell.support.completers.FileOrUriCompleter;
@@ -358,13 +359,12 @@ public class ConsoleSessionImpl implements Session {
 
     private String readAndParseCommand() throws IOException {
         String command = null;
-        boolean loop = true;
         boolean first = true;
-        while (loop) {
+        while (true) {
             checkInterrupt();
             String line = reader.readLine(first ? getPrompt() : "> ");
             if (line == null) {
-                break;
+                return null;
             }
             if (command == null) {
                 command = line;
@@ -386,24 +386,15 @@ public class ConsoleSessionImpl implements Session {
                 }
             }
             if (command.length() > 0 && command.charAt(command.length() - 1) == '\\') {
-                loop = true;
                 first = false;
             } else {
                 try {
-                    Class<?> cl = CommandSession.class.getClassLoader().loadClass("org.apache.felix.gogo.runtime.Parser");
-                    Object parser = cl.getConstructor(CharSequence.class).newInstance(command);
-                    cl.getMethod("program").invoke(parser);
-                    loop = false;
+                    return CommandLineParser.parse(this, command);
                 } catch (Exception e) {
-                    loop = true;
                     first = false;
-                } catch (Throwable t) {
-                    // Reflection problem ? just quit
-                    loop = false;
                 }
             }
         }
-        return command;
     }
 
     private void executeScript(String scriptFileName) {