Improve gogo parser to fully parse the command line instead of skipping inner constructs such as arrays or closures, and report missing terminators
git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@1735994 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/gogo/runtime/pom.xml b/gogo/runtime/pom.xml
index 23a686c..4ee7ff9 100644
--- a/gogo/runtime/pom.xml
+++ b/gogo/runtime/pom.xml
@@ -1,86 +1,86 @@
-<!-- Licensed to the Apache Software Foundation (ASF) under one or more contributor
- license agreements. See the NOTICE file distributed with this work for additional
- information regarding copyright ownership. The ASF licenses this file to
- you under the Apache License, Version 2.0 (the "License"); you may not use
- this file except in compliance with the License. You may obtain a copy of
- the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required
- by applicable law or agreed to in writing, software distributed under the
- License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS
- OF ANY KIND, either express or implied. See the License for the specific
- language governing permissions and limitations under the License. -->
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
- <parent>
- <groupId>org.apache.felix</groupId>
- <artifactId>gogo-parent</artifactId>
- <version>0.6.0</version>
- <relativePath>../gogo-parent/pom.xml</relativePath>
- </parent>
- <modelVersion>4.0.0</modelVersion>
- <packaging>bundle</packaging>
- <name>Apache Felix Gogo Runtime</name>
- <artifactId>org.apache.felix.gogo.runtime</artifactId>
- <version>0.16.3-SNAPSHOT</version>
- <dependencies>
- <dependency>
- <groupId>org.osgi</groupId>
- <artifactId>org.osgi.core</artifactId>
- <version>4.0.0</version>
- <scope>provided</scope>
- </dependency>
- <dependency>
- <groupId>org.osgi</groupId>
- <artifactId>org.osgi.compendium</artifactId>
- <version>4.0.0</version>
- <scope>provided</scope>
- </dependency>
- <dependency>
- <groupId>junit</groupId>
- <artifactId>junit</artifactId>
- <scope>test</scope>
- </dependency>
- <dependency>
- <groupId>org.mockito</groupId>
- <artifactId>mockito-core</artifactId>
- <version>1.10.19</version>
- <scope>test</scope>
- </dependency>
- </dependencies>
- <build>
- <plugins>
- <plugin>
- <groupId>org.apache.felix</groupId>
- <artifactId>maven-bundle-plugin</artifactId>
- <extensions>true</extensions>
- <configuration>
- <instructions>
- <Export-Service>
- org.apache.felix.service.threadio.ThreadIO,
- org.apache.felix.service.command.CommandProcessor
- </Export-Service>
- <Export-Package>
- org.apache.felix.service.command;
- org.apache.felix.service.threadio; version=${project.version}; status="provisional";
- mandatory:="status",
- org.apache.felix.gogo.api; version=${project.version}
- </Export-Package>
- <Import-Package>
- org.osgi.service.event*; resolution:=optional,
- org.osgi.service.log*; resolution:=optional,
- org.osgi.service.packageadmin*; resolution:=optional,
- org.osgi.service.startlevel*; resolution:=optional,
- *
- </Import-Package>
- <Private-Package>org.apache.felix.gogo.runtime*</Private-Package>
- <Bundle-SymbolicName>${pom.artifactId}</Bundle-SymbolicName>
- <Bundle-Vendor>The Apache Software Foundation</Bundle-Vendor>
- <Bundle-Activator>org.apache.felix.gogo.runtime.activator.Activator</Bundle-Activator>
- <Include-Resource>{maven-resources},META-INF/LICENSE=LICENSE,META-INF/NOTICE=NOTICE,META-INF/DEPENDENCIES=DEPENDENCIES</Include-Resource>
- <_versionpolicy>[$(version;==;$(@)),$(version;+;$(@)))</_versionpolicy>
- <_removeheaders>Private-Package,Ignore-Package,Include-Resource</_removeheaders>
- </instructions>
- </configuration>
- </plugin>
- </plugins>
- </build>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one
+ or more contributor license agreements. See the NOTICE file
+ distributed with this work for additional information
+ regarding copyright ownership. The ASF licenses this file
+ to you under the Apache License, Version 2.0 (the
+ "License"); you may not use this file except in compliance
+ with the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing,
+ software distributed under the License is distributed on an
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ KIND, either express or implied. See the License for the
+ specific language governing permissions and limitations
+ under the License.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+ <parent>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>gogo-parent</artifactId>
+ <version>0.6.0</version>
+ <relativePath>../gogo-parent/pom.xml</relativePath>
+ </parent>
+ <modelVersion>4.0.0</modelVersion>
+ <packaging>bundle</packaging>
+ <name>Apache Felix Gogo Runtime</name>
+ <artifactId>org.apache.felix.gogo.runtime</artifactId>
+ <version>0.16.3-SNAPSHOT</version>
+ <dependencies>
+ <dependency>
+ <groupId>org.osgi</groupId>
+ <artifactId>org.osgi.core</artifactId>
+ <version>4.0.0</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.osgi</groupId>
+ <artifactId>org.osgi.compendium</artifactId>
+ <version>4.0.0</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>maven-bundle-plugin</artifactId>
+ <extensions>true</extensions>
+ <configuration>
+ <instructions>
+ <Export-Service>
+ org.apache.felix.service.threadio.ThreadIO,
+ org.apache.felix.service.command.CommandProcessor
+ </Export-Service>
+ <Export-Package>
+ org.apache.felix.service.command;
+ org.apache.felix.service.threadio; version=${project.version}; status="provisional"; mandatory:="status",
+ org.apache.felix.gogo.api; version=${project.version}
+ </Export-Package>
+ <Import-Package>
+ org.osgi.service.event*; resolution:=optional,
+ org.osgi.service.log*; resolution:=optional,
+ org.osgi.service.packageadmin*; resolution:=optional,
+ org.osgi.service.startlevel*; resolution:=optional,
+ *
+ </Import-Package>
+ <Private-Package>org.apache.felix.gogo.runtime*</Private-Package>
+ <Bundle-SymbolicName>${project.artifactId}</Bundle-SymbolicName>
+ <Bundle-Vendor>The Apache Software Foundation</Bundle-Vendor>
+ <Bundle-Activator>org.apache.felix.gogo.runtime.activator.Activator</Bundle-Activator>
+ <Include-Resource>{maven-resources},META-INF/LICENSE=LICENSE,META-INF/NOTICE=NOTICE,META-INF/DEPENDENCIES=DEPENDENCIES</Include-Resource>
+ <_versionpolicy>[$(version;==;$(@)),$(version;+;$(@)))</_versionpolicy>
+ <_removeheaders>Private-Package,Ignore-Package,Include-Resource</_removeheaders>
+ </instructions>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
</project>
diff --git a/gogo/runtime/src/main/java/org/apache/felix/gogo/runtime/BaseTokenizer.java b/gogo/runtime/src/main/java/org/apache/felix/gogo/runtime/BaseTokenizer.java
new file mode 100644
index 0000000..2ec6887
--- /dev/null
+++ b/gogo/runtime/src/main/java/org/apache/felix/gogo/runtime/BaseTokenizer.java
@@ -0,0 +1,296 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.felix.gogo.runtime;
+
+public class BaseTokenizer
+{
+
+ protected static final char EOT = (char) -1;
+
+ protected final Token text;
+
+ protected short line;
+ protected short column;
+ protected char ch;
+ protected int index;
+
+ public BaseTokenizer(CharSequence text)
+ {
+ this.text = text instanceof Token ? (Token) text : new Token(text);
+ getch();
+ }
+
+ public Token text()
+ {
+ return text;
+ }
+
+ protected void find(char target, char deeper)
+ {
+ final short sLine = line;
+ final short sCol = column;
+ int start = ch;
+ int level = 1;
+
+ while (level != 0)
+ {
+ if (eot())
+ {
+ throw new EOFError(sLine, sCol, "unexpected eof found in the middle of a compound for '"
+ + deeper + target + "', begins at " + start, "compound", Character.toString(target));
+ // TODO: fill context correctly
+ }
+
+ getch();
+ if (ch == '\\')
+ {
+ escape();
+ }
+ if (ch == target)
+ {
+ level--;
+ }
+ else
+ {
+ if (ch == deeper)
+ {
+ level++;
+ }
+ else
+ {
+ if (ch == '"' || ch == '\'' || ch == '`')
+ {
+ skipQuote();
+ }
+ }
+ }
+ }
+ }
+
+ protected char escape()
+ {
+ assert '\\' == ch;
+ final short sLine = line;
+ final short sCol = column;
+
+ switch (getch())
+ {
+ case 'u':
+ getch();
+ int nb = 0;
+ for (int i = 0; i < 4; i++)
+ {
+ char ch = Character.toUpperCase(this.ch);
+ if (ch >= '0' && ch <= '9')
+ {
+ nb = nb * 16 + (ch - '0');
+ getch();
+ continue;
+ }
+ if (ch >= 'A' && ch <= 'F')
+ {
+ nb = nb * 16 + (ch - 'A' + 10);
+ getch();
+ continue;
+ }
+ if (ch == 0) {
+ throw new EOFError(sLine, sCol, "unexpected EOT in \\ escape", "escape", "0");
+ } else {
+ throw new SyntaxError(sLine, sCol, "bad unicode", text);
+ }
+ }
+ index--;
+ return (char) nb;
+
+ case EOT:
+ throw new EOFError(sLine, sCol, "unexpected EOT in \\ escape", "escape", " ");
+
+ case '\n':
+ return '\0'; // line continuation
+
+ case '\\':
+ case '\'':
+ case '"':
+ case '$':
+ return ch;
+
+ default:
+ return ch;
+ }
+ }
+
+ protected 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,
+ quote == '"' ? "dquote" : "quote",
+ Character.toString(quote));
+ }
+
+ protected void skipSpace()
+ {
+ skipSpace(false);
+ }
+
+ protected void skipSpace(boolean skipNewLines)
+ {
+ while (true)
+ {
+ while (isBlank(ch))
+ {
+ getch();
+ }
+
+ // skip continuation lines, but not other escapes
+ if (('\\' == ch) && (peek() == '\n'))
+ {
+ getch();
+ getch();
+ continue;
+ }
+
+ if (skipNewLines && ('\n' == ch))
+ {
+ getch();
+ continue;
+ }
+
+ if (skipNewLines && ('\r' == 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: */",
+ "comment",
+ "*/");
+ }
+
+ getch();
+ getch();
+ continue;
+ }
+ }
+
+ break;
+ }
+ }
+
+ protected boolean isBlank(char ch)
+ {
+ return ' ' == ch || '\t' == ch;
+ }
+
+ protected boolean eot()
+ {
+ return index >= text.length();
+ }
+
+ protected char getch()
+ {
+ return ch = getch(false);
+ }
+
+ protected char peek()
+ {
+ return getch(true);
+ }
+
+ protected 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;
+ }
+
+ public void skip(int length)
+ {
+ while (--length >= 0)
+ {
+ getch();
+ }
+ }
+
+}
diff --git a/gogo/runtime/src/main/java/org/apache/felix/gogo/runtime/Closure.java b/gogo/runtime/src/main/java/org/apache/felix/gogo/runtime/Closure.java
index 77db7e4..e945cdc 100644
--- a/gogo/runtime/src/main/java/org/apache/felix/gogo/runtime/Closure.java
+++ b/gogo/runtime/src/main/java/org/apache/felix/gogo/runtime/Closure.java
@@ -21,17 +21,24 @@
import java.io.EOFException;
import java.util.AbstractList;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
-import org.apache.felix.gogo.runtime.Tokenizer.Type;
+import org.apache.felix.gogo.runtime.Parser.Array;
+import org.apache.felix.gogo.runtime.Parser.Executable;
+import org.apache.felix.gogo.runtime.Parser.Pipeline;
+import org.apache.felix.gogo.runtime.Parser.Program;
+import org.apache.felix.gogo.runtime.Parser.Sequence;
+import org.apache.felix.gogo.runtime.Parser.Statement;
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";
@@ -40,7 +47,7 @@
private final CommandSessionImpl session;
private final Closure parent;
private final CharSequence source;
- private final List<List<List<Token>>> program;
+ private final Program program;
private final Object script;
private Token errTok;
@@ -53,18 +60,34 @@
this.session = session;
this.parent = parent;
this.source = source;
- script = session.get("0"); // by convention, $0 is script name
+ this.script = session.get("0"); // by convention, $0 is script name
- try
+ if (source instanceof Program)
{
- program = new Parser(source).program();
+ program = (Program) source;
}
- catch (Exception e)
+ else
{
- throw setLocation(e);
+ try
+ {
+ this.program = new Parser(source).program();
+ }
+ catch (Exception e)
+ {
+ throw setLocation(e);
+ }
}
}
+ public Closure(CommandSessionImpl session, Closure parent, Program program)
+ {
+ this.session = session;
+ this.parent = parent;
+ this.source = program;
+ this.script = session.get("0"); // by convention, $0 is script name
+ this.program = program;
+ }
+
public CommandSessionImpl session()
{
return session;
@@ -115,7 +138,7 @@
try
{
location.remove();
- session.variables.remove(LOCATION);
+ session.put(LOCATION, null);
return execute(values);
}
catch (Exception e)
@@ -152,15 +175,14 @@
Pipe last = null;
Object[] mark = Pipe.mark();
- for (List<List<Token>> pipeline : program)
+ for (Executable executable : program.tokens())
{
- ArrayList<Pipe> pipes = new ArrayList<Pipe>();
+ List<Pipe> pipes = toPipes(executable);
- for (List<Token> statement : pipeline)
+ for (int i = 0; i < pipes.size(); i++)
{
- Pipe current = new Pipe(this, statement);
-
- if (pipes.isEmpty())
+ Pipe current = pipes.get(i);
+ if (i == 0)
{
if (current.out == null)
{
@@ -171,10 +193,9 @@
}
else
{
- Pipe previous = pipes.get(pipes.size() - 1);
+ Pipe previous = pipes.get(i - 1);
previous.connect(current);
}
- pipes.add(current);
}
if (pipes.size() == 1)
@@ -204,10 +225,9 @@
}
}
- last = pipes.remove(pipes.size() - 1);
-
- for (Pipe pipe : pipes)
+ for (int i = 0; i < pipes.size() - 1; i++)
{
+ Pipe pipe = pipes.get(i);
if (pipe.exception != null)
{
// can't throw exception, as result is defined by last pipe
@@ -218,7 +238,7 @@
session.put("pipe-exception", pipe.exception);
}
}
-
+ last = pipes.get(pipes.size() - 1);
if (last.exception != null)
{
Pipe.reset(mark);
@@ -231,6 +251,24 @@
return last == null ? null : last.result;
}
+ private List<Pipe> toPipes(Executable executable)
+ {
+ if (executable instanceof Pipeline)
+ {
+ List<Pipe> pipes = new ArrayList<Pipe>();
+ Pipeline pipeline = (Pipeline) executable;
+ for (Executable ex : pipeline.tokens())
+ {
+ pipes.add(new Pipe(this, ex));
+ }
+ return pipes;
+ }
+ else
+ {
+ return Collections.singletonList(new Pipe(this, executable));
+ }
+ }
+
private Object eval(Object v)
{
String s = v.toString();
@@ -256,6 +294,7 @@
}
catch (NumberFormatException e)
{
+ // Ignore
}
}
return v;
@@ -263,44 +302,43 @@
public Object eval(final Token t) throws Exception
{
- Object v = null;
-
- switch (t.type)
+ if (t instanceof Parser.Closure)
{
- 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 new Closure(session, this, ((Parser.Closure) t).program());
}
+ else if (t instanceof Sequence)
+ {
+ return new Closure(session, this, ((Sequence) t).program())
+ .execute(session, parms);
+ }
+ else if (t instanceof Array)
+ {
+ return array((Array) t);
+ }
+ else {
+ Object v = Expander.expand(t, this);
+ if (t == v)
+ {
+ v = eval(v);
+ }
+ return v;
+ }
+ }
- return v;
+ public Object execute(Executable executable) throws Exception
+ {
+ if (executable instanceof Statement)
+ {
+ return executeStatement((Statement) executable);
+ }
+ else if (executable instanceof Sequence)
+ {
+ return new Closure(session, this, ((Sequence) executable).program()).execute(new ArrayList<Object>());
+ }
+ else
+ {
+ throw new IllegalStateException();
+ }
}
/*
@@ -311,7 +349,7 @@
* <object> // value of <object>
* <object> word.. // method call
*/
- public Object executeStatement(List<Token> statement) throws Exception
+ public Object executeStatement(Statement statement) throws Exception
{
Object echo = session.get("echo");
String xtrace = null;
@@ -319,32 +357,31 @@
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();
+ xtrace = "+" + statement;
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))
+ List<Token> tokens = statement.tokens();
+ if (tokens.isEmpty())
{
- errTok2 = statement.get(2);
+ return null;
}
- for (Token t : statement)
+ List<Object> values = new ArrayList<Object>();
+ errTok = tokens.get(0);
+
+ if ((tokens.size() > 3) && Token.eq("=", tokens.get(1)))
+ {
+ errTok2 = tokens.get(2);
+ }
+
+ for (Token t : tokens)
{
Object v = eval(t);
- if ((Type.EXECUTION == t.type) && (statement.size() == 1))
- {
- return v;
- }
+// if ((Token.Type.EXECUTION == t.type) && (tokens.size() == 1)) {
+// return v;
+// }
if (parms == v && parms != null)
{
@@ -368,7 +405,7 @@
}
if (cmd instanceof CharSequence && values.size() > 0
- && Type.ASSIGN.equals(values.get(0)))
+ && Token.eq("=", tokens.get(1)))
{
values.remove(0);
String scmd = cmd.toString();
@@ -376,7 +413,7 @@
if (values.size() == 0)
{
- return session.variables.remove(scmd);
+ return session.put(scmd, null);
}
if (values.size() == 1)
@@ -389,12 +426,12 @@
if (null == cmd)
{
throw new RuntimeException("Command name evaluates to null: "
- + errTok2);
+ + errTok2);
}
trace2(xtrace, cmd, values);
- value = bareword(statement.get(2), cmd) ? executeCmd(cmd.toString(), values)
+ value = bareword(tokens.get(2), cmd) ? executeCmd(cmd.toString(), values)
: executeMethod(cmd, values);
}
@@ -403,7 +440,7 @@
trace2(xtrace, cmd, values);
- return bareword(statement.get(0), cmd) ? executeCmd(cmd.toString(), values)
+ return bareword(tokens.get(0), cmd) ? executeCmd(cmd.toString(), values)
: executeMethod(cmd, values);
}
@@ -431,7 +468,7 @@
private boolean bareword(Token t, Object v) throws Exception
{
- return ((t.type == Type.WORD) && t.value.toString().equals(v));
+ return v instanceof CharSequence && Token.eq(t, (CharSequence) v);
}
private Object executeCmd(String scmd, List<Object> values) throws Exception
@@ -469,7 +506,7 @@
}
finally
{
- session.variables.remove(DEFAULT_LOCK);
+ session.put(DEFAULT_LOCK, null);
}
}
}
@@ -533,22 +570,21 @@
private Object assignment(String name, Object value)
{
- session.variables.put(name, value);
+ session.put(name, value);
return value;
}
- private Object expr(CharSequence expr) throws Exception
+ public Object expr(Token expr)
{
return session.expr(expr);
}
- private Object array(Token array) throws Exception
+ private Object array(Array array) throws Exception
{
- List<Token> list = new ArrayList<Token>();
- Map<Token, Token> map = new LinkedHashMap<Token, Token>();
- (new Parser(array)).array(list, map);
+ List<Token> list = array.list();
+ Map<Token, Token> map = array.map();
- if (map.isEmpty())
+ if (list != null)
{
List<Object> olist = new ArrayList<Object>();
for (Token t : list)
@@ -556,10 +592,7 @@
Object oval = eval(t);
if (oval.getClass().isArray())
{
- for (Object o : (Object[]) oval)
- {
- olist.add(o);
- }
+ Collections.addAll(olist, (Object[]) oval);
}
else
{
@@ -577,7 +610,7 @@
Object k = eval(key);
if (!(k instanceof String))
{
- throw new SyntaxError(key.line, key.column,
+ throw new SyntaxError(key.line(), key.column(),
"map key null or not String: " + key);
}
omap.put(k, eval(e.getValue()));
@@ -620,14 +653,14 @@
public Object put(String key, Object value)
{
- return session.variables.put(key, value);
+ return session.put(key, value);
}
@Override
public String toString()
{
return source.toString().trim().replaceAll("\n+", "\n").replaceAll(
- "([^\\\\{(\\[])\n", "\\1;").replaceAll("[ \\\\\t\n]+", " ");
+ "([^\\\\{}(\\[])[\\s\n]*\n", "$1;").replaceAll("[ \\\\\t\n]+", " ");
}
/**
diff --git a/gogo/runtime/src/main/java/org/apache/felix/gogo/runtime/CommandProcessorImpl.java b/gogo/runtime/src/main/java/org/apache/felix/gogo/runtime/CommandProcessorImpl.java
index 7f987dc..c8fcd3a 100644
--- a/gogo/runtime/src/main/java/org/apache/felix/gogo/runtime/CommandProcessorImpl.java
+++ b/gogo/runtime/src/main/java/org/apache/felix/gogo/runtime/CommandProcessorImpl.java
@@ -36,6 +36,7 @@
import org.apache.felix.service.command.CommandProcessor;
import org.apache.felix.service.command.CommandSession;
import org.apache.felix.service.command.Converter;
+import org.apache.felix.service.command.Descriptor;
import org.apache.felix.service.command.Function;
import org.apache.felix.service.threadio.ThreadIO;
@@ -68,7 +69,7 @@
}
}
- void removeSession(CommandSessionImpl session)
+ void closeSession(CommandSessionImpl session)
{
synchronized (sessions)
{
@@ -179,13 +180,15 @@
return new CommandProxy(cmd, cfunction.substring(1));
}
- public void addCommand(String scope, Object target)
+ @Descriptor("add commands")
+ public void addCommand(@Descriptor("scope") String scope, @Descriptor("target") Object target)
{
Class<?> tc = (target instanceof Class<?>) ? (Class<?>) target : target.getClass();
addCommand(scope, target, tc);
}
- public void addCommand(String scope, Object target, Class<?> functions)
+ @Descriptor("add commands")
+ public void addCommand(@Descriptor("scope") String scope, @Descriptor("target") Object target, @Descriptor("functions") Class<?> functions)
{
addCommand(scope, target, functions, 0);
}
@@ -283,7 +286,18 @@
return functions;
}
- public Object convert(Class<?> desiredType, Object in)
+ public Object convert(CommandSession session, Class<?> desiredType, Object in)
+ {
+ int[] cost = new int[1];
+ Object ret = Reflective.coerce(session, desiredType, in, cost);
+ if (ret == Reflective.NO_MATCH) {
+ throw new IllegalArgumentException(String.format(
+ "Cannot convert %s(%s) to %s", in, in != null ? in.getClass() : "null", desiredType));
+ }
+ return ret;
+ }
+
+ Object doConvert(Class<?> desiredType, Object in)
{
for (Converter c : converters)
{
@@ -297,9 +311,11 @@
}
catch (Exception e)
{
- e.printStackTrace();
+ // Ignore
+ e.getCause();
}
}
+
return null;
}
diff --git a/gogo/runtime/src/main/java/org/apache/felix/gogo/runtime/CommandSessionImpl.java b/gogo/runtime/src/main/java/org/apache/felix/gogo/runtime/CommandSessionImpl.java
index c1051ef..97ed681 100644
--- a/gogo/runtime/src/main/java/org/apache/felix/gogo/runtime/CommandSessionImpl.java
+++ b/gogo/runtime/src/main/java/org/apache/felix/gogo/runtime/CommandSessionImpl.java
@@ -24,6 +24,7 @@
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;
@@ -32,7 +33,10 @@
import java.util.Formatter;
import java.util.HashMap;
import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import org.apache.felix.service.command.CommandProcessor;
import org.apache.felix.service.command.CommandSession;
import org.apache.felix.service.command.Converter;
import org.apache.felix.service.command.Function;
@@ -43,14 +47,15 @@
public static final String SESSION_CLOSED = "session is closed";
public static final String VARIABLES = ".variables";
public static final String COMMANDS = ".commands";
+ public static final String CONSTANTS = ".constants";
private static final String COLUMN = "%-20s %s\n";
protected InputStream in;
protected PrintStream out;
- protected PrintStream err;
+ PrintStream err;
private final CommandProcessorImpl processor;
- protected final Map<String, Object> variables = new HashMap<String, Object>();
+ protected final ConcurrentMap<String, Object> variables = new ConcurrentHashMap<String, Object>();
private volatile boolean closed;
protected CommandSessionImpl(CommandProcessorImpl shell, InputStream in, PrintStream out, PrintStream err)
@@ -66,11 +71,21 @@
return processor.threadIO;
}
+ public CommandProcessor processor()
+ {
+ return processor;
+ }
+
+ public ConcurrentMap<String, Object> getVariables()
+ {
+ return variables;
+ }
+
public void close()
{
if (!this.closed)
{
- this.processor.removeSession(this);
+ this.processor.closeSession(this);
this.closed = true;
}
}
@@ -119,6 +134,11 @@
return processor.getCommands();
}
+ if (CONSTANTS.equals(name))
+ {
+ return Collections.unmodifiableSet(processor.constants.keySet());
+ }
+
Object val = processor.constants.get(name);
if (val != null)
{
@@ -152,11 +172,15 @@
return processor.getCommand(name, variables.get("SCOPE"));
}
- public void put(String name, Object value)
+ public Object put(String name, Object value)
{
- synchronized (variables)
+ if (value != null)
{
- variables.put(name, value);
+ return variables.put(name, value);
+ }
+ else
+ {
+ return variables.remove(name);
}
}
@@ -343,52 +367,48 @@
{
boolean found = false;
Formatter f = new Formatter();
-
- try
+ Method methods[] = b.getClass().getMethods();
+ for (Method m : methods)
{
- Method methods[] = b.getClass().getMethods();
- for (Method m : methods)
+ try
{
- try
+ String name = m.getName();
+ if (m.getName().startsWith("get") && !m.getName().equals("getClass") && m.getParameterTypes().length == 0 && Modifier.isPublic(m.getModifiers()))
{
- String name = m.getName();
- if (!name.equals("getClass") && name.startsWith("get") && m.getParameterTypes().length == 0)
- {
- m.setAccessible(true);
- Object value = m.invoke(b);
-
- found = true;
- name = name.substring(3);
- f.format(COLUMN, name, format(value, Converter.LINE, this));
- }
- }
- catch (IllegalAccessException e)
- {
- // Ignore
- }
- catch (Exception e)
- {
- e.printStackTrace();
+ 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));
}
}
- if (found)
+ catch (IllegalAccessException e)
{
- return (StringBuilder) f.out();
+ // Ignore
}
- else
+ catch (Exception e)
{
- return b.toString();
+ e.printStackTrace();
}
}
- finally
+ if (found)
{
- f.close();
+ return (StringBuilder) f.out();
+ }
+ else
+ {
+ return b.toString();
}
}
public Object convert(Class<?> desiredType, Object in)
{
- return processor.convert(desiredType, in);
+ return processor.convert(this, desiredType, in);
+ }
+
+ public Object doConvert(Class<?> desiredType, Object in)
+ {
+ return processor.doConvert(desiredType, in);
}
public CharSequence format(Object result, int inspect)
@@ -399,7 +419,7 @@
}
catch (Exception e)
{
- return "<can not format " + result + ">:" + e;
+ return "<can not format " + result + ":" + e;
}
}
@@ -407,4 +427,5 @@
{
return processor.expr(this, expr);
}
+
}
diff --git a/gogo/runtime/src/main/java/org/apache/felix/gogo/runtime/EOFError.java b/gogo/runtime/src/main/java/org/apache/felix/gogo/runtime/EOFError.java
index 81e57f4..b4d172d 100644
--- a/gogo/runtime/src/main/java/org/apache/felix/gogo/runtime/EOFError.java
+++ b/gogo/runtime/src/main/java/org/apache/felix/gogo/runtime/EOFError.java
@@ -21,9 +21,24 @@
public class EOFError extends SyntaxError
{
private static final long serialVersionUID = 1L;
-
- public EOFError(int line, int column, String message)
+
+ private final String missing;
+ private final String repair;
+
+ public EOFError(int line, int column, String message, String missing, String repair)
{
super(line, column, message);
+ this.missing = missing;
+ this.repair = repair;
}
+
+ public String repair() {
+ return repair;
+ }
+
+ public String missing()
+ {
+ return missing;
+ }
+
}
diff --git a/gogo/runtime/src/main/java/org/apache/felix/gogo/runtime/Evaluate.java b/gogo/runtime/src/main/java/org/apache/felix/gogo/runtime/Evaluate.java
index 1a54328..a015a8b 100644
--- a/gogo/runtime/src/main/java/org/apache/felix/gogo/runtime/Evaluate.java
+++ b/gogo/runtime/src/main/java/org/apache/felix/gogo/runtime/Evaluate.java
@@ -25,4 +25,6 @@
Object get(String key);
Object put(String key, Object value);
+
+ Object expr(Token t);
}
diff --git a/gogo/runtime/src/main/java/org/apache/felix/gogo/runtime/Expander.java b/gogo/runtime/src/main/java/org/apache/felix/gogo/runtime/Expander.java
new file mode 100644
index 0000000..08a233f
--- /dev/null
+++ b/gogo/runtime/src/main/java/org/apache/felix/gogo/runtime/Expander.java
@@ -0,0 +1,385 @@
+/*
+ * 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;
+
+@SuppressWarnings("fallthrough")
+public class Expander extends BaseTokenizer
+{
+
+ /**
+ * expand variables, quotes and escapes in word.
+ */
+ 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
+ {
+ return new Expander(word, eval, inQuote).expand();
+ }
+
+ private final Evaluate evaluate;
+ private final boolean inQuote;
+
+ public Expander(CharSequence text, Evaluate evaluate, boolean inQuote)
+ {
+ super(text);
+ this.evaluate = evaluate;
+ this.inQuote = inQuote;
+ }
+
+ public Object expand(CharSequence word) throws Exception
+ {
+ return expand(word, evaluate, inQuote);
+ }
+
+ private Object expand() throws Exception
+ {
+ final String special = "%$\\\"'";
+ int i = text.length();
+ while ((--i >= 0) && (special.indexOf(text.charAt(i)) == -1));
+ // shortcut if word doesn't contain any special characters
+ if (i < 0)
+ return text;
+
+ StringBuilder buf = new StringBuilder();
+ Token value;
+
+ while (ch != EOT)
+ {
+ int start = index;
+
+ switch (ch)
+ {
+ case '%':
+ Object exp = expandExp();
+
+ if (EOT == ch && buf.length() == 0)
+ {
+ return exp;
+ }
+
+ if (null != exp)
+ {
+ buf.append(exp);
+ }
+
+ continue; // expandVar() has already read next char
+
+ 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 '"':
+ skipQuote();
+ value = text.subSequence(start, index - 1);
+ Object expand = expand(value, evaluate, true);
+ if (eot() && buf.length() == 0 && value == expand)
+ {
+ // FELIX-2468 avoid returning CharSequence implementation
+ return 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.toString();
+ }
+
+ buf.append(value);
+ break;
+ }
+ // else fall through
+ default:
+ buf.append(ch);
+ }
+
+ getch();
+ }
+
+ return buf.toString();
+ }
+
+ private Object expandExp()
+ {
+ assert '%' == ch;
+ Object val;
+
+ if (getch() == '(')
+ {
+ val = evaluate.expr(group());
+ getch();
+ return val;
+ }
+ else
+ {
+ throw new SyntaxError(line, column, "bad expression: " + text);
+ }
+ }
+
+ private Token group()
+ {
+ final char push = ch;
+ final char pop;
+
+ switch (ch)
+ {
+ case '{':
+ pop = '}';
+ break;
+ case '(':
+ pop = ')';
+ break;
+ case '[':
+ pop = ']';
+ break;
+ default:
+ assert false;
+ pop = 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 + "'", "compound", Character.toString(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)
+ return text.subSequence(start, index - 1);
+ }
+ }
+
+ }
+
+ private Object expandVar() throws Exception
+ {
+ assert '$' == ch;
+ Object val;
+
+ if (getch() != '{')
+ {
+ if ('(' == ch)
+ { // support $(...) FELIX-2433
+ int start = index - 1;
+ find(')', '(');
+ Token p = text.subSequence(start, index);
+ val = evaluate.eval(new Parser(p).sequence());
+ 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;
+ Token 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)));
+
+ 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);
+ }
+
+ Token word = group.subSequence(i, group.length());
+
+ switch (c)
+ {
+ case '-':
+ case '=':
+ if (null == val)
+ {
+ val = expand(word, evaluate, false);
+ if (val instanceof Token)
+ {
+ val = val.toString();
+ }
+ if ('=' == c)
+ {
+ evaluate.put(name, val);
+ }
+ }
+ break;
+
+ case '+':
+ if (null != val)
+ {
+ val = expand(word, evaluate, false);
+ if (val instanceof Token)
+ {
+ val = val.toString();
+ }
+ }
+ break;
+
+ case '?':
+ if (null == val)
+ {
+ val = expand(word, evaluate, false);
+ if (val instanceof Token)
+ {
+ val = val.toString();
+ }
+ 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;
+ }
+
+ private boolean isName(char ch)
+ {
+ return Character.isJavaIdentifierPart(ch) && (ch != '$') || ('.' == ch);
+ }
+
+}
diff --git a/gogo/runtime/src/main/java/org/apache/felix/gogo/runtime/Parser.java b/gogo/runtime/src/main/java/org/apache/felix/gogo/runtime/Parser.java
index fce18d6..3b9192c 100644
--- a/gogo/runtime/src/main/java/org/apache/felix/gogo/runtime/Parser.java
+++ b/gogo/runtime/src/main/java/org/apache/felix/gogo/runtime/Parser.java
@@ -16,167 +16,516 @@
* 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.Collections;
+import java.util.Comparator;
+import java.util.LinkedHashMap;
+import java.util.LinkedList;
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)
+ public static abstract class Executable extends Token
{
- tz = new Tokenizer(program);
+ public Executable(Token cs)
+ {
+ super(cs);
+ }
}
- public List<List<List<Token>>> program()
+ public static class Statement extends Executable
{
- List<List<List<Token>>> program = new ArrayList<List<List<Token>>>();
+ private final List<Token> tokens;
- outer: while (tz.next() != Type.EOT)
+ public Statement(Token cs, List<Token> tokens)
{
- program.add(pipeline());
+ super(cs);
+ this.tokens = tokens;
+ }
- switch (tz.type())
- {
- case SEMICOLON:
- case NEWLINE:
- continue;
+ public List<Token> tokens()
+ {
+ return tokens;
+ }
+ }
- default:
- break outer;
+ /**
+ * pipe1 ; pipe2 ; ...
+ */
+ public static class Program extends Token
+ {
+ private final List<Executable> tokens;
+
+ public Program(Token cs, List<Executable> tokens)
+ {
+ super(cs);
+ this.tokens = tokens;
+ }
+
+ public List<Executable> tokens()
+ {
+ return tokens;
+ }
+ }
+
+ /**
+ * token1 | token2 | ...
+ */
+ public static class Pipeline extends Executable
+ {
+ private final List<Executable> tokens;
+
+ public Pipeline(Token cs, List<Executable> tokens)
+ {
+ super(cs);
+ this.tokens = tokens;
+ }
+
+ public List<Executable> tokens()
+ {
+ return tokens;
+ }
+
+ }
+
+ /**
+ * ( program )
+ */
+ public static class Sequence extends Executable
+ {
+ private final Program program;
+
+ public Sequence(Token cs, Program program)
+ {
+ super(cs);
+ this.program = program;
+ }
+
+ public Program program()
+ {
+ return program;
+ }
+ }
+
+ /**
+ * { program }
+ */
+ public static class Closure extends Token
+ {
+ private final Program program;
+
+ public Closure(Token cs, Program program)
+ {
+ super(cs);
+ this.program = program;
+ }
+
+ public Program program()
+ {
+ return program;
+ }
+ }
+
+ /**
+ * [ a b ...]
+ * [ k1=v1 k2=v2 ...]
+ */
+ public static class Array extends Token
+ {
+ private final List<Token> list;
+ private final Map<Token, Token> map;
+
+ public Array(Token cs, List<Token> list, Map<Token, Token> map)
+ {
+ super(cs);
+ assert list != null ^ map != null;
+ this.list = list;
+ this.map = map;
+ }
+
+ public List<Token> list()
+ {
+ return list;
+ }
+
+ public Map<Token, Token> map()
+ {
+ return map;
+ }
+ }
+
+ protected final Tokenizer tz;
+ protected final LinkedList<String> stack = new LinkedList<String>();
+ protected final List<Token> tokens = new ArrayList<Token>();
+ protected final List<Statement> statements = new ArrayList<Statement>();
+
+ public Parser(CharSequence line)
+ {
+ this.tz = new Tokenizer(line);
+ }
+
+ public List<Token> tokens() {
+ return Collections.unmodifiableList(tokens);
+ }
+
+ public List<Statement> statements() {
+ Collections.sort(statements, new Comparator<Statement>() {
+ public int compare(Statement o1, Statement o2) {
+ int x = o1.start();
+ int y = o2.start();
+ return (x < y) ? -1 : ((x == y) ? 0 : 1);
}
- }
-
- if (tz.next() != Type.EOT)
- {
- throw new RuntimeException("Program has trailing text: " + tz.value());
- }
- return program;
+ });
+ return Collections.unmodifiableList(statements);
}
- private List<List<Token>> pipeline()
+ public Program program()
{
- List<List<Token>> pipeline = new ArrayList<List<Token>>();
-
+ List<Executable> tokens = new ArrayList<Executable>();
+ List<Executable> pipes = null;
+ int start = tz.index - 1;
while (true)
{
- pipeline.add(command());
- switch (tz.type())
+ Executable ex;
+ Token t = next();
+ if (t == null)
{
- 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;
-
- default:
- break;
- }
- }
- }
-
- 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)
+ if (pipes != null)
{
- 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();
+ throw new EOFError(tz.line, tz.column, "unexpected EOT while looking for a statement after |", getMissing("pipe"), "0");
}
-
- Token k = (list.isEmpty() ? key : list.remove(0));
- Token v = tz.token();
- map.put(k, v);
+ else
+ {
+ return new Program(whole(tokens, start), tokens);
+ }
+ }
+ if (Token.eq("}", t) || Token.eq(")", t))
+ {
+ if (pipes != null)
+ {
+ throw new EOFError(t.line, t.column, "unexpected token '" + t + "' while looking for a statement after |", getMissing("pipe"), "0");
+ }
+ else
+ {
+ push(t);
+ return new Program(whole(tokens, start), tokens);
+ }
}
else
{
- switch (tz.type())
+ push(t);
+ ex = statement();
+ }
+ t = next();
+ if (t == null || Token.eq(";", t) || Token.eq("\n", t))
+ {
+ if (pipes != null)
{
- 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);
+ pipes.add(ex);
+ tokens.add(new Pipeline(whole(pipes, start), pipes));
+ pipes = null;
+ }
+ else
+ {
+ tokens.add(ex);
+ }
+ if (t == null)
+ {
+ return new Program(whole(tokens, start), tokens);
}
}
+ else if (Token.eq("|", t))
+ {
+ if (pipes == null)
+ {
+ pipes = new ArrayList<Executable>();
+ }
+ pipes.add(ex);
+ }
+ else
+ {
+ if (pipes != null)
+ {
+ pipes.add(ex);
+ tokens.add(new Pipeline(whole(pipes, start), pipes));
+ pipes = null;
+ }
+ else
+ {
+ tokens.add(ex);
+ }
+ push(t);
+ }
}
}
+ protected void push(Token t) {
+ tz.push(t);
+ }
+
+ protected Token next() {
+ boolean pushed = tz.pushed != null;
+ Token token = tz.next();
+ if (!pushed && token != null) {
+ tokens.add(token);
+ }
+ return token;
+ }
+
+ public Sequence sequence()
+ {
+ Token start = start("(", "sequence");
+ expectNotNull();
+ Program program = program();
+ Token end = end(")");
+ return new Sequence(whole(start, end), program);
+ }
+
+ public Closure closure()
+ {
+ Token start = start("{", "closure");
+ expectNotNull();
+ Program program = program();
+ Token end = end("}");
+ return new Closure(whole(start, end), program);
+ }
+
+ public Statement statement()
+ {
+ List<Token> tokens = new ArrayList<Token>();
+ int start = tz.index;
+ while (true)
+ {
+ Token t = next();
+ if (t == null
+ || Token.eq("|", t)
+ || Token.eq("\n", t)
+ || Token.eq(";", t)
+ || Token.eq("}", t)
+ || Token.eq(")", t)
+ || Token.eq("]", t))
+ {
+ push(t);
+ break;
+ }
+ if (Token.eq("{", t))
+ {
+ push(t);
+ tokens.add(closure());
+ }
+ else if (Token.eq("[", t))
+ {
+ push(t);
+ tokens.add(array());
+ }
+ else if (Token.eq("(", t))
+ {
+ push(t);
+ tokens.add(sequence());
+ }
+ else
+ {
+ tokens.add(t);
+ }
+ }
+ Statement statement = new Statement(whole(tokens, start), tokens);
+ statements.add(statement);
+ return statement;
+ }
+
+ public Array array()
+ {
+ Token start = start("[", "array");
+ Boolean isMap = null;
+ List<Token> list = new ArrayList<Token>();
+ Map<Token, Token> map = new LinkedHashMap<Token, Token>();
+ while (true)
+ {
+ Token key = next();
+ if (key == null)
+ {
+ throw new EOFError(tz.line, tz.column, "unexpected EOT", getMissing(), "]");
+ }
+ if (Token.eq("]", key))
+ {
+ push(key);
+ break;
+ }
+ if (Token.eq("\n", key))
+ {
+ continue;
+ }
+ if (Token.eq("{", key) || Token.eq(";", key)
+ || Token.eq("|", key) || Token.eq(")", key) || Token.eq("}", key) || Token.eq("=", key))
+ {
+ throw new SyntaxError(key.line(), key.column(), "unexpected token '" + key + "' while looking for array key");
+ }
+ if (Token.eq("(", key))
+ {
+ push(key);
+ key = sequence();
+ }
+ if (Token.eq("[", key))
+ {
+ push(key);
+ key = array();
+ }
+ if (isMap == null)
+ {
+ Token n = next();
+ if (n == null)
+ {
+ throw new EOFError(tz.line, tz.column, "unexpected EOF while looking for array token", getMissing(), "]");
+ }
+ isMap = Token.eq("=", n);
+ push(n);
+ }
+ if (isMap)
+ {
+ expect("=");
+ Token val = next();
+ if (val == null)
+ {
+ throw new EOFError(tz.line, tz.column, "unexpected EOF while looking for array value", getMissing(), "0");
+ }
+ else if (Token.eq(";", val) || Token.eq("|", val)
+ || Token.eq(")", key) || Token.eq("}", key) || Token.eq("=", key))
+ {
+ throw new SyntaxError(key.line(), key.column(), "unexpected token '" + key + "' while looking for array value");
+ }
+ else if (Token.eq("[", val))
+ {
+ push(val);
+ val = array();
+ }
+ else if (Token.eq("(", val))
+ {
+ push(val);
+ val = sequence();
+ }
+ else if (Token.eq("{", val))
+ {
+ push(val);
+ val = closure();
+ }
+ map.put(key, val);
+ }
+ else
+ {
+ list.add(key);
+ }
+ }
+ Token end = end("]");
+ if (isMap == null || !isMap)
+ {
+ return new Array(whole(start, end), list, null);
+ }
+ else
+ {
+ return new Array(whole(start, end), null, map);
+ }
+ }
+
+ protected void expectNotNull()
+ {
+ Token t = next();
+ if (t == null)
+ {
+ throw new EOFError(tz.line, tz.column,
+ "unexpected EOT",
+ getMissing(), "0");
+ }
+ push(t);
+ }
+
+ private String getMissing() {
+ return getMissing(null);
+ }
+
+ private String getMissing(String additional) {
+ StringBuilder sb = new StringBuilder();
+ LinkedList<String> stack = this.stack;
+ if (additional != null) {
+ stack = new LinkedList<String>(stack);
+ stack.addLast(additional);
+ }
+ String last = null;
+ int nb = 0;
+ for (String cur : stack) {
+ if (last == null) {
+ last = cur;
+ nb = 1;
+ } else if (last.equals(cur)) {
+ nb++;
+ } else {
+ if (sb.length() > 0) {
+ sb.append(" ");
+ }
+ sb.append(last);
+ if (nb > 1) {
+ sb.append("(").append(nb).append(")");
+ }
+ last = cur;
+ nb = 1;
+ }
+ }
+ if (sb.length() > 0) {
+ sb.append(" ");
+ }
+ sb.append(last);
+ if (nb > 1) {
+ sb.append("(").append(nb).append(")");
+ }
+ return sb.toString();
+ }
+
+ protected Token start(String str, String missing) {
+ stack.addLast(missing);
+ return expect(str);
+ }
+
+ protected Token end(String str) {
+ Token t = expect(str);
+ stack.removeLast();
+ return t;
+ }
+
+ protected Token expect(String str)
+ {
+ Token start = next();
+ if (start == null)
+ {
+ throw new EOFError(tz.line, tz.column,
+ "unexpected EOT looking for '" + str + "",
+ getMissing(), str);
+ }
+ if (!Token.eq(str, start))
+ {
+ throw new SyntaxError(start.line, start.column, "expected '" + str + "' but got '" + start.toString() + "'");
+ }
+ return start;
+ }
+
+ protected Token whole(List<? extends Token> tokens, int index)
+ {
+ if (tokens.isEmpty())
+ {
+ index = Math.min(index, tz.text().length());
+ return tz.text().subSequence(index, index);
+ }
+ Token b = tokens.get(0);
+ Token e = tokens.get(tokens.size() - 1);
+ return whole(b, e);
+ }
+
+ protected Token whole(Token b, Token e)
+ {
+ return tz.text.subSequence(b.start, e.start + e.length());
+ }
+
}
diff --git a/gogo/runtime/src/main/java/org/apache/felix/gogo/runtime/Pipe.java b/gogo/runtime/src/main/java/org/apache/felix/gogo/runtime/Pipe.java
index 7531835..ed2dba9 100644
--- a/gogo/runtime/src/main/java/org/apache/felix/gogo/runtime/Pipe.java
+++ b/gogo/runtime/src/main/java/org/apache/felix/gogo/runtime/Pipe.java
@@ -24,8 +24,8 @@
import java.io.PipedOutputStream;
import java.io.PrintStream;
import java.lang.reflect.Method;
-import java.util.List;
+import org.apache.felix.gogo.runtime.Parser.Executable;
import org.apache.felix.service.command.Converter;
public class Pipe extends Thread
@@ -40,7 +40,7 @@
Closure closure;
Exception exception;
Object result;
- List<Token> statement;
+ Executable executable;
public static Object[] mark()
{
@@ -55,11 +55,11 @@
tErr.set((PrintStream) mark[2]);
}
- public Pipe(Closure closure, List<Token> statement)
+ public Pipe(Closure closure, Executable executable)
{
- super("pipe-" + statement);
+ super("pipe-" + executable);
this.closure = closure;
- this.statement = statement;
+ this.executable = executable;
in = tIn.get();
out = tOut.get();
@@ -68,7 +68,7 @@
public String toString()
{
- return "pipe<" + statement + "> out=" + out;
+ return "pipe<" + executable + "> out=" + out;
}
public void setIn(InputStream in)
@@ -105,7 +105,7 @@
try
{
- result = closure.executeStatement(statement);
+ result = closure.execute(executable);
if (result != null && pout != null)
{
if (!Boolean.FALSE.equals(closure.session().get(".FormatPipe")))
diff --git a/gogo/runtime/src/main/java/org/apache/felix/gogo/runtime/Reflective.java b/gogo/runtime/src/main/java/org/apache/felix/gogo/runtime/Reflective.java
index d314fe0..442505a 100644
--- a/gogo/runtime/src/main/java/org/apache/felix/gogo/runtime/Reflective.java
+++ b/gogo/runtime/src/main/java/org/apache/felix/gogo/runtime/Reflective.java
@@ -346,7 +346,7 @@
* to allow the "best" conversion to be determined.
* @return converted arg or NO_MATCH if no conversion possible.
*/
- private static Object coerce(CommandSession session, Class<?> type, Object arg,
+ public static Object coerce(CommandSession session, Class<?> type, Object arg,
int[] convert)
{
if (arg == null)
@@ -386,7 +386,7 @@
// all following conversions cost 2 points
convert[0] += 2;
- Object converted = session.convert(type, arg);
+ Object converted = ((CommandSessionImpl) session).doConvert(type, arg);
if (converted != null)
{
return converted;
@@ -399,6 +399,17 @@
return string;
}
+ if (type.isEnum())
+ {
+ for (Object o : type.getEnumConstants())
+ {
+ if (o.toString().equalsIgnoreCase(string))
+ {
+ return o;
+ }
+ }
+ }
+
if (type.isPrimitive())
{
type = primitiveToObject(type);
diff --git a/gogo/runtime/src/main/java/org/apache/felix/gogo/runtime/SyntaxError.java b/gogo/runtime/src/main/java/org/apache/felix/gogo/runtime/SyntaxError.java
index b359938..ff87519 100644
--- a/gogo/runtime/src/main/java/org/apache/felix/gogo/runtime/SyntaxError.java
+++ b/gogo/runtime/src/main/java/org/apache/felix/gogo/runtime/SyntaxError.java
@@ -23,12 +23,19 @@
private static final long serialVersionUID = 1L;
private final int line;
private final int column;
+ private final Token statement;
public SyntaxError(int line, int column, String message)
{
+ this(line, column, message, null);
+ }
+
+ public SyntaxError(int line, int column, String message, Token statement)
+ {
super(message);
this.line = line;
this.column = column;
+ this.statement = statement;
}
public int column()
@@ -40,4 +47,9 @@
{
return line;
}
+
+ public Token statement() {
+ return statement;
+ }
+
}
diff --git a/gogo/runtime/src/main/java/org/apache/felix/gogo/runtime/Token.java b/gogo/runtime/src/main/java/org/apache/felix/gogo/runtime/Token.java
index 93320fe..bbc087a 100644
--- a/gogo/runtime/src/main/java/org/apache/felix/gogo/runtime/Token.java
+++ b/gogo/runtime/src/main/java/org/apache/felix/gogo/runtime/Token.java
@@ -18,63 +18,113 @@
*/
package org.apache.felix.gogo.runtime;
-import org.apache.felix.gogo.runtime.Tokenizer.Type;
+public class Token implements CharSequence {
-public class Token implements CharSequence
-{
- Type type;
- CharSequence value;
- short line;
- short column;
-
- public Token(Type type, CharSequence value, short line, short column)
+ protected final char[] ch;
+ protected final int start;
+ protected final int length;
+ protected final int line;
+ protected final int column;
+
+ public Token(CharSequence cs)
{
- this.type = type;
- this.value = value;
- this.line = line;
- this.column = column;
+ if (cs instanceof Token)
+ {
+ Token ca = (Token) cs;
+ this.ch = ca.ch;
+ this.start = ca.start;
+ this.length = ca.length;
+ this.line = ca.line;
+ this.column = ca.column;
+ }
+ else
+ {
+ this.ch = cs.toString().toCharArray();
+ this.start = 0;
+ this.length = ch.length;
+ this.line = 0;
+ this.column = 0;
+ }
}
- @Override
- public String toString()
+ public Token(char[] _ch, int _start, int _length, int _line, int _col)
{
- //return type + "<" + value + ">";
- return null == value ? type.toString() : value.toString();
+ this.ch = _ch;
+ this.start = _start;
+ this.length = _length;
+ this.line = _line;
+ this.column = _col;
}
-
- public char charAt(int index)
+
+ public int line()
{
- return value.charAt(index);
+ return line;
+ }
+
+ public int column()
+ {
+ return column;
+ }
+
+ public int start()
+ {
+ return start;
}
public int length()
{
- return (null == value ? 0 : value.length());
+ return this.length;
}
- public CharSequence subSequence(int start, int end)
+ public char charAt(int index)
{
- return value.subSequence(start, end);
+ return this.ch[this.start + index];
}
-
- public String source()
+
+ public Token subSequence(int start, int end)
{
- switch (type)
+ int line = this.line;
+ int col = this.column;
+ for (int i = this.start; i < this.start + start; i++)
{
- case WORD:
- return value.toString();
-
- case CLOSURE:
- return "{" + value + "}";
-
- case EXECUTION:
- return "(" + value + ")";
-
- case ARRAY:
- return "[" + value + "]";
-
- default:
- return type.toString();
+ if (ch[i] == '\n')
+ {
+ line++;
+ col = 0;
+ }
+ else
+ {
+ col++;
+ }
}
+ return new Token(this.ch, this.start + start, end - start, line, col);
}
+
+ public String toString()
+ {
+ return new String(this.ch, this.start, this.length);
+ }
+
+ public static boolean eq(CharSequence cs1, CharSequence cs2)
+ {
+ if (cs1 == cs2)
+ {
+ return true;
+ }
+ int l1 = cs1.length();
+ int l2 = cs2.length();
+ if (l1 != l2)
+ {
+ return false;
+ }
+ for (int i = 0; i < l1; i++)
+ {
+ if (cs1.charAt(i) != cs2.charAt(i))
+ {
+ return false;
+ }
+ }
+ return true;
+ }
+
}
diff --git a/gogo/runtime/src/main/java/org/apache/felix/gogo/runtime/Tokenizer.java b/gogo/runtime/src/main/java/org/apache/felix/gogo/runtime/Tokenizer.java
index c2fd39b..390becc 100644
--- a/gogo/runtime/src/main/java/org/apache/felix/gogo/runtime/Tokenizer.java
+++ b/gogo/runtime/src/main/java/org/apache/felix/gogo/runtime/Tokenizer.java
@@ -18,782 +18,138 @@
*/
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 class Tokenizer extends BaseTokenizer
{
- public enum Type
- {
- ASSIGN('='), PIPE('|'), SEMICOLON(';'), NEWLINE, ARRAY, CLOSURE, EXPR, EXECUTION, WORD, EOT;
- private char c;
+ protected boolean inArray;
+ protected int word = 0;
- 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;
-
- 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;
+ protected Token pushed;
+ protected Token last;
public Tokenizer(CharSequence text)
{
- this(text, null, false);
+ super(text);
}
- public Tokenizer(CharSequence text, Evaluate evaluate, boolean inQuote)
+ public Token text()
{
- this.text = text;
- this.evaluate = evaluate;
- this.inQuote = inQuote;
- index = 0;
- line = column = 1;
+ return text;
+ }
- boolean array = false;
-
- if (text instanceof Token)
+ public Token next()
+ {
+ if (pushed != null)
{
- Token t = (Token) text;
- line = t.line;
- column = t.column;
- array = (Type.ARRAY == t.type);
+ Token t = pushed;
+ pushed = null;
+ return t;
}
-
- 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;
-
+ skipSpace(last == null || Token.eq(last, "\n"));
+ int start = index - 1;
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;
-
+ return token(start);
+ case '[':
+ word = 0;
+ inArray = true;
+ return token(start);
+ case ']':
+ inArray = false;
+ word++;
+ return token(start);
case '{':
case '(':
- case '[':
- value = group();
- getch();
- break;
-
- case ';':
- getch();
- type = Type.SEMICOLON;
- break;
-
- case '|':
- getch();
- type = Type.PIPE;
- break;
-
- case '=':
- if (firstWord || inArray)
+ if (start == index - 1)
{
+ word = 0;
+ return token(start);
+ }
+ else
+ {
+ if (ch == '{')
+ {
+ find('}', '{');
+ }
+ else
+ {
+ find(')', '(');
+ }
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 '|':
+ word = 0;
+ return token(start);
+ case '}':
+ case ')':
case ' ':
case '\t':
- case '|':
- case ';':
- return text.subSequence(start, index - 1 - skipCR);
-
- case '(':
- case '{':
- group();
+ case '\n':
+ case '\r':
+ word++;
+ return token(start);
+ case '=':
+ if (inArray || word < 1 || index == start + 1)
+ {
+ word++;
+ return token(start);
+ }
+ getch();
break;
-
case '\\':
escape();
+ getch();
break;
-
case '\'':
case '"':
skipQuote();
+ getch();
break;
- }
- }
- while (getch() != EOT);
-
- return text.subSequence(start, index - 1);
- }
-
- private CharSequence group()
- {
- final char push = ch;
- final char pop;
-
- switch (ch)
- {
- case '{':
- type = Type.CLOSURE;
- pop = '}';
- break;
- case '(':
- type = Type.EXECUTION;
- pop = ')';
- break;
- case '[':
- type = Type.ARRAY;
- pop = ']';
- break;
- default:
- assert false;
- pop = 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)
- return text.subSequence(start, index - 1);
- }
- }
-
- }
-
- 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 exp = expandExp();
-
- if (EOT == ch && buf.length() == 0)
- {
- return exp;
- }
-
- if (null != exp)
- {
- buf.append(exp);
- }
-
- continue; // expandVar() has already read next char
-
- 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 expandExp() throws Exception
- {
- assert '%' == ch;
- Object val;
-
- if (getch() == '(')
- {
- short sLine = line;
- short sCol = column;
- val = evaluate.eval(new Token(Type.EXPR, group(), sLine, sCol));
- getch();
- return val;
- }
- else
- {
- throw new SyntaxError(line, column, "bad expression: " + text);
}
}
- private Object expandVar() throws Exception
+ private Token token(int start)
{
- assert '$' == ch;
- Object val;
-
- if (getch() != '{')
+ if (start == index - 1)
{
- if ('(' == ch)
- { // support $(...) FELIX-2433
- short sLine = line;
- short sCol = column;
- val = evaluate.eval(new Token(Type.EXECUTION, group(), sLine, sCol));
+ if (ch == EOT)
+ {
+ return null;
+ }
+ if (ch == '\r' && peek() == '\n')
+ {
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);
- }
- }
+ getch();
+ last = text.subSequence(index - 2, index - 1);
}
else
{
- // ${NAME[[:]-+=?]WORD}
- short sLine = line;
- short sCol = column;
- CharSequence group = group();
- char c;
- int i = 0;
+ last = text.subSequence(start, index - 1);
+ }
+ return last;
+ }
- while (i < group.length())
- {
- switch (group.charAt(i))
- {
- case ':':
- case '-':
- case '+':
- case '=':
- case '?':
- break;
+ public void push(Token token)
+ {
+ this.pushed = token;
+ }
- 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
- + "}");
- }
- }
+ public void skip(int length)
+ {
+ while (--length >= 0)
+ {
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;
}
}
diff --git a/gogo/runtime/src/main/java/org/apache/felix/service/command/CommandSession.java b/gogo/runtime/src/main/java/org/apache/felix/service/command/CommandSession.java
index 6aad74f..c81c8af 100644
--- a/gogo/runtime/src/main/java/org/apache/felix/service/command/CommandSession.java
+++ b/gogo/runtime/src/main/java/org/apache/felix/service/command/CommandSession.java
@@ -74,7 +74,7 @@
* @param name Name of the variable.
* @param value Value of the variable
*/
- void put(String name, Object value);
+ Object put(String name, Object value);
/**
* Convert an object to string form (CharSequence). The level is defined in
diff --git a/gogo/runtime/src/test/java/org/apache/felix/gogo/runtime/BaseTestCase.java b/gogo/runtime/src/test/java/org/apache/felix/gogo/runtime/AbstractParserTest.java
similarity index 62%
rename from gogo/runtime/src/test/java/org/apache/felix/gogo/runtime/BaseTestCase.java
rename to gogo/runtime/src/test/java/org/apache/felix/gogo/runtime/AbstractParserTest.java
index 488e49b..7522397 100644
--- a/gogo/runtime/src/test/java/org/apache/felix/gogo/runtime/BaseTestCase.java
+++ b/gogo/runtime/src/test/java/org/apache/felix/gogo/runtime/AbstractParserTest.java
@@ -16,27 +16,32 @@
* specific language governing permissions and limitations
* under the License.
*/
-
package org.apache.felix.gogo.runtime;
import junit.framework.TestCase;
+import org.apache.felix.gogo.runtime.threadio.ThreadIOImpl;
-/**
- * @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
- */
-abstract class BaseTestCase extends TestCase
-{
- protected Context m_ctx;
+public abstract class AbstractParserTest extends TestCase {
+
+ private ThreadIOImpl threadIO;
@Override
- protected final void setUp() throws Exception
- {
- m_ctx = new Context(true);
+ protected void setUp() throws Exception {
+ super.setUp();
+ threadIO = new ThreadIOImpl();
+ threadIO.start();
}
@Override
- protected final void tearDown() throws Exception
- {
- m_ctx.stop();
+ protected void tearDown() throws Exception {
+ threadIO.stop();
+ super.tearDown();
}
+
+ public class Context extends org.apache.felix.gogo.runtime.Context {
+ public Context() {
+ super(AbstractParserTest.this.threadIO);
+ }
+ }
+
}
diff --git a/gogo/runtime/src/test/java/org/apache/felix/gogo/runtime/Context.java b/gogo/runtime/src/test/java/org/apache/felix/gogo/runtime/Context.java
index bd40bf2..e4096d2 100644
--- a/gogo/runtime/src/test/java/org/apache/felix/gogo/runtime/Context.java
+++ b/gogo/runtime/src/test/java/org/apache/felix/gogo/runtime/Context.java
@@ -18,42 +18,22 @@
*/
package org.apache.felix.gogo.runtime;
-import org.apache.felix.gogo.runtime.threadio.ThreadIOImpl;
import org.apache.felix.service.command.CommandSession;
+import org.apache.felix.service.threadio.ThreadIO;
public class Context extends CommandProcessorImpl
{
public static final String EMPTY = "";
-
- private static final ThreadIOImpl threadio;
+
private final CommandSession session;
- static
- {
- threadio = new ThreadIOImpl();
- threadio.start();
- }
-
- public Context(boolean foo)
+ public Context(ThreadIO threadio)
{
super(threadio);
addCommand("osgi", this, "addCommand");
addCommand("osgi", this, "removeCommand");
addCommand("osgi", this, "eval");
- session = (CommandSessionImpl) createSession(System.in, System.out, System.err);
- }
-
- @Override
- public void stop()
- {
- try
- {
- super.stop();
- }
- finally
- {
- threadio.stop();
- }
+ session = createSession(System.in, System.out, System.err);
}
public Object execute(CharSequence source) throws Exception
@@ -76,9 +56,9 @@
addCommand("test", target, function);
}
- public void set(String name, Object value)
+ public Object set(String name, Object value)
{
- session.put(name, value);
+ return session.put(name, value);
}
public Object get(String name)
diff --git a/gogo/runtime/src/test/java/org/apache/felix/gogo/runtime/TestCoercion.java b/gogo/runtime/src/test/java/org/apache/felix/gogo/runtime/TestCoercion.java
index 7ab32a1..f4ea27c 100644
--- a/gogo/runtime/src/test/java/org/apache/felix/gogo/runtime/TestCoercion.java
+++ b/gogo/runtime/src/test/java/org/apache/felix/gogo/runtime/TestCoercion.java
@@ -22,7 +22,7 @@
import org.apache.felix.service.command.Descriptor;
import org.apache.felix.service.command.Parameter;
-public class TestCoercion extends BaseTestCase
+public class TestCoercion extends AbstractParserTest
{
public boolean fBool(boolean t)
{
@@ -57,31 +57,32 @@
public void testSimpleTypes() throws Exception
{
- m_ctx.addCommand("fBool", this);
- m_ctx.addCommand("fDouble", this);
- m_ctx.addCommand("fInt", this);
- m_ctx.addCommand("fLong", this);
- m_ctx.addCommand("fString", this);
+ Context c = new Context();
+ c.addCommand("fBool", this);
+ c.addCommand("fDouble", this);
+ c.addCommand("fInt", this);
+ c.addCommand("fLong", this);
+ c.addCommand("fString", this);
- assertEquals("fBool true", true, m_ctx.execute("fBool true"));
- assertEquals("fBool 'false'", false, m_ctx.execute("fBool 'false'"));
-
- assertEquals("fDouble 11", 11.0, m_ctx.execute("fDouble 11"));
- assertEquals("fDouble '11'", 11.0, m_ctx.execute("fDouble '11'"));
-
- assertEquals("fInt 22", 22, m_ctx.execute("fInt 22"));
- assertEquals("fInt '23'", 23, m_ctx.execute("fInt '23'"));
- assertEquals("fInt 1 2", "array", m_ctx.execute("fInt 1 2"));
-
- assertEquals("fLong 33", 33L, m_ctx.execute("fLong 33"));
- assertEquals("fLong '34'", 34L, m_ctx.execute("fLong '34'"));
-
- assertEquals("fString wibble", "wibble", m_ctx.execute("fString wibble"));
- assertEquals("fString 'wibble'", "wibble", m_ctx.execute("fString 'wibble'"));
+ assertEquals("fBool true", true, c.execute("fBool true"));
+ assertEquals("fBool 'false'", false, c.execute("fBool 'false'"));
+
+ assertEquals("fDouble 11", 11.0, c.execute("fDouble 11"));
+ assertEquals("fDouble '11'", 11.0, c.execute("fDouble '11'"));
+
+ assertEquals("fInt 22", 22, c.execute("fInt 22"));
+ assertEquals("fInt '23'", 23, c.execute("fInt '23'"));
+ assertEquals("fInt 1 2", "array", c.execute("fInt 1 2"));
+
+ assertEquals("fLong 33", 33L, c.execute("fLong 33"));
+ assertEquals("fLong '34'", 34L, c.execute("fLong '34'"));
+
+ assertEquals("fString wibble", "wibble", c.execute("fString wibble"));
+ assertEquals("fString 'wibble'", "wibble", c.execute("fString 'wibble'"));
try
{
- Object r = m_ctx.execute("fString ");
+ Object r = c.execute("fString ");
fail("too few args: expected IllegalArgumentException, got: " + r);
}
catch (IllegalArgumentException e)
@@ -90,7 +91,7 @@
try
{
- Object r = m_ctx.execute("fString a b");
+ Object r = c.execute("fString a b");
fail("too many args: expected IllegalArgumentException, got: " + r);
}
catch (IllegalArgumentException e)
@@ -99,7 +100,7 @@
try
{
- Object r = m_ctx.execute("fLong string");
+ Object r = c.execute("fLong string");
fail("wrong arg type: expected IllegalArgumentException, got: " + r);
}
catch (IllegalArgumentException e)
@@ -119,11 +120,12 @@
public void testBestCoercion() throws Exception
{
- m_ctx.addCommand("bundles", this);
+ Context c = new Context();
+ c.addCommand("bundles", this);
- assertEquals("bundles myloc", "string", m_ctx.execute("bundles myloc"));
- assertEquals("bundles 1", "long", m_ctx.execute("bundles 1"));
- assertEquals("bundles '1'", "string", m_ctx.execute("bundles '1'"));
+ assertEquals("bundles myloc", "string", c.execute("bundles myloc"));
+ assertEquals("bundles 1", "long", c.execute("bundles 1"));
+ assertEquals("bundles '1'", "string", c.execute("bundles '1'"));
}
@Descriptor("list all installed bundles")
@@ -145,23 +147,24 @@
public void testParameter0() throws Exception
{
- m_ctx.addCommand("p0", this);
- m_ctx.addCommand("p01", this);
+ Context c = new Context();
+ c.addCommand("p0", this);
+ c.addCommand("p01", this);
- assertEquals("p0", "false:false", m_ctx.execute("p0"));
- assertEquals("p0 -l", "true:false", m_ctx.execute("p0 -l"));
- assertEquals("p0 --location", "true:false", m_ctx.execute("p0 --location"));
- assertEquals("p0 -l -s", "true:true", m_ctx.execute("p0 -l -s"));
- assertEquals("p0 -s -l", "true:true", m_ctx.execute("p0 -s -l"));
+ assertEquals("p0", "false:false", c.execute("p0"));
+ assertEquals("p0 -l", "true:false", c.execute("p0 -l"));
+ assertEquals("p0 --location", "true:false", c.execute("p0 --location"));
+ assertEquals("p0 -l -s", "true:true", c.execute("p0 -l -s"));
+ assertEquals("p0 -s -l", "true:true", c.execute("p0 -s -l"));
try
{
- Object r = m_ctx.execute("p0 wibble");
+ Object r = c.execute("p0 wibble");
fail("too many args: expected IllegalArgumentException, got: " + r);
}
catch (IllegalArgumentException e)
{
}
- assertEquals("p01 -f", true, m_ctx.execute("p01 -f"));
+ assertEquals("p01 -f", true, c.execute("p01 -f"));
}
public String p1(
@@ -172,16 +175,17 @@
public void testParameter1() throws Exception
{
- m_ctx.addCommand("p1", this);
+ Context c = new Context();
+ c.addCommand("p1", this);
- assertEquals("no parameter", "absent", m_ctx.execute("p1"));
+ assertEquals("no parameter", "absent", c.execute("p1"));
// FELIX-2894
- assertEquals("correct parameter", "wibble", m_ctx.execute("p1 -p wibble"));
+ assertEquals("correct parameter", "wibble", c.execute("p1 -p wibble"));
try
{
- Object r = m_ctx.execute("p1 -p");
+ Object r = c.execute("p1 -p");
fail("missing parameter: expected IllegalArgumentException, got: " + r);
}
catch (IllegalArgumentException e)
@@ -190,11 +194,12 @@
try
{
- Object r = m_ctx.execute("p1 -X");
+ Object r = c.execute("p1 -X");
fail("wrong parameter: expected IllegalArgumentException, got: " + r);
}
catch (IllegalArgumentException e)
{
}
}
+
}
diff --git a/gogo/runtime/src/test/java/org/apache/felix/gogo/runtime/TestParser.java b/gogo/runtime/src/test/java/org/apache/felix/gogo/runtime/TestParser.java
index 7250b0a..a034f2b 100644
--- a/gogo/runtime/src/test/java/org/apache/felix/gogo/runtime/TestParser.java
+++ b/gogo/runtime/src/test/java/org/apache/felix/gogo/runtime/TestParser.java
@@ -27,28 +27,34 @@
import java.util.List;
import java.util.regex.Pattern;
+import org.apache.felix.gogo.runtime.Parser.Pipeline;
+import org.apache.felix.gogo.runtime.Parser.Program;
+import org.apache.felix.gogo.runtime.Parser.Sequence;
+import org.apache.felix.gogo.runtime.Parser.Statement;
import org.apache.felix.service.command.CommandSession;
import org.apache.felix.service.command.Function;
-public class TestParser extends BaseTestCase
+public class TestParser extends AbstractParserTest
{
int beentheredonethat = 0;
public void testEvaluatation() throws Exception
{
- m_ctx.addCommand("echo", this);
- m_ctx.addCommand("capture", this);
+ Context c = new Context();
+ c.addCommand("echo", this);
+ c.addCommand("capture", this);
- assertEquals("a", m_ctx.execute("echo a | capture"));
- assertEquals("a", m_ctx.execute("(echo a) | capture"));
- assertEquals("a", m_ctx.execute("((echo a)) | capture"));
+ assertEquals("a", c.execute("echo a | capture"));
+ assertEquals("a", c.execute("(echo a) | capture"));
+ assertEquals("a", c.execute("((echo a)) | capture"));
}
public void testUnknownCommand() throws Exception
{
+ Context c = new Context();
try
{
- m_ctx.execute("echo");
+ c.execute("echo");
fail("Execution should have failed due to missing command");
}
catch (IllegalArgumentException e)
@@ -59,120 +65,129 @@
public void testSpecialValues() throws Exception
{
- assertEquals(false, m_ctx.execute("false"));
- assertEquals(true, m_ctx.execute("true"));
- assertEquals(null, m_ctx.execute("null"));
+ Context c = new Context();
+ assertEquals(false, c.execute("false"));
+ assertEquals(true, c.execute("true"));
+ assertEquals(null, c.execute("null"));
}
public void testQuotes() throws Exception
{
- m_ctx.addCommand("echo", this);
- m_ctx.set("c", "a");
+ Context c = new Context();
+ c.addCommand("echo", this);
+ c.set("c", "a");
- assertEquals("a b", m_ctx.execute("echo a b"));
- assertEquals("a b", m_ctx.execute("echo 'a b'"));
- assertEquals("a b", m_ctx.execute("echo \"a b\""));
- assertEquals("a b", m_ctx.execute("echo a b"));
- assertEquals("a b", m_ctx.execute("echo 'a b'"));
- assertEquals("a b", m_ctx.execute("echo \"a b\""));
- assertEquals("a b", m_ctx.execute("echo $c b"));
- assertEquals("$c b", m_ctx.execute("echo '$c b'"));
- assertEquals("a b", m_ctx.execute("echo \"$c b\""));
- assertEquals("a b", m_ctx.execute("echo ${c} b"));
- assertEquals("${c} b", m_ctx.execute("echo '${c} b'"));
- assertEquals("a b", m_ctx.execute("echo \"${c} b\""));
- assertEquals("aa", m_ctx.execute("echo $c$c"));
- assertEquals("a ;a", m_ctx.execute("echo a\\ \\;a"));
- assertEquals("baabab", m_ctx.execute("echo b${c}${c}b${c}b"));
+ assertEquals("a b", c.execute("echo a b"));
+ assertEquals("a b", c.execute("echo 'a b'"));
+ assertEquals("a b", c.execute("echo \"a b\""));
+ assertEquals("a b", c.execute("echo a b"));
+ assertEquals("a b", c.execute("echo 'a b'"));
+ assertEquals("a b", c.execute("echo \"a b\""));
+ assertEquals("a b", c.execute("echo $c b"));
+ assertEquals("$c b", c.execute("echo '$c b'"));
+ assertEquals("a b", c.execute("echo \"$c b\""));
+ assertEquals("a b", c.execute("echo ${c} b"));
+ assertEquals("${c} b", c.execute("echo '${c} b'"));
+ assertEquals("a b", c.execute("echo \"${c} b\""));
+ assertEquals("aa", c.execute("echo $c$c"));
+ assertEquals("a ;a", c.execute("echo a\\ \\;a"));
+ assertEquals("baabab", c.execute("echo b${c}${c}b${c}b"));
- m_ctx.set("d", "a b ");
- assertEquals("a b ", m_ctx.execute("echo \"$d\""));
+ c.set("d", "a b ");
+ assertEquals("a b ", c.execute("echo \"$d\""));
}
public void testScope() throws Exception
{
- m_ctx.addCommand("echo", this);
- assertEquals("$a", m_ctx.execute("test:echo \\$a"));
- assertEquals("file://poo", m_ctx.execute("test:echo file://poo"));
+ Context c = new Context();
+ c.addCommand("echo", this);
+ assertEquals("$a", c.execute("test:echo \\$a"));
+ assertEquals("file://poo", c.execute("test:echo file://poo"));
}
public void testPipe() throws Exception
{
- m_ctx.addCommand("echo", this);
- m_ctx.addCommand("capture", this);
- m_ctx.addCommand("grep", this);
- m_ctx.addCommand("echoout", this);
- m_ctx.execute("myecho = { echoout $args }");
- assertEquals("def", m_ctx.execute("echo def|grep d.*|capture"));
- assertEquals("def", m_ctx.execute("echoout def|grep d.*|capture"));
- assertEquals("def", m_ctx.execute("myecho def|grep d.*|capture"));
+ Context c = new Context();
+ c.addCommand("echo", this);
+ c.addCommand("capture", this);
+ c.addCommand("grep", this);
+ c.addCommand("echoout", this);
+ c.execute("myecho = { echoout $args }");
+ assertEquals("def", c.execute("echo def|grep d.*|capture"));
+ assertEquals("def", c.execute("echoout def|grep d.*|capture"));
+ assertEquals("def", c.execute("myecho def|grep d.*|capture"));
assertEquals("def",
- m_ctx.execute("(echoout abc; echoout def; echoout ghi)|grep d.*|capture"));
- assertEquals("", m_ctx.execute("echoout def; echoout ghi | grep d.* | capture"));
- assertEquals("hello world", m_ctx.execute("echo hello world|capture"));
+ c.execute("(echoout abc; echoout def; echoout ghi)|grep d.*|capture"));
+ assertEquals("", c.execute("echoout def; echoout ghi | grep d.* | capture"));
+ assertEquals("hello world", c.execute("echo hello world|capture"));
assertEquals("defghi",
- m_ctx.execute("(echoout abc; echoout def; echoout ghi)|grep 'def|ghi'|capture"));
+ c.execute("(echoout abc; echoout def; echoout ghi)|grep 'def|ghi'|capture"));
}
public void testAssignment() throws Exception
{
- m_ctx.addCommand("echo", this);
- m_ctx.addCommand("grep", this);
- assertEquals("a", m_ctx.execute("a = a; echo ${$a}"));
+ Context c = new Context();
+ c.addCommand("echo", this);
+ c.addCommand("grep", this);
+ assertEquals("a", c.execute("a = a; echo ${$a}"));
- assertEquals("hello", m_ctx.execute("echo hello"));
- assertEquals("hello", m_ctx.execute("a = (echo hello)"));
- //assertEquals("a", m_ctx.execute("a = a; echo $(echo a)")); // #p2 - no eval in var expansion
- assertEquals("3", m_ctx.execute("a=3; echo $a"));
- assertEquals("3", m_ctx.execute("a = 3; echo $a"));
- assertEquals("a", m_ctx.execute("a = a; echo ${$a}"));
+ assertEquals("hello", c.execute("echo hello"));
+ assertEquals("hello", c.execute("a = (echo hello)"));
+ //assertEquals("a", c.execute("a = a; echo $(echo a)")); // #p2 - no eval in var expansion
+ assertEquals("3", c.execute("a=3; echo $a"));
+ assertEquals("3", c.execute("a = 3; echo $a"));
+ assertEquals("a", c.execute("a = a; echo ${$a}"));
}
public void testComment() throws Exception
{
- m_ctx.addCommand("echo", this);
- assertEquals("1", m_ctx.execute("echo 1 // hello"));
+ Context c = new Context();
+ c.addCommand("echo", this);
+ assertEquals("1", c.execute("echo 1 // hello"));
}
public void testClosure() throws Exception
{
- m_ctx.addCommand("echo", this);
- m_ctx.addCommand("capture", this);
+ Context c = new Context();
+ c.addCommand("echo", this);
+ c.addCommand("capture", this);
- assertEquals("a", m_ctx.execute("e = { echo $1 } ; e a b"));
- assertEquals("b", m_ctx.execute("e = { echo $2 } ; e a b"));
- assertEquals("b", m_ctx.execute("e = { eval $args } ; e echo b"));
- assertEquals("ca b", m_ctx.execute("e = { echo c$args } ; e a b"));
- assertEquals("c a b", m_ctx.execute("e = { echo c $args } ; e a b"));
- assertEquals("ca b", m_ctx.execute("e = { echo c$args } ; e 'a b'"));
+ assertEquals("a", c.execute("e = { echo $1 } ; e a b"));
+ assertEquals("b", c.execute("e = { echo $2 } ; e a b"));
+ assertEquals("b", c.execute("e = { eval $args } ; e echo b"));
+ assertEquals("ca b", c.execute("e = { echo c$args } ; e a b"));
+ assertEquals("c a b", c.execute("e = { echo c $args } ; e a b"));
+ assertEquals("ca b", c.execute("e = { echo c$args } ; e 'a b'"));
}
public void testArray() throws Exception
{
- m_ctx.set("echo", this);
+ Context c = new Context();
+ c.set("echo", true);
assertEquals("http://www.aqute.biz?com=2&biz=1",
- m_ctx.execute("['http://www.aqute.biz?com=2&biz=1'] get 0"));
- assertEquals("{a=2, b=3}", m_ctx.execute("[a=2 b=3]").toString());
- assertEquals(3L, m_ctx.execute("[a=2 b=3] get b"));
- assertEquals("[3, 4]", m_ctx.execute("[1 2 [3 4] 5 6] get 2").toString());
- assertEquals(5, m_ctx.execute("[1 2 [3 4] 5 6] size"));
+ c.execute("['http://www.aqute.biz?com=2&biz=1'] get 0"));
+ assertEquals("{a=2, b=3}", c.execute("[a=2 b=3]").toString());
+ assertEquals(3L, c.execute("[a=2 b=3] get b"));
+ assertEquals("[3, 4]", c.execute("[1 2 [3 4] 5 6] get 2").toString());
+ assertEquals(5, c.execute("[1 2 [3 4] 5 6] size"));
}
public void testParentheses()
{
Parser parser = new Parser("(a|b)|(d|f)");
- List<List<List<Token>>> p = parser.program();
- assertEquals("a|b", p.get(0).get(0).get(0).toString());
+ Program p = parser.program();
+ assertEquals("a|b", ((Sequence) ((Statement) ((Pipeline) p.tokens().get(0)).tokens().get(0)).tokens().get(0)).program().toString());
parser = new Parser("grep (d.*)|grep (d|f)");
p = parser.program();
- assertEquals("d.*", p.get(0).get(0).get(1).toString());
+ assertEquals("d.*", ((Sequence)((Statement) ((Pipeline) p.tokens().get(0)).tokens().get(0)).tokens().get(1)).program().toString());
}
public void testEcho() throws Exception
{
- m_ctx.addCommand("echo", this);
- m_ctx.execute("echo peter");
+ Context c = new Context();
+ c.addCommand("echo", this);
+ c.execute("echo peter");
}
public void grep(String match) throws IOException
@@ -205,20 +220,22 @@
public void testVars() throws Exception
{
- m_ctx.addCommand("echo", this);
+ Context c = new Context();
+ c.addCommand("echo", this);
- assertEquals("", m_ctx.execute("echo ${very.likely.that.this.does.not.exist}"));
- assertNotNull(m_ctx.execute("echo ${java.shell.name}"));
- assertEquals("a", m_ctx.execute("a = a; echo ${a}"));
+ assertEquals("", c.execute("echo ${very.likely.that.this.does.not.exist}"));
+ assertNotNull(c.execute("echo ${java.shell.name}"));
+ assertEquals("a", c.execute("a = a; echo ${a}"));
}
public void testFunny() throws Exception
{
- m_ctx.addCommand("echo", this);
- assertEquals("a", m_ctx.execute("echo a") + "");
- assertEquals("a", m_ctx.execute("eval (echo echo) a") + "");
- //assertEquals("a", m_ctx.execute("((echo echo) echo) (echo a)") + "");
- assertEquals("3", m_ctx.execute("[a=2 (echo b)=(echo 3)] get b").toString());
+ Context c = new Context();
+ c.addCommand("echo", this);
+ assertEquals("a", c.execute("echo a") + "");
+ assertEquals("a", c.execute("eval (echo echo) a") + "");
+ //assertEquals("a", c.execute("((echo echo) echo) (echo a)") + "");
+ assertEquals("3", c.execute("[a=2 (echo b)=(echo 3)] get b").toString());
}
public CharSequence echo(Object args[])
@@ -248,21 +265,22 @@
public void testContext() throws Exception
{
- m_ctx.addCommand("ls", this);
+ Context c = new Context();
+ c.addCommand("ls", this);
beentheredonethat = 0;
- m_ctx.execute("ls");
+ c.execute("ls");
assertEquals(1, beentheredonethat);
beentheredonethat = 0;
- m_ctx.execute("ls 10");
+ c.execute("ls 10");
assertEquals(10, beentheredonethat);
beentheredonethat = 0;
- m_ctx.execute("ls a b c d e f g h i j");
+ c.execute("ls a b c d e f g h i j");
assertEquals(10, beentheredonethat);
beentheredonethat = 0;
- Integer result = (Integer) m_ctx.execute("ls (ls 5)");
+ Integer result = (Integer) c.execute("ls (ls 5)");
assertEquals(10, beentheredonethat);
assertEquals((Integer) 5, result);
}
@@ -293,40 +311,52 @@
public void testProgram()
{
- List<List<List<Token>>> x = new Parser("abc def|ghi jkl;mno pqr|stu vwx").program();
- assertEquals("abc", x.get(0).get(0).get(0).toString());
- assertEquals("def", x.get(0).get(0).get(1).toString());
- assertEquals("ghi", x.get(0).get(1).get(0).toString());
- assertEquals("jkl", x.get(0).get(1).get(1).toString());
- assertEquals("mno", x.get(1).get(0).get(0).toString());
- assertEquals("pqr", x.get(1).get(0).get(1).toString());
- assertEquals("stu", x.get(1).get(1).get(0).toString());
- assertEquals("vwx", x.get(1).get(1).get(1).toString());
+ Program x = new Parser("abc def|ghi jkl;mno pqr|stu vwx").program();
+ Pipeline p0 = (Pipeline) x.tokens().get(0);
+ Statement s00 = (Statement) p0.tokens().get(0);
+ Statement s01 = (Statement) p0.tokens().get(1);
+ Pipeline p1 = (Pipeline) x.tokens().get(1);
+ Statement s10 = (Statement) p1.tokens().get(0);
+ Statement s11 = (Statement) p1.tokens().get(1);
+ assertEquals("abc", s00.tokens().get(0).toString());
+ assertEquals("def", s00.tokens().get(1).toString());
+ assertEquals("ghi", s01.tokens().get(0).toString());
+ assertEquals("jkl", s01.tokens().get(1).toString());
+ assertEquals("mno", s10.tokens().get(0).toString());
+ assertEquals("pqr", s10.tokens().get(1).toString());
+ assertEquals("stu", s11.tokens().get(0).toString());
+ assertEquals("vwx", s11.tokens().get(1).toString());
}
public void testStatements()
{
- List<List<Token>> x = new Parser("abc def|ghi jkl|mno pqr").program().get(0);
- assertEquals("abc", x.get(0).get(0).toString());
- assertEquals("def", x.get(0).get(1).toString());
- assertEquals("ghi", x.get(1).get(0).toString());
- assertEquals("jkl", x.get(1).get(1).toString());
- assertEquals("mno", x.get(2).get(0).toString());
- assertEquals("pqr", x.get(2).get(1).toString());
+ Program x = new Parser("abc def|ghi jkl|mno pqr").program();
+ Pipeline p0 = (Pipeline) x.tokens().get(0);
+ Statement s00 = (Statement) p0.tokens().get(0);
+ Statement s01 = (Statement) p0.tokens().get(1);
+ Statement s02 = (Statement) p0.tokens().get(2);
+ assertEquals("abc", s00.tokens().get(0).toString());
+ assertEquals("def", s00.tokens().get(1).toString());
+ assertEquals("ghi", s01.tokens().get(0).toString());
+ assertEquals("jkl", s01.tokens().get(1).toString());
+ assertEquals("mno", s02.tokens().get(0).toString());
+ assertEquals("pqr", s02.tokens().get(1).toString());
}
public void testSimpleValue()
{
- List<Token> x = new Parser(
- "abc def.ghi http://www.osgi.org?abc=&x=1 [1,2,3] {{{{{{{xyz}}}}}}} (immediate) {'{{{{{'} {\\{} 'abc{}'").program().get(0).get(0);
+ Program p = new Parser(
+ "abc def.ghi http://www.osgi.org?abc=&x=1 [1,2,3] {{{{{{{xyz}}}}}}} (immediate) {'{{{{{'} {\\{} 'abc{}'")
+ .program();
+ List<Token> x = ((Statement) p.tokens().get(0)).tokens();
assertEquals("abc", x.get(0).toString());
assertEquals("def.ghi", x.get(1).toString());
assertEquals("http://www.osgi.org?abc=&x=1", x.get(2).toString());
- assertEquals("1,2,3", x.get(3).toString());
- assertEquals("{{{{{{xyz}}}}}}", x.get(4).toString());
- assertEquals("immediate", x.get(5).toString());
- assertEquals("'{{{{{'", x.get(6).toString());
- assertEquals("\\{", x.get(7).toString());
+ assertEquals("[1,2,3]", x.get(3).toString());
+ assertEquals("{{{{{{{xyz}}}}}}}", x.get(4).toString());
+ assertEquals("(immediate)", x.get(5).toString());
+ assertEquals("{'{{{{{'}", x.get(6).toString());
+ assertEquals("{\\{}", x.get(7).toString());
assertEquals("'abc{}'", x.get(8).toString());
}
diff --git a/gogo/runtime/src/test/java/org/apache/felix/gogo/runtime/TestParser2.java b/gogo/runtime/src/test/java/org/apache/felix/gogo/runtime/TestParser2.java
index 4df5dab..1966d43 100644
--- a/gogo/runtime/src/test/java/org/apache/felix/gogo/runtime/TestParser2.java
+++ b/gogo/runtime/src/test/java/org/apache/felix/gogo/runtime/TestParser2.java
@@ -24,19 +24,20 @@
* Test features of the new parser/tokenizer, many of which are not supported
* by the original parser.
*/
-public class TestParser2 extends BaseTestCase
+public class TestParser2 extends AbstractParserTest
{
public void testComment() throws Exception
{
- m_ctx.addCommand("echo", this);
+ Context c = new Context();
+ c.addCommand("echo", this);
- assertEquals("file://wibble#tag", m_ctx.execute("echo file://wibble#tag"));
- assertEquals("file:", m_ctx.execute("echo file: //wibble#tag"));
+ assertEquals("file://wibble#tag", c.execute("echo file://wibble#tag"));
+ assertEquals("file:", c.execute("echo file: //wibble#tag"));
- assertEquals("PWD/*.java", m_ctx.execute("echo PWD/*.java"));
+ assertEquals("PWD/*.java", c.execute("echo PWD/*.java"));
try
{
- m_ctx.execute("echo PWD /*.java");
+ c.execute("echo PWD /*.java");
fail("expected EOFException");
}
catch (EOFException e)
@@ -44,42 +45,44 @@
// expected
}
- assertEquals("ok", m_ctx.execute("// can't quote\necho ok\n"));
+ assertEquals("ok", c.execute("// can't quote\necho ok\n"));
// quote in comment in closure
- assertEquals("ok", m_ctx.execute("x = { // can't quote\necho ok\n}; x"));
- assertEquals("ok", m_ctx.execute("x = {\n// can't quote\necho ok\n}; x"));
- assertEquals("ok", m_ctx.execute("x = {// can't quote\necho ok\n}; x"));
+ assertEquals("ok", c.execute("x = { // can't quote\necho ok\n}; x"));
+ assertEquals("ok", c.execute("x = {\n// can't quote\necho ok\n}; x"));
+ assertEquals("ok", c.execute("x = {// can't quote\necho ok\n}; x"));
}
public void testCoercion() throws Exception
{
- m_ctx.addCommand("echo", this);
+ Context c = new Context();
+ c.addCommand("echo", this);
// FELIX-2432
- assertEquals("null x", m_ctx.execute("echo $expandsToNull x"));
+ assertEquals("null x", c.execute("echo $expandsToNull x"));
}
public void testStringExecution() throws Exception
{
- m_ctx.addCommand("echo", this);
- m_ctx.addCommand("new", this);
+ Context c = new Context();
+ c.addCommand("echo", this);
+ c.addCommand("new", this);
// FELIX-2433
- assertEquals("helloworld", m_ctx.execute("echo \"$(echo hello)world\""));
+ assertEquals("helloworld", c.execute("echo \"$(echo hello)world\""));
// FELIX-1473 - allow method calls on String objects
- assertEquals("hello", m_ctx.execute("cmd = echo; eval $cmd hello"));
- assertEquals(4, m_ctx.execute("'four' length"));
+ assertEquals("hello", c.execute("cmd = echo; eval $cmd hello"));
+ assertEquals(4, c.execute("'four' length"));
try {
- m_ctx.execute("four length");
+ c.execute("four length");
fail("expected: command not found: four");
} catch (IllegalArgumentException e) {
}
// check CharSequence types are preserved
- Object b = m_ctx.execute("b = new java.lang.StringBuilder");
+ Object b = c.execute("b = new java.lang.StringBuilder");
assertTrue(b instanceof StringBuilder);
- assertEquals(b, m_ctx.execute("c = $b"));
+ assertEquals(b, c.execute("c = $b"));
}
public CharSequence echo(Object args[])
diff --git a/gogo/runtime/src/test/java/org/apache/felix/gogo/runtime/TestParser3.java b/gogo/runtime/src/test/java/org/apache/felix/gogo/runtime/TestParser3.java
index 2bfdbf8..4021fdb 100644
--- a/gogo/runtime/src/test/java/org/apache/felix/gogo/runtime/TestParser3.java
+++ b/gogo/runtime/src/test/java/org/apache/felix/gogo/runtime/TestParser3.java
@@ -22,25 +22,19 @@
* Test features of the new parser/tokenizer, many of which are not supported
* by the original parser.
*/
-public class TestParser3 extends BaseTestCase
+public class TestParser3 extends AbstractParserTest
{
public void testArithmetic() throws Exception
{
- m_ctx.addCommand("echo", this);
+ Context c = new Context();
+ c.addCommand("echo", this);
- try
- {
- assertEquals("10d", m_ctx.execute("echo %(2*(3+2))d"));
- assertEquals(3l, m_ctx.execute("%(1+2)"));
+ assertEquals("10d", c.execute("echo %(2*(3+2))d"));
+ assertEquals(3l, c.execute("%(1+2)"));
- m_ctx.set("a", 2l);
- assertEquals(3l, m_ctx.execute("%(a+=1)"));
- assertEquals(3l, m_ctx.get("a"));
- }
- finally
- {
- m_ctx.stop();
- }
+ c.set("a", 2l);
+ assertEquals(3l, c.execute("%(a+=1)"));
+ assertEquals(3l, c.get("a"));
}
public CharSequence echo(Object args[])
diff --git a/gogo/runtime/src/test/java/org/apache/felix/gogo/runtime/TestTokenizer.java b/gogo/runtime/src/test/java/org/apache/felix/gogo/runtime/TestTokenizer.java
index 434d280..c7f17f7 100644
--- a/gogo/runtime/src/test/java/org/apache/felix/gogo/runtime/TestTokenizer.java
+++ b/gogo/runtime/src/test/java/org/apache/felix/gogo/runtime/TestTokenizer.java
@@ -18,6 +18,9 @@
*/
package org.apache.felix.gogo.runtime;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.fail;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
@@ -28,11 +31,15 @@
import java.util.HashMap;
import java.util.Map;
-import org.apache.felix.gogo.runtime.Tokenizer.Type;
+import junit.framework.TestCase;
+
+import org.apache.felix.gogo.runtime.threadio.ThreadIOImpl;
+import org.junit.Ignore;
+import org.junit.Test;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;
-public class TestTokenizer extends BaseTestCase
+public class TestTokenizer
{
private final Map<String, Object> vars = new HashMap<String, Object>();
private final Evaluate evaluate;
@@ -55,9 +62,14 @@
{
return vars.put(key, value);
}
+
+ public Object expr(Token t) {
+ throw new UnsupportedOperationException("expr not implemented.");
+ }
};
}
+ @Test
public void testHello() throws Exception
{
testHello("hello world\n");
@@ -91,14 +103,13 @@
private void testHello(CharSequence text) throws Exception
{
Tokenizer t = new Tokenizer(text);
- assertEquals(Type.WORD, t.next());
- assertEquals("hello", t.value().toString());
- assertEquals(Type.WORD, t.next());
- assertEquals("world", t.value().toString());
- assertEquals(Type.NEWLINE, t.next());
- assertEquals(Type.EOT, t.next());
+ assertEquals("hello", t.next().toString());
+ assertEquals("world", t.next().toString());
+ assertEquals("\n", t.next().toString());
+ assertNull(t.next());
}
-
+
+ @Test
public void testString() throws Exception
{
testString("'single $quote' \"double $quote\"\n");
@@ -108,45 +119,52 @@
private void testString(CharSequence text) throws Exception
{
Tokenizer t = new Tokenizer(text);
- assertEquals(Type.WORD, t.next());
- assertEquals("'single $quote'", t.value().toString());
- assertEquals(Type.WORD, t.next());
- assertEquals("\"double $quote\"", t.value().toString());
- assertEquals(Type.NEWLINE, t.next());
- assertEquals(Type.EOT, t.next());
+ assertEquals("'single $quote'", t.next().toString());
+ assertEquals("\"double $quote\"", t.next().toString());
+ assertEquals("\n", t.next().toString());
+ assertNull(t.next());
}
+ @Test
public void testClosure() throws Exception
{
testClosure2("x = { echo '}' $args //comment's\n}\n");
testClosure2("x={ echo '}' $args //comment's\n}\n");
- assertEquals(Type.CLOSURE, token1("{ echo \\{ $args \n}"));
- assertEquals(Type.CLOSURE, token1("{ echo \\} $args \n}"));
+ token1("{ echo \\{ $args \n}");
+ token1("{ echo \\} $args \n}");
}
- /*
- * x = {echo $args};
- */
+ //
+ // x = {echo $args};
+ //
private void testClosure2(CharSequence text) throws Exception
{
Tokenizer t = new Tokenizer(text);
- assertEquals(Type.WORD, t.next());
- assertEquals("x", t.value().toString());
- assertEquals(Type.ASSIGN, t.next());
- assertEquals(Type.CLOSURE, t.next());
- assertEquals(" echo '}' $args //comment's\n", t.value().toString());
- assertEquals(Type.NEWLINE, t.next());
- assertEquals(Type.EOT, t.next());
+ assertEquals("x", t.next().toString());
+ assertEquals("=", t.next().toString());
+ assertEquals("{", t.next().toString());
+ assertEquals("echo", t.next().toString());
+ assertEquals("'}'", t.next().toString());
+ assertEquals("$args", t.next().toString());
+ assertEquals("\n", t.next().toString());
+ assertEquals("}", t.next().toString());
+ assertEquals("\n", t.next().toString());
+ assertEquals(null, t.next());
}
- private Type token1(CharSequence text) throws Exception
+ private void token1(CharSequence text) throws Exception
{
Tokenizer t = new Tokenizer(text);
- Type type = t.next();
- assertEquals(Type.EOT, t.next());
- return type;
+ assertEquals("{", t.next().toString());
+ assertEquals("echo", t.next().toString());
+ t.next();
+ assertEquals("$args", t.next().toString());
+ assertEquals("\n", t.next().toString());
+ assertEquals("}", t.next().toString());
+ assertNull(t.next());
}
+ @Test
public void testExpand() throws Exception
{
final URI home = new URI("/home/derek");
@@ -160,7 +178,7 @@
vars.put(user, "Derek Baum");
// quote removal
- assertEquals("hello", expand("hello"));
+ assertEquals("hello", expand("hello").toString());
assertEquals("hello", expand("'hello'"));
assertEquals("\"hello\"", expand("'\"hello\"'"));
assertEquals("hello", expand("\"hello\""));
@@ -266,33 +284,46 @@
private Object expand(CharSequence word) throws Exception
{
- return Tokenizer.expand(word, evaluate);
+ return Expander.expand(word, evaluate);
}
+ @Test
public void testParser() throws Exception
{
new Parser("// comment\n" + "a=\"who's there?\"; ps -ef;\n" + "ls | \n grep y\n").program();
String p1 = "a=1 \\$b=2 c={closure}\n";
new Parser(p1).program();
- new Parser(new Token(Type.ARRAY, p1, (short) 0, (short) 0)).program();
+ new Parser("[" + p1 + "]").program();
}
- /**
- * FELIX-4679 / FELIX-4671.
- */
+ //
+ // FELIX-4679 / FELIX-4671.
+ //
+ @Test
public void testScriptFelix4679() throws Exception
{
String script = "addcommand system (((${.context} bundles) 0) loadclass java.lang.System)";
- BundleContext bc = createMockContext();
+ ThreadIOImpl tio = new ThreadIOImpl();
+ tio.start();
- m_ctx.addCommand("gogo", m_ctx, "addcommand");
- m_ctx.addConstant(".context", bc);
+ try
+ {
+ BundleContext bc = createMockContext();
- CommandSessionImpl session = new CommandSessionImpl(m_ctx, new ByteArrayInputStream(script.getBytes()), System.out, System.err);
+ CommandProcessorImpl processor = new CommandProcessorImpl(tio);
+ processor.addCommand("gogo", processor, "addcommand");
+ processor.addConstant(".context", bc);
- Closure c = new Closure(session, null, script);
- assertNull(c.execute(session, null));
+ CommandSessionImpl session = new CommandSessionImpl(processor, new ByteArrayInputStream(script.getBytes()), System.out, System.err);
+
+ Closure c = new Closure(session, null, script);
+ assertNull(c.execute(session, null));
+ }
+ finally
+ {
+ tio.stop();
+ }
}
private BundleContext createMockContext() throws ClassNotFoundException
diff --git a/gogo/runtime/src/test/java/org/apache/felix/gogo/runtime/threadio/TestThreadIO.java b/gogo/runtime/src/test/java/org/apache/felix/gogo/runtime/threadio/TestThreadIO.java
index 2cc60df..32ea917 100644
--- a/gogo/runtime/src/test/java/org/apache/felix/gogo/runtime/threadio/TestThreadIO.java
+++ b/gogo/runtime/src/test/java/org/apache/felix/gogo/runtime/threadio/TestThreadIO.java
@@ -38,7 +38,6 @@
{
ThreadIOImpl tio = new ThreadIOImpl();
tio.start();
-
List<ByteArrayOutputStream> list = new ArrayList<ByteArrayOutputStream>();
for (int i = 0; i < 10; i++)
{
@@ -67,7 +66,6 @@
{
ThreadIOImpl tio = new ThreadIOImpl();
tio.start();
-
System.out.println("Hello World");
ByteArrayOutputStream out = new ByteArrayOutputStream();
ByteArrayOutputStream err = new ByteArrayOutputStream();