karaf-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From anierb...@apache.org
Subject karaf git commit: [KARAF-3455] - Enhance Shell to support "less" completion
Date Tue, 20 Jan 2015 22:12:48 GMT
Repository: karaf
Updated Branches:
  refs/heads/master cf654b1d4 -> c19a22d0b


[KARAF-3455] - Enhance Shell to support "less" completion

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

Branch: refs/heads/master
Commit: c19a22d0b3a56e97d46d7b8ebbe02b5e7463f298
Parents: cf654b1
Author: anierbeck <bcanhome@googlemail.com>
Authored: Tue Jan 20 23:09:10 2015 +0100
Committer: anierbeck <bcanhome@googlemail.com>
Committed: Tue Jan 20 23:09:10 2015 +0100

----------------------------------------------------------------------
 .../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 +++++++++++++++++++
 .../impl/console/CompleterAsCompletor.java      |  13 +-
 .../impl/console/parsing/CommandLineImpl.java   |   8 +-
 .../shell/impl/console/parsing/Parser.java      |  20 +-
 7 files changed, 2115 insertions(+), 10 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/karaf/blob/c19a22d0/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
new file mode 100644
index 0000000..4994ec3
--- /dev/null
+++ b/shell/core/src/main/java/org/apache/felix/gogo/runtime/Closure.java
@@ -0,0 +1,686 @@
+/*
+ * 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/c19a22d0/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
new file mode 100644
index 0000000..ee3c09b
--- /dev/null
+++ b/shell/core/src/main/java/org/apache/felix/gogo/runtime/CommandSessionImpl.java
@@ -0,0 +1,404 @@
+/*
+ * 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/c19a22d0/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
new file mode 100644
index 0000000..b036830
--- /dev/null
+++ b/shell/core/src/main/java/org/apache/felix/gogo/runtime/Parser.java
@@ -0,0 +1,181 @@
+/*
+ * 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/c19a22d0/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
new file mode 100644
index 0000000..866db9e
--- /dev/null
+++ b/shell/core/src/main/java/org/apache/felix/gogo/runtime/Tokenizer.java
@@ -0,0 +1,813 @@
+/*
+ * 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/c19a22d0/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 fb8ebfb..e732dfa 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
@@ -34,8 +34,17 @@ public class CompleterAsCompletor implements jline.console.completer.Completer {
         this.completer = completer;
     }
 
-    public int complete(String buffer, int cursor, List candidates) {
-        return completer.complete(session, CommandLineImpl.build(buffer, cursor), candidates);
+    @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
     }
 
+    //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/c19a22d0/shell/core/src/main/java/org/apache/karaf/shell/impl/console/parsing/CommandLineImpl.java
----------------------------------------------------------------------
diff --git a/shell/core/src/main/java/org/apache/karaf/shell/impl/console/parsing/CommandLineImpl.java b/shell/core/src/main/java/org/apache/karaf/shell/impl/console/parsing/CommandLineImpl.java
index 6a603d0..9124795 100644
--- a/shell/core/src/main/java/org/apache/karaf/shell/impl/console/parsing/CommandLineImpl.java
+++ b/shell/core/src/main/java/org/apache/karaf/shell/impl/console/parsing/CommandLineImpl.java
@@ -34,7 +34,11 @@ public class CommandLineImpl implements CommandLine {
     private final String buffer;
 
     public static CommandLine build(String buffer, int cursor) {
-        Parser parser = new Parser(buffer, cursor);
+        return build(buffer, cursor, true);
+    }
+    
+	public static CommandLine build(String buffer, int cursor, boolean expansionEnabled) { //CQL-Handling
+		Parser parser = new Parser(buffer, cursor, expansionEnabled);
         try {
             List<List<List<String>>> program = parser.program();
             List<String> pipe = program.get(parser.c0).get(parser.c1);
@@ -42,7 +46,7 @@ public class CommandLineImpl implements CommandLine {
         } catch (Throwable t) {
             return new CommandLineImpl(new String[] { buffer }, 0, cursor, cursor, buffer);
         }
-    }
+	}
 
     /**
      *  @param  arguments           the array of tokens

http://git-wip-us.apache.org/repos/asf/karaf/blob/c19a22d0/shell/core/src/main/java/org/apache/karaf/shell/impl/console/parsing/Parser.java
----------------------------------------------------------------------
diff --git a/shell/core/src/main/java/org/apache/karaf/shell/impl/console/parsing/Parser.java b/shell/core/src/main/java/org/apache/karaf/shell/impl/console/parsing/Parser.java
index 16f6a21..73f4b98 100644
--- a/shell/core/src/main/java/org/apache/karaf/shell/impl/console/parsing/Parser.java
+++ b/shell/core/src/main/java/org/apache/karaf/shell/impl/console/parsing/Parser.java
@@ -39,13 +39,21 @@ public class Parser {
     int c1;
     int c2;
     int c3;
+    
+    private boolean isExpansionEnabled; //CQL-Handling
 
     public Parser(String text, int cursor) {
-        this.text = text;
-        this.cursor = cursor;
+        this(text, cursor, true);
     }
 
-    void ws() {
+    //CQL-Handling
+    public Parser(String text, int cursor, boolean expansionEnabled) {
+    	this.text = text;
+        this.cursor = cursor;
+        this.isExpansionEnabled = expansionEnabled; //CQL-Handling
+	}
+
+	void ws() {
         // derek: BUGFIX: loop if comment  at beginning of input
         //while (!eof() && Character.isWhitespace(peek())) {
         while (!eof() && (!escaped && Character.isWhitespace(peek()) || current == 0)) {
@@ -226,7 +234,7 @@ public class Parser {
         start = current;
         try {
             char c = next();
-            if (!escaped) {
+            if (!escaped && isExpansionEnabled) { //CQL-Handling
                 switch (c) {
                     case '{':
                         return text.substring(start, find('}', '{'));
@@ -252,11 +260,11 @@ public class Parser {
                     if (Character.isWhitespace(c) || c == ';' || c == '|' || c == '=') {
                         break;
                     }
-                    else if (c == '{') {
+                    else if (c == '{' && isExpansionEnabled) { //CQL-Handling
                         next();
                         find('}', '{');
                     }
-                    else if (c == '(') {
+                    else if (c == '(' && isExpansionEnabled) { //CQL-Handling
                         next();
                         find(')', '(');
                     }


Mime
View raw message