FELIX-4679 - Revert patch for FELIX-4671:
- revert patch for expression support as the parser couldn't cope
with the double parenthesises properly for non-expressions.
git-svn-id: https://svn.apache.org/repos/asf/felix/trunk@1653078 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/gogo/command/src/main/java/org/apache/felix/gogo/command/Inspect.java b/gogo/command/src/main/java/org/apache/felix/gogo/command/Inspect.java
index 39a5822..8690d6b 100644
--- a/gogo/command/src/main/java/org/apache/felix/gogo/command/Inspect.java
+++ b/gogo/command/src/main/java/org/apache/felix/gogo/command/Inspect.java
@@ -21,8 +21,8 @@
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
-
import java.util.Map;
+
import org.apache.felix.service.command.Descriptor;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;
@@ -32,9 +32,6 @@
import org.osgi.framework.wiring.BundleRequirement;
import org.osgi.framework.wiring.BundleWire;
import org.osgi.framework.wiring.BundleWiring;
-import org.osgi.service.packageadmin.ExportedPackage;
-import org.osgi.service.packageadmin.PackageAdmin;
-import org.osgi.service.packageadmin.RequiredBundle;
public class Inspect
{
diff --git a/gogo/runtime/NOTICE b/gogo/runtime/NOTICE
index cb48c52..6447d3e 100644
--- a/gogo/runtime/NOTICE
+++ b/gogo/runtime/NOTICE
@@ -4,7 +4,3 @@
This product includes software developed at
The Apache Software Foundation (http://www.apache.org/).
Licensed under the Apache License 2.0.
-
-This product includes software developped by
-Udo Klimaschewski (http://UdoJava.com/).
-Licensed under the MIT License.
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 7dec8ec..aa63b0f 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
@@ -19,7 +19,11 @@
package org.apache.felix.gogo.runtime;
import java.io.EOFException;
-import java.util.*;
+import java.util.AbstractList;
+import java.util.ArrayList;
+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;
@@ -31,7 +35,6 @@
public static final String LOCATION = ".location";
private static final String DEFAULT_LOCK = ".defaultLock";
- private static final long serialVersionUID = 1L;
private static final ThreadLocal<String> location = new ThreadLocal<String>();
private final CommandSessionImpl session;
@@ -289,10 +292,6 @@
v = t.type;
break;
- case EXPR:
- v = expr(t.value);
- break;
-
default:
throw new SyntaxError(t.line, t.column, "unexpected token: " + t.type);
}
@@ -534,11 +533,6 @@
return value;
}
- private Object expr(CharSequence expr) throws Exception
- {
- return session.expr(expr);
- }
-
private Object array(Token array) throws Exception
{
List<Token> list = new ArrayList<Token>();
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 e89878d..97a1dc7 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
@@ -21,16 +21,11 @@
import java.io.InputStream;
import java.io.PrintStream;
import java.lang.reflect.Method;
-import java.math.BigDecimal;
import java.util.Collections;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
-import java.util.SortedMap;
import java.util.TreeSet;
import java.util.WeakHashMap;
import java.util.concurrent.ConcurrentHashMap;
@@ -63,7 +58,8 @@
{
synchronized (sessions)
{
- if (stopped) {
+ if (stopped)
+ {
throw new IllegalStateException("CommandProcessor has been stopped");
}
CommandSessionImpl session = new CommandSessionImpl(this, in, out, err);
@@ -72,6 +68,17 @@
}
}
+ void closeSession(CommandSessionImpl session)
+ {
+ synchronized (sessions)
+ {
+ if (sessions.remove(session) != null)
+ {
+ System.out.println("CLOSED: " + session);
+ }
+ }
+ }
+
public void stop()
{
synchronized (sessions)
@@ -173,8 +180,7 @@
public void addCommand(String scope, Object target)
{
- Class<?> tc = (target instanceof Class<?>) ? (Class<?>) target
- : target.getClass();
+ Class<?> tc = (target instanceof Class<?>) ? (Class<?>) target : target.getClass();
addCommand(scope, target, tc);
}
@@ -326,8 +332,7 @@
}
}
- void afterExecute(CommandSession session, CharSequence commandline,
- Exception exception)
+ void afterExecute(CommandSession session, CharSequence commandline, Exception exception)
{
for (CommandSessionListener l : listeners)
{
@@ -356,8 +361,4 @@
}
}
}
-
- public Object expr(CommandSessionImpl session, CharSequence expr) {
- return new Expression(expr.toString()).eval(session.variables);
- }
}
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 ee3c09b..bf75e48 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
@@ -32,9 +32,7 @@
import java.util.Enumeration;
import java.util.Formatter;
import java.util.HashMap;
-import java.util.HashSet;
import java.util.Map;
-import java.util.concurrent.ConcurrentHashMap;
import org.apache.felix.service.command.CommandSession;
import org.apache.felix.service.command.Converter;
@@ -47,14 +45,14 @@
public static final String VARIABLES = ".variables";
public static final String COMMANDS = ".commands";
private static final String COLUMN = "%-20s %s\n";
-
+
protected InputStream in;
protected PrintStream out;
PrintStream err;
-
+
private final CommandProcessorImpl processor;
protected final Map<String, Object> variables = new HashMap<String, Object>();
- private boolean closed;
+ private volatile boolean closed;
protected CommandSessionImpl(CommandProcessorImpl shell, InputStream in, PrintStream out, PrintStream err)
{
@@ -63,7 +61,7 @@
this.out = out;
this.err = err;
}
-
+
ThreadIO threadIO()
{
return processor.threadIO;
@@ -71,7 +69,12 @@
public void close()
{
- this.closed = true;
+ if (!this.closed)
+ {
+ System.out.println("CLOSING SESSION!");
+ this.processor.closeSession(this);
+ this.closed = true;
+ }
}
public Object execute(CharSequence commandline) throws Exception
@@ -119,7 +122,7 @@
}
Object val = processor.constants.get(name);
- if( val != null )
+ if (val != null)
{
return val;
}
@@ -137,13 +140,13 @@
}
return val;
}
- else if( val != null )
+ else if (val != null)
{
return val;
}
val = variables.get(name);
- if( val != null )
+ if (val != null)
{
return val;
}
@@ -165,8 +168,7 @@
}
@SuppressWarnings("unchecked")
- public CharSequence format(Object target, int level, Converter escape)
- throws Exception
+ public CharSequence format(Object target, int level, Converter escape) throws Exception
{
if (target == null)
{
@@ -349,9 +351,7 @@
try
{
String name = m.getName();
- if (m.getName().startsWith("get") && !m.getName().equals("getClass")
- && m.getParameterTypes().length == 0
- && Modifier.isPublic(m.getModifiers()))
+ if (m.getName().startsWith("get") && !m.getName().equals("getClass") && m.getParameterTypes().length == 0 && Modifier.isPublic(m.getModifiers()))
{
found = true;
name = name.substring(3);
@@ -396,9 +396,4 @@
}
}
- public Object expr(CharSequence expr)
- {
- return processor.expr(this, expr);
- }
-
}
diff --git a/gogo/runtime/src/main/java/org/apache/felix/gogo/runtime/Expression.java b/gogo/runtime/src/main/java/org/apache/felix/gogo/runtime/Expression.java
deleted file mode 100644
index b34c805..0000000
--- a/gogo/runtime/src/main/java/org/apache/felix/gogo/runtime/Expression.java
+++ /dev/null
@@ -1,1332 +0,0 @@
-/*
- * Copyright 2012 Udo Klimaschewski
- *
- * http://UdoJava.com/
- * http://about.me/udo.klimaschewski
- *
- * Permission is hereby granted, free of charge, to any person obtaining
- * a copy of this software and associated documentation files (the
- * "Software"), to deal in the Software without restriction, including
- * without limitation the rights to use, copy, modify, merge, publish,
- * distribute, sublicense, and/or sell copies of the Software, and to
- * permit persons to whom the Software is furnished to do so, subject to
- * the following conditions:
- *
- * The above copyright notice and this permission notice shall be
- * included in all copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
- * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
- * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
- * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
- * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
- * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
- * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
- *
- */
-package org.apache.felix.gogo.runtime;
-
-import java.math.BigDecimal;
-import java.math.BigInteger;
-import java.math.MathContext;
-import java.math.RoundingMode;
-import java.util.*;
-
-/**
- * Enhanced to provide assignment operators and variables from a map, comparison operators, string operations and more.
- */
-/**
- * <h1>EvalEx - Java Expression Evaluator</h1>
- *
- * <h2>Introduction</h2>
- * EvalEx is a handy expression evaluator for Java, that allows to evaluate simple mathematical and boolean expressions.
- * <br>
- * Key Features:
- * <ul>
- * <li>Uses BigDecimal for calculation and result</li>
- * <li>Single class implementation, very compact</li>
- * <li>No dependencies to external libraries</li>
- * <li>Precision and rounding mode can be set</li>
- * <li>Supports variables</li>
- * <li>Standard boolean and mathematical operators</li>
- * <li>Standard basic mathematical and boolean functions</li>
- * <li>Custom functions and operators can be added at runtime</li>
- * </ul>
- * <br>
- * <h2>Examples</h2>
- * <pre>
- * BigDecimal result = null;
- *
- * Expression expression = new Expression("1+1/3");
- * result = expression.eval():
- * expression.setPrecision(2);
- * result = expression.eval():
- *
- * result = new Expression("(3.4 + -4.1)/2").eval();
- *
- * result = new Expression("SQRT(a^2 + b^2").with("a","2.4").and("b","9.253").eval();
- *
- * BigDecimal a = new BigDecimal("2.4");
- * BigDecimal b = new BigDecimal("9.235");
- * result = new Expression("SQRT(a^2 + b^2").with("a",a).and("b",b).eval();
- *
- * result = new Expression("2.4/PI").setPrecision(128).setRoundingMode(RoundingMode.UP).eval();
- *
- * result = new Expression("random() > 0.5").eval();
- *
- * result = new Expression("not(x<7 || sqrt(max(x,9)) <= 3))").with("x","22.9").eval();
- * </pre>
- * <br>
- * <h2>Supported Operators</h2>
- * <table>
- * <tr><th>Mathematical Operators</th></tr>
- * <tr><th>Operator</th><th>Description</th></tr>
- * <tr><td>+</td><td>Additive operator</td></tr>
- * <tr><td>-</td><td>Subtraction operator</td></tr>
- * <tr><td>*</td><td>Multiplication operator</td></tr>
- * <tr><td>/</td><td>Division operator</td></tr>
- * <tr><td>%</td><td>Remainder operator (Modulo)</td></tr>
- * <tr><td>^</td><td>Power operator</td></tr>
- * </table>
- * <br>
- * <table>
- * <tr><th>Boolean Operators<sup>*</sup></th></tr>
- * <tr><th>Operator</th><th>Description</th></tr>
- * <tr><td>=</td><td>Equals</td></tr>
- * <tr><td>==</td><td>Equals</td></tr>
- * <tr><td>!=</td><td>Not equals</td></tr>
- * <tr><td><></td><td>Not equals</td></tr>
- * <tr><td><</td><td>Less than</td></tr>
- * <tr><td><=</td><td>Less than or equal to</td></tr>
- * <tr><td>></td><td>Greater than</td></tr>
- * <tr><td>>=</td><td>Greater than or equal to</td></tr>
- * <tr><td>&&</td><td>Boolean and</td></tr>
- * <tr><td>||</td><td>Boolean or</td></tr>
- * </table>
- * *Boolean operators result always in a BigDecimal value of 1 or 0 (zero). Any non-zero value is treated as a _true_ value. Boolean _not_ is implemented by a function.
- * <br>
- * <h2>Supported Functions</h2>
- * <table>
- * <tr><th>Function<sup>*</sup></th><th>Description</th></tr>
- * <tr><td>NOT(<i>expression</i>)</td><td>Boolean negation, 1 (means true) if the expression is not zero</td></tr>
- * <tr><td>IF(<i>condition</i>,<i>value_if_true</i>,<i>value_if_false</i>)</td><td>Returns one value if the condition evaluates to true or the other if it evaluates to false</td></tr>
- * <tr><td>RANDOM()</td><td>Produces a random number between 0 and 1</td></tr>
- * <tr><td>MIN(<i>e1</i>,<i>e2</i>)</td><td>Returns the smaller of both expressions</td></tr>
- * <tr><td>MAX(<i>e1</i>,<i>e2</i>)</td><td>Returns the bigger of both expressions</td></tr>
- * <tr><td>ABS(<i>expression</i>)</td><td>Returns the absolute (non-negative) value of the expression</td></tr>
- * <tr><td>ROUND(<i>expression</i>,precision)</td><td>Rounds a value to a certain number of digits, uses the current rounding mode</td></tr>
- * <tr><td>FLOOR(<i>expression</i>)</td><td>Rounds the value down to the nearest integer</td></tr>
- * <tr><td>CEILING(<i>expression</i>)</td><td>Rounds the value up to the nearest integer</td></tr>
- * <tr><td>LOG(<i>expression</i>)</td><td>Returns the natural logarithm (base e) of an expression</td></tr>
- * <tr><td>SQRT(<i>expression</i>)</td><td>Returns the square root of an expression</td></tr>
- * <tr><td>SIN(<i>expression</i>)</td><td>Returns the trigonometric sine of an angle (in degrees)</td></tr>
- * <tr><td>COS(<i>expression</i>)</td><td>Returns the trigonometric cosine of an angle (in degrees)</td></tr>
- * <tr><td>TAN(<i>expression</i>)</td><td>Returns the trigonometric tangens of an angle (in degrees)</td></tr>
- * <tr><td>SINH(<i>expression</i>)</td><td>Returns the hyperbolic sine of a value</td></tr>
- * <tr><td>COSH(<i>expression</i>)</td><td>Returns the hyperbolic cosine of a value</td></tr>
- * <tr><td>TANH(<i>expression</i>)</td><td>Returns the hyperbolic tangens of a value</td></tr>
- * <tr><td>RAD(<i>expression</i>)</td><td>Converts an angle measured in degrees to an approximately equivalent angle measured in radians</td></tr>
- * <tr><td>DEG(<i>expression</i>)</td><td>Converts an angle measured in radians to an approximately equivalent angle measured in degrees</td></tr>
- * </table>
- * *Functions names are case insensitive.
- * <br>
- * <h2>Supported Constants</h2>
- * <table>
- * <tr><th>Constant</th><th>Description</th></tr>
- * <tr><td>PI</td><td>The value of <i>PI</i>, exact to 100 digits</td></tr>
- * <tr><td>TRUE</td><td>The value one</td></tr>
- * <tr><td>FALSE</td><td>The value zero</td></tr>
- * </table>
- *
- * <h2>Add Custom Operators</h2>
- *
- * Custom operators can be added easily, simply create an instance of `Expression.Operator` and add it to the expression.
- * Parameters are the operator string, its precedence and if it is left associative. The operators `eval()` method will be called with the BigDecimal values of the operands.
- * All existing operators can also be overridden.
- * <br>
- * For example, add an operator `x >> n`, that moves the decimal point of _x_ _n_ digits to the right:
- *
- * <pre>
- * Expression e = new Expression("2.1234 >> 2");
- *
- * e.addOperator(e.new Operator(">>", 30, true) {
- * {@literal @}Override
- * public BigDecimal eval(BigDecimal v1, BigDecimal v2) {
- * return v1.movePointRight(v2.toBigInteger().intValue());
- * }
- * });
- *
- * e.eval(); // returns 212.34
- * </pre>
- * <br>
- * <h2>Add Custom Functions</h2>
- *
- * Adding custom functions is as easy as adding custom operators. Create an instance of `Expression.Function`and add it to the expression.
- * Parameters are the function name and the count of required parameters. The functions `eval()` method will be called with a list of the BigDecimal parameters.
- * All existing functions can also be overridden.
- * <br>
- * For example, add a function `average(a,b,c)`, that will calculate the average value of a, b and c:
- * <br>
- * <pre>
- * Expression e = new Expression("2 * average(12,4,8)");
- *
- * e.addFunction(e.new Function("average", 3) {
- * {@literal @}Override
- * public BigDecimal eval(List<BigDecimal> parameters) {
- * BigDecimal sum = parameters.get(0).add(parameters.get(1)).add(parameters.get(2));
- * return sum.divide(new BigDecimal(3));
- * }
- * });
- *
- * e.eval(); // returns 16
- * </pre>
- * The software is licensed under the MIT Open Source license (see LICENSE file).
- * <br>
- * <ul>
- * <li>The *power of* operator (^) implementation was copied from [Stack Overflow](http://stackoverflow.com/questions/3579779/how-to-do-a-fractional-power-on-bigdecimal-in-java) Thanks to Gene Marin</li>
- * <li>The SQRT() function implementation was taken from the book [The Java Programmers Guide To numerical Computing](http://www.amazon.de/Java-Number-Cruncher-Programmers-Numerical/dp/0130460419) (Ronald Mak, 2002)</li>
- * </ul>
- *
- *@author Udo Klimaschewski (http://about.me/udo.klimaschewski)
- */
-public class Expression {
-
- /**
- * Definition of PI as a constant, can be used in expressions as variable.
- */
- public static final BigDecimal PI = new BigDecimal(
- "3.1415926535897932384626433832795028841971693993751058209749445923078164062862089986280348253421170679");
-
- /**
- * The {@link MathContext} to use for calculations.
- */
- private MathContext mc = MathContext.DECIMAL32;
-
- /**
- * The original infix expression.
- */
- private String expression = null;
-
- /**
- * The cached RPN (Reverse Polish Notation) of the expression.
- */
- private List<Token> rpn = null;
-
- /**
- * All defined operators with name and implementation.
- */
- private Map<String, Operator> operators = new HashMap<String, Expression.Operator>();
-
- /**
- * All defined functions with name and implementation.
- */
- private Map<String, Function> functions = new HashMap<String, Expression.Function>();
-
- /**
- * All defined variables with name and value.
- */
- private Map<String, Object> constants = new HashMap<String, Object>();
-
- /**
- * What character to use for decimal separators.
- */
- private final char decimalSeparator = '.';
-
- /**
- * What character to use for minus sign (negative values).
- */
- private final char minusSign = '-';
-
- /**
- * The expression evaluators exception class.
- */
- public class ExpressionException extends RuntimeException {
- private static final long serialVersionUID = 1118142866870779047L;
-
- public ExpressionException(String message) {
- super(message);
- }
- }
-
- interface Token {
-
- }
-
- public class Constant implements Token {
-
- private final Object value;
-
- public Constant(Object value) {
- this.value = value;
- }
-
- public Object getValue() {
- return value;
- }
- }
-
- public class Variable implements Token {
-
- private final String name;
-
- public Variable(String name) {
- this.name = name;
- }
-
- public String getName() {
- return name;
- }
-
- @Override
- public String toString() {
- return name;
- }
- }
-
- public class LeftParen implements Token {
- @Override
- public String toString() {
- return "(";
- }
- }
-
- public class Comma implements Token {
- }
-
- /**
- * Abstract definition of a supported expression function. A function is
- * defined by a name, the number of parameters and the actual processing
- * implementation.
- */
- public abstract class Function implements Token {
- /**
- * Name of this function.
- */
- private String name;
- /**
- * Number of parameters expected for this function.
- */
- private int numParams;
-
- /**
- * Creates a new function with given name and parameter count.
- *
- * @param name
- * The name of the function.
- * @param numParams
- * The number of parameters for this function.
- */
- public Function(String name, int numParams) {
- this.name = name.toUpperCase();
- this.numParams = numParams;
- }
-
- public String getName() {
- return name;
- }
-
- public int getNumParams() {
- return numParams;
- }
-
- public BigDecimal eval(Map<String, Object> variables, List<Object> parameters) {
- List<BigDecimal> numericParameters = new ArrayList<BigDecimal>(parameters.size());
- for (Object o : parameters) {
- numericParameters.add(toBigDecimal(variables, o));
- }
- return eval(numericParameters);
- }
-
- /**
- * Implementation for this function.
- *
- * @param parameters
- * Parameters will be passed by the expression evaluator as a
- * {@link List} of {@link BigDecimal} values.
- * @return The function must return a new {@link BigDecimal} value as a
- * computing result.
- */
- public abstract BigDecimal eval(List<BigDecimal> parameters);
-
- @Override
- public String toString() {
- return name;
- }
- }
-
- /**
- * Abstract definition of a supported operator. An operator is defined by
- * its name (pattern), precedence and if it is left- or right associative.
- */
- public abstract class Operator implements Token {
- /**
- * This operators name (pattern).
- */
- private String oper;
- /**
- * Operators precedence.
- */
- private int precedence;
- /**
- * Operator is left associative.
- */
- private boolean leftAssoc;
-
- /**
- * Creates a new operator.
- *
- * @param oper
- * The operator name (pattern).
- * @param precedence
- * The operators precedence.
- * @param leftAssoc
- * <code>true</code> if the operator is left associative,
- * else <code>false</code>.
- */
- public Operator(String oper, int precedence, boolean leftAssoc) {
- this.oper = oper;
- this.precedence = precedence;
- this.leftAssoc = leftAssoc;
- }
-
- public String getOper() {
- return oper;
- }
-
- public int getPrecedence() {
- return precedence;
- }
-
- public boolean isLeftAssoc() {
- return leftAssoc;
- }
-
- public Object eval(Map<String, Object> variables, Object v1, Object v2) {
- if (v1 instanceof Variable) {
- v1 = variables.get(((Variable) v1).getName());
- }
- if (v2 instanceof Variable) {
- v2 = variables.get(((Variable) v2).getName());
- }
- boolean numeric = (v1 instanceof Number || isNumber(v1.toString()))
- && (v2 instanceof Number || isNumber(v2.toString()));
- if (numeric) {
- return eval(toBigDecimal(variables, v1), toBigDecimal(variables, v2));
- } else {
- return eval(v1.toString(), v2.toString());
- }
- }
-
- public Object eval(String v1, String v2) {
- return eval(toBigDecimal(v1), toBigDecimal(v2));
- }
-
- /**
- * Implementation for this operator.
- *
- * @param v1
- * Operand 1.
- * @param v2
- * Operand 2.
- * @return The result of the operation.
- */
- public abstract BigDecimal eval(BigDecimal v1, BigDecimal v2);
-
- @Override
- public String toString() {
- return oper;
- }
- }
-
- public abstract class Comparator extends Operator {
-
- public Comparator(String oper, int precedence) {
- super(oper, precedence, false);
- }
-
- @Override
- public BigDecimal eval(BigDecimal v1, BigDecimal v2) {
- return compare(v1, v2) ? BigDecimal.ONE : BigDecimal.ZERO;
- }
-
- @Override
- public Object eval(String v1, String v2) {
- return compare(v1, v2) ? BigDecimal.ONE : BigDecimal.ZERO;
- }
-
- /**
- * This method actually implements the comparison.
- * It will be called with either 2 BigIntegers or 2 Strings.
- *
- * @param v1
- * Operand 1.
- * @param v2
- * Operand 2.
- * @return The result of the comparison.
- */
- public abstract boolean compare(Comparable v1, Comparable v2);
- }
-
- /**
- * Marker class for assignment operators.
- * Those operators need a variable on the left hand side.
- */
- public abstract class Assignment extends Operator {
-
- public Assignment(String assign, int precedence) {
- super(assign, precedence, true);
- }
-
- public Object eval(Map<String, Object> variables, Object v1, Object v2) {
- if (!(v1 instanceof Variable)) {
- throw new IllegalArgumentException("Left hand side of operator " + getOper() + " should be a variable but found " + v1.toString());
- }
- String name = ((Variable) v1).getName();
- Object r = super.eval(variables, v1, v2);
- if (r instanceof Number) {
- r = toResult(toBigDecimal(r));
- }
- variables.put(name, r);
- return r;
- }
-
- }
-
-
- /**
- * Expression tokenizer that allows to iterate over a {@link String}
- * expression token by token. Blank characters will be skipped.
- */
- private class Tokenizer implements Iterator<String> {
-
- /**
- * Actual position in expression string.
- */
- private int pos = 0;
-
- /**
- * The original input expression.
- */
- private String input;
- /**
- * The previous token or <code>null</code> if none.
- */
- private String previousToken;
-
- /**
- * Creates a new tokenizer for an expression.
- *
- * @param input
- * The expression string.
- */
- public Tokenizer(String input) {
- this.input = input;
- }
-
- public boolean hasNext() {
- return (pos < input.length());
- }
-
- /**
- * Peek at the next character, without advancing the iterator.
- *
- * @return The next character or character 0, if at end of string.
- */
- private char peekNextChar() {
- if (pos < (input.length() - 1)) {
- return input.charAt(pos + 1);
- } else {
- return 0;
- }
- }
-
- public String next() {
- StringBuilder token = new StringBuilder();
- if (pos >= input.length()) {
- return previousToken = null;
- }
- char ch = input.charAt(pos);
- while (Character.isWhitespace(ch) && pos < input.length()) {
- ch = input.charAt(++pos);
- }
- if (Character.isDigit(ch)) {
- while ((Character.isDigit(ch) || ch == decimalSeparator)
- && (pos < input.length())) {
- token.append(input.charAt(pos++));
- ch = pos == input.length() ? 0 : input.charAt(pos);
- }
- } else if (ch == minusSign
- && Character.isDigit(peekNextChar())
- && ("(".equals(previousToken) || ",".equals(previousToken)
- || previousToken == null || operators
- .containsKey(previousToken))) {
- token.append(minusSign);
- pos++;
- token.append(next());
- } else if (Character.isLetter(ch)) {
- while ((Character.isLetter(ch) || Character.isDigit(ch) || (ch == '_')) && (pos < input.length())) {
- token.append(input.charAt(pos++));
- ch = pos == input.length() ? 0 : input.charAt(pos);
- }
- } else if (ch == '"') {
- boolean escaped = false;
- token.append(input.charAt(pos++));
- do {
- if (pos == input.length()) {
- throw new IllegalArgumentException("Non terminated quote");
- }
- ch = input.charAt(pos++);
- escaped = (!escaped && ch == '\\');
- token.append(ch);
- } while (escaped || ch != '"');
- } else if (ch == '(' || ch == ')' || ch == ',') {
- token.append(ch);
- pos++;
- } else {
- while (!Character.isLetter(ch) && !Character.isDigit(ch)
- && !Character.isWhitespace(ch) && ch != '('
- && ch != ')' && ch != ',' && (pos < input.length())) {
- token.append(input.charAt(pos));
- pos++;
- ch = pos == input.length() ? 0 : input.charAt(pos);
- if (ch == minusSign) {
- break;
- }
- }
- if (!operators.containsKey(token.toString())) {
- throw new ExpressionException("Unknown operator '" + token
- + "' at position " + (pos - token.length() + 1));
- }
- }
- return previousToken = token.toString();
- }
-
- public void remove() {
- throw new ExpressionException("remove() not supported");
- }
-
- /**
- * Get the actual character position in the string.
- *
- * @return The actual character position.
- */
- public int getPos() {
- return pos;
- }
-
- }
-
- /**
- * Creates a new expression instance from an expression string.
- *
- * @param expression
- * The expression. E.g. <code>"2.4*sin(3)/(2-4)"</code> or
- * <code>"sin(y)>0 & max(z, 3)>3"</code>
- */
- public Expression(String expression) {
- this.expression = expression;
-
- addOperator(new Assignment("=", 5) {
- @Override
- public BigDecimal eval(BigDecimal v1, BigDecimal v2) {
- return v2;
- }
- });
- addOperator(new Assignment("+=", 5) {
- @Override
- public BigDecimal eval(BigDecimal v1, BigDecimal v2) {
- return v1.add(v2, mc);
- }
-
- @Override
- public Object eval(String v1, String v2) {
- return v1 + v2;
- }
- });
- addOperator(new Assignment("-=", 5) {
- @Override
- public BigDecimal eval(BigDecimal v1, BigDecimal v2) {
- return v1.subtract(v2, mc);
- }
- });
- addOperator(new Assignment("*=", 5) {
- @Override
- public BigDecimal eval(BigDecimal v1, BigDecimal v2) {
- return v1.multiply(v2, mc);
- }
- });
- addOperator(new Assignment("/=", 5) {
- @Override
- public BigDecimal eval(BigDecimal v1, BigDecimal v2) {
- return v1.divide(v2, mc);
- }
- });
- addOperator(new Assignment("%=", 5) {
- @Override
- public BigDecimal eval(BigDecimal v1, BigDecimal v2) {
- return v1.remainder(v2, mc);
- }
- });
- addOperator(new Assignment("|=", 5) {
- @Override
- public BigDecimal eval(BigDecimal v1, BigDecimal v2) {
- return new BigDecimal(v1.toBigInteger().or(v2.toBigInteger()), mc);
- }
- });
- addOperator(new Assignment("&=", 5) {
- @Override
- public BigDecimal eval(BigDecimal v1, BigDecimal v2) {
- return new BigDecimal(v1.toBigInteger().and(v2.toBigInteger()), mc);
- }
- });
- addOperator(new Assignment("^=", 5) {
- @Override
- public BigDecimal eval(BigDecimal v1, BigDecimal v2) {
- return new BigDecimal(v1.toBigInteger().xor(v2.toBigInteger()), mc);
- }
- });
- addOperator(new Assignment("<<=", 5) {
- @Override
- public BigDecimal eval(BigDecimal v1, BigDecimal v2) {
- return new BigDecimal(v1.toBigInteger().shiftLeft(v2.intValue()), mc);
- }
- });
- addOperator(new Assignment(">>=", 5) {
- @Override
- public BigDecimal eval(BigDecimal v1, BigDecimal v2) {
- return new BigDecimal(v1.toBigInteger().shiftRight(v2.intValue()), mc);
- }
- });
-
- addOperator(new Operator("<<", 10, true) {
- @Override
- public BigDecimal eval(BigDecimal v1, BigDecimal v2) {
- return new BigDecimal(v1.toBigInteger().shiftLeft(v2.intValue()), mc);
- }
- });
- addOperator(new Operator(">>", 10, true) {
- @Override
- public BigDecimal eval(BigDecimal v1, BigDecimal v2) {
- return new BigDecimal(v1.toBigInteger().shiftRight(v2.intValue()), mc);
- }
- });
- addOperator(new Operator("|", 15, true) {
- @Override
- public BigDecimal eval(BigDecimal v1, BigDecimal v2) {
- return new BigDecimal(v1.toBigInteger().or(v2.toBigInteger()), mc);
- }
- });
- addOperator(new Operator("&", 15, true) {
- @Override
- public BigDecimal eval(BigDecimal v1, BigDecimal v2) {
- return new BigDecimal(v1.toBigInteger().and(v2.toBigInteger()), mc);
- }
- });
- addOperator(new Operator("^", 15, true) {
- @Override
- public BigDecimal eval(BigDecimal v1, BigDecimal v2) {
- return new BigDecimal(v1.toBigInteger().xor(v2.toBigInteger()), mc);
- }
- });
- addOperator(new Operator("+", 20, true) {
- @Override
- public BigDecimal eval(BigDecimal v1, BigDecimal v2) {
- return v1.add(v2, mc);
- }
- @Override
- public Object eval(String v1, String v2) {
- return v1 + v2;
- }
- });
- addOperator(new Operator("-", 20, true) {
- @Override
- public BigDecimal eval(BigDecimal v1, BigDecimal v2) {
- return v1.subtract(v2, mc);
- }
- });
- addOperator(new Operator("*", 30, true) {
- @Override
- public BigDecimal eval(BigDecimal v1, BigDecimal v2) {
- return v1.multiply(v2, mc);
- }
- });
- addOperator(new Operator("/", 30, true) {
- @Override
- public BigDecimal eval(BigDecimal v1, BigDecimal v2) {
- return v1.divide(v2, mc);
- }
- });
- addOperator(new Operator("%", 30, true) {
- @Override
- public BigDecimal eval(BigDecimal v1, BigDecimal v2) {
- return v1.remainder(v2, mc);
- }
- });
- addOperator(new Operator("**", 40, false) {
- @Override
- public BigDecimal eval(BigDecimal v1, BigDecimal v2) {
- /*-
- * Thanks to Gene Marin:
- * http://stackoverflow.com/questions/3579779/how-to-do-a-fractional-power-on-bigdecimal-in-java
- */
- int signOf2 = v2.signum();
- double dn1 = v1.doubleValue();
- v2 = v2.multiply(new BigDecimal(signOf2)); // n2 is now positive
- BigDecimal remainderOf2 = v2.remainder(BigDecimal.ONE);
- BigDecimal n2IntPart = v2.subtract(remainderOf2);
- BigDecimal intPow = v1.pow(n2IntPart.intValueExact(), mc);
- BigDecimal doublePow = new BigDecimal(Math.pow(dn1,
- remainderOf2.doubleValue()));
-
- BigDecimal result = intPow.multiply(doublePow, mc);
- if (signOf2 == -1) {
- result = BigDecimal.ONE.divide(result, mc.getPrecision(),
- RoundingMode.HALF_UP);
- }
- return result;
- }
- });
- addOperator(new Operator("&&", 4, false) {
- @Override
- public BigDecimal eval(BigDecimal v1, BigDecimal v2) {
- boolean b1 = !v1.equals(BigDecimal.ZERO);
- boolean b2 = !v2.equals(BigDecimal.ZERO);
- return b1 && b2 ? BigDecimal.ONE : BigDecimal.ZERO;
- }
- });
-
- addOperator(new Operator("||", 2, false) {
- @Override
- public BigDecimal eval(BigDecimal v1, BigDecimal v2) {
- boolean b1 = !v1.equals(BigDecimal.ZERO);
- boolean b2 = !v2.equals(BigDecimal.ZERO);
- return b1 || b2 ? BigDecimal.ONE : BigDecimal.ZERO;
- }
- });
-
- addOperator(new Comparator(">", 10) {
- @Override @SuppressWarnings("unchecked")
- public boolean compare(Comparable v1, Comparable v2) {
- return v1.compareTo(v2) > 0;
- }
- });
-
- addOperator(new Comparator(">=", 10) {
- @Override @SuppressWarnings("unchecked")
- public boolean compare(Comparable v1, Comparable v2) {
- return v1.compareTo(v2) >= 0;
- }
- });
-
- addOperator(new Comparator("<", 10) {
- @Override @SuppressWarnings("unchecked")
- public boolean compare(Comparable v1, Comparable v2) {
- return v1.compareTo(v2) < 0;
- }
- });
-
- addOperator(new Comparator("<=", 10) {
- @Override @SuppressWarnings("unchecked")
- public boolean compare(Comparable v1, Comparable v2) {
- return v1.compareTo(v2) <= 0;
- }
- });
-
- addOperator(new Comparator("==", 7) {
- @Override @SuppressWarnings("unchecked")
- public boolean compare(Comparable v1, Comparable v2) {
- return v1.compareTo(v2) == 0;
- }
- });
-
- addOperator(new Comparator("!=", 7) {
- @Override @SuppressWarnings("unchecked")
- public boolean compare(Comparable v1, Comparable v2) {
- return v1.compareTo(v2) != 0;
- }
- });
-
- addFunction(new Function("NOT", 1) {
- @Override
- public BigDecimal eval(List<BigDecimal> parameters) {
- boolean zero = parameters.get(0).compareTo(BigDecimal.ZERO) == 0;
- return zero ? BigDecimal.ONE : BigDecimal.ZERO;
- }
- });
-
- addFunction(new Function("IF", 3) {
- @Override
- public BigDecimal eval(List<BigDecimal> parameters) {
- boolean isTrue = !parameters.get(0).equals(BigDecimal.ZERO);
- return isTrue ? parameters.get(1) : parameters.get(2);
- }
- });
-
- addFunction(new Function("RANDOM", 0) {
- @Override
- public BigDecimal eval(List<BigDecimal> parameters) {
- double d = Math.random();
- return new BigDecimal(d, mc);
- }
- });
- addFunction(new Function("SIN", 1) {
- @Override
- public BigDecimal eval(List<BigDecimal> parameters) {
- double d = Math.sin(Math.toRadians(parameters.get(0)
- .doubleValue()));
- return new BigDecimal(d, mc);
- }
- });
- addFunction(new Function("COS", 1) {
- @Override
- public BigDecimal eval(List<BigDecimal> parameters) {
- double d = Math.cos(Math.toRadians(parameters.get(0)
- .doubleValue()));
- return new BigDecimal(d, mc);
- }
- });
- addFunction(new Function("TAN", 1) {
- @Override
- public BigDecimal eval(List<BigDecimal> parameters) {
- double d = Math.tan(Math.toRadians(parameters.get(0)
- .doubleValue()));
- return new BigDecimal(d, mc);
- }
- });
- addFunction(new Function("SINH", 1) {
- @Override
- public BigDecimal eval(List<BigDecimal> parameters) {
- double d = Math.sinh(parameters.get(0).doubleValue());
- return new BigDecimal(d, mc);
- }
- });
- addFunction(new Function("COSH", 1) {
- @Override
- public BigDecimal eval(List<BigDecimal> parameters) {
- double d = Math.cosh(parameters.get(0).doubleValue());
- return new BigDecimal(d, mc);
- }
- });
- addFunction(new Function("TANH", 1) {
- @Override
- public BigDecimal eval(List<BigDecimal> parameters) {
- double d = Math.tanh(parameters.get(0).doubleValue());
- return new BigDecimal(d, mc);
- }
- });
- addFunction(new Function("RAD", 1) {
- @Override
- public BigDecimal eval(List<BigDecimal> parameters) {
- double d = Math.toRadians(parameters.get(0).doubleValue());
- return new BigDecimal(d, mc);
- }
- });
- addFunction(new Function("DEG", 1) {
- @Override
- public BigDecimal eval(List<BigDecimal> parameters) {
- double d = Math.toDegrees(parameters.get(0).doubleValue());
- return new BigDecimal(d, mc);
- }
- });
- addFunction(new Function("MAX", 2) {
- @Override
- public BigDecimal eval(List<BigDecimal> parameters) {
- BigDecimal v1 = parameters.get(0);
- BigDecimal v2 = parameters.get(1);
- return v1.compareTo(v2) > 0 ? v1 : v2;
- }
- });
- addFunction(new Function("MIN", 2) {
- @Override
- public BigDecimal eval(List<BigDecimal> parameters) {
- BigDecimal v1 = parameters.get(0);
- BigDecimal v2 = parameters.get(1);
- return v1.compareTo(v2) < 0 ? v1 : v2;
- }
- });
- addFunction(new Function("ABS", 1) {
- @Override
- public BigDecimal eval(List<BigDecimal> parameters) {
- return parameters.get(0).abs(mc);
- }
- });
- addFunction(new Function("LOG", 1) {
- @Override
- public BigDecimal eval(List<BigDecimal> parameters) {
- double d = Math.log(parameters.get(0).doubleValue());
- return new BigDecimal(d, mc);
- }
- });
- addFunction(new Function("ROUND", 2) {
- @Override
- public BigDecimal eval(List<BigDecimal> parameters) {
- BigDecimal toRound = parameters.get(0);
- int precision = parameters.get(1).intValue();
- return toRound.setScale(precision, mc.getRoundingMode());
- }
- });
- addFunction(new Function("FLOOR", 1) {
- @Override
- public BigDecimal eval(List<BigDecimal> parameters) {
- BigDecimal toRound = parameters.get(0);
- return toRound.setScale(0, RoundingMode.FLOOR);
- }
- });
- addFunction(new Function("CEILING", 1) {
- @Override
- public BigDecimal eval(List<BigDecimal> parameters) {
- BigDecimal toRound = parameters.get(0);
- return toRound.setScale(0, RoundingMode.CEILING);
- }
- });
- addFunction(new Function("SQRT", 1) {
- @Override
- public BigDecimal eval(List<BigDecimal> parameters) {
- /*
- * From The Java Programmers Guide To numerical Computing
- * (Ronald Mak, 2003)
- */
- BigDecimal x = parameters.get(0);
- if (x.compareTo(BigDecimal.ZERO) == 0) {
- return new BigDecimal(0);
- }
- if (x.signum() < 0) {
- throw new ExpressionException(
- "Argument to SQRT() function must not be negative");
- }
- BigInteger n = x.movePointRight(mc.getPrecision() << 1)
- .toBigInteger();
-
- int bits = (n.bitLength() + 1) >> 1;
- BigInteger ix = n.shiftRight(bits);
- BigInteger ixPrev;
-
- do {
- ixPrev = ix;
- ix = ix.add(n.divide(ix)).shiftRight(1);
- // Give other threads a chance to work;
- Thread.yield();
- } while (ix.compareTo(ixPrev) != 0);
-
- return new BigDecimal(ix, mc.getPrecision());
- }
- });
-
- constants.put("PI", PI);
- constants.put("TRUE", Boolean.TRUE);
- constants.put("FALSE", Boolean.FALSE);
-
- }
-
- /**
- * Is the string a number?
- *
- * @param st
- * The string.
- * @return <code>true</code>, if the input string is a number.
- */
- private boolean isNumber(String st) {
- if (st.charAt(0) == minusSign && st.length() == 1)
- return false;
- for (char ch : st.toCharArray()) {
- if (!Character.isDigit(ch) && ch != minusSign
- && ch != decimalSeparator)
- return false;
- }
- return true;
- }
-
- /**
- * Implementation of the <i>Shunting Yard</i> algorithm to transform an
- * infix expression to a RPN expression.
- *
- * @param expression
- * The input expression in infx.
- * @return A RPN representation of the expression, with each token as a list
- * member.
- */
- private List<Token> shuntingYard(String expression) {
- List<Token> outputQueue = new ArrayList<Token>();
- Stack<Token> stack = new Stack<Token>();
-
- Tokenizer tokenizer = new Tokenizer(expression);
-
- String previousToken = null;
- while (tokenizer.hasNext()) {
- String token = tokenizer.next();
- if (token.charAt(0) == '"') {
- StringBuilder sb = new StringBuilder();
- boolean escaped = false;
- for (int i = 1; i < token.length() - 1; i++) {
- char ch = token.charAt(i);
- if (escaped || ch != '\\') {
- sb.append(ch);
- } else {
- escaped = true;
- }
- }
- outputQueue.add(new Constant(sb.toString()));
- } else if (isNumber(token)) {
- outputQueue.add(new Constant(toBigDecimal(token)));
- } else if (constants.containsKey(token)) {
- outputQueue.add(new Constant(constants.get(token)));
- } else if (functions.containsKey(token.toUpperCase())) {
- stack.push(functions.get(token.toUpperCase()));
- } else if (Character.isLetter(token.charAt(0))) {
- outputQueue.add(new Variable(token));
- } else if (",".equals(token)) {
- while (!stack.isEmpty() && !(stack.peek() instanceof LeftParen)) {
- outputQueue.add(stack.pop());
- }
- if (stack.isEmpty()) {
- outputQueue.add(new Comma());
- }
- } else if (operators.containsKey(token)) {
- Operator o1 = operators.get(token);
- Token token2 = stack.isEmpty() ? null : stack.peek();
- while (token2 instanceof Operator
- && ((o1.isLeftAssoc() && o1.getPrecedence() <= ((Operator) token2).getPrecedence())
- || (o1.getPrecedence() < ((Operator) token2).getPrecedence()))) {
- outputQueue.add(stack.pop());
- token2 = stack.isEmpty() ? null : stack.peek();
- }
- stack.push(o1);
- } else if ("(".equals(token)) {
- if (previousToken != null) {
- if (isNumber(previousToken)) {
- throw new ExpressionException("Missing operator at character position " + tokenizer.getPos());
- }
- }
- stack.push(new LeftParen());
- } else if (")".equals(token)) {
- while (!stack.isEmpty() && !(stack.peek() instanceof LeftParen)) {
- outputQueue.add(stack.pop());
- }
- if (stack.isEmpty()) {
- throw new RuntimeException("Mismatched parentheses");
- }
- stack.pop();
- if (!stack.isEmpty() && stack.peek() instanceof Function) {
- outputQueue.add(stack.pop());
- }
- }
- previousToken = token;
- }
- while (!stack.isEmpty()) {
- Token element = stack.pop();
- if (element instanceof LeftParen) {
- throw new RuntimeException("Mismatched parentheses");
- }
- if (!(element instanceof Operator)) {
- throw new RuntimeException("Unknown operator or function: " + element);
- }
- outputQueue.add(element);
- }
- return outputQueue;
- }
-
- /**
- * Evaluates the expression.
- *
- * @return The result of the expression.
- */
- public Object eval() {
- return eval(new HashMap<String, Object>());
- }
-
- /**
- * Evaluates the expression.
- *
- * @return The result of the expression.
- */
- public Object eval(Map<String, Object> variables) {
-
- Stack<Object> stack = new Stack<Object>();
-
- for (Token token : getRPN()) {
- if (token instanceof Operator) {
- Object v1 = stack.pop();
- Object v2 = stack.pop();
- Object oResult = ((Operator) token).eval(variables, v2, v1);
- stack.push(oResult);
- } else if (token instanceof Constant) {
- stack.push(((Constant) token).getValue());
- } else if (token instanceof Function) {
- Function f = (Function) token;
- List<Object> p = new ArrayList<Object>(f.getNumParams());
- for (int i = 0; i < f.numParams; i++) {
- p.add(0, stack.pop());
- }
- Object fResult = f.eval(variables, p);
- stack.push(fResult);
- } else if (token instanceof Comma) {
- stack.pop();
- } else {
- stack.push(token);
- }
- }
- if (stack.size() > 1) {
- throw new IllegalArgumentException("Missing operator");
- }
- Object result = stack.pop();
- if (result instanceof Variable) {
- result = variables.get(((Variable) result).getName());
- }
- if (result instanceof BigDecimal) {
- result = toResult((BigDecimal) result);
- }
- return result;
- }
-
- private Number toResult(BigDecimal r) {
- long l = r.longValue();
- if (new BigDecimal(l).compareTo(r) == 0) {
- return l;
- }
- double d = r.doubleValue();
- if (new BigDecimal(d).compareTo(r) == 0) {
- return d;
- } else {
- return r.stripTrailingZeros();
- }
- }
-
- private BigDecimal toBigDecimal(Map<String, Object> variables, Object o) {
- if (o instanceof Variable) {
- o = variables.get(((Variable) o).getName());
- }
- if (o instanceof String) {
- if (isNumber((String) o)) {
- return new BigDecimal((String) o, mc);
- } else if (Character.isLetter(((String) o).charAt(0))) {
- o = variables.get(o);
- }
- }
- return toBigDecimal(o);
- }
-
- private BigDecimal toBigDecimal(Object o) {
- if (o == null) {
- return BigDecimal.ZERO;
- } else if (o instanceof Boolean) {
- return ((Boolean) o) ? BigDecimal.ONE : BigDecimal.ZERO;
- } else if (o instanceof BigDecimal) {
- return ((BigDecimal) o).round(mc);
- } else if (o instanceof BigInteger) {
- return new BigDecimal((BigInteger) o, mc);
- } else if (o instanceof Number) {
- return new BigDecimal(((Number) o).doubleValue(), mc);
- } else {
- try {
- return new BigDecimal(o.toString(), mc);
- } catch (NumberFormatException e) {
- return new BigDecimal(Double.NaN);
- }
- }
- }
-
- /**
- * Sets the precision for expression evaluation.
- *
- * @param precision
- * The new precision.
- *
- * @return The expression, allows to chain methods.
- */
- public Expression setPrecision(int precision) {
- this.mc = new MathContext(precision);
- return this;
- }
-
- /**
- * Sets the rounding mode for expression evaluation.
- *
- * @param roundingMode
- * The new rounding mode.
- * @return The expression, allows to chain methods.
- */
- public Expression setRoundingMode(RoundingMode roundingMode) {
- this.mc = new MathContext(mc.getPrecision(), roundingMode);
- return this;
- }
-
- /**
- * Adds an operator to the list of supported operators.
- *
- * @param operator
- * The operator to add.
- * @return The previous operator with that name, or <code>null</code> if
- * there was none.
- */
- public Operator addOperator(Operator operator) {
- return operators.put(operator.getOper(), operator);
- }
-
- /**
- * Adds a function to the list of supported functions
- *
- * @param function
- * The function to add.
- * @return The previous operator with that name, or <code>null</code> if
- * there was none.
- */
- public Function addFunction(Function function) {
- return functions.put(function.getName(), function);
- }
-
- /**
- * Sets a constant value.
- *
- * @param name
- * The constant name.
- * @param value
- * The constant value.
- * @return The expression, allows to chain methods.
- */
- public Expression addConstant(String name, Object value) {
- constants.put(name, value);
- return this;
- }
-
- /**
- * Get an iterator for this expression, allows iterating over an expression
- * token by token.
- *
- * @return A new iterator instance for this expression.
- */
- public Iterator<String> getExpressionTokenizer() {
- return new Tokenizer(this.expression);
- }
-
- /**
- * Cached access to the RPN notation of this expression, ensures only one
- * calculation of the RPN per expression instance. If no cached instance
- * exists, a new one will be created and put to the cache.
- *
- * @return The cached RPN instance.
- */
- private List<Token> getRPN() {
- if (rpn == null) {
- rpn = shuntingYard(this.expression);
- }
- return rpn;
- }
-
- /**
- * Get a string representation of the RPN (Reverse Polish Notation) for this
- * expression.
- *
- * @return A string with the RPN representation for this expression.
- */
- public String toRPN() {
- StringBuilder result = new StringBuilder();
- for (Token st : getRPN()) {
- if (result.length() > 0) {
- result.append(" ");
- }
- result.append(st);
- }
- return result.toString();
- }
-
-}
\ No newline at end of file
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 23c2f65..271bb8a 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
@@ -97,7 +97,6 @@
case EXECUTION:
case ARRAY:
case ASSIGN:
- case EXPR:
break;
default:
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 8b5d91f..b1c529e 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
@@ -46,7 +46,7 @@
{
public enum Type
{
- ASSIGN('='), PIPE('|'), SEMICOLON(';'), NEWLINE, ARRAY, CLOSURE, EXPR, EXECUTION, WORD, EOT;
+ ASSIGN('='), PIPE('|'), SEMICOLON(';'), NEWLINE, ARRAY, CLOSURE, EXECUTION, WORD, EOT;
private char c;
@@ -265,43 +265,25 @@
private CharSequence group()
{
final char push = ch;
- final char push2;
final char pop;
- final char pop2;
switch (ch)
{
case '{':
type = Type.CLOSURE;
- push2 = 0;
pop = '}';
- pop2 = 0;
break;
case '(':
- if (peek() == '(') {
- getch();
- push2 = '(';
- type = Type.EXPR;
- pop = ')';
- pop2 = ')';
- } else {
- type = Type.EXECUTION;
- push2 = 0;
- pop = ')';
- pop2 = 0;
- }
+ type = Type.EXECUTION;
+ pop = ')';
break;
case '[':
type = Type.ARRAY;
- push2 = 0;
pop = ']';
- pop2 = 0;
break;
default:
assert false;
- push2 = 0;
pop = 0;
- pop2 = 0;
}
short sLine = line;
@@ -345,17 +327,10 @@
break;
default:
- if (push == ch) {
+ if (push == ch)
depth++;
- }
- else if (pop == ch && --depth == 0) {
- if (pop2 == 0)
- return text.subSequence(start, index - 1);
- else if (pop2 == peek()) {
- getch();
- return text.subSequence(start, index - 2);
- }
- }
+ else if (pop == ch && --depth == 0)
+ return text.subSequence(start, index - 1);
}
}
@@ -614,20 +589,11 @@
if (getch() != '{')
{
if ('(' == ch)
- {
+ { // support $(...) FELIX-2433
short sLine = line;
short sCol = column;
- if ('(' == peek())
- {
- val = evaluate.eval(new Token(Type.EXPR, group(), sLine, sCol));
- getch();
- }
- else
- {
- // support $(...) FELIX-2433
- val = evaluate.eval(new Token(Type.EXECUTION, group(), sLine, sCol));
- getch();
- }
+ val = evaluate.eval(new Token(Type.EXECUTION, group(), sLine, sCol));
+ getch();
}
else
{
diff --git a/gogo/runtime/src/main/java/org/apache/felix/gogo/runtime/activator/Activator.java b/gogo/runtime/src/main/java/org/apache/felix/gogo/runtime/activator/Activator.java
index 37a26a8..fbd8aed 100644
--- a/gogo/runtime/src/main/java/org/apache/felix/gogo/runtime/activator/Activator.java
+++ b/gogo/runtime/src/main/java/org/apache/felix/gogo/runtime/activator/Activator.java
@@ -25,10 +25,13 @@
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
+import org.apache.felix.gogo.api.CommandSessionListener;
import org.apache.felix.gogo.runtime.CommandProcessorImpl;
import org.apache.felix.gogo.runtime.CommandProxy;
-import org.apache.felix.gogo.api.CommandSessionListener;
import org.apache.felix.gogo.runtime.threadio.ThreadIOImpl;
+import org.apache.felix.service.command.CommandProcessor;
+import org.apache.felix.service.command.Converter;
+import org.apache.felix.service.threadio.ThreadIO;
import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;
import org.osgi.framework.Constants;
@@ -36,10 +39,6 @@
import org.osgi.framework.InvalidSyntaxException;
import org.osgi.framework.ServiceReference;
import org.osgi.framework.ServiceRegistration;
-import org.apache.felix.service.command.CommandProcessor;
-import org.apache.felix.service.command.Converter;
-import org.apache.felix.service.command.Function;
-import org.apache.felix.service.threadio.ThreadIO;
import org.osgi.util.tracker.ServiceTracker;
public class Activator implements BundleActivator
diff --git a/gogo/runtime/src/main/java/org/apache/felix/gogo/runtime/activator/EventAdminListener.java b/gogo/runtime/src/main/java/org/apache/felix/gogo/runtime/activator/EventAdminListener.java
index 2368ce1..19ca3b5 100644
--- a/gogo/runtime/src/main/java/org/apache/felix/gogo/runtime/activator/EventAdminListener.java
+++ b/gogo/runtime/src/main/java/org/apache/felix/gogo/runtime/activator/EventAdminListener.java
@@ -29,13 +29,10 @@
public class EventAdminListener implements CommandSessionListener
{
-
- private BundleContext bundleContext;
private ServiceTracker tracker;
public EventAdminListener(BundleContext bundleContext)
{
- this.bundleContext = bundleContext;
tracker = new ServiceTracker(bundleContext, EventAdmin.class.getName(), null);
tracker.open();
}
diff --git a/gogo/runtime/src/main/java/org/apache/felix/gogo/runtime/threadio/ThreadPrintStream.java b/gogo/runtime/src/main/java/org/apache/felix/gogo/runtime/threadio/ThreadPrintStream.java
index 6cd14bc..1cfaf61 100644
--- a/gogo/runtime/src/main/java/org/apache/felix/gogo/runtime/threadio/ThreadPrintStream.java
+++ b/gogo/runtime/src/main/java/org/apache/felix/gogo/runtime/threadio/ThreadPrintStream.java
@@ -20,7 +20,6 @@
import java.io.IOException;
import java.io.PrintStream;
-import java.io.InputStream;
import java.util.Locale;
public class ThreadPrintStream extends PrintStream
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 b96a313..adbb66f 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
@@ -68,9 +68,4 @@
session.put(name, value);
}
- public Object get(String name)
- {
- return session.get(name);
- }
-
}
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 937ef80..13c52f1 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
@@ -46,7 +46,7 @@
assertEquals("a", c.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
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
deleted file mode 100644
index 7e65c09..0000000
--- a/gogo/runtime/src/test/java/org/apache/felix/gogo/runtime/TestParser3.java
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.felix.gogo.runtime;
-
-import junit.framework.TestCase;
-
-import java.io.EOFException;
-
-/*
- * Test features of the new parser/tokenizer, many of which are not supported
- * by the original parser.
- */
-public class TestParser3 extends TestCase
-{
- public void testArithmetic() throws Exception
- {
- Context c = new Context();
- c.addCommand("echo", this);
-
- assertEquals("10", c.execute("echo $((2*(3+2)))"));
- assertEquals(3l, c.execute("((1+2))"));
-
- c.set("a", 2l);
- assertEquals(3l, c.execute("((a+=1))"));
- assertEquals(3l, c.get("a"));
- }
-
- public CharSequence echo(Object args[])
- {
- if (args == null)
- {
- return "null args!";
- }
-
- StringBuilder sb = new StringBuilder();
- for (Object arg : args)
- {
- if (sb.length() > 0)
- sb.append(' ');
- sb.append(String.valueOf(arg));
- }
- return sb.toString();
- }
-
-}
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 ae9f18c..da1f959 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,20 +18,24 @@
*/
package org.apache.felix.gogo.runtime;
+import static org.mockito.Matchers.eq;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import java.io.ByteArrayInputStream;
import java.io.File;
import java.net.URI;
import java.util.HashMap;
import java.util.Map;
-import org.apache.felix.gogo.runtime.Evaluate;
-import org.apache.felix.gogo.runtime.Parser;
-import org.apache.felix.gogo.runtime.SyntaxError;
-import org.apache.felix.gogo.runtime.Token;
-import org.apache.felix.gogo.runtime.Tokenizer;
-import org.apache.felix.gogo.runtime.Tokenizer.Type;
-
import junit.framework.TestCase;
+import org.apache.felix.gogo.runtime.Tokenizer.Type;
+import org.apache.felix.gogo.runtime.threadio.ThreadIOImpl;
+import org.junit.Test;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+
public class TestTokenizer extends TestCase
{
private final Map<String, Object> vars = new HashMap<String, Object>();
@@ -58,6 +62,7 @@
};
}
+ @Test
public void testHello() throws Exception
{
testHello("hello world\n");
@@ -98,7 +103,8 @@
assertEquals(Type.NEWLINE, t.next());
assertEquals(Type.EOT, t.next());
}
-
+
+ @Test
public void testString() throws Exception
{
testString("'single $quote' \"double $quote\"\n");
@@ -116,6 +122,7 @@
assertEquals(Type.EOT, t.next());
}
+ @Test
public void testClosure() throws Exception
{
testClosure2("x = { echo '}' $args //comment's\n}\n");
@@ -127,6 +134,7 @@
/*
* x = {echo $args};
*/
+ @Test
private void testClosure2(CharSequence text) throws Exception
{
Tokenizer t = new Tokenizer(text);
@@ -147,6 +155,7 @@
return type;
}
+ @Test
public void testExpand() throws Exception
{
final URI home = new URI("/home/derek");
@@ -269,6 +278,7 @@
return Tokenizer.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();
@@ -277,4 +287,43 @@
new Parser(new Token(Type.ARRAY, p1, (short) 0, (short) 0)).program();
}
+ /**
+ * FELIX-4679 / FELIX-4671.
+ */
+ @Test
+ public void testScriptFelix4679() throws Exception
+ {
+ String script = "addcommand system (((${.context} bundles) 0) loadclass java.lang.System)";
+
+ ThreadIOImpl tio = new ThreadIOImpl();
+ tio.start();
+
+ try
+ {
+ BundleContext bc = createMockContext();
+
+ CommandProcessorImpl processor = new CommandProcessorImpl(tio);
+ processor.addCommand("gogo", processor, "addcommand");
+ processor.addConstant(".context", bc);
+
+ 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
+ {
+ Bundle systemBundle = mock(Bundle.class);
+ when(systemBundle.loadClass(eq("java.lang.System"))).thenReturn(System.class);
+
+ BundleContext bc = mock(BundleContext.class);
+ when(bc.getBundles()).thenReturn(new Bundle[] { systemBundle });
+ return bc;
+ }
}
diff --git a/gogo/runtime/src/test/java/org/apache/felix/gogo/runtime/expr/ExpressionTest.java b/gogo/runtime/src/test/java/org/apache/felix/gogo/runtime/expr/ExpressionTest.java
deleted file mode 100644
index bb16cd9..0000000
--- a/gogo/runtime/src/test/java/org/apache/felix/gogo/runtime/expr/ExpressionTest.java
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-package org.apache.felix.gogo.runtime.expr;
-
-import junit.framework.TestCase;
-import org.apache.felix.gogo.runtime.Expression;
-
-import java.util.HashMap;
-import java.util.Map;
-
-public class ExpressionTest extends TestCase {
-
- public void testExpr() {
-
- Map<String, Object> variables = new HashMap<String, Object>();
- variables.put("a", 2d);
- variables.put("b", 5l);
- variables.put("c", 1l);
- variables.put("d", 2l);
- variables.put("s", " foo ");
- variables.put("t", "bar");
-
- assertEquals(4l, new Expression("c+=1, d+=2").eval(variables));
-
- assertEquals(" foo ", new Expression("\" foo \"").eval());
- assertEquals(" foo bar", new Expression("s + t").eval(variables));
- assertEquals(1l, new Expression("s < t").eval(variables));
- assertEquals(1l, new Expression("s > t || t == \"bar\"").eval(variables));
-
- assertEquals(3l, new Expression("a += 1").eval(variables));
- assertEquals(3l, variables.get("a"));
-
- assertEquals(30l, new Expression("10 + 20 | 30").eval());
-
- assertEquals(8l, new Expression("a + b").eval(variables));
- assertEquals(3l, new Expression("if(a < b, a, b)").eval(variables));
-
- assertEquals(16l, new Expression("2 + 2 << 2").eval());
- assertEquals(8l, new Expression("2 | 2 << 2").eval());
- }
-
-}
diff --git a/gogo/shell/src/main/java/org/apache/felix/gogo/shell/Activator.java b/gogo/shell/src/main/java/org/apache/felix/gogo/shell/Activator.java
index ec9b62e..dcf4ebb 100644
--- a/gogo/shell/src/main/java/org/apache/felix/gogo/shell/Activator.java
+++ b/gogo/shell/src/main/java/org/apache/felix/gogo/shell/Activator.java
@@ -23,6 +23,11 @@
import java.util.Hashtable;
import java.util.Iterator;
import java.util.Set;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ThreadFactory;
+import java.util.concurrent.TimeUnit;
+
import org.apache.felix.service.command.CommandProcessor;
import org.apache.felix.service.command.CommandSession;
import org.apache.felix.service.command.Converter;
@@ -32,30 +37,28 @@
import org.osgi.framework.ServiceRegistration;
import org.osgi.util.tracker.ServiceTracker;
-public class Activator implements BundleActivator, Runnable
+public class Activator implements BundleActivator
{
private BundleContext context;
private ServiceTracker commandProcessorTracker;
- private Set<ServiceRegistration> regs = new HashSet<ServiceRegistration>();
- private CommandSession session;
- private Shell shell;
- private Thread thread;
+ private Set<ServiceRegistration> regs;
- public void start(final BundleContext ctxt) throws Exception
+ private ExecutorService executor;
+
+ public Activator()
{
- context = ctxt;
- commandProcessorTracker = processorTracker();
+ regs = new HashSet<ServiceRegistration>();
+ }
+
+ public void start(BundleContext context) throws Exception
+ {
+ this.context = context;
+ this.commandProcessorTracker = createCommandProcessorTracker();
+ this.commandProcessorTracker.open();
}
public void stop(BundleContext context) throws Exception
{
- if (thread != null)
- {
- thread.interrupt();
- }
-
- commandProcessorTracker.close();
-
Iterator<ServiceRegistration> iterator = regs.iterator();
while (iterator.hasNext())
{
@@ -63,74 +66,15 @@
reg.unregister();
iterator.remove();
}
+
+ stopShell();
+
+ this.commandProcessorTracker.close();
}
- public void run()
+ private ServiceTracker createCommandProcessorTracker()
{
- try
- {
- // wait for gosh command to be registered
- for (int i = 0; (i < 100) && session.get("gogo:gosh") == null; ++i) {
- Thread.sleep(10);
- }
-
- String args = context.getProperty("gosh.args");
- args = (args == null) ? "" : args;
- session.execute("gosh --login " + args);
- }
- catch (Exception e)
- {
- Object loc = session.get(".location");
- if (null == loc || !loc.toString().contains(":"))
- {
- loc = "gogo";
- }
-
- System.err.println(loc + ": " + e.getClass().getSimpleName() + ": " + e.getMessage());
- e.printStackTrace();
- }
- finally
- {
- session.close();
- }
- }
-
- private void startShell(BundleContext context, CommandProcessor processor)
- {
- Dictionary<String, Object> dict = new Hashtable<String, Object>();
- dict.put(CommandProcessor.COMMAND_SCOPE, "gogo");
-
- // register converters
- regs.add(context.registerService(Converter.class.getName(), new Converters(context), null));
-
- // register commands
-
- dict.put(CommandProcessor.COMMAND_FUNCTION, Builtin.functions);
- regs.add(context.registerService(Builtin.class.getName(), new Builtin(), dict));
-
- dict.put(CommandProcessor.COMMAND_FUNCTION, Procedural.functions);
- regs.add(context.registerService(Procedural.class.getName(), new Procedural(), dict));
-
- dict.put(CommandProcessor.COMMAND_FUNCTION, Posix.functions);
- regs.add(context.registerService(Posix.class.getName(), new Posix(), dict));
-
- dict.put(CommandProcessor.COMMAND_FUNCTION, Telnet.functions);
- regs.add(context.registerService(Telnet.class.getName(), new Telnet(processor), dict));
-
- shell = new Shell(context, processor);
- dict.put(CommandProcessor.COMMAND_FUNCTION, Shell.functions);
- regs.add(context.registerService(Shell.class.getName(), shell, dict));
-
- // start shell
- session = processor.createSession(System.in, System.out, System.err);
- thread = new Thread(this, "Gogo shell");
- thread.start();
- }
-
- private ServiceTracker processorTracker()
- {
- ServiceTracker t = new ServiceTracker(context, CommandProcessor.class.getName(),
- null)
+ return new ServiceTracker(context, CommandProcessor.class.getName(), null)
{
@Override
public Object addingService(ServiceReference reference)
@@ -143,15 +87,112 @@
@Override
public void removedService(ServiceReference reference, Object service)
{
- if (thread != null)
- {
- thread.interrupt();
- }
+ stopShell();
super.removedService(reference, service);
}
};
+ }
- t.open();
- return t;
+ private void startShell(BundleContext context, CommandProcessor processor)
+ {
+ Dictionary<String, Object> dict = new Hashtable<String, Object>();
+ dict.put(CommandProcessor.COMMAND_SCOPE, "gogo");
+
+ // register converters
+ regs.add(context.registerService(Converter.class.getName(), new Converters(context), null));
+
+ // register commands
+
+ dict.put(CommandProcessor.COMMAND_FUNCTION, Builtin.functions);
+ regs.add(context.registerService(Builtin.class.getName(), new Builtin(), dict));
+
+ dict.put(CommandProcessor.COMMAND_FUNCTION, Procedural.functions);
+ regs.add(context.registerService(Procedural.class.getName(), new Procedural(), dict));
+
+ dict.put(CommandProcessor.COMMAND_FUNCTION, Posix.functions);
+ regs.add(context.registerService(Posix.class.getName(), new Posix(), dict));
+
+ dict.put(CommandProcessor.COMMAND_FUNCTION, Telnet.functions);
+ regs.add(context.registerService(Telnet.class.getName(), new Telnet(processor), dict));
+
+ Shell shell = new Shell(context, processor);
+ dict.put(CommandProcessor.COMMAND_FUNCTION, Shell.functions);
+ regs.add(context.registerService(Shell.class.getName(), shell, dict));
+
+ // start shell on a separate thread...
+ executor = Executors.newSingleThreadExecutor(new ThreadFactory()
+ {
+ public Thread newThread(Runnable runnable)
+ {
+ return new Thread(runnable, "Gogo shell");
+ }
+ });
+ executor.submit(new StartShellJob(context, processor));
+ }
+
+ private void stopShell()
+ {
+ if (executor != null && !(executor.isShutdown() || executor.isTerminated()))
+ {
+ executor.shutdownNow();
+
+ try
+ {
+ if (!executor.awaitTermination(5, TimeUnit.SECONDS))
+ {
+ System.err.println("!!! FAILED TO STOP EXECUTOR !!!");
+ }
+ }
+ catch (InterruptedException e)
+ {
+ // Restore administration...
+ Thread.currentThread().interrupt();
+ }
+ executor = null;
+ }
+ }
+
+ private static class StartShellJob implements Runnable
+ {
+ private final BundleContext context;
+ private final CommandProcessor processor;
+
+ public StartShellJob(BundleContext context, CommandProcessor processor)
+ {
+ this.context = context;
+ this.processor = processor;
+ }
+
+ public void run()
+ {
+ CommandSession session = processor.createSession(System.in, System.out, System.err);
+ try
+ {
+ // wait for gosh command to be registered
+ for (int i = 0; (i < 100) && session.get("gogo:gosh") == null; ++i)
+ {
+ TimeUnit.MILLISECONDS.sleep(10);
+ }
+
+ String args = context.getProperty("gosh.args");
+ args = (args == null) ? "" : args;
+ session.execute("gosh --login " + args);
+ }
+ catch (Exception e)
+ {
+ Object loc = session.get(".location");
+ if (null == loc || !loc.toString().contains(":"))
+ {
+ loc = "gogo";
+ }
+
+ System.err.println(loc + ": " + e.getClass().getSimpleName() + ": " + e.getMessage());
+ e.printStackTrace();
+ }
+ finally
+ {
+ session.close();
+ }
+ }
}
}
\ No newline at end of file