FELIX-1304: Better support for variables evaluation in arguments
git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@791913 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/gogo/commands/src/main/java/org/apache/felix/gogo/commands/basic/AbstractCommand.java b/gogo/commands/src/main/java/org/apache/felix/gogo/commands/basic/AbstractCommand.java
index 39c7cf3..1c1fe04 100644
--- a/gogo/commands/src/main/java/org/apache/felix/gogo/commands/basic/AbstractCommand.java
+++ b/gogo/commands/src/main/java/org/apache/felix/gogo/commands/basic/AbstractCommand.java
@@ -30,8 +30,11 @@
public Object execute(CommandSession session, List<Object> arguments) throws Exception {
Action action = createNewAction();
- getPreparator().prepare(action, session, arguments);
- return action.execute(session);
+ if (getPreparator().prepare(action, session, arguments)) {
+ return action.execute(session);
+ } else {
+ return null;
+ }
}
protected abstract Action createNewAction() throws Exception;
diff --git a/gogo/commands/src/main/java/org/apache/felix/gogo/commands/basic/ActionPreparator.java b/gogo/commands/src/main/java/org/apache/felix/gogo/commands/basic/ActionPreparator.java
index b10303a..4e29889 100644
--- a/gogo/commands/src/main/java/org/apache/felix/gogo/commands/basic/ActionPreparator.java
+++ b/gogo/commands/src/main/java/org/apache/felix/gogo/commands/basic/ActionPreparator.java
@@ -7,6 +7,6 @@
public interface ActionPreparator {
- void prepare(Action action, CommandSession session, List<Object> arguments) throws Exception;
+ boolean prepare(Action action, CommandSession session, List<Object> arguments) throws Exception;
}
diff --git a/gogo/commands/src/main/java/org/apache/felix/gogo/commands/basic/DefaultActionPreparator.java b/gogo/commands/src/main/java/org/apache/felix/gogo/commands/basic/DefaultActionPreparator.java
index a7d650c..7ea0cd8 100644
--- a/gogo/commands/src/main/java/org/apache/felix/gogo/commands/basic/DefaultActionPreparator.java
+++ b/gogo/commands/src/main/java/org/apache/felix/gogo/commands/basic/DefaultActionPreparator.java
@@ -72,7 +72,7 @@
}
};
- public void prepare(Action action, CommandSession session, List<Object> params) throws Exception
+ public boolean prepare(Action action, CommandSession session, List<Object> params) throws Exception
{
Map<Option, Field> options = new HashMap<Option, Field>();
Map<Argument, Field> arguments = new HashMap<Argument, Field>();
@@ -114,7 +114,7 @@
// Check for help
if (HELP.name().equals(param) || Arrays.asList(HELP.aliases()).contains(param)) {
printUsage(action.getClass().getAnnotation(Command.class), options.keySet(), arguments.keySet(), System.out);
- return;
+ return false;
}
if (processOptions && param instanceof String && ((String) param).startsWith("-")) {
boolean isKeyValuePair = ((String) param).indexOf('=') != -1;
@@ -201,6 +201,7 @@
field.setAccessible(true);
field.set(action, value);
}
+ return true;
}
protected void printUsage(Command command, Set<Option> options, Set<Argument> arguments, PrintStream out)
diff --git a/gogo/runtime/src/main/java/org/apache/felix/gogo/runtime/shell/Context.java b/gogo/commands/src/test/java/org/apache/felix/gogo/commands/Context.java
similarity index 84%
copy from gogo/runtime/src/main/java/org/apache/felix/gogo/runtime/shell/Context.java
copy to gogo/commands/src/test/java/org/apache/felix/gogo/commands/Context.java
index c14a790..1e266da 100644
--- a/gogo/runtime/src/main/java/org/apache/felix/gogo/runtime/shell/Context.java
+++ b/gogo/commands/src/test/java/org/apache/felix/gogo/commands/Context.java
@@ -16,9 +16,11 @@
* specific language governing permissions and limitations
* under the License.
*/
-package org.apache.felix.gogo.runtime.shell;
+package org.apache.felix.gogo.commands;
import org.apache.felix.gogo.runtime.threadio.ThreadIOImpl;
+import org.apache.felix.gogo.runtime.shell.CommandShellImpl;
+import org.apache.felix.gogo.runtime.shell.CommandSessionImpl;
public class Context extends CommandShellImpl
{
@@ -48,5 +50,10 @@
put("test:" + name, target);
}
+ public void set(String name, Object value)
+ {
+ session.put(name, value);
+ }
-}
+
+}
\ No newline at end of file
diff --git a/gogo/commands/src/test/java/org/apache/felix/gogo/commands/TestCommands.java b/gogo/commands/src/test/java/org/apache/felix/gogo/commands/TestCommands.java
index 7d25977..c9da9b8 100644
--- a/gogo/commands/src/test/java/org/apache/felix/gogo/commands/TestCommands.java
+++ b/gogo/commands/src/test/java/org/apache/felix/gogo/commands/TestCommands.java
@@ -27,12 +27,21 @@
import junit.framework.TestCase;
import org.osgi.service.command.CommandSession;
-import org.apache.felix.gogo.runtime.shell.Context;
import org.apache.felix.gogo.commands.basic.SimpleCommand;
public class TestCommands extends TestCase {
+ public void testPrompt() throws Exception {
+ Context c = new Context();
+ c.addCommand("echo", this);
+ c.set("USER", "gnodet");
+ c.set("APPLICATION", "karaf");
+ //c.set("SCOPE", "");
+ Object p = c.execute("\"@|bold ${USER}|@${APPLICATION}:@|bold ${SCOPE}|> \"");
+ System.out.println("Prompt: " + p);
+ }
+
public void testCommand() throws Exception {
Context c= new Context();
c.addCommand("capture", this);
@@ -85,6 +94,27 @@
return sw.toString();
}
+ public CharSequence echo(Object args[])
+ {
+ if (args == null)
+ {
+ return "";
+ }
+
+ StringBuilder sb = new StringBuilder();
+ String del = "";
+ for (Object arg : args)
+ {
+ sb.append(del);
+ if (arg != null)
+ {
+ sb.append(arg);
+ del = " ";
+ }
+ }
+ return sb;
+ }
+
@Command(scope = "test", name = "my-action", description = "My Action")
public static class MyAction implements Action
{
diff --git a/gogo/runtime/src/main/java/org/apache/felix/gogo/runtime/osgi/OSGiCommands.java b/gogo/runtime/src/main/java/org/apache/felix/gogo/runtime/osgi/OSGiCommands.java
index 21197d6..3bfca82 100644
--- a/gogo/runtime/src/main/java/org/apache/felix/gogo/runtime/osgi/OSGiCommands.java
+++ b/gogo/runtime/src/main/java/org/apache/felix/gogo/runtime/osgi/OSGiCommands.java
@@ -48,7 +48,8 @@
public BundleContext getContext()
{
- if (bundle.getState() != Bundle.ACTIVE)
+ if (bundle.getState() != Bundle.ACTIVE && bundle.getState() != Bundle.STARTING
+ && bundle.getState() != Bundle.STOPPING)
{
throw new IllegalStateException("Framework is not started yet");
}
diff --git a/gogo/runtime/src/main/java/org/apache/felix/gogo/runtime/osgi/OSGiShell.java b/gogo/runtime/src/main/java/org/apache/felix/gogo/runtime/osgi/OSGiShell.java
index e968740..7c8bb37 100644
--- a/gogo/runtime/src/main/java/org/apache/felix/gogo/runtime/osgi/OSGiShell.java
+++ b/gogo/runtime/src/main/java/org/apache/felix/gogo/runtime/osgi/OSGiShell.java
@@ -40,7 +40,7 @@
addCommand("osgi", this.bundle);
addCommand("osgi", commands);
setConverter(commands);
- if (bundle.getState() == Bundle.ACTIVE)
+ if (bundle.getState() == Bundle.ACTIVE || bundle.getState() == Bundle.STARTING)
{
addCommand("osgi", commands.service(PackageAdmin.class.getName(), null), PackageAdmin.class);
addCommand("osgi", commands.getContext(), BundleContext.class);
diff --git a/gogo/runtime/src/main/java/org/apache/felix/gogo/runtime/osgi/ServiceCommand.java b/gogo/runtime/src/main/java/org/apache/felix/gogo/runtime/osgi/ServiceCommand.java
index c285d6b..5970424 100644
--- a/gogo/runtime/src/main/java/org/apache/felix/gogo/runtime/osgi/ServiceCommand.java
+++ b/gogo/runtime/src/main/java/org/apache/felix/gogo/runtime/osgi/ServiceCommand.java
@@ -44,13 +44,19 @@
try
{
Object target = shell.bundle.getBundleContext().getService(ref);
- Object result = method(session, target, name, arguments);
- if (result != CommandShellImpl.NO_SUCH_COMMAND)
+ if (target instanceof Function)
{
- return result;
+ return ((Function) target).execute(session, arguments);
}
-
- throw new IllegalArgumentException("Service does not implement promised command " + ref + " " + name);
+ else
+ {
+ Object result = method(session, target, name, arguments);
+ if (result != CommandShellImpl.NO_SUCH_COMMAND)
+ {
+ return result;
+ }
+ throw new IllegalArgumentException("Service does not implement promised command " + ref + " " + name);
+ }
}
finally
{
diff --git a/gogo/runtime/src/main/java/org/apache/felix/gogo/runtime/shell/Closure.java b/gogo/runtime/src/main/java/org/apache/felix/gogo/runtime/shell/Closure.java
index 3bc61a5..8b8d0f7 100644
--- a/gogo/runtime/src/main/java/org/apache/felix/gogo/runtime/shell/Closure.java
+++ b/gogo/runtime/src/main/java/org/apache/felix/gogo/runtime/shell/Closure.java
@@ -106,22 +106,23 @@
CharSequence statement0 = statement.remove(0);
// derek: FEATURE: add set -x facility if echo is set
- StringBuilder buf = new StringBuilder("+ ");
- buf.append(statement0);
+ if (Boolean.TRUE.equals(session.get("echo"))) {
+ StringBuilder buf = new StringBuilder("+ ");
+ buf.append(statement0);
+ for (CharSequence token : statement)
+ {
+ buf.append(' ');
+ buf.append(token);
+ }
+ System.err.println(buf);
+ }
Object cmd = eval(statement0);
for (CharSequence token : statement)
{
- buf.append(' ');
- buf.append(token);
values.add(eval(token));
}
- if (Boolean.TRUE.equals(session.get("echo")))
- {
- System.err.println(buf);
- }
-
result = execute(cmd, values);
return result;
}
@@ -207,36 +208,87 @@
private Object eval(CharSequence seq) throws Exception
{
- int end = seq.length();
- switch (seq.charAt(0))
- {
- case '$':
- return var(seq);
- case '<':
- Closure c = new Closure(session, this, seq.subSequence(1, end - 1));
- return c.execute(session, parms);
- case '[':
- return array(seq.subSequence(1, end - 1));
-
- case '{':
- return new Closure(session, this, seq.subSequence(1, end - 1));
-
- default:
- String result = new Parser(seq).unescape();
- if ("null".equals(result))
- {
- return null;
+ Object res = null;
+ StringBuilder sb = null;
+ Parser p = new Parser(seq);
+ int start = p.current;
+ while (!p.eof()) {
+ char c = p.peek();
+ if (!p.escaped) {
+ if (c == '$' || c == '<' || c == '\'' || c == '"' || c == '[' || c == '{') {
+ if (start != p.current || res != null) {
+ if (sb == null) {
+ sb = new StringBuilder();
+ if (res != null) {
+ sb.append(res);
+ res = null;
+ }
+ }
+ if (start != p.current) {
+ sb.append(new Parser(p.text.subSequence(start, p.current)).unescape());
+ start = p.current;
+ continue;
+ }
+ }
+ switch (c) {
+ case '\'':
+ p.next();
+ p.quote(c);
+ res = new Parser(p.text.subSequence(start + 1, p.current - 1)).unescape();
+ start = p.current;
+ continue;
+ case '\"':
+ p.next();
+ p.quote(c);
+ res = eval(p.text.subSequence(start + 1, p.current - 1));
+ start = p.current;
+ continue;
+ case '[':
+ p.next();
+ res = array(seq.subSequence(start + 1, p.find(']', '[') - 1));
+ start = p.current;
+ continue;
+ case '<':
+ p.next();
+ Closure cl = new Closure(session, this, p.text.subSequence(start + 1, p.find('>', '<') - 1));
+ res = cl.execute(session, parms);
+ start = p.current;
+ continue;
+ case '{':
+ p.next();
+ res = new Closure(session, this, p.text.subSequence(start + 1, p.find('}', '{') - 1));
+ start = p.current;
+ continue;
+ case '$':
+ p.next();
+ res = var(p.findVar());
+ start = p.current;
+ continue;
+ }
}
- if ("true".equalsIgnoreCase(result))
- {
- return true;
- }
- if ("false".equalsIgnoreCase(result))
- {
- return false;
- }
- return result;
+ }
+ p.next();
}
+ if (start != p.current) {
+ if (sb == null) {
+ sb = new StringBuilder();
+ if (res != null) {
+ sb.append(res);
+ res = null;
+ }
+ }
+ sb.append(new Parser(p.text.subSequence(start, p.current)).unescape());
+ }
+ if (sb != null) {
+ if (res != null) {
+ sb.append(res);
+ }
+ return sb.toString();
+ }
+ if (res instanceof CharSequence) {
+ return res.toString();
+ }
+ return res;
}
private Object array(CharSequence array) throws Exception
@@ -294,7 +346,11 @@
private Object var(CharSequence var) throws Exception
{
- String name = eval(var.subSequence(1, var.length())).toString();
+ Object v = eval(var);
+ if (v instanceof Closure) {
+ v = ((Closure) v).execute(session, null);
+ }
+ String name = v.toString();
return get(name);
}
diff --git a/gogo/runtime/src/main/java/org/apache/felix/gogo/runtime/shell/CommandSessionImpl.java b/gogo/runtime/src/main/java/org/apache/felix/gogo/runtime/shell/CommandSessionImpl.java
index c56142e..ab8888f 100644
--- a/gogo/runtime/src/main/java/org/apache/felix/gogo/runtime/shell/CommandSessionImpl.java
+++ b/gogo/runtime/src/main/java/org/apache/felix/gogo/runtime/shell/CommandSessionImpl.java
@@ -32,12 +32,16 @@
public class CommandSessionImpl implements CommandSession, Converter
{
- String COLUMN = "%-20s %s\n";
+ public static final String VARIABLES = ".variables";
+ public static final String COMMANDS = ".commands";
+
+ private static final String COLUMN = "%-20s %s\n";
+
InputStream in;
PrintStream out;
PrintStream err;
CommandShellImpl service;
- Map<Object, Object> variables = new HashMap<Object, Object>();
+ final Map<Object, Object> variables = new HashMap<Object, Object>();
private boolean closed; // derek
CommandSessionImpl(CommandShellImpl service, InputStream in, PrintStream out, PrintStream err)
@@ -77,12 +81,16 @@
{
// XXX: derek.baum@paremus.com
// there is no API to list all variables, so overload name == null
- if (name == null)
+ if (name == null || VARIABLES.equals(name))
{
return variables.keySet();
}
+ if (COMMANDS.equals(name))
+ {
+ return service.get(null);
+ }
- if (variables != null && variables.containsKey(name))
+ if (variables.containsKey(name))
{
return variables.get(name);
}
diff --git a/gogo/runtime/src/main/java/org/apache/felix/gogo/runtime/shell/CommandShellImpl.java b/gogo/runtime/src/main/java/org/apache/felix/gogo/runtime/shell/CommandShellImpl.java
index 5b8d6b1..492e114 100644
--- a/gogo/runtime/src/main/java/org/apache/felix/gogo/runtime/shell/CommandShellImpl.java
+++ b/gogo/runtime/src/main/java/org/apache/felix/gogo/runtime/shell/CommandShellImpl.java
@@ -68,6 +68,10 @@
public Object get(String name)
{
+ if (name == null)
+ {
+ return commands.keySet();
+ }
name = name.toLowerCase();
int n = name.indexOf(':');
if (n < 0)
diff --git a/gogo/runtime/src/main/java/org/apache/felix/gogo/runtime/shell/Parser.java b/gogo/runtime/src/main/java/org/apache/felix/gogo/runtime/shell/Parser.java
index 44a5153..a2d639b 100644
--- a/gogo/runtime/src/main/java/org/apache/felix/gogo/runtime/shell/Parser.java
+++ b/gogo/runtime/src/main/java/org/apache/felix/gogo/runtime/shell/Parser.java
@@ -39,9 +39,9 @@
{
// derek: BUGFIX: loop if comment at beginning of input
//while (!eof() && Character.isWhitespace(peek())) {
- while (!eof() && (Character.isWhitespace(peek()) || current == 0))
+ while (!eof() && (!escaped && Character.isWhitespace(peek()) || current == 0))
{
- if (current != 0 || Character.isWhitespace(peek()))
+ if (current != 0 || !escaped && Character.isWhitespace(peek()))
{
current++;
}
@@ -203,7 +203,7 @@
while (!eof())
{
c = peek();
- if (c == ';' || c == '|' || Character.isWhitespace(c))
+ if (!escaped && (c == ';' || c == '|' || Character.isWhitespace(c)))
{
break;
}
@@ -232,17 +232,13 @@
return text.subSequence(start, find(')', '('));
case '[':
return text.subSequence(start, find(']', '['));
- case '"':
- return text.subSequence(start + 1, quote('"'));
- case '\'':
- return text.subSequence(start + 1, quote('\''));
case '<':
return text.subSequence(start, find('>', '<'));
- case '$':
- value();
- return text.subSequence(start, current);
case '=':
return text.subSequence(start, current);
+ case '"':
+ case '\'':
+ quote(c); break;
}
}
@@ -250,15 +246,35 @@
while (!eof())
{
c = peek();
- if ((!escaped && SPECIAL.indexOf(c) >= 0) || Character.isWhitespace(c))
+ if (!escaped)
{
- break;
+ if (Character.isWhitespace(c) || c == ';' || c =='|' || c == '=') {
+ break;
+ } else if (c == '{') {
+ next(); find('}', '{');
+ } else if (c == '(') {
+ next(); find(')', '(');
+ } else if (c == '<') {
+ next(); find('>', '<');
+ } else if (c == '[') {
+ next(); find(']', '[');
+ } else if (c == '\'' || c == '"') {
+ next(); quote(c); next();
+ } else {
+ next();
+ }
+ } else {
+ next();
}
- next();
}
return text.subSequence(start, current);
}
+ boolean escaped()
+ {
+ return escaped;
+ }
+
char next()
{
return peek(true);
@@ -276,7 +292,7 @@
return (char) n;
}
- private int find(char target, char deeper)
+ int find(char target, char deeper)
{
int start = current;
int level = 1;
@@ -340,7 +356,7 @@
CharSequence findVar()
{
- int start = current - 1;
+ int start = current;
char c = peek();
if (c == '{')
@@ -349,12 +365,22 @@
int end = find('}', '{');
return text.subSequence(start, end);
}
+ if (c == '<')
+ {
+ next();
+ int end = find('>', '<');
+ return text.subSequence(start, end);
+ }
if (Character.isJavaIdentifierStart(c))
{
- while (!eof() && Character.isJavaIdentifierPart(c) || c == '.')
+ while (c == '$') {
+ c = next();
+ }
+ while (!eof() && (Character.isJavaIdentifierPart(c) || c == '.') && c != '$')
{
next();
+ c = peek();
}
return text.subSequence(start, current);
}
diff --git a/gogo/runtime/src/main/java/org/apache/felix/gogo/runtime/shell/Context.java b/gogo/runtime/src/test/java/org/apache/felix/gogo/runtime/shell/Context.java
similarity index 93%
rename from gogo/runtime/src/main/java/org/apache/felix/gogo/runtime/shell/Context.java
rename to gogo/runtime/src/test/java/org/apache/felix/gogo/runtime/shell/Context.java
index c14a790..a2c397c 100644
--- a/gogo/runtime/src/main/java/org/apache/felix/gogo/runtime/shell/Context.java
+++ b/gogo/runtime/src/test/java/org/apache/felix/gogo/runtime/shell/Context.java
@@ -48,5 +48,10 @@
put("test:" + name, target);
}
+ public void set(String name, Object value)
+ {
+ session.put(name, value);
+ }
+
}
diff --git a/gogo/runtime/src/test/java/org/apache/felix/gogo/runtime/shell/TestParser.java b/gogo/runtime/src/test/java/org/apache/felix/gogo/runtime/shell/TestParser.java
index 0aa294c..adc9802 100644
--- a/gogo/runtime/src/test/java/org/apache/felix/gogo/runtime/shell/TestParser.java
+++ b/gogo/runtime/src/test/java/org/apache/felix/gogo/runtime/shell/TestParser.java
@@ -35,9 +35,32 @@
{
int beentheredonethat = 0;
+ public void testQuotes() throws Exception {
+ Context c = new Context();
+ c.addCommand("echo", this);
+ c.addCommand("capture", this);
+ c.set("c", "a");
+ assertEquals("a b", c.execute("echo \"$c b\" | capture"));
+
+ assertEquals("a b", c.execute("echo a b | capture"));
+ assertEquals("a b", c.execute("echo 'a b' | capture"));
+ assertEquals("a b", c.execute("echo \"a b\" | capture"));
+ assertEquals("a b", c.execute("echo a b | capture"));
+ assertEquals("a b", c.execute("echo 'a b' | capture"));
+ assertEquals("a b", c.execute("echo \"a b\" | capture"));
+ assertEquals("a b", c.execute("echo $c b | capture"));
+ assertEquals("$c b", c.execute("echo '$c b' | capture"));
+ assertEquals("a b", c.execute("echo \"$c b\" | capture"));
+ assertEquals("a b", c.execute("echo ${c} b | capture"));
+ assertEquals("${c} b", c.execute("echo '${c} b' | capture"));
+ assertEquals("a b", c.execute("echo \"${c} b\" | capture"));
+ assertEquals("aa", c.execute("echo $c$c | capture"));
+ assertEquals("a ;a", c.execute("echo a\\ \\;a | capture"));
+ }
+
public void testScope() throws Exception
{
- Context c= new Context();
+ Context c = new Context();
c.addCommand("echo", this);
c.addCommand("capture", this);
assertEquals("$a", c.execute("test:echo \\$a | capture"));
@@ -62,6 +85,8 @@
c.addCommand("echo", this);
c.addCommand("capture", this);
c.addCommand("grep", this);
+ assertEquals("a", c.execute("a = a; echo $$a").toString());
+
assertEquals("hello", c.execute("echo hello|capture").toString());
assertEquals("hello", c.execute("a = <echo hello|capture>").toString());
assertEquals("a", c.execute("a = a; echo $<echo a>").toString());
@@ -105,8 +130,8 @@
{
Parser parser = new Parser("'a|b;c'");
CharSequence cs = parser.messy();
- assertEquals("a|b;c", cs.toString());
- assertEquals("a|b;c", new Parser(cs).unescape());
+ assertEquals("'a|b;c'", cs.toString());
+ assertEquals("'a|b;c'", new Parser(cs).unescape());
assertEquals("$a", new Parser("\\$a").unescape());
}
@@ -164,6 +189,7 @@
assertEquals("", c.execute("echo ${very.likely.that.this.does.not.exist}").toString());
assertNotNull(c.execute("echo ${java.shell.name}"));
+ assertEquals("a", c.execute("a = a; echo ${a}").toString());
}
public void testFunny() throws Exception
@@ -278,7 +304,7 @@
assertEquals("<immediate>", x.get(5));
assertEquals("{'{{{{{'}", x.get(6));
assertEquals("{\\}}", x.get(7));
- assertEquals("abc{}", x.get(8));
+ assertEquals("'abc{}'", x.get(8));
}
void each(CommandSession session, Collection<Object> list, Function closure) throws Exception