Replaced exp4j library with source code.

This commit is contained in:
MobiusDev 2017-08-07 23:07:38 +00:00
parent 28ebef24f4
commit 95e7d1939d
72 changed files with 6708 additions and 15 deletions

View File

@ -3,7 +3,6 @@
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8"/>
<classpathentry kind="lib" path="dist/libs/c3p0-0.9.5.2.jar"/>
<classpathentry kind="lib" path="dist/libs/ecj-4.4.2.jar"/>
<classpathentry kind="lib" path="dist/libs/exp4j-0.4.8.jar"/>
<classpathentry kind="lib" path="dist/libs/mail-1.5.2.jar"/>
<classpathentry kind="lib" path="dist/libs/mchange-commons-java-0.2.12.jar"/>
<classpathentry kind="lib" path="dist/libs/mysql-connector-java-5.1.43.jar"/>

Binary file not shown.

View File

@ -45,8 +45,7 @@ import com.l2jmobius.gameserver.model.skills.EffectScope;
import com.l2jmobius.gameserver.model.skills.ISkillCondition;
import com.l2jmobius.gameserver.model.skills.Skill;
import com.l2jmobius.gameserver.model.skills.SkillConditionScope;
import net.objecthunter.exp4j.ExpressionBuilder;
import com.l2jmobius.gameserver.util.exp4j.ExpressionBuilder;
/**
* Skill data parser.

View File

@ -0,0 +1,86 @@
/*
* Copyright 2015 Federico Vera
*
* Licensed 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 com.l2jmobius.gameserver.util.exp4j;
import java.util.EmptyStackException;
/**
* Simple double stack using a double array as data storage
* @author Federico Vera (dktcoding [at] gmail)
*/
class ArrayStack
{
private double[] data;
private int idx;
ArrayStack()
{
this(5);
}
ArrayStack(int initialCapacity)
{
if (initialCapacity <= 0)
{
throw new IllegalArgumentException("Stack's capacity must be positive");
}
data = new double[initialCapacity];
idx = -1;
}
void push(double value)
{
if ((idx + 1) == data.length)
{
double[] temp = new double[(int) (data.length * 1.2) + 1];
System.arraycopy(data, 0, temp, 0, data.length);
data = temp;
}
data[++idx] = value;
}
double peek()
{
if (idx == -1)
{
throw new EmptyStackException();
}
return data[idx];
}
double pop()
{
if (idx == -1)
{
throw new EmptyStackException();
}
return data[idx--];
}
boolean isEmpty()
{
return idx == -1;
}
int size()
{
return idx + 1;
}
}

View File

@ -0,0 +1,263 @@
/*
* Copyright 2014 Frank Asseg
*
* Licensed 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 com.l2jmobius.gameserver.util.exp4j;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;
import com.l2jmobius.gameserver.util.exp4j.function.Function;
import com.l2jmobius.gameserver.util.exp4j.function.Functions;
import com.l2jmobius.gameserver.util.exp4j.operator.Operator;
import com.l2jmobius.gameserver.util.exp4j.tokenizer.FunctionToken;
import com.l2jmobius.gameserver.util.exp4j.tokenizer.NumberToken;
import com.l2jmobius.gameserver.util.exp4j.tokenizer.OperatorToken;
import com.l2jmobius.gameserver.util.exp4j.tokenizer.Token;
import com.l2jmobius.gameserver.util.exp4j.tokenizer.VariableToken;
public class Expression
{
private final Token[] tokens;
private final Map<String, Double> variables;
private final Set<String> userFunctionNames;
private static Map<String, Double> createDefaultVariables()
{
final Map<String, Double> vars = new HashMap<>(4);
vars.put("pi", Math.PI);
vars.put("π", Math.PI);
vars.put("φ", 1.61803398874d);
vars.put("e", Math.E);
return vars;
}
/**
* Creates a new expression that is a copy of the existing one.
* @param existing the expression to copy
*/
public Expression(final Expression existing)
{
tokens = Arrays.copyOf(existing.tokens, existing.tokens.length);
variables = new HashMap<>();
variables.putAll(existing.variables);
userFunctionNames = new HashSet<>(existing.userFunctionNames);
}
Expression(final Token[] tokens)
{
this.tokens = tokens;
variables = createDefaultVariables();
userFunctionNames = Collections.<String> emptySet();
}
Expression(final Token[] tokens, Set<String> userFunctionNames)
{
this.tokens = tokens;
variables = createDefaultVariables();
this.userFunctionNames = userFunctionNames;
}
public Expression setVariable(final String name, final double value)
{
checkVariableName(name);
variables.put(name, Double.valueOf(value));
return this;
}
private void checkVariableName(String name)
{
if (userFunctionNames.contains(name) || (Functions.getBuiltinFunction(name) != null))
{
throw new IllegalArgumentException("The variable name '" + name + "' is invalid. Since there exists a function with the same name");
}
}
public Expression setVariables(Map<String, Double> variables)
{
for (Map.Entry<String, Double> v : variables.entrySet())
{
setVariable(v.getKey(), v.getValue());
}
return this;
}
public Set<String> getVariableNames()
{
final Set<String> variables = new HashSet<>();
for (final Token t : tokens)
{
if (t.getType() == Token.TOKEN_VARIABLE)
{
variables.add(((VariableToken) t).getName());
}
}
return variables;
}
public ValidationResult validate(boolean checkVariablesSet)
{
final List<String> errors = new ArrayList<>(0);
if (checkVariablesSet)
{
/* check that all vars have a value set */
for (final Token t : tokens)
{
if (t.getType() == Token.TOKEN_VARIABLE)
{
final String var = ((VariableToken) t).getName();
if (!variables.containsKey(var))
{
errors.add("The setVariable '" + var + "' has not been set");
}
}
}
}
/*
* Check if the number of operands, functions and operators match. The idea is to increment a counter for operands and decrease it for operators. When a function occurs the number of available arguments has to be greater than or equals to the function's expected number of arguments. The
* count has to be larger than 1 at all times and exactly 1 after all tokens have been processed
*/
int count = 0;
for (Token tok : tokens)
{
switch (tok.getType())
{
case Token.TOKEN_NUMBER:
case Token.TOKEN_VARIABLE:
count++;
break;
case Token.TOKEN_FUNCTION:
final Function func = ((FunctionToken) tok).getFunction();
final int argsNum = func.getNumArguments();
if (argsNum > count)
{
errors.add("Not enough arguments for '" + func.getName() + "'");
}
if (argsNum > 1)
{
count -= argsNum - 1;
}
else if (argsNum == 0)
{
// see https://github.com/fasseg/exp4j/issues/59
count++;
}
break;
case Token.TOKEN_OPERATOR:
Operator op = ((OperatorToken) tok).getOperator();
if (op.getNumOperands() == 2)
{
count--;
}
break;
}
if (count < 1)
{
errors.add("Too many operators");
return new ValidationResult(false, errors);
}
}
if (count > 1)
{
errors.add("Too many operands");
}
return errors.size() == 0 ? ValidationResult.SUCCESS : new ValidationResult(false, errors);
}
public ValidationResult validate()
{
return validate(true);
}
public Future<Double> evaluateAsync(ExecutorService executor)
{
return executor.submit(() -> evaluate());
}
public double evaluate()
{
final ArrayStack output = new ArrayStack();
for (Token t : tokens)
{
if (t.getType() == Token.TOKEN_NUMBER)
{
output.push(((NumberToken) t).getValue());
}
else if (t.getType() == Token.TOKEN_VARIABLE)
{
final String name = ((VariableToken) t).getName();
final Double value = variables.get(name);
if (value == null)
{
throw new IllegalArgumentException("No value has been set for the setVariable '" + name + "'.");
}
output.push(value);
}
else if (t.getType() == Token.TOKEN_OPERATOR)
{
OperatorToken op = (OperatorToken) t;
if (output.size() < op.getOperator().getNumOperands())
{
throw new IllegalArgumentException("Invalid number of operands available for '" + op.getOperator().getSymbol() + "' operator");
}
if (op.getOperator().getNumOperands() == 2)
{
/* pop the operands and push the result of the operation */
double rightArg = output.pop();
double leftArg = output.pop();
output.push(op.getOperator().apply(leftArg, rightArg));
}
else if (op.getOperator().getNumOperands() == 1)
{
/* pop the operand and push the result of the operation */
double arg = output.pop();
output.push(op.getOperator().apply(arg));
}
}
else if (t.getType() == Token.TOKEN_FUNCTION)
{
FunctionToken func = (FunctionToken) t;
final int numArguments = func.getFunction().getNumArguments();
if (output.size() < numArguments)
{
throw new IllegalArgumentException("Invalid number of arguments available for '" + func.getFunction().getName() + "' function");
}
/* collect the arguments from the stack */
double[] args = new double[numArguments];
for (int j = numArguments - 1; j >= 0; j--)
{
args[j] = output.pop();
}
output.push(func.getFunction().apply(args));
}
}
if (output.size() > 1)
{
throw new IllegalArgumentException("Invalid number of items on the output queue. Might be caused by an invalid number of arguments for a function.");
}
return output.pop();
}
}

View File

@ -0,0 +1,218 @@
/*
* Copyright 2014 Frank Asseg
*
* Licensed 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 com.l2jmobius.gameserver.util.exp4j;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import com.l2jmobius.gameserver.util.exp4j.function.Function;
import com.l2jmobius.gameserver.util.exp4j.function.Functions;
import com.l2jmobius.gameserver.util.exp4j.operator.Operator;
import com.l2jmobius.gameserver.util.exp4j.shuntingyard.ShuntingYard;
/**
* Factory class for {@link Expression} instances. This class is the main API entrypoint. Users should create new {@link Expression} instances using this factory class.
*/
public class ExpressionBuilder
{
private final String expression;
private final Map<String, Function> userFunctions;
private final Map<String, Operator> userOperators;
private final Set<String> variableNames;
private boolean implicitMultiplication = true;
/**
* Create a new ExpressionBuilder instance and initialize it with a given expression string.
* @param expression the expression to be parsed
*/
public ExpressionBuilder(String expression)
{
if ((expression == null) || (expression.trim().length() == 0))
{
throw new IllegalArgumentException("Expression can not be empty");
}
this.expression = expression;
userOperators = new HashMap<>(4);
userFunctions = new HashMap<>(4);
variableNames = new HashSet<>(4);
}
/**
* Add a {@link com.l2jmobius.gameserver.util.exp4j.function.Function} implementation available for use in the expression
* @param function the custom {@link com.l2jmobius.gameserver.util.exp4j.function.Function} implementation that should be available for use in the expression.
* @return the ExpressionBuilder instance
*/
public ExpressionBuilder function(Function function)
{
userFunctions.put(function.getName(), function);
return this;
}
/**
* Add multiple {@link com.l2jmobius.gameserver.util.exp4j.function.Function} implementations available for use in the expression
* @param functions the custom {@link com.l2jmobius.gameserver.util.exp4j.function.Function} implementations
* @return the ExpressionBuilder instance
*/
public ExpressionBuilder functions(Function... functions)
{
for (Function f : functions)
{
userFunctions.put(f.getName(), f);
}
return this;
}
/**
* Add multiple {@link com.l2jmobius.gameserver.util.exp4j.function.Function} implementations available for use in the expression
* @param functions A {@link java.util.List} of custom {@link com.l2jmobius.gameserver.util.exp4j.function.Function} implementations
* @return the ExpressionBuilder instance
*/
public ExpressionBuilder functions(List<Function> functions)
{
for (Function f : functions)
{
userFunctions.put(f.getName(), f);
}
return this;
}
/**
* Declare variable names used in the expression
* @param variableNames the variables used in the expression
* @return the ExpressionBuilder instance
*/
public ExpressionBuilder variables(Set<String> variableNames)
{
this.variableNames.addAll(variableNames);
return this;
}
/**
* Declare variable names used in the expression
* @param variableNames the variables used in the expression
* @return the ExpressionBuilder instance
*/
public ExpressionBuilder variables(String... variableNames)
{
Collections.addAll(this.variableNames, variableNames);
return this;
}
/**
* Declare a variable used in the expression
* @param variableName the variable used in the expression
* @return the ExpressionBuilder instance
*/
public ExpressionBuilder variable(String variableName)
{
variableNames.add(variableName);
return this;
}
public ExpressionBuilder implicitMultiplication(boolean enabled)
{
implicitMultiplication = enabled;
return this;
}
/**
* Add an {@link com.l2jmobius.gameserver.util.exp4j.operator.Operator} which should be available for use in the expression
* @param operator the custom {@link com.l2jmobius.gameserver.util.exp4j.operator.Operator} to add
* @return the ExpressionBuilder instance
*/
public ExpressionBuilder operator(Operator operator)
{
checkOperatorSymbol(operator);
userOperators.put(operator.getSymbol(), operator);
return this;
}
private void checkOperatorSymbol(Operator op)
{
String name = op.getSymbol();
for (char ch : name.toCharArray())
{
if (!Operator.isAllowedOperatorChar(ch))
{
throw new IllegalArgumentException("The operator symbol '" + name + "' is invalid");
}
}
}
/**
* Add multiple {@link com.l2jmobius.gameserver.util.exp4j.operator.Operator} implementations which should be available for use in the expression
* @param operators the set of custom {@link com.l2jmobius.gameserver.util.exp4j.operator.Operator} implementations to add
* @return the ExpressionBuilder instance
*/
public ExpressionBuilder operator(Operator... operators)
{
for (Operator o : operators)
{
this.operator(o);
}
return this;
}
/**
* Add multiple {@link com.l2jmobius.gameserver.util.exp4j.operator.Operator} implementations which should be available for use in the expression
* @param operators the {@link java.util.List} of custom {@link com.l2jmobius.gameserver.util.exp4j.operator.Operator} implementations to add
* @return the ExpressionBuilder instance
*/
public ExpressionBuilder operator(List<Operator> operators)
{
for (Operator o : operators)
{
this.operator(o);
}
return this;
}
/**
* Build the {@link Expression} instance using the custom operators and functions set.
* @return an {@link Expression} instance which can be used to evaluate the result of the expression
*/
public Expression build()
{
if (expression.length() == 0)
{
throw new IllegalArgumentException("The expression can not be empty");
}
/* set the contants' varibale names */
variableNames.add("pi");
variableNames.add("π");
variableNames.add("e");
variableNames.add("φ");
/* Check if there are duplicate vars/functions */
for (String var : variableNames)
{
if ((Functions.getBuiltinFunction(var) != null) || userFunctions.containsKey(var))
{
throw new IllegalArgumentException("A variable can not have the same name as a function [" + var + "]");
}
}
return new Expression(ShuntingYard.convertToRPN(expression, userFunctions, userOperators, variableNames, implicitMultiplication), userFunctions.keySet());
}
}

View File

@ -0,0 +1,61 @@
/*
* Copyright 2014 Frank Asseg
*
* Licensed 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 com.l2jmobius.gameserver.util.exp4j;
import java.util.List;
/**
* Contains the validation result for a given {@link Expression}
*/
public class ValidationResult
{
private final boolean valid;
private final List<String> errors;
/**
* Create a new instance
* @param valid Whether the validation of the expression was successful
* @param errors The list of errors returned if the validation was unsuccessful
*/
public ValidationResult(boolean valid, List<String> errors)
{
this.valid = valid;
this.errors = errors;
}
/**
* Check if an expression has been validated successfully
* @return true if the validation was successful, false otherwise
*/
public boolean isValid()
{
return valid;
}
/**
* Get the list of errors describing the issues while validating the expression
* @return The List of errors
*/
public List<String> getErrors()
{
return errors;
}
/**
* A static class representing a successful validation result
*/
public static final ValidationResult SUCCESS = new ValidationResult(true, null);
}

View File

@ -0,0 +1,132 @@
/*
* Copyright 2014 Frank Asseg
*
* Licensed 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 com.l2jmobius.gameserver.util.exp4j.function;
/**
* A class representing a Function which can be used in an expression
*/
public abstract class Function
{
protected final String name;
protected final int numArguments;
/**
* Create a new Function with a given name and number of arguments
* @param name the name of the Function
* @param numArguments the number of arguments the function takes
*/
public Function(String name, int numArguments)
{
if (numArguments < 0)
{
throw new IllegalArgumentException("The number of function arguments can not be less than 0 for '" + name + "'");
}
if (!isValidFunctionName(name))
{
throw new IllegalArgumentException("The function name '" + name + "' is invalid");
}
this.name = name;
this.numArguments = numArguments;
}
/**
* Create a new Function with a given name that takes a single argument
* @param name the name of the Function
*/
public Function(String name)
{
this(name, 1);
}
/**
* Get the name of the Function
* @return the name
*/
public String getName()
{
return name;
}
/**
* Get the number of arguments for this function
* @return the number of arguments
*/
public int getNumArguments()
{
return numArguments;
}
/**
* Method that does the actual calculation of the function value given the arguments
* @param args the set of arguments used for calculating the function
* @return the result of the function evaluation
*/
public abstract double apply(double... args);
/**
* Get the set of characters which are allowed for use in Function names.
* @return the set of characters allowed
* @deprecated since 0.4.5 All unicode letters are allowed to be used in function names since 0.4.3. This API Function can be safely ignored. Checks for function name validity can be done using Character.isLetter() et al.
*/
@Deprecated
public static char[] getAllowedFunctionCharacters()
{
char[] chars = new char[53];
int count = 0;
for (int i = 65; i < 91; i++)
{
chars[count++] = (char) i;
}
for (int i = 97; i < 123; i++)
{
chars[count++] = (char) i;
}
chars[count] = '_';
return chars;
}
public static boolean isValidFunctionName(final String name)
{
if (name == null)
{
return false;
}
final int size = name.length();
if (size == 0)
{
return false;
}
for (int i = 0; i < size; i++)
{
final char c = name.charAt(i);
if (Character.isLetter(c) || (c == '_'))
{
continue;
}
else if (Character.isDigit(c) && (i > 0))
{
continue;
}
return false;
}
return true;
}
}

View File

@ -0,0 +1,356 @@
/*
* Copyright 2014 Frank Asseg
*
* Licensed 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 com.l2jmobius.gameserver.util.exp4j.function;
/**
* Class representing the builtin functions available for use in expressions
*/
public class Functions
{
private static final int INDEX_SIN = 0;
private static final int INDEX_COS = 1;
private static final int INDEX_TAN = 2;
private static final int INDEX_COT = 3;
private static final int INDEX_LOG = 4;
private static final int INDEX_LOG1P = 5;
private static final int INDEX_ABS = 6;
private static final int INDEX_ACOS = 7;
private static final int INDEX_ASIN = 8;
private static final int INDEX_ATAN = 9;
private static final int INDEX_CBRT = 10;
private static final int INDEX_CEIL = 11;
private static final int INDEX_FLOOR = 12;
private static final int INDEX_SINH = 13;
private static final int INDEX_SQRT = 14;
private static final int INDEX_TANH = 15;
private static final int INDEX_COSH = 16;
private static final int INDEX_POW = 17;
private static final int INDEX_EXP = 18;
private static final int INDEX_EXPM1 = 19;
private static final int INDEX_LOG10 = 20;
private static final int INDEX_LOG2 = 21;
private static final int INDEX_SGN = 22;
private static final Function[] builtinFunctions = new Function[23];
static
{
builtinFunctions[INDEX_SIN] = new Function("sin")
{
@Override
public double apply(double... args)
{
return Math.sin(args[0]);
}
};
builtinFunctions[INDEX_COS] = new Function("cos")
{
@Override
public double apply(double... args)
{
return Math.cos(args[0]);
}
};
builtinFunctions[INDEX_TAN] = new Function("tan")
{
@Override
public double apply(double... args)
{
return Math.tan(args[0]);
}
};
builtinFunctions[INDEX_COT] = new Function("cot")
{
@Override
public double apply(double... args)
{
double tan = Math.tan(args[0]);
if (tan == 0d)
{
throw new ArithmeticException("Division by zero in cotangent!");
}
return 1d / Math.tan(args[0]);
}
};
builtinFunctions[INDEX_LOG] = new Function("log")
{
@Override
public double apply(double... args)
{
return Math.log(args[0]);
}
};
builtinFunctions[INDEX_LOG2] = new Function("log2")
{
@Override
public double apply(double... args)
{
return Math.log(args[0]) / Math.log(2d);
}
};
builtinFunctions[INDEX_LOG10] = new Function("log10")
{
@Override
public double apply(double... args)
{
return Math.log10(args[0]);
}
};
builtinFunctions[INDEX_LOG1P] = new Function("log1p")
{
@Override
public double apply(double... args)
{
return Math.log1p(args[0]);
}
};
builtinFunctions[INDEX_ABS] = new Function("abs")
{
@Override
public double apply(double... args)
{
return Math.abs(args[0]);
}
};
builtinFunctions[INDEX_ACOS] = new Function("acos")
{
@Override
public double apply(double... args)
{
return Math.acos(args[0]);
}
};
builtinFunctions[INDEX_ASIN] = new Function("asin")
{
@Override
public double apply(double... args)
{
return Math.asin(args[0]);
}
};
builtinFunctions[INDEX_ATAN] = new Function("atan")
{
@Override
public double apply(double... args)
{
return Math.atan(args[0]);
}
};
builtinFunctions[INDEX_CBRT] = new Function("cbrt")
{
@Override
public double apply(double... args)
{
return Math.cbrt(args[0]);
}
};
builtinFunctions[INDEX_FLOOR] = new Function("floor")
{
@Override
public double apply(double... args)
{
return Math.floor(args[0]);
}
};
builtinFunctions[INDEX_SINH] = new Function("sinh")
{
@Override
public double apply(double... args)
{
return Math.sinh(args[0]);
}
};
builtinFunctions[INDEX_SQRT] = new Function("sqrt")
{
@Override
public double apply(double... args)
{
return Math.sqrt(args[0]);
}
};
builtinFunctions[INDEX_TANH] = new Function("tanh")
{
@Override
public double apply(double... args)
{
return Math.tanh(args[0]);
}
};
builtinFunctions[INDEX_COSH] = new Function("cosh")
{
@Override
public double apply(double... args)
{
return Math.cosh(args[0]);
}
};
builtinFunctions[INDEX_CEIL] = new Function("ceil")
{
@Override
public double apply(double... args)
{
return Math.ceil(args[0]);
}
};
builtinFunctions[INDEX_POW] = new Function("pow", 2)
{
@Override
public double apply(double... args)
{
return Math.pow(args[0], args[1]);
}
};
builtinFunctions[INDEX_EXP] = new Function("exp", 1)
{
@Override
public double apply(double... args)
{
return Math.exp(args[0]);
}
};
builtinFunctions[INDEX_EXPM1] = new Function("expm1", 1)
{
@Override
public double apply(double... args)
{
return Math.expm1(args[0]);
}
};
builtinFunctions[INDEX_SGN] = new Function("signum", 1)
{
@Override
public double apply(double... args)
{
if (args[0] > 0)
{
return 1;
}
else if (args[0] < 0)
{
return -1;
}
else
{
return 0;
}
}
};
}
/**
* Get the builtin function for a given name
* @param name te name of the function
* @return a Function instance
*/
public static Function getBuiltinFunction(final String name)
{
if (name.equals("sin"))
{
return builtinFunctions[INDEX_SIN];
}
else if (name.equals("cos"))
{
return builtinFunctions[INDEX_COS];
}
else if (name.equals("tan"))
{
return builtinFunctions[INDEX_TAN];
}
else if (name.equals("cot"))
{
return builtinFunctions[INDEX_COT];
}
else if (name.equals("asin"))
{
return builtinFunctions[INDEX_ASIN];
}
else if (name.equals("acos"))
{
return builtinFunctions[INDEX_ACOS];
}
else if (name.equals("atan"))
{
return builtinFunctions[INDEX_ATAN];
}
else if (name.equals("sinh"))
{
return builtinFunctions[INDEX_SINH];
}
else if (name.equals("cosh"))
{
return builtinFunctions[INDEX_COSH];
}
else if (name.equals("tanh"))
{
return builtinFunctions[INDEX_TANH];
}
else if (name.equals("abs"))
{
return builtinFunctions[INDEX_ABS];
}
else if (name.equals("log"))
{
return builtinFunctions[INDEX_LOG];
}
else if (name.equals("log10"))
{
return builtinFunctions[INDEX_LOG10];
}
else if (name.equals("log2"))
{
return builtinFunctions[INDEX_LOG2];
}
else if (name.equals("log1p"))
{
return builtinFunctions[INDEX_LOG1P];
}
else if (name.equals("ceil"))
{
return builtinFunctions[INDEX_CEIL];
}
else if (name.equals("floor"))
{
return builtinFunctions[INDEX_FLOOR];
}
else if (name.equals("sqrt"))
{
return builtinFunctions[INDEX_SQRT];
}
else if (name.equals("cbrt"))
{
return builtinFunctions[INDEX_CBRT];
}
else if (name.equals("pow"))
{
return builtinFunctions[INDEX_POW];
}
else if (name.equals("exp"))
{
return builtinFunctions[INDEX_EXP];
}
else if (name.equals("expm1"))
{
return builtinFunctions[INDEX_EXPM1];
}
else if (name.equals("signum"))
{
return builtinFunctions[INDEX_SGN];
}
else
{
return null;
}
}
}

View File

@ -0,0 +1,161 @@
/*
* Copyright 2014 Frank Asseg
*
* Licensed 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 com.l2jmobius.gameserver.util.exp4j.operator;
/**
* Class representing operators that can be used in an expression
*/
public abstract class Operator
{
/**
* The precedence value for the addition operation
*/
public static final int PRECEDENCE_ADDITION = 500;
/**
* The precedence value for the subtraction operation
*/
public static final int PRECEDENCE_SUBTRACTION = PRECEDENCE_ADDITION;
/**
* The precedence value for the multiplication operation
*/
public static final int PRECEDENCE_MULTIPLICATION = 1000;
/**
* The precedence value for the division operation
*/
public static final int PRECEDENCE_DIVISION = PRECEDENCE_MULTIPLICATION;
/**
* The precedence value for the modulo operation
*/
public static final int PRECEDENCE_MODULO = PRECEDENCE_DIVISION;
/**
* The precedence value for the power operation
*/
public static final int PRECEDENCE_POWER = 10000;
/**
* The precedence value for the unary minus operation
*/
public static final int PRECEDENCE_UNARY_MINUS = 5000;
/**
* The precedence value for the unary plus operation
*/
public static final int PRECEDENCE_UNARY_PLUS = PRECEDENCE_UNARY_MINUS;
/**
* The set of allowed operator chars
*/
public static final char[] ALLOWED_OPERATOR_CHARS =
{
'+',
'-',
'*',
'/',
'%',
'^',
'!',
'#',
'§',
'$',
'&',
';',
':',
'~',
'<',
'>',
'|',
'='
};
protected final int numOperands;
protected final boolean leftAssociative;
protected final String symbol;
protected final int precedence;
/**
* Create a new operator for use in expressions
* @param symbol the symbol of the operator
* @param numberOfOperands the number of operands the operator takes (1 or 2)
* @param leftAssociative set to true if the operator is left associative, false if it is right associative
* @param precedence the precedence value of the operator
*/
public Operator(String symbol, int numberOfOperands, boolean leftAssociative, int precedence)
{
super();
this.numOperands = numberOfOperands;
this.leftAssociative = leftAssociative;
this.symbol = symbol;
this.precedence = precedence;
}
/**
* Check if a character is an allowed operator char
* @param ch the char to check
* @return true if the char is allowed an an operator symbol, false otherwise
*/
public static boolean isAllowedOperatorChar(char ch)
{
for (char allowed : ALLOWED_OPERATOR_CHARS)
{
if (ch == allowed)
{
return true;
}
}
return false;
}
/**
* Check if the operator is left associative
* @return true os the operator is left associative, false otherwise
*/
public boolean isLeftAssociative()
{
return leftAssociative;
}
/**
* Check the precedence value for the operator
* @return the precedence value
*/
public int getPrecedence()
{
return precedence;
}
/**
* Apply the operation on the given operands
* @param args the operands for the operation
* @return the calculated result of the operation
*/
public abstract double apply(double... args);
/**
* Get the operator symbol
* @return the symbol
*/
public String getSymbol()
{
return symbol;
}
/**
* Get the number of operands
* @return the number of operands
*/
public int getNumOperands()
{
return numOperands;
}
}

View File

@ -0,0 +1,135 @@
/*
* Copyright 2014 Frank Asseg
*
* Licensed 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 com.l2jmobius.gameserver.util.exp4j.operator;
public abstract class Operators
{
private static final int INDEX_ADDITION = 0;
private static final int INDEX_SUBTRACTION = 1;
private static final int INDEX_MUTLIPLICATION = 2;
private static final int INDEX_DIVISION = 3;
private static final int INDEX_POWER = 4;
private static final int INDEX_MODULO = 5;
private static final int INDEX_UNARYMINUS = 6;
private static final int INDEX_UNARYPLUS = 7;
private static final Operator[] builtinOperators = new Operator[8];
static
{
builtinOperators[INDEX_ADDITION] = new Operator("+", 2, true, Operator.PRECEDENCE_ADDITION)
{
@Override
public double apply(final double... args)
{
return args[0] + args[1];
}
};
builtinOperators[INDEX_SUBTRACTION] = new Operator("-", 2, true, Operator.PRECEDENCE_ADDITION)
{
@Override
public double apply(final double... args)
{
return args[0] - args[1];
}
};
builtinOperators[INDEX_UNARYMINUS] = new Operator("-", 1, false, Operator.PRECEDENCE_UNARY_MINUS)
{
@Override
public double apply(final double... args)
{
return -args[0];
}
};
builtinOperators[INDEX_UNARYPLUS] = new Operator("+", 1, false, Operator.PRECEDENCE_UNARY_PLUS)
{
@Override
public double apply(final double... args)
{
return args[0];
}
};
builtinOperators[INDEX_MUTLIPLICATION] = new Operator("*", 2, true, Operator.PRECEDENCE_MULTIPLICATION)
{
@Override
public double apply(final double... args)
{
return args[0] * args[1];
}
};
builtinOperators[INDEX_DIVISION] = new Operator("/", 2, true, Operator.PRECEDENCE_DIVISION)
{
@Override
public double apply(final double... args)
{
if (args[1] == 0d)
{
throw new ArithmeticException("Division by zero!");
}
return args[0] / args[1];
}
};
builtinOperators[INDEX_POWER] = new Operator("^", 2, false, Operator.PRECEDENCE_POWER)
{
@Override
public double apply(final double... args)
{
return Math.pow(args[0], args[1]);
}
};
builtinOperators[INDEX_MODULO] = new Operator("%", 2, true, Operator.PRECEDENCE_MODULO)
{
@Override
public double apply(final double... args)
{
if (args[1] == 0d)
{
throw new ArithmeticException("Division by zero!");
}
return args[0] % args[1];
}
};
}
public static Operator getBuiltinOperator(final char symbol, final int numArguments)
{
switch (symbol)
{
case '+':
if (numArguments != 1)
{
return builtinOperators[INDEX_ADDITION];
}
return builtinOperators[INDEX_UNARYPLUS];
case '-':
if (numArguments != 1)
{
return builtinOperators[INDEX_SUBTRACTION];
}
return builtinOperators[INDEX_UNARYMINUS];
case '*':
return builtinOperators[INDEX_MUTLIPLICATION];
case '/':
return builtinOperators[INDEX_DIVISION];
case '^':
return builtinOperators[INDEX_POWER];
case '%':
return builtinOperators[INDEX_MODULO];
default:
return null;
}
}
}

View File

@ -0,0 +1,121 @@
/*
* Copyright 2014 Frank Asseg
*
* Licensed 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 com.l2jmobius.gameserver.util.exp4j.shuntingyard;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Stack;
import com.l2jmobius.gameserver.util.exp4j.function.Function;
import com.l2jmobius.gameserver.util.exp4j.operator.Operator;
import com.l2jmobius.gameserver.util.exp4j.tokenizer.OperatorToken;
import com.l2jmobius.gameserver.util.exp4j.tokenizer.Token;
import com.l2jmobius.gameserver.util.exp4j.tokenizer.Tokenizer;
/**
* Shunting yard implementation to convert infix to reverse polish notation
*/
public class ShuntingYard
{
/**
* Convert a Set of tokens from infix to reverse polish notation
* @param expression the expression to convert
* @param userFunctions the custom functions used
* @param userOperators the custom operators used
* @param variableNames the variable names used in the expression
* @param implicitMultiplication set to fasle to turn off implicit multiplication
* @return a {@link com.l2jmobius.gameserver.util.exp4j.tokenizer.Token} array containing the result
*/
public static Token[] convertToRPN(final String expression, final Map<String, Function> userFunctions, final Map<String, Operator> userOperators, final Set<String> variableNames, final boolean implicitMultiplication)
{
final Stack<Token> stack = new Stack<>();
final List<Token> output = new ArrayList<>();
final Tokenizer tokenizer = new Tokenizer(expression, userFunctions, userOperators, variableNames, implicitMultiplication);
while (tokenizer.hasNext())
{
Token token = tokenizer.nextToken();
switch (token.getType())
{
case Token.TOKEN_NUMBER:
case Token.TOKEN_VARIABLE:
output.add(token);
break;
case Token.TOKEN_FUNCTION:
stack.add(token);
break;
case Token.TOKEN_SEPARATOR:
while (!stack.empty() && (stack.peek().getType() != Token.TOKEN_PARENTHESES_OPEN))
{
output.add(stack.pop());
}
if (stack.empty() || (stack.peek().getType() != Token.TOKEN_PARENTHESES_OPEN))
{
throw new IllegalArgumentException("Misplaced function separator ',' or mismatched parentheses");
}
break;
case Token.TOKEN_OPERATOR:
while (!stack.empty() && (stack.peek().getType() == Token.TOKEN_OPERATOR))
{
OperatorToken o1 = (OperatorToken) token;
OperatorToken o2 = (OperatorToken) stack.peek();
if ((o1.getOperator().getNumOperands() == 1) && (o2.getOperator().getNumOperands() == 2))
{
break;
}
else if ((o1.getOperator().isLeftAssociative() && (o1.getOperator().getPrecedence() <= o2.getOperator().getPrecedence())) || (o1.getOperator().getPrecedence() < o2.getOperator().getPrecedence()))
{
output.add(stack.pop());
}
else
{
break;
}
}
stack.push(token);
break;
case Token.TOKEN_PARENTHESES_OPEN:
stack.push(token);
break;
case Token.TOKEN_PARENTHESES_CLOSE:
while (stack.peek().getType() != Token.TOKEN_PARENTHESES_OPEN)
{
output.add(stack.pop());
}
stack.pop();
if (!stack.isEmpty() && (stack.peek().getType() == Token.TOKEN_FUNCTION))
{
output.add(stack.pop());
}
break;
default:
throw new IllegalArgumentException("Unknown Token type encountered. This should not happen");
}
}
while (!stack.empty())
{
Token t = stack.pop();
if ((t.getType() == Token.TOKEN_PARENTHESES_CLOSE) || (t.getType() == Token.TOKEN_PARENTHESES_OPEN))
{
throw new IllegalArgumentException("Mismatched parentheses detected. Please check the expression");
}
output.add(t);
}
return output.toArray(new Token[output.size()]);
}
}

View File

@ -0,0 +1,30 @@
/*
* Copyright 2014 Frank Asseg
*
* Licensed 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 com.l2jmobius.gameserver.util.exp4j.tokenizer;
/**
* Represents an argument separator in functions i.e: ','
*/
class ArgumentSeparatorToken extends Token
{
/**
* Create a new instance
*/
ArgumentSeparatorToken()
{
super(Token.TOKEN_SEPARATOR);
}
}

View File

@ -0,0 +1,30 @@
/*
* Copyright 2014 Frank Asseg
*
* Licensed 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 com.l2jmobius.gameserver.util.exp4j.tokenizer;
/**
* represents closed parentheses
*/
class CloseParenthesesToken extends Token
{
/**
* Creare a new instance
*/
CloseParenthesesToken()
{
super(Token.TOKEN_PARENTHESES_CLOSE);
}
}

View File

@ -0,0 +1,34 @@
/*
* Copyright 2014 Frank Asseg
*
* Licensed 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 com.l2jmobius.gameserver.util.exp4j.tokenizer;
import com.l2jmobius.gameserver.util.exp4j.function.Function;
public class FunctionToken extends Token
{
private final Function function;
public FunctionToken(final Function function)
{
super(Token.TOKEN_FUNCTION);
this.function = function;
}
public Function getFunction()
{
return function;
}
}

View File

@ -0,0 +1,48 @@
/*
* Copyright 2014 Frank Asseg
*
* Licensed 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 com.l2jmobius.gameserver.util.exp4j.tokenizer;
/**
* Represents a number in the expression
*/
public final class NumberToken extends Token
{
private final double value;
/**
* Create a new instance
* @param value the value of the number
*/
public NumberToken(double value)
{
super(TOKEN_NUMBER);
this.value = value;
}
NumberToken(final char[] expression, final int offset, final int len)
{
this(Double.parseDouble(String.valueOf(expression, offset, len)));
}
/**
* Get the value of the number
* @return the value
*/
public double getValue()
{
return value;
}
}

View File

@ -0,0 +1,24 @@
/*
* Copyright 2014 Frank Asseg
*
* Licensed 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 com.l2jmobius.gameserver.util.exp4j.tokenizer;
class OpenParenthesesToken extends Token
{
OpenParenthesesToken()
{
super(TOKEN_PARENTHESES_OPEN);
}
}

View File

@ -0,0 +1,49 @@
/*
* Copyright 2014 Frank Asseg
*
* Licensed 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 com.l2jmobius.gameserver.util.exp4j.tokenizer;
import com.l2jmobius.gameserver.util.exp4j.operator.Operator;
/**
* Represents an operator used in expressions
*/
public class OperatorToken extends Token
{
private final Operator operator;
/**
* Create a new instance
* @param op the operator
*/
public OperatorToken(Operator op)
{
super(Token.TOKEN_OPERATOR);
if (op == null)
{
throw new IllegalArgumentException("Operator is unknown for token.");
}
this.operator = op;
}
/**
* Get the operator for that token
* @return the operator
*/
public Operator getOperator()
{
return operator;
}
}

View File

@ -0,0 +1,43 @@
/*
* Copyright 2014 Frank Asseg
*
* Licensed 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 com.l2jmobius.gameserver.util.exp4j.tokenizer;
/**
* Abstract class for tokens used by exp4j to tokenize expressions
*/
public abstract class Token
{
public static final short TOKEN_NUMBER = 1;
public static final short TOKEN_OPERATOR = 2;
public static final short TOKEN_FUNCTION = 3;
public static final short TOKEN_PARENTHESES_OPEN = 4;
public static final short TOKEN_PARENTHESES_CLOSE = 5;
public static final short TOKEN_VARIABLE = 6;
public static final short TOKEN_SEPARATOR = 7;
protected final int type;
Token(int type)
{
this.type = type;
}
public int getType()
{
return type;
}
}

View File

@ -0,0 +1,334 @@
/*
* Copyright 2014 Frank Asseg
*
* Licensed 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 com.l2jmobius.gameserver.util.exp4j.tokenizer;
import java.util.Map;
import java.util.Set;
import com.l2jmobius.gameserver.util.exp4j.function.Function;
import com.l2jmobius.gameserver.util.exp4j.function.Functions;
import com.l2jmobius.gameserver.util.exp4j.operator.Operator;
import com.l2jmobius.gameserver.util.exp4j.operator.Operators;
public class Tokenizer
{
private final char[] expression;
private final int expressionLength;
private final Map<String, Function> userFunctions;
private final Map<String, Operator> userOperators;
private final Set<String> variableNames;
private final boolean implicitMultiplication;
private int pos = 0;
private Token lastToken;
public Tokenizer(String expression, final Map<String, Function> userFunctions, final Map<String, Operator> userOperators, final Set<String> variableNames, final boolean implicitMultiplication)
{
this.expression = expression.trim().toCharArray();
this.expressionLength = this.expression.length;
this.userFunctions = userFunctions;
this.userOperators = userOperators;
this.variableNames = variableNames;
this.implicitMultiplication = implicitMultiplication;
}
public Tokenizer(String expression, final Map<String, Function> userFunctions, final Map<String, Operator> userOperators, final Set<String> variableNames)
{
this.expression = expression.trim().toCharArray();
this.expressionLength = this.expression.length;
this.userFunctions = userFunctions;
this.userOperators = userOperators;
this.variableNames = variableNames;
this.implicitMultiplication = true;
}
public boolean hasNext()
{
return this.expression.length > pos;
}
public Token nextToken()
{
char ch = expression[pos];
while (Character.isWhitespace(ch))
{
ch = expression[++pos];
}
if (Character.isDigit(ch) || (ch == '.'))
{
if (lastToken != null)
{
if (lastToken.getType() == Token.TOKEN_NUMBER)
{
throw new IllegalArgumentException("Unable to parse char '" + ch + "' (Code:" + (int) ch + ") at [" + pos + "]");
}
else if (implicitMultiplication && ((lastToken.getType() != Token.TOKEN_OPERATOR) && (lastToken.getType() != Token.TOKEN_PARENTHESES_OPEN) && (lastToken.getType() != Token.TOKEN_FUNCTION) && (lastToken.getType() != Token.TOKEN_SEPARATOR)))
{
// insert an implicit multiplication token
lastToken = new OperatorToken(Operators.getBuiltinOperator('*', 2));
return lastToken;
}
}
return parseNumberToken(ch);
}
else if (isArgumentSeparator(ch))
{
return parseArgumentSeparatorToken(ch);
}
else if (isOpenParentheses(ch))
{
if ((lastToken != null) && implicitMultiplication && ((lastToken.getType() != Token.TOKEN_OPERATOR) && (lastToken.getType() != Token.TOKEN_PARENTHESES_OPEN) && (lastToken.getType() != Token.TOKEN_FUNCTION) && (lastToken.getType() != Token.TOKEN_SEPARATOR)))
{
// insert an implicit multiplication token
lastToken = new OperatorToken(Operators.getBuiltinOperator('*', 2));
return lastToken;
}
return parseParentheses(true);
}
else if (isCloseParentheses(ch))
{
return parseParentheses(false);
}
else if (Operator.isAllowedOperatorChar(ch))
{
return parseOperatorToken(ch);
}
else if (isAlphabetic(ch) || (ch == '_'))
{
// parse the name which can be a setVariable or a function
if ((lastToken != null) && implicitMultiplication && ((lastToken.getType() != Token.TOKEN_OPERATOR) && (lastToken.getType() != Token.TOKEN_PARENTHESES_OPEN) && (lastToken.getType() != Token.TOKEN_FUNCTION) && (lastToken.getType() != Token.TOKEN_SEPARATOR)))
{
// insert an implicit multiplication token
lastToken = new OperatorToken(Operators.getBuiltinOperator('*', 2));
return lastToken;
}
return parseFunctionOrVariable();
}
throw new IllegalArgumentException("Unable to parse char '" + ch + "' (Code:" + (int) ch + ") at [" + pos + "]");
}
private Token parseArgumentSeparatorToken(char ch)
{
this.pos++;
this.lastToken = new ArgumentSeparatorToken();
return lastToken;
}
private boolean isArgumentSeparator(char ch)
{
return ch == ',';
}
private Token parseParentheses(final boolean open)
{
if (open)
{
this.lastToken = new OpenParenthesesToken();
}
else
{
this.lastToken = new CloseParenthesesToken();
}
this.pos++;
return lastToken;
}
private boolean isOpenParentheses(char ch)
{
return (ch == '(') || (ch == '{') || (ch == '[');
}
private boolean isCloseParentheses(char ch)
{
return (ch == ')') || (ch == '}') || (ch == ']');
}
private Token parseFunctionOrVariable()
{
final int offset = this.pos;
int testPos;
int lastValidLen = 1;
Token lastValidToken = null;
int len = 1;
if (isEndOfExpression(offset))
{
this.pos++;
}
testPos = (offset + len) - 1;
while (!isEndOfExpression(testPos) && isVariableOrFunctionCharacter(expression[testPos]))
{
String name = new String(expression, offset, len);
if ((variableNames != null) && variableNames.contains(name))
{
lastValidLen = len;
lastValidToken = new VariableToken(name);
}
else
{
final Function f = getFunction(name);
if (f != null)
{
lastValidLen = len;
lastValidToken = new FunctionToken(f);
}
}
len++;
testPos = (offset + len) - 1;
}
if (lastValidToken == null)
{
throw new UnknownFunctionOrVariableException(new String(expression), pos, len);
}
pos += lastValidLen;
lastToken = lastValidToken;
return lastToken;
}
private Function getFunction(String name)
{
Function f = null;
if (this.userFunctions != null)
{
f = this.userFunctions.get(name);
}
if (f == null)
{
f = Functions.getBuiltinFunction(name);
}
return f;
}
private Token parseOperatorToken(char firstChar)
{
final int offset = this.pos;
int len = 1;
final StringBuilder symbol = new StringBuilder();
Operator lastValid = null;
symbol.append(firstChar);
while (!isEndOfExpression(offset + len) && Operator.isAllowedOperatorChar(expression[offset + len]))
{
symbol.append(expression[offset + len++]);
}
while (symbol.length() > 0)
{
Operator op = this.getOperator(symbol.toString());
if (op == null)
{
symbol.setLength(symbol.length() - 1);
}
else
{
lastValid = op;
break;
}
}
pos += symbol.length();
lastToken = new OperatorToken(lastValid);
return lastToken;
}
private Operator getOperator(String symbol)
{
Operator op = null;
if (this.userOperators != null)
{
op = this.userOperators.get(symbol);
}
if ((op == null) && (symbol.length() == 1))
{
int argc = 2;
if (lastToken == null)
{
argc = 1;
}
else
{
int lastTokenType = lastToken.getType();
if ((lastTokenType == Token.TOKEN_PARENTHESES_OPEN) || (lastTokenType == Token.TOKEN_SEPARATOR))
{
argc = 1;
}
else if (lastTokenType == Token.TOKEN_OPERATOR)
{
final Operator lastOp = ((OperatorToken) lastToken).getOperator();
if ((lastOp.getNumOperands() == 2) || ((lastOp.getNumOperands() == 1) && !lastOp.isLeftAssociative()))
{
argc = 1;
}
}
}
op = Operators.getBuiltinOperator(symbol.charAt(0), argc);
}
return op;
}
private Token parseNumberToken(final char firstChar)
{
final int offset = this.pos;
int len = 1;
this.pos++;
if (isEndOfExpression(offset + len))
{
lastToken = new NumberToken(Double.parseDouble(String.valueOf(firstChar)));
return lastToken;
}
while (!isEndOfExpression(offset + len) && isNumeric(expression[offset + len], (expression[(offset + len) - 1] == 'e') || (expression[(offset + len) - 1] == 'E')))
{
len++;
this.pos++;
}
// check if the e is at the end
if ((expression[(offset + len) - 1] == 'e') || (expression[(offset + len) - 1] == 'E'))
{
// since the e is at the end it's not part of the number and a rollback is necessary
len--;
pos--;
}
lastToken = new NumberToken(expression, offset, len);
return lastToken;
}
private static boolean isNumeric(char ch, boolean lastCharE)
{
return Character.isDigit(ch) || (ch == '.') || (ch == 'e') || (ch == 'E') || (lastCharE && ((ch == '-') || (ch == '+')));
}
public static boolean isAlphabetic(int codePoint)
{
return Character.isLetter(codePoint);
}
public static boolean isVariableOrFunctionCharacter(int codePoint)
{
return isAlphabetic(codePoint) || Character.isDigit(codePoint) || (codePoint == '_') || (codePoint == '.');
}
private boolean isEndOfExpression(int offset)
{
return this.expressionLength <= offset;
}
}

View File

@ -0,0 +1,65 @@
package com.l2jmobius.gameserver.util.exp4j.tokenizer;
/**
* This exception is being thrown whenever {@link Tokenizer} finds unknown function or variable.
* @author Bartosz Firyn (sarxos)
*/
public class UnknownFunctionOrVariableException extends IllegalArgumentException
{
private final String message;
private final String expression;
private final String token;
private final int position;
public UnknownFunctionOrVariableException(String expression, int position, int length)
{
this.expression = expression;
this.token = token(expression, position, length);
this.position = position;
this.message = "Unknown function or variable '" + token + "' at pos " + position + " in expression '" + expression + "'";
}
private static String token(String expression, int position, int length)
{
int len = expression.length();
int end = (position + length) - 1;
if (len < end)
{
end = len;
}
return expression.substring(position, end);
}
@Override
public String getMessage()
{
return message;
}
/**
* @return Expression which contains unknown function or variable
*/
public String getExpression()
{
return expression;
}
/**
* @return The name of unknown function or variable
*/
public String getToken()
{
return token;
}
/**
* @return The position of unknown function or variable
*/
public int getPosition()
{
return position;
}
}

View File

@ -0,0 +1,43 @@
/*
* Copyright 2014 Frank Asseg
*
* Licensed 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 com.l2jmobius.gameserver.util.exp4j.tokenizer;
/**
* represents a setVariable used in an expression
*/
public class VariableToken extends Token
{
private final String name;
/**
* Get the name of the setVariable
* @return the name
*/
public String getName()
{
return name;
}
/**
* Create a new instance
* @param name the name of the setVariable
*/
public VariableToken(String name)
{
super(TOKEN_VARIABLE);
this.name = name;
}
}

View File

@ -7,7 +7,7 @@
<listEntry value="1"/>
</listAttribute>
<stringAttribute key="org.eclipse.debug.core.source_locator_id" value="org.eclipse.jdt.launching.sourceLocator.JavaSourceLookupDirector"/>
<stringAttribute key="org.eclipse.debug.core.source_locator_memento" value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;sourceLookupDirector&gt;&#13;&#10;&lt;sourceContainers duplicates=&quot;false&quot;&gt;&#13;&#10;&lt;container memento=&quot;&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot; standalone=&amp;quot;no&amp;quot;?&amp;gt;&amp;#13;&amp;#10;&amp;lt;archive detectRoot=&amp;quot;true&amp;quot; path=&amp;quot;/L2J_Mobius_Classic/dist/libs/c3p0-0.9.5.2.jar&amp;quot;/&amp;gt;&amp;#13;&amp;#10;&quot; typeId=&quot;org.eclipse.debug.core.containerType.archive&quot;/&gt;&#13;&#10;&lt;container memento=&quot;&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot; standalone=&amp;quot;no&amp;quot;?&amp;gt;&amp;#13;&amp;#10;&amp;lt;archive detectRoot=&amp;quot;true&amp;quot; path=&amp;quot;/L2J_Mobius_Classic/dist/libs/ecj-4.4.2.jar&amp;quot;/&amp;gt;&amp;#13;&amp;#10;&quot; typeId=&quot;org.eclipse.debug.core.containerType.archive&quot;/&gt;&#13;&#10;&lt;container memento=&quot;&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot; standalone=&amp;quot;no&amp;quot;?&amp;gt;&amp;#13;&amp;#10;&amp;lt;archive detectRoot=&amp;quot;true&amp;quot; path=&amp;quot;/L2J_Mobius_Classic/dist/libs/exp4j-0.4.8.jar&amp;quot;/&amp;gt;&amp;#13;&amp;#10;&quot; typeId=&quot;org.eclipse.debug.core.containerType.archive&quot;/&gt;&#13;&#10;&lt;container memento=&quot;&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot; standalone=&amp;quot;no&amp;quot;?&amp;gt;&amp;#13;&amp;#10;&amp;lt;archive detectRoot=&amp;quot;true&amp;quot; path=&amp;quot;/L2J_Mobius_Classic/dist/libs/mail-1.5.2.jar&amp;quot;/&amp;gt;&amp;#13;&amp;#10;&quot; typeId=&quot;org.eclipse.debug.core.containerType.archive&quot;/&gt;&#13;&#10;&lt;container memento=&quot;&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot; standalone=&amp;quot;no&amp;quot;?&amp;gt;&amp;#13;&amp;#10;&amp;lt;archive detectRoot=&amp;quot;true&amp;quot; path=&amp;quot;/L2J_Mobius_Classic/dist/libs/mchange-commons-java-0.2.12.jar&amp;quot;/&amp;gt;&amp;#13;&amp;#10;&quot; typeId=&quot;org.eclipse.debug.core.containerType.archive&quot;/&gt;&#13;&#10;&lt;container memento=&quot;&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot; standalone=&amp;quot;no&amp;quot;?&amp;gt;&amp;#13;&amp;#10;&amp;lt;archive detectRoot=&amp;quot;true&amp;quot; path=&amp;quot;/L2J_Mobius_Classic/dist/libs/mysql-connector-java-5.1.43.jar&amp;quot;/&amp;gt;&amp;#13;&amp;#10;&quot; typeId=&quot;org.eclipse.debug.core.containerType.archive&quot;/&gt;&#13;&#10;&lt;container memento=&quot;&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot; standalone=&amp;quot;no&amp;quot;?&amp;gt;&amp;#13;&amp;#10;&amp;lt;archive detectRoot=&amp;quot;true&amp;quot; path=&amp;quot;/L2J_Mobius_Classic/dist/libs/netty-all-4.1.14.Final.jar&amp;quot;/&amp;gt;&amp;#13;&amp;#10;&quot; typeId=&quot;org.eclipse.debug.core.containerType.archive&quot;/&gt;&#13;&#10;&lt;container memento=&quot;&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot; standalone=&amp;quot;no&amp;quot;?&amp;gt;&amp;#13;&amp;#10;&amp;lt;default/&amp;gt;&amp;#13;&amp;#10;&quot; typeId=&quot;org.eclipse.debug.core.containerType.default&quot;/&gt;&#13;&#10;&lt;/sourceContainers&gt;&#13;&#10;&lt;/sourceLookupDirector&gt;&#13;&#10;"/>
<stringAttribute key="org.eclipse.debug.core.source_locator_memento" value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;sourceLookupDirector&gt;&#13;&#10;&lt;sourceContainers duplicates=&quot;false&quot;&gt;&#13;&#10;&lt;container memento=&quot;&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot; standalone=&amp;quot;no&amp;quot;?&amp;gt;&amp;#13;&amp;#10;&amp;lt;archive detectRoot=&amp;quot;true&amp;quot; path=&amp;quot;/L2J_Mobius_Classic/dist/libs/c3p0-0.9.5.2.jar&amp;quot;/&amp;gt;&amp;#13;&amp;#10;&quot; typeId=&quot;org.eclipse.debug.core.containerType.archive&quot;/&gt;&#13;&#10;&lt;container memento=&quot;&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot; standalone=&amp;quot;no&amp;quot;?&amp;gt;&amp;#13;&amp;#10;&amp;lt;archive detectRoot=&amp;quot;true&amp;quot; path=&amp;quot;/L2J_Mobius_Classic/dist/libs/ecj-4.4.2.jar&amp;quot;/&amp;gt;&amp;#13;&amp;#10;&quot; typeId=&quot;org.eclipse.debug.core.containerType.archive&quot;/&gt;&#13;&#10;&lt;container memento=&quot;&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot; standalone=&amp;quot;no&amp;quot;?&amp;gt;&amp;#13;&amp;#10;&amp;lt;archive detectRoot=&amp;quot;true&amp;quot; path=&amp;quot;/L2J_Mobius_Classic/dist/libs/mail-1.5.2.jar&amp;quot;/&amp;gt;&amp;#13;&amp;#10;&quot; typeId=&quot;org.eclipse.debug.core.containerType.archive&quot;/&gt;&#13;&#10;&lt;container memento=&quot;&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot; standalone=&amp;quot;no&amp;quot;?&amp;gt;&amp;#13;&amp;#10;&amp;lt;archive detectRoot=&amp;quot;true&amp;quot; path=&amp;quot;/L2J_Mobius_Classic/dist/libs/mchange-commons-java-0.2.12.jar&amp;quot;/&amp;gt;&amp;#13;&amp;#10;&quot; typeId=&quot;org.eclipse.debug.core.containerType.archive&quot;/&gt;&#13;&#10;&lt;container memento=&quot;&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot; standalone=&amp;quot;no&amp;quot;?&amp;gt;&amp;#13;&amp;#10;&amp;lt;archive detectRoot=&amp;quot;true&amp;quot; path=&amp;quot;/L2J_Mobius_Classic/dist/libs/mysql-connector-java-5.1.43.jar&amp;quot;/&amp;gt;&amp;#13;&amp;#10;&quot; typeId=&quot;org.eclipse.debug.core.containerType.archive&quot;/&gt;&#13;&#10;&lt;container memento=&quot;&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot; standalone=&amp;quot;no&amp;quot;?&amp;gt;&amp;#13;&amp;#10;&amp;lt;archive detectRoot=&amp;quot;true&amp;quot; path=&amp;quot;/L2J_Mobius_Classic/dist/libs/netty-all-4.1.14.Final.jar&amp;quot;/&amp;gt;&amp;#13;&amp;#10;&quot; typeId=&quot;org.eclipse.debug.core.containerType.archive&quot;/&gt;&#13;&#10;&lt;container memento=&quot;&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot; standalone=&amp;quot;no&amp;quot;?&amp;gt;&amp;#13;&amp;#10;&amp;lt;default/&amp;gt;&amp;#13;&amp;#10;&quot; typeId=&quot;org.eclipse.debug.core.containerType.default&quot;/&gt;&#13;&#10;&lt;/sourceContainers&gt;&#13;&#10;&lt;/sourceLookupDirector&gt;&#13;&#10;"/>
<listAttribute key="org.eclipse.debug.ui.favoriteGroups">
<listEntry value="org.eclipse.debug.ui.launchGroup.debug"/>
</listAttribute>

View File

@ -7,7 +7,7 @@
<listEntry value="1"/>
</listAttribute>
<stringAttribute key="org.eclipse.debug.core.source_locator_id" value="org.eclipse.jdt.launching.sourceLocator.JavaSourceLookupDirector"/>
<stringAttribute key="org.eclipse.debug.core.source_locator_memento" value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;sourceLookupDirector&gt;&#13;&#10;&lt;sourceContainers duplicates=&quot;false&quot;&gt;&#13;&#10;&lt;container memento=&quot;&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot; standalone=&amp;quot;no&amp;quot;?&amp;gt;&amp;#13;&amp;#10;&amp;lt;archive detectRoot=&amp;quot;true&amp;quot; path=&amp;quot;/L2J_Mobius_Classic/dist/libs/c3p0-0.9.5.2.jar&amp;quot;/&amp;gt;&amp;#13;&amp;#10;&quot; typeId=&quot;org.eclipse.debug.core.containerType.archive&quot;/&gt;&#13;&#10;&lt;container memento=&quot;&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot; standalone=&amp;quot;no&amp;quot;?&amp;gt;&amp;#13;&amp;#10;&amp;lt;archive detectRoot=&amp;quot;true&amp;quot; path=&amp;quot;/L2J_Mobius_Classic/dist/libs/ecj-4.4.2.jar&amp;quot;/&amp;gt;&amp;#13;&amp;#10;&quot; typeId=&quot;org.eclipse.debug.core.containerType.archive&quot;/&gt;&#13;&#10;&lt;container memento=&quot;&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot; standalone=&amp;quot;no&amp;quot;?&amp;gt;&amp;#13;&amp;#10;&amp;lt;archive detectRoot=&amp;quot;true&amp;quot; path=&amp;quot;/L2J_Mobius_Classic/dist/libs/exp4j-0.4.8.jar&amp;quot;/&amp;gt;&amp;#13;&amp;#10;&quot; typeId=&quot;org.eclipse.debug.core.containerType.archive&quot;/&gt;&#13;&#10;&lt;container memento=&quot;&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot; standalone=&amp;quot;no&amp;quot;?&amp;gt;&amp;#13;&amp;#10;&amp;lt;archive detectRoot=&amp;quot;true&amp;quot; path=&amp;quot;/L2J_Mobius_Classic/dist/libs/mail-1.5.2.jar&amp;quot;/&amp;gt;&amp;#13;&amp;#10;&quot; typeId=&quot;org.eclipse.debug.core.containerType.archive&quot;/&gt;&#13;&#10;&lt;container memento=&quot;&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot; standalone=&amp;quot;no&amp;quot;?&amp;gt;&amp;#13;&amp;#10;&amp;lt;archive detectRoot=&amp;quot;true&amp;quot; path=&amp;quot;/L2J_Mobius_Classic/dist/libs/mchange-commons-java-0.2.12.jar&amp;quot;/&amp;gt;&amp;#13;&amp;#10;&quot; typeId=&quot;org.eclipse.debug.core.containerType.archive&quot;/&gt;&#13;&#10;&lt;container memento=&quot;&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot; standalone=&amp;quot;no&amp;quot;?&amp;gt;&amp;#13;&amp;#10;&amp;lt;archive detectRoot=&amp;quot;true&amp;quot; path=&amp;quot;/L2J_Mobius_Classic/dist/libs/mysql-connector-java-5.1.43.jar&amp;quot;/&amp;gt;&amp;#13;&amp;#10;&quot; typeId=&quot;org.eclipse.debug.core.containerType.archive&quot;/&gt;&#13;&#10;&lt;container memento=&quot;&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot; standalone=&amp;quot;no&amp;quot;?&amp;gt;&amp;#13;&amp;#10;&amp;lt;archive detectRoot=&amp;quot;true&amp;quot; path=&amp;quot;/L2J_Mobius_Classic/dist/libs/netty-all-4.1.14.Final.jar&amp;quot;/&amp;gt;&amp;#13;&amp;#10;&quot; typeId=&quot;org.eclipse.debug.core.containerType.archive&quot;/&gt;&#13;&#10;&lt;container memento=&quot;&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot; standalone=&amp;quot;no&amp;quot;?&amp;gt;&amp;#13;&amp;#10;&amp;lt;default/&amp;gt;&amp;#13;&amp;#10;&quot; typeId=&quot;org.eclipse.debug.core.containerType.default&quot;/&gt;&#13;&#10;&lt;/sourceContainers&gt;&#13;&#10;&lt;/sourceLookupDirector&gt;&#13;&#10;"/>
<stringAttribute key="org.eclipse.debug.core.source_locator_memento" value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;sourceLookupDirector&gt;&#13;&#10;&lt;sourceContainers duplicates=&quot;false&quot;&gt;&#13;&#10;&lt;container memento=&quot;&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot; standalone=&amp;quot;no&amp;quot;?&amp;gt;&amp;#13;&amp;#10;&amp;lt;archive detectRoot=&amp;quot;true&amp;quot; path=&amp;quot;/L2J_Mobius_Classic/dist/libs/c3p0-0.9.5.2.jar&amp;quot;/&amp;gt;&amp;#13;&amp;#10;&quot; typeId=&quot;org.eclipse.debug.core.containerType.archive&quot;/&gt;&#13;&#10;&lt;container memento=&quot;&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot; standalone=&amp;quot;no&amp;quot;?&amp;gt;&amp;#13;&amp;#10;&amp;lt;archive detectRoot=&amp;quot;true&amp;quot; path=&amp;quot;/L2J_Mobius_Classic/dist/libs/ecj-4.4.2.jar&amp;quot;/&amp;gt;&amp;#13;&amp;#10;&quot; typeId=&quot;org.eclipse.debug.core.containerType.archive&quot;/&gt;&#13;&#10;&lt;container memento=&quot;&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot; standalone=&amp;quot;no&amp;quot;?&amp;gt;&amp;#13;&amp;#10;&amp;lt;archive detectRoot=&amp;quot;true&amp;quot; path=&amp;quot;/L2J_Mobius_Classic/dist/libs/mail-1.5.2.jar&amp;quot;/&amp;gt;&amp;#13;&amp;#10;&quot; typeId=&quot;org.eclipse.debug.core.containerType.archive&quot;/&gt;&#13;&#10;&lt;container memento=&quot;&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot; standalone=&amp;quot;no&amp;quot;?&amp;gt;&amp;#13;&amp;#10;&amp;lt;archive detectRoot=&amp;quot;true&amp;quot; path=&amp;quot;/L2J_Mobius_Classic/dist/libs/mchange-commons-java-0.2.12.jar&amp;quot;/&amp;gt;&amp;#13;&amp;#10;&quot; typeId=&quot;org.eclipse.debug.core.containerType.archive&quot;/&gt;&#13;&#10;&lt;container memento=&quot;&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot; standalone=&amp;quot;no&amp;quot;?&amp;gt;&amp;#13;&amp;#10;&amp;lt;archive detectRoot=&amp;quot;true&amp;quot; path=&amp;quot;/L2J_Mobius_Classic/dist/libs/mysql-connector-java-5.1.43.jar&amp;quot;/&amp;gt;&amp;#13;&amp;#10;&quot; typeId=&quot;org.eclipse.debug.core.containerType.archive&quot;/&gt;&#13;&#10;&lt;container memento=&quot;&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot; standalone=&amp;quot;no&amp;quot;?&amp;gt;&amp;#13;&amp;#10;&amp;lt;archive detectRoot=&amp;quot;true&amp;quot; path=&amp;quot;/L2J_Mobius_Classic/dist/libs/netty-all-4.1.14.Final.jar&amp;quot;/&amp;gt;&amp;#13;&amp;#10;&quot; typeId=&quot;org.eclipse.debug.core.containerType.archive&quot;/&gt;&#13;&#10;&lt;container memento=&quot;&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot; standalone=&amp;quot;no&amp;quot;?&amp;gt;&amp;#13;&amp;#10;&amp;lt;default/&amp;gt;&amp;#13;&amp;#10;&quot; typeId=&quot;org.eclipse.debug.core.containerType.default&quot;/&gt;&#13;&#10;&lt;/sourceContainers&gt;&#13;&#10;&lt;/sourceLookupDirector&gt;&#13;&#10;"/>
<listAttribute key="org.eclipse.debug.ui.favoriteGroups">
<listEntry value="org.eclipse.debug.ui.launchGroup.debug"/>
</listAttribute>

View File

@ -3,7 +3,6 @@
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8"/>
<classpathentry kind="lib" path="dist/libs/c3p0-0.9.5.2.jar"/>
<classpathentry kind="lib" path="dist/libs/ecj-4.4.2.jar"/>
<classpathentry kind="lib" path="dist/libs/exp4j-0.4.8.jar"/>
<classpathentry kind="lib" path="dist/libs/mail-1.5.2.jar"/>
<classpathentry kind="lib" path="dist/libs/mchange-commons-java-0.2.12.jar"/>
<classpathentry kind="lib" path="dist/libs/mysql-connector-java-5.1.43.jar"/>

Binary file not shown.

View File

@ -45,8 +45,7 @@ import com.l2jmobius.gameserver.model.skills.EffectScope;
import com.l2jmobius.gameserver.model.skills.ISkillCondition;
import com.l2jmobius.gameserver.model.skills.Skill;
import com.l2jmobius.gameserver.model.skills.SkillConditionScope;
import net.objecthunter.exp4j.ExpressionBuilder;
import com.l2jmobius.gameserver.util.exp4j.ExpressionBuilder;
/**
* Skill data parser.

View File

@ -0,0 +1,86 @@
/*
* Copyright 2015 Federico Vera
*
* Licensed 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 com.l2jmobius.gameserver.util.exp4j;
import java.util.EmptyStackException;
/**
* Simple double stack using a double array as data storage
* @author Federico Vera (dktcoding [at] gmail)
*/
class ArrayStack
{
private double[] data;
private int idx;
ArrayStack()
{
this(5);
}
ArrayStack(int initialCapacity)
{
if (initialCapacity <= 0)
{
throw new IllegalArgumentException("Stack's capacity must be positive");
}
data = new double[initialCapacity];
idx = -1;
}
void push(double value)
{
if ((idx + 1) == data.length)
{
double[] temp = new double[(int) (data.length * 1.2) + 1];
System.arraycopy(data, 0, temp, 0, data.length);
data = temp;
}
data[++idx] = value;
}
double peek()
{
if (idx == -1)
{
throw new EmptyStackException();
}
return data[idx];
}
double pop()
{
if (idx == -1)
{
throw new EmptyStackException();
}
return data[idx--];
}
boolean isEmpty()
{
return idx == -1;
}
int size()
{
return idx + 1;
}
}

View File

@ -0,0 +1,263 @@
/*
* Copyright 2014 Frank Asseg
*
* Licensed 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 com.l2jmobius.gameserver.util.exp4j;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;
import com.l2jmobius.gameserver.util.exp4j.function.Function;
import com.l2jmobius.gameserver.util.exp4j.function.Functions;
import com.l2jmobius.gameserver.util.exp4j.operator.Operator;
import com.l2jmobius.gameserver.util.exp4j.tokenizer.FunctionToken;
import com.l2jmobius.gameserver.util.exp4j.tokenizer.NumberToken;
import com.l2jmobius.gameserver.util.exp4j.tokenizer.OperatorToken;
import com.l2jmobius.gameserver.util.exp4j.tokenizer.Token;
import com.l2jmobius.gameserver.util.exp4j.tokenizer.VariableToken;
public class Expression
{
private final Token[] tokens;
private final Map<String, Double> variables;
private final Set<String> userFunctionNames;
private static Map<String, Double> createDefaultVariables()
{
final Map<String, Double> vars = new HashMap<>(4);
vars.put("pi", Math.PI);
vars.put("Ο€", Math.PI);
vars.put("Ο†", 1.61803398874d);
vars.put("e", Math.E);
return vars;
}
/**
* Creates a new expression that is a copy of the existing one.
* @param existing the expression to copy
*/
public Expression(final Expression existing)
{
tokens = Arrays.copyOf(existing.tokens, existing.tokens.length);
variables = new HashMap<>();
variables.putAll(existing.variables);
userFunctionNames = new HashSet<>(existing.userFunctionNames);
}
Expression(final Token[] tokens)
{
this.tokens = tokens;
variables = createDefaultVariables();
userFunctionNames = Collections.<String> emptySet();
}
Expression(final Token[] tokens, Set<String> userFunctionNames)
{
this.tokens = tokens;
variables = createDefaultVariables();
this.userFunctionNames = userFunctionNames;
}
public Expression setVariable(final String name, final double value)
{
checkVariableName(name);
variables.put(name, Double.valueOf(value));
return this;
}
private void checkVariableName(String name)
{
if (userFunctionNames.contains(name) || (Functions.getBuiltinFunction(name) != null))
{
throw new IllegalArgumentException("The variable name '" + name + "' is invalid. Since there exists a function with the same name");
}
}
public Expression setVariables(Map<String, Double> variables)
{
for (Map.Entry<String, Double> v : variables.entrySet())
{
setVariable(v.getKey(), v.getValue());
}
return this;
}
public Set<String> getVariableNames()
{
final Set<String> variables = new HashSet<>();
for (final Token t : tokens)
{
if (t.getType() == Token.TOKEN_VARIABLE)
{
variables.add(((VariableToken) t).getName());
}
}
return variables;
}
public ValidationResult validate(boolean checkVariablesSet)
{
final List<String> errors = new ArrayList<>(0);
if (checkVariablesSet)
{
/* check that all vars have a value set */
for (final Token t : tokens)
{
if (t.getType() == Token.TOKEN_VARIABLE)
{
final String var = ((VariableToken) t).getName();
if (!variables.containsKey(var))
{
errors.add("The setVariable '" + var + "' has not been set");
}
}
}
}
/*
* Check if the number of operands, functions and operators match. The idea is to increment a counter for operands and decrease it for operators. When a function occurs the number of available arguments has to be greater than or equals to the function's expected number of arguments. The
* count has to be larger than 1 at all times and exactly 1 after all tokens have been processed
*/
int count = 0;
for (Token tok : tokens)
{
switch (tok.getType())
{
case Token.TOKEN_NUMBER:
case Token.TOKEN_VARIABLE:
count++;
break;
case Token.TOKEN_FUNCTION:
final Function func = ((FunctionToken) tok).getFunction();
final int argsNum = func.getNumArguments();
if (argsNum > count)
{
errors.add("Not enough arguments for '" + func.getName() + "'");
}
if (argsNum > 1)
{
count -= argsNum - 1;
}
else if (argsNum == 0)
{
// see https://github.com/fasseg/exp4j/issues/59
count++;
}
break;
case Token.TOKEN_OPERATOR:
Operator op = ((OperatorToken) tok).getOperator();
if (op.getNumOperands() == 2)
{
count--;
}
break;
}
if (count < 1)
{
errors.add("Too many operators");
return new ValidationResult(false, errors);
}
}
if (count > 1)
{
errors.add("Too many operands");
}
return errors.size() == 0 ? ValidationResult.SUCCESS : new ValidationResult(false, errors);
}
public ValidationResult validate()
{
return validate(true);
}
public Future<Double> evaluateAsync(ExecutorService executor)
{
return executor.submit(() -> evaluate());
}
public double evaluate()
{
final ArrayStack output = new ArrayStack();
for (Token t : tokens)
{
if (t.getType() == Token.TOKEN_NUMBER)
{
output.push(((NumberToken) t).getValue());
}
else if (t.getType() == Token.TOKEN_VARIABLE)
{
final String name = ((VariableToken) t).getName();
final Double value = variables.get(name);
if (value == null)
{
throw new IllegalArgumentException("No value has been set for the setVariable '" + name + "'.");
}
output.push(value);
}
else if (t.getType() == Token.TOKEN_OPERATOR)
{
OperatorToken op = (OperatorToken) t;
if (output.size() < op.getOperator().getNumOperands())
{
throw new IllegalArgumentException("Invalid number of operands available for '" + op.getOperator().getSymbol() + "' operator");
}
if (op.getOperator().getNumOperands() == 2)
{
/* pop the operands and push the result of the operation */
double rightArg = output.pop();
double leftArg = output.pop();
output.push(op.getOperator().apply(leftArg, rightArg));
}
else if (op.getOperator().getNumOperands() == 1)
{
/* pop the operand and push the result of the operation */
double arg = output.pop();
output.push(op.getOperator().apply(arg));
}
}
else if (t.getType() == Token.TOKEN_FUNCTION)
{
FunctionToken func = (FunctionToken) t;
final int numArguments = func.getFunction().getNumArguments();
if (output.size() < numArguments)
{
throw new IllegalArgumentException("Invalid number of arguments available for '" + func.getFunction().getName() + "' function");
}
/* collect the arguments from the stack */
double[] args = new double[numArguments];
for (int j = numArguments - 1; j >= 0; j--)
{
args[j] = output.pop();
}
output.push(func.getFunction().apply(args));
}
}
if (output.size() > 1)
{
throw new IllegalArgumentException("Invalid number of items on the output queue. Might be caused by an invalid number of arguments for a function.");
}
return output.pop();
}
}

View File

@ -0,0 +1,218 @@
/*
* Copyright 2014 Frank Asseg
*
* Licensed 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 com.l2jmobius.gameserver.util.exp4j;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import com.l2jmobius.gameserver.util.exp4j.function.Function;
import com.l2jmobius.gameserver.util.exp4j.function.Functions;
import com.l2jmobius.gameserver.util.exp4j.operator.Operator;
import com.l2jmobius.gameserver.util.exp4j.shuntingyard.ShuntingYard;
/**
* Factory class for {@link Expression} instances. This class is the main API entrypoint. Users should create new {@link Expression} instances using this factory class.
*/
public class ExpressionBuilder
{
private final String expression;
private final Map<String, Function> userFunctions;
private final Map<String, Operator> userOperators;
private final Set<String> variableNames;
private boolean implicitMultiplication = true;
/**
* Create a new ExpressionBuilder instance and initialize it with a given expression string.
* @param expression the expression to be parsed
*/
public ExpressionBuilder(String expression)
{
if ((expression == null) || (expression.trim().length() == 0))
{
throw new IllegalArgumentException("Expression can not be empty");
}
this.expression = expression;
userOperators = new HashMap<>(4);
userFunctions = new HashMap<>(4);
variableNames = new HashSet<>(4);
}
/**
* Add a {@link com.l2jmobius.gameserver.util.exp4j.function.Function} implementation available for use in the expression
* @param function the custom {@link com.l2jmobius.gameserver.util.exp4j.function.Function} implementation that should be available for use in the expression.
* @return the ExpressionBuilder instance
*/
public ExpressionBuilder function(Function function)
{
userFunctions.put(function.getName(), function);
return this;
}
/**
* Add multiple {@link com.l2jmobius.gameserver.util.exp4j.function.Function} implementations available for use in the expression
* @param functions the custom {@link com.l2jmobius.gameserver.util.exp4j.function.Function} implementations
* @return the ExpressionBuilder instance
*/
public ExpressionBuilder functions(Function... functions)
{
for (Function f : functions)
{
userFunctions.put(f.getName(), f);
}
return this;
}
/**
* Add multiple {@link com.l2jmobius.gameserver.util.exp4j.function.Function} implementations available for use in the expression
* @param functions A {@link java.util.List} of custom {@link com.l2jmobius.gameserver.util.exp4j.function.Function} implementations
* @return the ExpressionBuilder instance
*/
public ExpressionBuilder functions(List<Function> functions)
{
for (Function f : functions)
{
userFunctions.put(f.getName(), f);
}
return this;
}
/**
* Declare variable names used in the expression
* @param variableNames the variables used in the expression
* @return the ExpressionBuilder instance
*/
public ExpressionBuilder variables(Set<String> variableNames)
{
this.variableNames.addAll(variableNames);
return this;
}
/**
* Declare variable names used in the expression
* @param variableNames the variables used in the expression
* @return the ExpressionBuilder instance
*/
public ExpressionBuilder variables(String... variableNames)
{
Collections.addAll(this.variableNames, variableNames);
return this;
}
/**
* Declare a variable used in the expression
* @param variableName the variable used in the expression
* @return the ExpressionBuilder instance
*/
public ExpressionBuilder variable(String variableName)
{
variableNames.add(variableName);
return this;
}
public ExpressionBuilder implicitMultiplication(boolean enabled)
{
implicitMultiplication = enabled;
return this;
}
/**
* Add an {@link com.l2jmobius.gameserver.util.exp4j.operator.Operator} which should be available for use in the expression
* @param operator the custom {@link com.l2jmobius.gameserver.util.exp4j.operator.Operator} to add
* @return the ExpressionBuilder instance
*/
public ExpressionBuilder operator(Operator operator)
{
checkOperatorSymbol(operator);
userOperators.put(operator.getSymbol(), operator);
return this;
}
private void checkOperatorSymbol(Operator op)
{
String name = op.getSymbol();
for (char ch : name.toCharArray())
{
if (!Operator.isAllowedOperatorChar(ch))
{
throw new IllegalArgumentException("The operator symbol '" + name + "' is invalid");
}
}
}
/**
* Add multiple {@link com.l2jmobius.gameserver.util.exp4j.operator.Operator} implementations which should be available for use in the expression
* @param operators the set of custom {@link com.l2jmobius.gameserver.util.exp4j.operator.Operator} implementations to add
* @return the ExpressionBuilder instance
*/
public ExpressionBuilder operator(Operator... operators)
{
for (Operator o : operators)
{
this.operator(o);
}
return this;
}
/**
* Add multiple {@link com.l2jmobius.gameserver.util.exp4j.operator.Operator} implementations which should be available for use in the expression
* @param operators the {@link java.util.List} of custom {@link com.l2jmobius.gameserver.util.exp4j.operator.Operator} implementations to add
* @return the ExpressionBuilder instance
*/
public ExpressionBuilder operator(List<Operator> operators)
{
for (Operator o : operators)
{
this.operator(o);
}
return this;
}
/**
* Build the {@link Expression} instance using the custom operators and functions set.
* @return an {@link Expression} instance which can be used to evaluate the result of the expression
*/
public Expression build()
{
if (expression.length() == 0)
{
throw new IllegalArgumentException("The expression can not be empty");
}
/* set the contants' varibale names */
variableNames.add("pi");
variableNames.add("Ο€");
variableNames.add("e");
variableNames.add("Ο†");
/* Check if there are duplicate vars/functions */
for (String var : variableNames)
{
if ((Functions.getBuiltinFunction(var) != null) || userFunctions.containsKey(var))
{
throw new IllegalArgumentException("A variable can not have the same name as a function [" + var + "]");
}
}
return new Expression(ShuntingYard.convertToRPN(expression, userFunctions, userOperators, variableNames, implicitMultiplication), userFunctions.keySet());
}
}

View File

@ -0,0 +1,61 @@
/*
* Copyright 2014 Frank Asseg
*
* Licensed 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 com.l2jmobius.gameserver.util.exp4j;
import java.util.List;
/**
* Contains the validation result for a given {@link Expression}
*/
public class ValidationResult
{
private final boolean valid;
private final List<String> errors;
/**
* Create a new instance
* @param valid Whether the validation of the expression was successful
* @param errors The list of errors returned if the validation was unsuccessful
*/
public ValidationResult(boolean valid, List<String> errors)
{
this.valid = valid;
this.errors = errors;
}
/**
* Check if an expression has been validated successfully
* @return true if the validation was successful, false otherwise
*/
public boolean isValid()
{
return valid;
}
/**
* Get the list of errors describing the issues while validating the expression
* @return The List of errors
*/
public List<String> getErrors()
{
return errors;
}
/**
* A static class representing a successful validation result
*/
public static final ValidationResult SUCCESS = new ValidationResult(true, null);
}

View File

@ -0,0 +1,132 @@
/*
* Copyright 2014 Frank Asseg
*
* Licensed 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 com.l2jmobius.gameserver.util.exp4j.function;
/**
* A class representing a Function which can be used in an expression
*/
public abstract class Function
{
protected final String name;
protected final int numArguments;
/**
* Create a new Function with a given name and number of arguments
* @param name the name of the Function
* @param numArguments the number of arguments the function takes
*/
public Function(String name, int numArguments)
{
if (numArguments < 0)
{
throw new IllegalArgumentException("The number of function arguments can not be less than 0 for '" + name + "'");
}
if (!isValidFunctionName(name))
{
throw new IllegalArgumentException("The function name '" + name + "' is invalid");
}
this.name = name;
this.numArguments = numArguments;
}
/**
* Create a new Function with a given name that takes a single argument
* @param name the name of the Function
*/
public Function(String name)
{
this(name, 1);
}
/**
* Get the name of the Function
* @return the name
*/
public String getName()
{
return name;
}
/**
* Get the number of arguments for this function
* @return the number of arguments
*/
public int getNumArguments()
{
return numArguments;
}
/**
* Method that does the actual calculation of the function value given the arguments
* @param args the set of arguments used for calculating the function
* @return the result of the function evaluation
*/
public abstract double apply(double... args);
/**
* Get the set of characters which are allowed for use in Function names.
* @return the set of characters allowed
* @deprecated since 0.4.5 All unicode letters are allowed to be used in function names since 0.4.3. This API Function can be safely ignored. Checks for function name validity can be done using Character.isLetter() et al.
*/
@Deprecated
public static char[] getAllowedFunctionCharacters()
{
char[] chars = new char[53];
int count = 0;
for (int i = 65; i < 91; i++)
{
chars[count++] = (char) i;
}
for (int i = 97; i < 123; i++)
{
chars[count++] = (char) i;
}
chars[count] = '_';
return chars;
}
public static boolean isValidFunctionName(final String name)
{
if (name == null)
{
return false;
}
final int size = name.length();
if (size == 0)
{
return false;
}
for (int i = 0; i < size; i++)
{
final char c = name.charAt(i);
if (Character.isLetter(c) || (c == '_'))
{
continue;
}
else if (Character.isDigit(c) && (i > 0))
{
continue;
}
return false;
}
return true;
}
}

View File

@ -0,0 +1,356 @@
/*
* Copyright 2014 Frank Asseg
*
* Licensed 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 com.l2jmobius.gameserver.util.exp4j.function;
/**
* Class representing the builtin functions available for use in expressions
*/
public class Functions
{
private static final int INDEX_SIN = 0;
private static final int INDEX_COS = 1;
private static final int INDEX_TAN = 2;
private static final int INDEX_COT = 3;
private static final int INDEX_LOG = 4;
private static final int INDEX_LOG1P = 5;
private static final int INDEX_ABS = 6;
private static final int INDEX_ACOS = 7;
private static final int INDEX_ASIN = 8;
private static final int INDEX_ATAN = 9;
private static final int INDEX_CBRT = 10;
private static final int INDEX_CEIL = 11;
private static final int INDEX_FLOOR = 12;
private static final int INDEX_SINH = 13;
private static final int INDEX_SQRT = 14;
private static final int INDEX_TANH = 15;
private static final int INDEX_COSH = 16;
private static final int INDEX_POW = 17;
private static final int INDEX_EXP = 18;
private static final int INDEX_EXPM1 = 19;
private static final int INDEX_LOG10 = 20;
private static final int INDEX_LOG2 = 21;
private static final int INDEX_SGN = 22;
private static final Function[] builtinFunctions = new Function[23];
static
{
builtinFunctions[INDEX_SIN] = new Function("sin")
{
@Override
public double apply(double... args)
{
return Math.sin(args[0]);
}
};
builtinFunctions[INDEX_COS] = new Function("cos")
{
@Override
public double apply(double... args)
{
return Math.cos(args[0]);
}
};
builtinFunctions[INDEX_TAN] = new Function("tan")
{
@Override
public double apply(double... args)
{
return Math.tan(args[0]);
}
};
builtinFunctions[INDEX_COT] = new Function("cot")
{
@Override
public double apply(double... args)
{
double tan = Math.tan(args[0]);
if (tan == 0d)
{
throw new ArithmeticException("Division by zero in cotangent!");
}
return 1d / Math.tan(args[0]);
}
};
builtinFunctions[INDEX_LOG] = new Function("log")
{
@Override
public double apply(double... args)
{
return Math.log(args[0]);
}
};
builtinFunctions[INDEX_LOG2] = new Function("log2")
{
@Override
public double apply(double... args)
{
return Math.log(args[0]) / Math.log(2d);
}
};
builtinFunctions[INDEX_LOG10] = new Function("log10")
{
@Override
public double apply(double... args)
{
return Math.log10(args[0]);
}
};
builtinFunctions[INDEX_LOG1P] = new Function("log1p")
{
@Override
public double apply(double... args)
{
return Math.log1p(args[0]);
}
};
builtinFunctions[INDEX_ABS] = new Function("abs")
{
@Override
public double apply(double... args)
{
return Math.abs(args[0]);
}
};
builtinFunctions[INDEX_ACOS] = new Function("acos")
{
@Override
public double apply(double... args)
{
return Math.acos(args[0]);
}
};
builtinFunctions[INDEX_ASIN] = new Function("asin")
{
@Override
public double apply(double... args)
{
return Math.asin(args[0]);
}
};
builtinFunctions[INDEX_ATAN] = new Function("atan")
{
@Override
public double apply(double... args)
{
return Math.atan(args[0]);
}
};
builtinFunctions[INDEX_CBRT] = new Function("cbrt")
{
@Override
public double apply(double... args)
{
return Math.cbrt(args[0]);
}
};
builtinFunctions[INDEX_FLOOR] = new Function("floor")
{
@Override
public double apply(double... args)
{
return Math.floor(args[0]);
}
};
builtinFunctions[INDEX_SINH] = new Function("sinh")
{
@Override
public double apply(double... args)
{
return Math.sinh(args[0]);
}
};
builtinFunctions[INDEX_SQRT] = new Function("sqrt")
{
@Override
public double apply(double... args)
{
return Math.sqrt(args[0]);
}
};
builtinFunctions[INDEX_TANH] = new Function("tanh")
{
@Override
public double apply(double... args)
{
return Math.tanh(args[0]);
}
};
builtinFunctions[INDEX_COSH] = new Function("cosh")
{
@Override
public double apply(double... args)
{
return Math.cosh(args[0]);
}
};
builtinFunctions[INDEX_CEIL] = new Function("ceil")
{
@Override
public double apply(double... args)
{
return Math.ceil(args[0]);
}
};
builtinFunctions[INDEX_POW] = new Function("pow", 2)
{
@Override
public double apply(double... args)
{
return Math.pow(args[0], args[1]);
}
};
builtinFunctions[INDEX_EXP] = new Function("exp", 1)
{
@Override
public double apply(double... args)
{
return Math.exp(args[0]);
}
};
builtinFunctions[INDEX_EXPM1] = new Function("expm1", 1)
{
@Override
public double apply(double... args)
{
return Math.expm1(args[0]);
}
};
builtinFunctions[INDEX_SGN] = new Function("signum", 1)
{
@Override
public double apply(double... args)
{
if (args[0] > 0)
{
return 1;
}
else if (args[0] < 0)
{
return -1;
}
else
{
return 0;
}
}
};
}
/**
* Get the builtin function for a given name
* @param name te name of the function
* @return a Function instance
*/
public static Function getBuiltinFunction(final String name)
{
if (name.equals("sin"))
{
return builtinFunctions[INDEX_SIN];
}
else if (name.equals("cos"))
{
return builtinFunctions[INDEX_COS];
}
else if (name.equals("tan"))
{
return builtinFunctions[INDEX_TAN];
}
else if (name.equals("cot"))
{
return builtinFunctions[INDEX_COT];
}
else if (name.equals("asin"))
{
return builtinFunctions[INDEX_ASIN];
}
else if (name.equals("acos"))
{
return builtinFunctions[INDEX_ACOS];
}
else if (name.equals("atan"))
{
return builtinFunctions[INDEX_ATAN];
}
else if (name.equals("sinh"))
{
return builtinFunctions[INDEX_SINH];
}
else if (name.equals("cosh"))
{
return builtinFunctions[INDEX_COSH];
}
else if (name.equals("tanh"))
{
return builtinFunctions[INDEX_TANH];
}
else if (name.equals("abs"))
{
return builtinFunctions[INDEX_ABS];
}
else if (name.equals("log"))
{
return builtinFunctions[INDEX_LOG];
}
else if (name.equals("log10"))
{
return builtinFunctions[INDEX_LOG10];
}
else if (name.equals("log2"))
{
return builtinFunctions[INDEX_LOG2];
}
else if (name.equals("log1p"))
{
return builtinFunctions[INDEX_LOG1P];
}
else if (name.equals("ceil"))
{
return builtinFunctions[INDEX_CEIL];
}
else if (name.equals("floor"))
{
return builtinFunctions[INDEX_FLOOR];
}
else if (name.equals("sqrt"))
{
return builtinFunctions[INDEX_SQRT];
}
else if (name.equals("cbrt"))
{
return builtinFunctions[INDEX_CBRT];
}
else if (name.equals("pow"))
{
return builtinFunctions[INDEX_POW];
}
else if (name.equals("exp"))
{
return builtinFunctions[INDEX_EXP];
}
else if (name.equals("expm1"))
{
return builtinFunctions[INDEX_EXPM1];
}
else if (name.equals("signum"))
{
return builtinFunctions[INDEX_SGN];
}
else
{
return null;
}
}
}

View File

@ -0,0 +1,161 @@
/*
* Copyright 2014 Frank Asseg
*
* Licensed 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 com.l2jmobius.gameserver.util.exp4j.operator;
/**
* Class representing operators that can be used in an expression
*/
public abstract class Operator
{
/**
* The precedence value for the addition operation
*/
public static final int PRECEDENCE_ADDITION = 500;
/**
* The precedence value for the subtraction operation
*/
public static final int PRECEDENCE_SUBTRACTION = PRECEDENCE_ADDITION;
/**
* The precedence value for the multiplication operation
*/
public static final int PRECEDENCE_MULTIPLICATION = 1000;
/**
* The precedence value for the division operation
*/
public static final int PRECEDENCE_DIVISION = PRECEDENCE_MULTIPLICATION;
/**
* The precedence value for the modulo operation
*/
public static final int PRECEDENCE_MODULO = PRECEDENCE_DIVISION;
/**
* The precedence value for the power operation
*/
public static final int PRECEDENCE_POWER = 10000;
/**
* The precedence value for the unary minus operation
*/
public static final int PRECEDENCE_UNARY_MINUS = 5000;
/**
* The precedence value for the unary plus operation
*/
public static final int PRECEDENCE_UNARY_PLUS = PRECEDENCE_UNARY_MINUS;
/**
* The set of allowed operator chars
*/
public static final char[] ALLOWED_OPERATOR_CHARS =
{
'+',
'-',
'*',
'/',
'%',
'^',
'!',
'#',
'§',
'$',
'&',
';',
':',
'~',
'<',
'>',
'|',
'='
};
protected final int numOperands;
protected final boolean leftAssociative;
protected final String symbol;
protected final int precedence;
/**
* Create a new operator for use in expressions
* @param symbol the symbol of the operator
* @param numberOfOperands the number of operands the operator takes (1 or 2)
* @param leftAssociative set to true if the operator is left associative, false if it is right associative
* @param precedence the precedence value of the operator
*/
public Operator(String symbol, int numberOfOperands, boolean leftAssociative, int precedence)
{
super();
this.numOperands = numberOfOperands;
this.leftAssociative = leftAssociative;
this.symbol = symbol;
this.precedence = precedence;
}
/**
* Check if a character is an allowed operator char
* @param ch the char to check
* @return true if the char is allowed an an operator symbol, false otherwise
*/
public static boolean isAllowedOperatorChar(char ch)
{
for (char allowed : ALLOWED_OPERATOR_CHARS)
{
if (ch == allowed)
{
return true;
}
}
return false;
}
/**
* Check if the operator is left associative
* @return true os the operator is left associative, false otherwise
*/
public boolean isLeftAssociative()
{
return leftAssociative;
}
/**
* Check the precedence value for the operator
* @return the precedence value
*/
public int getPrecedence()
{
return precedence;
}
/**
* Apply the operation on the given operands
* @param args the operands for the operation
* @return the calculated result of the operation
*/
public abstract double apply(double... args);
/**
* Get the operator symbol
* @return the symbol
*/
public String getSymbol()
{
return symbol;
}
/**
* Get the number of operands
* @return the number of operands
*/
public int getNumOperands()
{
return numOperands;
}
}

View File

@ -0,0 +1,135 @@
/*
* Copyright 2014 Frank Asseg
*
* Licensed 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 com.l2jmobius.gameserver.util.exp4j.operator;
public abstract class Operators
{
private static final int INDEX_ADDITION = 0;
private static final int INDEX_SUBTRACTION = 1;
private static final int INDEX_MUTLIPLICATION = 2;
private static final int INDEX_DIVISION = 3;
private static final int INDEX_POWER = 4;
private static final int INDEX_MODULO = 5;
private static final int INDEX_UNARYMINUS = 6;
private static final int INDEX_UNARYPLUS = 7;
private static final Operator[] builtinOperators = new Operator[8];
static
{
builtinOperators[INDEX_ADDITION] = new Operator("+", 2, true, Operator.PRECEDENCE_ADDITION)
{
@Override
public double apply(final double... args)
{
return args[0] + args[1];
}
};
builtinOperators[INDEX_SUBTRACTION] = new Operator("-", 2, true, Operator.PRECEDENCE_ADDITION)
{
@Override
public double apply(final double... args)
{
return args[0] - args[1];
}
};
builtinOperators[INDEX_UNARYMINUS] = new Operator("-", 1, false, Operator.PRECEDENCE_UNARY_MINUS)
{
@Override
public double apply(final double... args)
{
return -args[0];
}
};
builtinOperators[INDEX_UNARYPLUS] = new Operator("+", 1, false, Operator.PRECEDENCE_UNARY_PLUS)
{
@Override
public double apply(final double... args)
{
return args[0];
}
};
builtinOperators[INDEX_MUTLIPLICATION] = new Operator("*", 2, true, Operator.PRECEDENCE_MULTIPLICATION)
{
@Override
public double apply(final double... args)
{
return args[0] * args[1];
}
};
builtinOperators[INDEX_DIVISION] = new Operator("/", 2, true, Operator.PRECEDENCE_DIVISION)
{
@Override
public double apply(final double... args)
{
if (args[1] == 0d)
{
throw new ArithmeticException("Division by zero!");
}
return args[0] / args[1];
}
};
builtinOperators[INDEX_POWER] = new Operator("^", 2, false, Operator.PRECEDENCE_POWER)
{
@Override
public double apply(final double... args)
{
return Math.pow(args[0], args[1]);
}
};
builtinOperators[INDEX_MODULO] = new Operator("%", 2, true, Operator.PRECEDENCE_MODULO)
{
@Override
public double apply(final double... args)
{
if (args[1] == 0d)
{
throw new ArithmeticException("Division by zero!");
}
return args[0] % args[1];
}
};
}
public static Operator getBuiltinOperator(final char symbol, final int numArguments)
{
switch (symbol)
{
case '+':
if (numArguments != 1)
{
return builtinOperators[INDEX_ADDITION];
}
return builtinOperators[INDEX_UNARYPLUS];
case '-':
if (numArguments != 1)
{
return builtinOperators[INDEX_SUBTRACTION];
}
return builtinOperators[INDEX_UNARYMINUS];
case '*':
return builtinOperators[INDEX_MUTLIPLICATION];
case '/':
return builtinOperators[INDEX_DIVISION];
case '^':
return builtinOperators[INDEX_POWER];
case '%':
return builtinOperators[INDEX_MODULO];
default:
return null;
}
}
}

View File

@ -0,0 +1,121 @@
/*
* Copyright 2014 Frank Asseg
*
* Licensed 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 com.l2jmobius.gameserver.util.exp4j.shuntingyard;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Stack;
import com.l2jmobius.gameserver.util.exp4j.function.Function;
import com.l2jmobius.gameserver.util.exp4j.operator.Operator;
import com.l2jmobius.gameserver.util.exp4j.tokenizer.OperatorToken;
import com.l2jmobius.gameserver.util.exp4j.tokenizer.Token;
import com.l2jmobius.gameserver.util.exp4j.tokenizer.Tokenizer;
/**
* Shunting yard implementation to convert infix to reverse polish notation
*/
public class ShuntingYard
{
/**
* Convert a Set of tokens from infix to reverse polish notation
* @param expression the expression to convert
* @param userFunctions the custom functions used
* @param userOperators the custom operators used
* @param variableNames the variable names used in the expression
* @param implicitMultiplication set to fasle to turn off implicit multiplication
* @return a {@link com.l2jmobius.gameserver.util.exp4j.tokenizer.Token} array containing the result
*/
public static Token[] convertToRPN(final String expression, final Map<String, Function> userFunctions, final Map<String, Operator> userOperators, final Set<String> variableNames, final boolean implicitMultiplication)
{
final Stack<Token> stack = new Stack<>();
final List<Token> output = new ArrayList<>();
final Tokenizer tokenizer = new Tokenizer(expression, userFunctions, userOperators, variableNames, implicitMultiplication);
while (tokenizer.hasNext())
{
Token token = tokenizer.nextToken();
switch (token.getType())
{
case Token.TOKEN_NUMBER:
case Token.TOKEN_VARIABLE:
output.add(token);
break;
case Token.TOKEN_FUNCTION:
stack.add(token);
break;
case Token.TOKEN_SEPARATOR:
while (!stack.empty() && (stack.peek().getType() != Token.TOKEN_PARENTHESES_OPEN))
{
output.add(stack.pop());
}
if (stack.empty() || (stack.peek().getType() != Token.TOKEN_PARENTHESES_OPEN))
{
throw new IllegalArgumentException("Misplaced function separator ',' or mismatched parentheses");
}
break;
case Token.TOKEN_OPERATOR:
while (!stack.empty() && (stack.peek().getType() == Token.TOKEN_OPERATOR))
{
OperatorToken o1 = (OperatorToken) token;
OperatorToken o2 = (OperatorToken) stack.peek();
if ((o1.getOperator().getNumOperands() == 1) && (o2.getOperator().getNumOperands() == 2))
{
break;
}
else if ((o1.getOperator().isLeftAssociative() && (o1.getOperator().getPrecedence() <= o2.getOperator().getPrecedence())) || (o1.getOperator().getPrecedence() < o2.getOperator().getPrecedence()))
{
output.add(stack.pop());
}
else
{
break;
}
}
stack.push(token);
break;
case Token.TOKEN_PARENTHESES_OPEN:
stack.push(token);
break;
case Token.TOKEN_PARENTHESES_CLOSE:
while (stack.peek().getType() != Token.TOKEN_PARENTHESES_OPEN)
{
output.add(stack.pop());
}
stack.pop();
if (!stack.isEmpty() && (stack.peek().getType() == Token.TOKEN_FUNCTION))
{
output.add(stack.pop());
}
break;
default:
throw new IllegalArgumentException("Unknown Token type encountered. This should not happen");
}
}
while (!stack.empty())
{
Token t = stack.pop();
if ((t.getType() == Token.TOKEN_PARENTHESES_CLOSE) || (t.getType() == Token.TOKEN_PARENTHESES_OPEN))
{
throw new IllegalArgumentException("Mismatched parentheses detected. Please check the expression");
}
output.add(t);
}
return output.toArray(new Token[output.size()]);
}
}

View File

@ -0,0 +1,30 @@
/*
* Copyright 2014 Frank Asseg
*
* Licensed 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 com.l2jmobius.gameserver.util.exp4j.tokenizer;
/**
* Represents an argument separator in functions i.e: ','
*/
class ArgumentSeparatorToken extends Token
{
/**
* Create a new instance
*/
ArgumentSeparatorToken()
{
super(Token.TOKEN_SEPARATOR);
}
}

View File

@ -0,0 +1,30 @@
/*
* Copyright 2014 Frank Asseg
*
* Licensed 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 com.l2jmobius.gameserver.util.exp4j.tokenizer;
/**
* represents closed parentheses
*/
class CloseParenthesesToken extends Token
{
/**
* Creare a new instance
*/
CloseParenthesesToken()
{
super(Token.TOKEN_PARENTHESES_CLOSE);
}
}

View File

@ -0,0 +1,34 @@
/*
* Copyright 2014 Frank Asseg
*
* Licensed 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 com.l2jmobius.gameserver.util.exp4j.tokenizer;
import com.l2jmobius.gameserver.util.exp4j.function.Function;
public class FunctionToken extends Token
{
private final Function function;
public FunctionToken(final Function function)
{
super(Token.TOKEN_FUNCTION);
this.function = function;
}
public Function getFunction()
{
return function;
}
}

View File

@ -0,0 +1,48 @@
/*
* Copyright 2014 Frank Asseg
*
* Licensed 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 com.l2jmobius.gameserver.util.exp4j.tokenizer;
/**
* Represents a number in the expression
*/
public final class NumberToken extends Token
{
private final double value;
/**
* Create a new instance
* @param value the value of the number
*/
public NumberToken(double value)
{
super(TOKEN_NUMBER);
this.value = value;
}
NumberToken(final char[] expression, final int offset, final int len)
{
this(Double.parseDouble(String.valueOf(expression, offset, len)));
}
/**
* Get the value of the number
* @return the value
*/
public double getValue()
{
return value;
}
}

View File

@ -0,0 +1,24 @@
/*
* Copyright 2014 Frank Asseg
*
* Licensed 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 com.l2jmobius.gameserver.util.exp4j.tokenizer;
class OpenParenthesesToken extends Token
{
OpenParenthesesToken()
{
super(TOKEN_PARENTHESES_OPEN);
}
}

View File

@ -0,0 +1,49 @@
/*
* Copyright 2014 Frank Asseg
*
* Licensed 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 com.l2jmobius.gameserver.util.exp4j.tokenizer;
import com.l2jmobius.gameserver.util.exp4j.operator.Operator;
/**
* Represents an operator used in expressions
*/
public class OperatorToken extends Token
{
private final Operator operator;
/**
* Create a new instance
* @param op the operator
*/
public OperatorToken(Operator op)
{
super(Token.TOKEN_OPERATOR);
if (op == null)
{
throw new IllegalArgumentException("Operator is unknown for token.");
}
this.operator = op;
}
/**
* Get the operator for that token
* @return the operator
*/
public Operator getOperator()
{
return operator;
}
}

View File

@ -0,0 +1,43 @@
/*
* Copyright 2014 Frank Asseg
*
* Licensed 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 com.l2jmobius.gameserver.util.exp4j.tokenizer;
/**
* Abstract class for tokens used by exp4j to tokenize expressions
*/
public abstract class Token
{
public static final short TOKEN_NUMBER = 1;
public static final short TOKEN_OPERATOR = 2;
public static final short TOKEN_FUNCTION = 3;
public static final short TOKEN_PARENTHESES_OPEN = 4;
public static final short TOKEN_PARENTHESES_CLOSE = 5;
public static final short TOKEN_VARIABLE = 6;
public static final short TOKEN_SEPARATOR = 7;
protected final int type;
Token(int type)
{
this.type = type;
}
public int getType()
{
return type;
}
}

View File

@ -0,0 +1,334 @@
/*
* Copyright 2014 Frank Asseg
*
* Licensed 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 com.l2jmobius.gameserver.util.exp4j.tokenizer;
import java.util.Map;
import java.util.Set;
import com.l2jmobius.gameserver.util.exp4j.function.Function;
import com.l2jmobius.gameserver.util.exp4j.function.Functions;
import com.l2jmobius.gameserver.util.exp4j.operator.Operator;
import com.l2jmobius.gameserver.util.exp4j.operator.Operators;
public class Tokenizer
{
private final char[] expression;
private final int expressionLength;
private final Map<String, Function> userFunctions;
private final Map<String, Operator> userOperators;
private final Set<String> variableNames;
private final boolean implicitMultiplication;
private int pos = 0;
private Token lastToken;
public Tokenizer(String expression, final Map<String, Function> userFunctions, final Map<String, Operator> userOperators, final Set<String> variableNames, final boolean implicitMultiplication)
{
this.expression = expression.trim().toCharArray();
this.expressionLength = this.expression.length;
this.userFunctions = userFunctions;
this.userOperators = userOperators;
this.variableNames = variableNames;
this.implicitMultiplication = implicitMultiplication;
}
public Tokenizer(String expression, final Map<String, Function> userFunctions, final Map<String, Operator> userOperators, final Set<String> variableNames)
{
this.expression = expression.trim().toCharArray();
this.expressionLength = this.expression.length;
this.userFunctions = userFunctions;
this.userOperators = userOperators;
this.variableNames = variableNames;
this.implicitMultiplication = true;
}
public boolean hasNext()
{
return this.expression.length > pos;
}
public Token nextToken()
{
char ch = expression[pos];
while (Character.isWhitespace(ch))
{
ch = expression[++pos];
}
if (Character.isDigit(ch) || (ch == '.'))
{
if (lastToken != null)
{
if (lastToken.getType() == Token.TOKEN_NUMBER)
{
throw new IllegalArgumentException("Unable to parse char '" + ch + "' (Code:" + (int) ch + ") at [" + pos + "]");
}
else if (implicitMultiplication && ((lastToken.getType() != Token.TOKEN_OPERATOR) && (lastToken.getType() != Token.TOKEN_PARENTHESES_OPEN) && (lastToken.getType() != Token.TOKEN_FUNCTION) && (lastToken.getType() != Token.TOKEN_SEPARATOR)))
{
// insert an implicit multiplication token
lastToken = new OperatorToken(Operators.getBuiltinOperator('*', 2));
return lastToken;
}
}
return parseNumberToken(ch);
}
else if (isArgumentSeparator(ch))
{
return parseArgumentSeparatorToken(ch);
}
else if (isOpenParentheses(ch))
{
if ((lastToken != null) && implicitMultiplication && ((lastToken.getType() != Token.TOKEN_OPERATOR) && (lastToken.getType() != Token.TOKEN_PARENTHESES_OPEN) && (lastToken.getType() != Token.TOKEN_FUNCTION) && (lastToken.getType() != Token.TOKEN_SEPARATOR)))
{
// insert an implicit multiplication token
lastToken = new OperatorToken(Operators.getBuiltinOperator('*', 2));
return lastToken;
}
return parseParentheses(true);
}
else if (isCloseParentheses(ch))
{
return parseParentheses(false);
}
else if (Operator.isAllowedOperatorChar(ch))
{
return parseOperatorToken(ch);
}
else if (isAlphabetic(ch) || (ch == '_'))
{
// parse the name which can be a setVariable or a function
if ((lastToken != null) && implicitMultiplication && ((lastToken.getType() != Token.TOKEN_OPERATOR) && (lastToken.getType() != Token.TOKEN_PARENTHESES_OPEN) && (lastToken.getType() != Token.TOKEN_FUNCTION) && (lastToken.getType() != Token.TOKEN_SEPARATOR)))
{
// insert an implicit multiplication token
lastToken = new OperatorToken(Operators.getBuiltinOperator('*', 2));
return lastToken;
}
return parseFunctionOrVariable();
}
throw new IllegalArgumentException("Unable to parse char '" + ch + "' (Code:" + (int) ch + ") at [" + pos + "]");
}
private Token parseArgumentSeparatorToken(char ch)
{
this.pos++;
this.lastToken = new ArgumentSeparatorToken();
return lastToken;
}
private boolean isArgumentSeparator(char ch)
{
return ch == ',';
}
private Token parseParentheses(final boolean open)
{
if (open)
{
this.lastToken = new OpenParenthesesToken();
}
else
{
this.lastToken = new CloseParenthesesToken();
}
this.pos++;
return lastToken;
}
private boolean isOpenParentheses(char ch)
{
return (ch == '(') || (ch == '{') || (ch == '[');
}
private boolean isCloseParentheses(char ch)
{
return (ch == ')') || (ch == '}') || (ch == ']');
}
private Token parseFunctionOrVariable()
{
final int offset = this.pos;
int testPos;
int lastValidLen = 1;
Token lastValidToken = null;
int len = 1;
if (isEndOfExpression(offset))
{
this.pos++;
}
testPos = (offset + len) - 1;
while (!isEndOfExpression(testPos) && isVariableOrFunctionCharacter(expression[testPos]))
{
String name = new String(expression, offset, len);
if ((variableNames != null) && variableNames.contains(name))
{
lastValidLen = len;
lastValidToken = new VariableToken(name);
}
else
{
final Function f = getFunction(name);
if (f != null)
{
lastValidLen = len;
lastValidToken = new FunctionToken(f);
}
}
len++;
testPos = (offset + len) - 1;
}
if (lastValidToken == null)
{
throw new UnknownFunctionOrVariableException(new String(expression), pos, len);
}
pos += lastValidLen;
lastToken = lastValidToken;
return lastToken;
}
private Function getFunction(String name)
{
Function f = null;
if (this.userFunctions != null)
{
f = this.userFunctions.get(name);
}
if (f == null)
{
f = Functions.getBuiltinFunction(name);
}
return f;
}
private Token parseOperatorToken(char firstChar)
{
final int offset = this.pos;
int len = 1;
final StringBuilder symbol = new StringBuilder();
Operator lastValid = null;
symbol.append(firstChar);
while (!isEndOfExpression(offset + len) && Operator.isAllowedOperatorChar(expression[offset + len]))
{
symbol.append(expression[offset + len++]);
}
while (symbol.length() > 0)
{
Operator op = this.getOperator(symbol.toString());
if (op == null)
{
symbol.setLength(symbol.length() - 1);
}
else
{
lastValid = op;
break;
}
}
pos += symbol.length();
lastToken = new OperatorToken(lastValid);
return lastToken;
}
private Operator getOperator(String symbol)
{
Operator op = null;
if (this.userOperators != null)
{
op = this.userOperators.get(symbol);
}
if ((op == null) && (symbol.length() == 1))
{
int argc = 2;
if (lastToken == null)
{
argc = 1;
}
else
{
int lastTokenType = lastToken.getType();
if ((lastTokenType == Token.TOKEN_PARENTHESES_OPEN) || (lastTokenType == Token.TOKEN_SEPARATOR))
{
argc = 1;
}
else if (lastTokenType == Token.TOKEN_OPERATOR)
{
final Operator lastOp = ((OperatorToken) lastToken).getOperator();
if ((lastOp.getNumOperands() == 2) || ((lastOp.getNumOperands() == 1) && !lastOp.isLeftAssociative()))
{
argc = 1;
}
}
}
op = Operators.getBuiltinOperator(symbol.charAt(0), argc);
}
return op;
}
private Token parseNumberToken(final char firstChar)
{
final int offset = this.pos;
int len = 1;
this.pos++;
if (isEndOfExpression(offset + len))
{
lastToken = new NumberToken(Double.parseDouble(String.valueOf(firstChar)));
return lastToken;
}
while (!isEndOfExpression(offset + len) && isNumeric(expression[offset + len], (expression[(offset + len) - 1] == 'e') || (expression[(offset + len) - 1] == 'E')))
{
len++;
this.pos++;
}
// check if the e is at the end
if ((expression[(offset + len) - 1] == 'e') || (expression[(offset + len) - 1] == 'E'))
{
// since the e is at the end it's not part of the number and a rollback is necessary
len--;
pos--;
}
lastToken = new NumberToken(expression, offset, len);
return lastToken;
}
private static boolean isNumeric(char ch, boolean lastCharE)
{
return Character.isDigit(ch) || (ch == '.') || (ch == 'e') || (ch == 'E') || (lastCharE && ((ch == '-') || (ch == '+')));
}
public static boolean isAlphabetic(int codePoint)
{
return Character.isLetter(codePoint);
}
public static boolean isVariableOrFunctionCharacter(int codePoint)
{
return isAlphabetic(codePoint) || Character.isDigit(codePoint) || (codePoint == '_') || (codePoint == '.');
}
private boolean isEndOfExpression(int offset)
{
return this.expressionLength <= offset;
}
}

View File

@ -0,0 +1,65 @@
package com.l2jmobius.gameserver.util.exp4j.tokenizer;
/**
* This exception is being thrown whenever {@link Tokenizer} finds unknown function or variable.
* @author Bartosz Firyn (sarxos)
*/
public class UnknownFunctionOrVariableException extends IllegalArgumentException
{
private final String message;
private final String expression;
private final String token;
private final int position;
public UnknownFunctionOrVariableException(String expression, int position, int length)
{
this.expression = expression;
this.token = token(expression, position, length);
this.position = position;
this.message = "Unknown function or variable '" + token + "' at pos " + position + " in expression '" + expression + "'";
}
private static String token(String expression, int position, int length)
{
int len = expression.length();
int end = (position + length) - 1;
if (len < end)
{
end = len;
}
return expression.substring(position, end);
}
@Override
public String getMessage()
{
return message;
}
/**
* @return Expression which contains unknown function or variable
*/
public String getExpression()
{
return expression;
}
/**
* @return The name of unknown function or variable
*/
public String getToken()
{
return token;
}
/**
* @return The position of unknown function or variable
*/
public int getPosition()
{
return position;
}
}

View File

@ -0,0 +1,43 @@
/*
* Copyright 2014 Frank Asseg
*
* Licensed 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 com.l2jmobius.gameserver.util.exp4j.tokenizer;
/**
* represents a setVariable used in an expression
*/
public class VariableToken extends Token
{
private final String name;
/**
* Get the name of the setVariable
* @return the name
*/
public String getName()
{
return name;
}
/**
* Create a new instance
* @param name the name of the setVariable
*/
public VariableToken(String name)
{
super(TOKEN_VARIABLE);
this.name = name;
}
}

View File

@ -7,7 +7,7 @@
<listEntry value="1"/>
</listAttribute>
<stringAttribute key="org.eclipse.debug.core.source_locator_id" value="org.eclipse.jdt.launching.sourceLocator.JavaSourceLookupDirector"/>
<stringAttribute key="org.eclipse.debug.core.source_locator_memento" value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;sourceLookupDirector&gt;&#13;&#10;&lt;sourceContainers duplicates=&quot;false&quot;&gt;&#13;&#10;&lt;container memento=&quot;&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot; standalone=&amp;quot;no&amp;quot;?&amp;gt;&amp;#13;&amp;#10;&amp;lt;archive detectRoot=&amp;quot;true&amp;quot; path=&amp;quot;/L2J_Mobius_Helios/dist/libs/c3p0-0.9.5.2.jar&amp;quot;/&amp;gt;&amp;#13;&amp;#10;&quot; typeId=&quot;org.eclipse.debug.core.containerType.archive&quot;/&gt;&#13;&#10;&lt;container memento=&quot;&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot; standalone=&amp;quot;no&amp;quot;?&amp;gt;&amp;#13;&amp;#10;&amp;lt;archive detectRoot=&amp;quot;true&amp;quot; path=&amp;quot;/L2J_Mobius_Helios/dist/libs/ecj-4.4.2.jar&amp;quot;/&amp;gt;&amp;#13;&amp;#10;&quot; typeId=&quot;org.eclipse.debug.core.containerType.archive&quot;/&gt;&#13;&#10;&lt;container memento=&quot;&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot; standalone=&amp;quot;no&amp;quot;?&amp;gt;&amp;#13;&amp;#10;&amp;lt;archive detectRoot=&amp;quot;true&amp;quot; path=&amp;quot;/L2J_Mobius_Helios/dist/libs/exp4j-0.4.8.jar&amp;quot;/&amp;gt;&amp;#13;&amp;#10;&quot; typeId=&quot;org.eclipse.debug.core.containerType.archive&quot;/&gt;&#13;&#10;&lt;container memento=&quot;&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot; standalone=&amp;quot;no&amp;quot;?&amp;gt;&amp;#13;&amp;#10;&amp;lt;archive detectRoot=&amp;quot;true&amp;quot; path=&amp;quot;/L2J_Mobius_Helios/dist/libs/mail-1.5.2.jar&amp;quot;/&amp;gt;&amp;#13;&amp;#10;&quot; typeId=&quot;org.eclipse.debug.core.containerType.archive&quot;/&gt;&#13;&#10;&lt;container memento=&quot;&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot; standalone=&amp;quot;no&amp;quot;?&amp;gt;&amp;#13;&amp;#10;&amp;lt;archive detectRoot=&amp;quot;true&amp;quot; path=&amp;quot;/L2J_Mobius_Helios/dist/libs/mchange-commons-java-0.2.12.jar&amp;quot;/&amp;gt;&amp;#13;&amp;#10;&quot; typeId=&quot;org.eclipse.debug.core.containerType.archive&quot;/&gt;&#13;&#10;&lt;container memento=&quot;&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot; standalone=&amp;quot;no&amp;quot;?&amp;gt;&amp;#13;&amp;#10;&amp;lt;archive detectRoot=&amp;quot;true&amp;quot; path=&amp;quot;/L2J_Mobius_Helios/dist/libs/mysql-connector-java-5.1.43.jar&amp;quot;/&amp;gt;&amp;#13;&amp;#10;&quot; typeId=&quot;org.eclipse.debug.core.containerType.archive&quot;/&gt;&#13;&#10;&lt;container memento=&quot;&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot; standalone=&amp;quot;no&amp;quot;?&amp;gt;&amp;#13;&amp;#10;&amp;lt;archive detectRoot=&amp;quot;true&amp;quot; path=&amp;quot;/L2J_Mobius_Helios/dist/libs/netty-all-4.1.14.Final.jar&amp;quot;/&amp;gt;&amp;#13;&amp;#10;&quot; typeId=&quot;org.eclipse.debug.core.containerType.archive&quot;/&gt;&#13;&#10;&lt;container memento=&quot;&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot; standalone=&amp;quot;no&amp;quot;?&amp;gt;&amp;#13;&amp;#10;&amp;lt;default/&amp;gt;&amp;#13;&amp;#10;&quot; typeId=&quot;org.eclipse.debug.core.containerType.default&quot;/&gt;&#13;&#10;&lt;/sourceContainers&gt;&#13;&#10;&lt;/sourceLookupDirector&gt;&#13;&#10;"/>
<stringAttribute key="org.eclipse.debug.core.source_locator_memento" value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;sourceLookupDirector&gt;&#13;&#10;&lt;sourceContainers duplicates=&quot;false&quot;&gt;&#13;&#10;&lt;container memento=&quot;&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot; standalone=&amp;quot;no&amp;quot;?&amp;gt;&amp;#13;&amp;#10;&amp;lt;archive detectRoot=&amp;quot;true&amp;quot; path=&amp;quot;/L2J_Mobius_Helios/dist/libs/c3p0-0.9.5.2.jar&amp;quot;/&amp;gt;&amp;#13;&amp;#10;&quot; typeId=&quot;org.eclipse.debug.core.containerType.archive&quot;/&gt;&#13;&#10;&lt;container memento=&quot;&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot; standalone=&amp;quot;no&amp;quot;?&amp;gt;&amp;#13;&amp;#10;&amp;lt;archive detectRoot=&amp;quot;true&amp;quot; path=&amp;quot;/L2J_Mobius_Helios/dist/libs/ecj-4.4.2.jar&amp;quot;/&amp;gt;&amp;#13;&amp;#10;&quot; typeId=&quot;org.eclipse.debug.core.containerType.archive&quot;/&gt;&#13;&#10;&lt;container memento=&quot;&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot; standalone=&amp;quot;no&amp;quot;?&amp;gt;&amp;#13;&amp;#10;&amp;lt;archive detectRoot=&amp;quot;true&amp;quot; path=&amp;quot;/L2J_Mobius_Helios/dist/libs/mail-1.5.2.jar&amp;quot;/&amp;gt;&amp;#13;&amp;#10;&quot; typeId=&quot;org.eclipse.debug.core.containerType.archive&quot;/&gt;&#13;&#10;&lt;container memento=&quot;&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot; standalone=&amp;quot;no&amp;quot;?&amp;gt;&amp;#13;&amp;#10;&amp;lt;archive detectRoot=&amp;quot;true&amp;quot; path=&amp;quot;/L2J_Mobius_Helios/dist/libs/mchange-commons-java-0.2.12.jar&amp;quot;/&amp;gt;&amp;#13;&amp;#10;&quot; typeId=&quot;org.eclipse.debug.core.containerType.archive&quot;/&gt;&#13;&#10;&lt;container memento=&quot;&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot; standalone=&amp;quot;no&amp;quot;?&amp;gt;&amp;#13;&amp;#10;&amp;lt;archive detectRoot=&amp;quot;true&amp;quot; path=&amp;quot;/L2J_Mobius_Helios/dist/libs/mysql-connector-java-5.1.43.jar&amp;quot;/&amp;gt;&amp;#13;&amp;#10;&quot; typeId=&quot;org.eclipse.debug.core.containerType.archive&quot;/&gt;&#13;&#10;&lt;container memento=&quot;&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot; standalone=&amp;quot;no&amp;quot;?&amp;gt;&amp;#13;&amp;#10;&amp;lt;archive detectRoot=&amp;quot;true&amp;quot; path=&amp;quot;/L2J_Mobius_Helios/dist/libs/netty-all-4.1.14.Final.jar&amp;quot;/&amp;gt;&amp;#13;&amp;#10;&quot; typeId=&quot;org.eclipse.debug.core.containerType.archive&quot;/&gt;&#13;&#10;&lt;container memento=&quot;&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot; standalone=&amp;quot;no&amp;quot;?&amp;gt;&amp;#13;&amp;#10;&amp;lt;default/&amp;gt;&amp;#13;&amp;#10;&quot; typeId=&quot;org.eclipse.debug.core.containerType.default&quot;/&gt;&#13;&#10;&lt;/sourceContainers&gt;&#13;&#10;&lt;/sourceLookupDirector&gt;&#13;&#10;"/>
<listAttribute key="org.eclipse.debug.ui.favoriteGroups">
<listEntry value="org.eclipse.debug.ui.launchGroup.debug"/>
</listAttribute>

View File

@ -7,7 +7,7 @@
<listEntry value="1"/>
</listAttribute>
<stringAttribute key="org.eclipse.debug.core.source_locator_id" value="org.eclipse.jdt.launching.sourceLocator.JavaSourceLookupDirector"/>
<stringAttribute key="org.eclipse.debug.core.source_locator_memento" value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;sourceLookupDirector&gt;&#13;&#10;&lt;sourceContainers duplicates=&quot;false&quot;&gt;&#13;&#10;&lt;container memento=&quot;&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot; standalone=&amp;quot;no&amp;quot;?&amp;gt;&amp;#13;&amp;#10;&amp;lt;archive detectRoot=&amp;quot;true&amp;quot; path=&amp;quot;/L2J_Mobius_Helios/dist/libs/c3p0-0.9.5.2.jar&amp;quot;/&amp;gt;&amp;#13;&amp;#10;&quot; typeId=&quot;org.eclipse.debug.core.containerType.archive&quot;/&gt;&#13;&#10;&lt;container memento=&quot;&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot; standalone=&amp;quot;no&amp;quot;?&amp;gt;&amp;#13;&amp;#10;&amp;lt;archive detectRoot=&amp;quot;true&amp;quot; path=&amp;quot;/L2J_Mobius_Helios/dist/libs/ecj-4.4.2.jar&amp;quot;/&amp;gt;&amp;#13;&amp;#10;&quot; typeId=&quot;org.eclipse.debug.core.containerType.archive&quot;/&gt;&#13;&#10;&lt;container memento=&quot;&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot; standalone=&amp;quot;no&amp;quot;?&amp;gt;&amp;#13;&amp;#10;&amp;lt;archive detectRoot=&amp;quot;true&amp;quot; path=&amp;quot;/L2J_Mobius_Helios/dist/libs/exp4j-0.4.8.jar&amp;quot;/&amp;gt;&amp;#13;&amp;#10;&quot; typeId=&quot;org.eclipse.debug.core.containerType.archive&quot;/&gt;&#13;&#10;&lt;container memento=&quot;&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot; standalone=&amp;quot;no&amp;quot;?&amp;gt;&amp;#13;&amp;#10;&amp;lt;archive detectRoot=&amp;quot;true&amp;quot; path=&amp;quot;/L2J_Mobius_Helios/dist/libs/mail-1.5.2.jar&amp;quot;/&amp;gt;&amp;#13;&amp;#10;&quot; typeId=&quot;org.eclipse.debug.core.containerType.archive&quot;/&gt;&#13;&#10;&lt;container memento=&quot;&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot; standalone=&amp;quot;no&amp;quot;?&amp;gt;&amp;#13;&amp;#10;&amp;lt;archive detectRoot=&amp;quot;true&amp;quot; path=&amp;quot;/L2J_Mobius_Helios/dist/libs/mchange-commons-java-0.2.12.jar&amp;quot;/&amp;gt;&amp;#13;&amp;#10;&quot; typeId=&quot;org.eclipse.debug.core.containerType.archive&quot;/&gt;&#13;&#10;&lt;container memento=&quot;&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot; standalone=&amp;quot;no&amp;quot;?&amp;gt;&amp;#13;&amp;#10;&amp;lt;archive detectRoot=&amp;quot;true&amp;quot; path=&amp;quot;/L2J_Mobius_Helios/dist/libs/mysql-connector-java-5.1.43.jar&amp;quot;/&amp;gt;&amp;#13;&amp;#10;&quot; typeId=&quot;org.eclipse.debug.core.containerType.archive&quot;/&gt;&#13;&#10;&lt;container memento=&quot;&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot; standalone=&amp;quot;no&amp;quot;?&amp;gt;&amp;#13;&amp;#10;&amp;lt;archive detectRoot=&amp;quot;true&amp;quot; path=&amp;quot;/L2J_Mobius_Helios/dist/libs/netty-all-4.1.14.Final.jar&amp;quot;/&amp;gt;&amp;#13;&amp;#10;&quot; typeId=&quot;org.eclipse.debug.core.containerType.archive&quot;/&gt;&#13;&#10;&lt;container memento=&quot;&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot; standalone=&amp;quot;no&amp;quot;?&amp;gt;&amp;#13;&amp;#10;&amp;lt;default/&amp;gt;&amp;#13;&amp;#10;&quot; typeId=&quot;org.eclipse.debug.core.containerType.default&quot;/&gt;&#13;&#10;&lt;/sourceContainers&gt;&#13;&#10;&lt;/sourceLookupDirector&gt;&#13;&#10;"/>
<stringAttribute key="org.eclipse.debug.core.source_locator_memento" value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;sourceLookupDirector&gt;&#13;&#10;&lt;sourceContainers duplicates=&quot;false&quot;&gt;&#13;&#10;&lt;container memento=&quot;&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot; standalone=&amp;quot;no&amp;quot;?&amp;gt;&amp;#13;&amp;#10;&amp;lt;archive detectRoot=&amp;quot;true&amp;quot; path=&amp;quot;/L2J_Mobius_Helios/dist/libs/c3p0-0.9.5.2.jar&amp;quot;/&amp;gt;&amp;#13;&amp;#10;&quot; typeId=&quot;org.eclipse.debug.core.containerType.archive&quot;/&gt;&#13;&#10;&lt;container memento=&quot;&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot; standalone=&amp;quot;no&amp;quot;?&amp;gt;&amp;#13;&amp;#10;&amp;lt;archive detectRoot=&amp;quot;true&amp;quot; path=&amp;quot;/L2J_Mobius_Helios/dist/libs/ecj-4.4.2.jar&amp;quot;/&amp;gt;&amp;#13;&amp;#10;&quot; typeId=&quot;org.eclipse.debug.core.containerType.archive&quot;/&gt;&#13;&#10;&lt;container memento=&quot;&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot; standalone=&amp;quot;no&amp;quot;?&amp;gt;&amp;#13;&amp;#10;&amp;lt;archive detectRoot=&amp;quot;true&amp;quot; path=&amp;quot;/L2J_Mobius_Helios/dist/libs/mail-1.5.2.jar&amp;quot;/&amp;gt;&amp;#13;&amp;#10;&quot; typeId=&quot;org.eclipse.debug.core.containerType.archive&quot;/&gt;&#13;&#10;&lt;container memento=&quot;&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot; standalone=&amp;quot;no&amp;quot;?&amp;gt;&amp;#13;&amp;#10;&amp;lt;archive detectRoot=&amp;quot;true&amp;quot; path=&amp;quot;/L2J_Mobius_Helios/dist/libs/mchange-commons-java-0.2.12.jar&amp;quot;/&amp;gt;&amp;#13;&amp;#10;&quot; typeId=&quot;org.eclipse.debug.core.containerType.archive&quot;/&gt;&#13;&#10;&lt;container memento=&quot;&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot; standalone=&amp;quot;no&amp;quot;?&amp;gt;&amp;#13;&amp;#10;&amp;lt;archive detectRoot=&amp;quot;true&amp;quot; path=&amp;quot;/L2J_Mobius_Helios/dist/libs/mysql-connector-java-5.1.43.jar&amp;quot;/&amp;gt;&amp;#13;&amp;#10;&quot; typeId=&quot;org.eclipse.debug.core.containerType.archive&quot;/&gt;&#13;&#10;&lt;container memento=&quot;&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot; standalone=&amp;quot;no&amp;quot;?&amp;gt;&amp;#13;&amp;#10;&amp;lt;archive detectRoot=&amp;quot;true&amp;quot; path=&amp;quot;/L2J_Mobius_Helios/dist/libs/netty-all-4.1.14.Final.jar&amp;quot;/&amp;gt;&amp;#13;&amp;#10;&quot; typeId=&quot;org.eclipse.debug.core.containerType.archive&quot;/&gt;&#13;&#10;&lt;container memento=&quot;&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot; standalone=&amp;quot;no&amp;quot;?&amp;gt;&amp;#13;&amp;#10;&amp;lt;default/&amp;gt;&amp;#13;&amp;#10;&quot; typeId=&quot;org.eclipse.debug.core.containerType.default&quot;/&gt;&#13;&#10;&lt;/sourceContainers&gt;&#13;&#10;&lt;/sourceLookupDirector&gt;&#13;&#10;"/>
<listAttribute key="org.eclipse.debug.ui.favoriteGroups">
<listEntry value="org.eclipse.debug.ui.launchGroup.debug"/>
</listAttribute>

View File

@ -3,7 +3,6 @@
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8"/>
<classpathentry kind="lib" path="dist/libs/c3p0-0.9.5.2.jar"/>
<classpathentry kind="lib" path="dist/libs/ecj-4.4.2.jar"/>
<classpathentry kind="lib" path="dist/libs/exp4j-0.4.8.jar"/>
<classpathentry kind="lib" path="dist/libs/mail-1.5.2.jar"/>
<classpathentry kind="lib" path="dist/libs/mchange-commons-java-0.2.12.jar"/>
<classpathentry kind="lib" path="dist/libs/mysql-connector-java-5.1.43.jar"/>

Binary file not shown.

View File

@ -45,8 +45,7 @@ import com.l2jmobius.gameserver.model.skills.EffectScope;
import com.l2jmobius.gameserver.model.skills.ISkillCondition;
import com.l2jmobius.gameserver.model.skills.Skill;
import com.l2jmobius.gameserver.model.skills.SkillConditionScope;
import net.objecthunter.exp4j.ExpressionBuilder;
import com.l2jmobius.gameserver.util.exp4j.ExpressionBuilder;
/**
* Skill data parser.

View File

@ -0,0 +1,86 @@
/*
* Copyright 2015 Federico Vera
*
* Licensed 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 com.l2jmobius.gameserver.util.exp4j;
import java.util.EmptyStackException;
/**
* Simple double stack using a double array as data storage
* @author Federico Vera (dktcoding [at] gmail)
*/
class ArrayStack
{
private double[] data;
private int idx;
ArrayStack()
{
this(5);
}
ArrayStack(int initialCapacity)
{
if (initialCapacity <= 0)
{
throw new IllegalArgumentException("Stack's capacity must be positive");
}
data = new double[initialCapacity];
idx = -1;
}
void push(double value)
{
if ((idx + 1) == data.length)
{
double[] temp = new double[(int) (data.length * 1.2) + 1];
System.arraycopy(data, 0, temp, 0, data.length);
data = temp;
}
data[++idx] = value;
}
double peek()
{
if (idx == -1)
{
throw new EmptyStackException();
}
return data[idx];
}
double pop()
{
if (idx == -1)
{
throw new EmptyStackException();
}
return data[idx--];
}
boolean isEmpty()
{
return idx == -1;
}
int size()
{
return idx + 1;
}
}

View File

@ -0,0 +1,263 @@
/*
* Copyright 2014 Frank Asseg
*
* Licensed 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 com.l2jmobius.gameserver.util.exp4j;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;
import com.l2jmobius.gameserver.util.exp4j.function.Function;
import com.l2jmobius.gameserver.util.exp4j.function.Functions;
import com.l2jmobius.gameserver.util.exp4j.operator.Operator;
import com.l2jmobius.gameserver.util.exp4j.tokenizer.FunctionToken;
import com.l2jmobius.gameserver.util.exp4j.tokenizer.NumberToken;
import com.l2jmobius.gameserver.util.exp4j.tokenizer.OperatorToken;
import com.l2jmobius.gameserver.util.exp4j.tokenizer.Token;
import com.l2jmobius.gameserver.util.exp4j.tokenizer.VariableToken;
public class Expression
{
private final Token[] tokens;
private final Map<String, Double> variables;
private final Set<String> userFunctionNames;
private static Map<String, Double> createDefaultVariables()
{
final Map<String, Double> vars = new HashMap<>(4);
vars.put("pi", Math.PI);
vars.put("Ο€", Math.PI);
vars.put("Ο†", 1.61803398874d);
vars.put("e", Math.E);
return vars;
}
/**
* Creates a new expression that is a copy of the existing one.
* @param existing the expression to copy
*/
public Expression(final Expression existing)
{
tokens = Arrays.copyOf(existing.tokens, existing.tokens.length);
variables = new HashMap<>();
variables.putAll(existing.variables);
userFunctionNames = new HashSet<>(existing.userFunctionNames);
}
Expression(final Token[] tokens)
{
this.tokens = tokens;
variables = createDefaultVariables();
userFunctionNames = Collections.<String> emptySet();
}
Expression(final Token[] tokens, Set<String> userFunctionNames)
{
this.tokens = tokens;
variables = createDefaultVariables();
this.userFunctionNames = userFunctionNames;
}
public Expression setVariable(final String name, final double value)
{
checkVariableName(name);
variables.put(name, Double.valueOf(value));
return this;
}
private void checkVariableName(String name)
{
if (userFunctionNames.contains(name) || (Functions.getBuiltinFunction(name) != null))
{
throw new IllegalArgumentException("The variable name '" + name + "' is invalid. Since there exists a function with the same name");
}
}
public Expression setVariables(Map<String, Double> variables)
{
for (Map.Entry<String, Double> v : variables.entrySet())
{
setVariable(v.getKey(), v.getValue());
}
return this;
}
public Set<String> getVariableNames()
{
final Set<String> variables = new HashSet<>();
for (final Token t : tokens)
{
if (t.getType() == Token.TOKEN_VARIABLE)
{
variables.add(((VariableToken) t).getName());
}
}
return variables;
}
public ValidationResult validate(boolean checkVariablesSet)
{
final List<String> errors = new ArrayList<>(0);
if (checkVariablesSet)
{
/* check that all vars have a value set */
for (final Token t : tokens)
{
if (t.getType() == Token.TOKEN_VARIABLE)
{
final String var = ((VariableToken) t).getName();
if (!variables.containsKey(var))
{
errors.add("The setVariable '" + var + "' has not been set");
}
}
}
}
/*
* Check if the number of operands, functions and operators match. The idea is to increment a counter for operands and decrease it for operators. When a function occurs the number of available arguments has to be greater than or equals to the function's expected number of arguments. The
* count has to be larger than 1 at all times and exactly 1 after all tokens have been processed
*/
int count = 0;
for (Token tok : tokens)
{
switch (tok.getType())
{
case Token.TOKEN_NUMBER:
case Token.TOKEN_VARIABLE:
count++;
break;
case Token.TOKEN_FUNCTION:
final Function func = ((FunctionToken) tok).getFunction();
final int argsNum = func.getNumArguments();
if (argsNum > count)
{
errors.add("Not enough arguments for '" + func.getName() + "'");
}
if (argsNum > 1)
{
count -= argsNum - 1;
}
else if (argsNum == 0)
{
// see https://github.com/fasseg/exp4j/issues/59
count++;
}
break;
case Token.TOKEN_OPERATOR:
Operator op = ((OperatorToken) tok).getOperator();
if (op.getNumOperands() == 2)
{
count--;
}
break;
}
if (count < 1)
{
errors.add("Too many operators");
return new ValidationResult(false, errors);
}
}
if (count > 1)
{
errors.add("Too many operands");
}
return errors.size() == 0 ? ValidationResult.SUCCESS : new ValidationResult(false, errors);
}
public ValidationResult validate()
{
return validate(true);
}
public Future<Double> evaluateAsync(ExecutorService executor)
{
return executor.submit(() -> evaluate());
}
public double evaluate()
{
final ArrayStack output = new ArrayStack();
for (Token t : tokens)
{
if (t.getType() == Token.TOKEN_NUMBER)
{
output.push(((NumberToken) t).getValue());
}
else if (t.getType() == Token.TOKEN_VARIABLE)
{
final String name = ((VariableToken) t).getName();
final Double value = variables.get(name);
if (value == null)
{
throw new IllegalArgumentException("No value has been set for the setVariable '" + name + "'.");
}
output.push(value);
}
else if (t.getType() == Token.TOKEN_OPERATOR)
{
OperatorToken op = (OperatorToken) t;
if (output.size() < op.getOperator().getNumOperands())
{
throw new IllegalArgumentException("Invalid number of operands available for '" + op.getOperator().getSymbol() + "' operator");
}
if (op.getOperator().getNumOperands() == 2)
{
/* pop the operands and push the result of the operation */
double rightArg = output.pop();
double leftArg = output.pop();
output.push(op.getOperator().apply(leftArg, rightArg));
}
else if (op.getOperator().getNumOperands() == 1)
{
/* pop the operand and push the result of the operation */
double arg = output.pop();
output.push(op.getOperator().apply(arg));
}
}
else if (t.getType() == Token.TOKEN_FUNCTION)
{
FunctionToken func = (FunctionToken) t;
final int numArguments = func.getFunction().getNumArguments();
if (output.size() < numArguments)
{
throw new IllegalArgumentException("Invalid number of arguments available for '" + func.getFunction().getName() + "' function");
}
/* collect the arguments from the stack */
double[] args = new double[numArguments];
for (int j = numArguments - 1; j >= 0; j--)
{
args[j] = output.pop();
}
output.push(func.getFunction().apply(args));
}
}
if (output.size() > 1)
{
throw new IllegalArgumentException("Invalid number of items on the output queue. Might be caused by an invalid number of arguments for a function.");
}
return output.pop();
}
}

View File

@ -0,0 +1,218 @@
/*
* Copyright 2014 Frank Asseg
*
* Licensed 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 com.l2jmobius.gameserver.util.exp4j;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import com.l2jmobius.gameserver.util.exp4j.function.Function;
import com.l2jmobius.gameserver.util.exp4j.function.Functions;
import com.l2jmobius.gameserver.util.exp4j.operator.Operator;
import com.l2jmobius.gameserver.util.exp4j.shuntingyard.ShuntingYard;
/**
* Factory class for {@link Expression} instances. This class is the main API entrypoint. Users should create new {@link Expression} instances using this factory class.
*/
public class ExpressionBuilder
{
private final String expression;
private final Map<String, Function> userFunctions;
private final Map<String, Operator> userOperators;
private final Set<String> variableNames;
private boolean implicitMultiplication = true;
/**
* Create a new ExpressionBuilder instance and initialize it with a given expression string.
* @param expression the expression to be parsed
*/
public ExpressionBuilder(String expression)
{
if ((expression == null) || (expression.trim().length() == 0))
{
throw new IllegalArgumentException("Expression can not be empty");
}
this.expression = expression;
userOperators = new HashMap<>(4);
userFunctions = new HashMap<>(4);
variableNames = new HashSet<>(4);
}
/**
* Add a {@link com.l2jmobius.gameserver.util.exp4j.function.Function} implementation available for use in the expression
* @param function the custom {@link com.l2jmobius.gameserver.util.exp4j.function.Function} implementation that should be available for use in the expression.
* @return the ExpressionBuilder instance
*/
public ExpressionBuilder function(Function function)
{
userFunctions.put(function.getName(), function);
return this;
}
/**
* Add multiple {@link com.l2jmobius.gameserver.util.exp4j.function.Function} implementations available for use in the expression
* @param functions the custom {@link com.l2jmobius.gameserver.util.exp4j.function.Function} implementations
* @return the ExpressionBuilder instance
*/
public ExpressionBuilder functions(Function... functions)
{
for (Function f : functions)
{
userFunctions.put(f.getName(), f);
}
return this;
}
/**
* Add multiple {@link com.l2jmobius.gameserver.util.exp4j.function.Function} implementations available for use in the expression
* @param functions A {@link java.util.List} of custom {@link com.l2jmobius.gameserver.util.exp4j.function.Function} implementations
* @return the ExpressionBuilder instance
*/
public ExpressionBuilder functions(List<Function> functions)
{
for (Function f : functions)
{
userFunctions.put(f.getName(), f);
}
return this;
}
/**
* Declare variable names used in the expression
* @param variableNames the variables used in the expression
* @return the ExpressionBuilder instance
*/
public ExpressionBuilder variables(Set<String> variableNames)
{
this.variableNames.addAll(variableNames);
return this;
}
/**
* Declare variable names used in the expression
* @param variableNames the variables used in the expression
* @return the ExpressionBuilder instance
*/
public ExpressionBuilder variables(String... variableNames)
{
Collections.addAll(this.variableNames, variableNames);
return this;
}
/**
* Declare a variable used in the expression
* @param variableName the variable used in the expression
* @return the ExpressionBuilder instance
*/
public ExpressionBuilder variable(String variableName)
{
variableNames.add(variableName);
return this;
}
public ExpressionBuilder implicitMultiplication(boolean enabled)
{
implicitMultiplication = enabled;
return this;
}
/**
* Add an {@link com.l2jmobius.gameserver.util.exp4j.operator.Operator} which should be available for use in the expression
* @param operator the custom {@link com.l2jmobius.gameserver.util.exp4j.operator.Operator} to add
* @return the ExpressionBuilder instance
*/
public ExpressionBuilder operator(Operator operator)
{
checkOperatorSymbol(operator);
userOperators.put(operator.getSymbol(), operator);
return this;
}
private void checkOperatorSymbol(Operator op)
{
String name = op.getSymbol();
for (char ch : name.toCharArray())
{
if (!Operator.isAllowedOperatorChar(ch))
{
throw new IllegalArgumentException("The operator symbol '" + name + "' is invalid");
}
}
}
/**
* Add multiple {@link com.l2jmobius.gameserver.util.exp4j.operator.Operator} implementations which should be available for use in the expression
* @param operators the set of custom {@link com.l2jmobius.gameserver.util.exp4j.operator.Operator} implementations to add
* @return the ExpressionBuilder instance
*/
public ExpressionBuilder operator(Operator... operators)
{
for (Operator o : operators)
{
this.operator(o);
}
return this;
}
/**
* Add multiple {@link com.l2jmobius.gameserver.util.exp4j.operator.Operator} implementations which should be available for use in the expression
* @param operators the {@link java.util.List} of custom {@link com.l2jmobius.gameserver.util.exp4j.operator.Operator} implementations to add
* @return the ExpressionBuilder instance
*/
public ExpressionBuilder operator(List<Operator> operators)
{
for (Operator o : operators)
{
this.operator(o);
}
return this;
}
/**
* Build the {@link Expression} instance using the custom operators and functions set.
* @return an {@link Expression} instance which can be used to evaluate the result of the expression
*/
public Expression build()
{
if (expression.length() == 0)
{
throw new IllegalArgumentException("The expression can not be empty");
}
/* set the contants' varibale names */
variableNames.add("pi");
variableNames.add("Ο€");
variableNames.add("e");
variableNames.add("Ο†");
/* Check if there are duplicate vars/functions */
for (String var : variableNames)
{
if ((Functions.getBuiltinFunction(var) != null) || userFunctions.containsKey(var))
{
throw new IllegalArgumentException("A variable can not have the same name as a function [" + var + "]");
}
}
return new Expression(ShuntingYard.convertToRPN(expression, userFunctions, userOperators, variableNames, implicitMultiplication), userFunctions.keySet());
}
}

View File

@ -0,0 +1,61 @@
/*
* Copyright 2014 Frank Asseg
*
* Licensed 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 com.l2jmobius.gameserver.util.exp4j;
import java.util.List;
/**
* Contains the validation result for a given {@link Expression}
*/
public class ValidationResult
{
private final boolean valid;
private final List<String> errors;
/**
* Create a new instance
* @param valid Whether the validation of the expression was successful
* @param errors The list of errors returned if the validation was unsuccessful
*/
public ValidationResult(boolean valid, List<String> errors)
{
this.valid = valid;
this.errors = errors;
}
/**
* Check if an expression has been validated successfully
* @return true if the validation was successful, false otherwise
*/
public boolean isValid()
{
return valid;
}
/**
* Get the list of errors describing the issues while validating the expression
* @return The List of errors
*/
public List<String> getErrors()
{
return errors;
}
/**
* A static class representing a successful validation result
*/
public static final ValidationResult SUCCESS = new ValidationResult(true, null);
}

View File

@ -0,0 +1,132 @@
/*
* Copyright 2014 Frank Asseg
*
* Licensed 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 com.l2jmobius.gameserver.util.exp4j.function;
/**
* A class representing a Function which can be used in an expression
*/
public abstract class Function
{
protected final String name;
protected final int numArguments;
/**
* Create a new Function with a given name and number of arguments
* @param name the name of the Function
* @param numArguments the number of arguments the function takes
*/
public Function(String name, int numArguments)
{
if (numArguments < 0)
{
throw new IllegalArgumentException("The number of function arguments can not be less than 0 for '" + name + "'");
}
if (!isValidFunctionName(name))
{
throw new IllegalArgumentException("The function name '" + name + "' is invalid");
}
this.name = name;
this.numArguments = numArguments;
}
/**
* Create a new Function with a given name that takes a single argument
* @param name the name of the Function
*/
public Function(String name)
{
this(name, 1);
}
/**
* Get the name of the Function
* @return the name
*/
public String getName()
{
return name;
}
/**
* Get the number of arguments for this function
* @return the number of arguments
*/
public int getNumArguments()
{
return numArguments;
}
/**
* Method that does the actual calculation of the function value given the arguments
* @param args the set of arguments used for calculating the function
* @return the result of the function evaluation
*/
public abstract double apply(double... args);
/**
* Get the set of characters which are allowed for use in Function names.
* @return the set of characters allowed
* @deprecated since 0.4.5 All unicode letters are allowed to be used in function names since 0.4.3. This API Function can be safely ignored. Checks for function name validity can be done using Character.isLetter() et al.
*/
@Deprecated
public static char[] getAllowedFunctionCharacters()
{
char[] chars = new char[53];
int count = 0;
for (int i = 65; i < 91; i++)
{
chars[count++] = (char) i;
}
for (int i = 97; i < 123; i++)
{
chars[count++] = (char) i;
}
chars[count] = '_';
return chars;
}
public static boolean isValidFunctionName(final String name)
{
if (name == null)
{
return false;
}
final int size = name.length();
if (size == 0)
{
return false;
}
for (int i = 0; i < size; i++)
{
final char c = name.charAt(i);
if (Character.isLetter(c) || (c == '_'))
{
continue;
}
else if (Character.isDigit(c) && (i > 0))
{
continue;
}
return false;
}
return true;
}
}

View File

@ -0,0 +1,356 @@
/*
* Copyright 2014 Frank Asseg
*
* Licensed 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 com.l2jmobius.gameserver.util.exp4j.function;
/**
* Class representing the builtin functions available for use in expressions
*/
public class Functions
{
private static final int INDEX_SIN = 0;
private static final int INDEX_COS = 1;
private static final int INDEX_TAN = 2;
private static final int INDEX_COT = 3;
private static final int INDEX_LOG = 4;
private static final int INDEX_LOG1P = 5;
private static final int INDEX_ABS = 6;
private static final int INDEX_ACOS = 7;
private static final int INDEX_ASIN = 8;
private static final int INDEX_ATAN = 9;
private static final int INDEX_CBRT = 10;
private static final int INDEX_CEIL = 11;
private static final int INDEX_FLOOR = 12;
private static final int INDEX_SINH = 13;
private static final int INDEX_SQRT = 14;
private static final int INDEX_TANH = 15;
private static final int INDEX_COSH = 16;
private static final int INDEX_POW = 17;
private static final int INDEX_EXP = 18;
private static final int INDEX_EXPM1 = 19;
private static final int INDEX_LOG10 = 20;
private static final int INDEX_LOG2 = 21;
private static final int INDEX_SGN = 22;
private static final Function[] builtinFunctions = new Function[23];
static
{
builtinFunctions[INDEX_SIN] = new Function("sin")
{
@Override
public double apply(double... args)
{
return Math.sin(args[0]);
}
};
builtinFunctions[INDEX_COS] = new Function("cos")
{
@Override
public double apply(double... args)
{
return Math.cos(args[0]);
}
};
builtinFunctions[INDEX_TAN] = new Function("tan")
{
@Override
public double apply(double... args)
{
return Math.tan(args[0]);
}
};
builtinFunctions[INDEX_COT] = new Function("cot")
{
@Override
public double apply(double... args)
{
double tan = Math.tan(args[0]);
if (tan == 0d)
{
throw new ArithmeticException("Division by zero in cotangent!");
}
return 1d / Math.tan(args[0]);
}
};
builtinFunctions[INDEX_LOG] = new Function("log")
{
@Override
public double apply(double... args)
{
return Math.log(args[0]);
}
};
builtinFunctions[INDEX_LOG2] = new Function("log2")
{
@Override
public double apply(double... args)
{
return Math.log(args[0]) / Math.log(2d);
}
};
builtinFunctions[INDEX_LOG10] = new Function("log10")
{
@Override
public double apply(double... args)
{
return Math.log10(args[0]);
}
};
builtinFunctions[INDEX_LOG1P] = new Function("log1p")
{
@Override
public double apply(double... args)
{
return Math.log1p(args[0]);
}
};
builtinFunctions[INDEX_ABS] = new Function("abs")
{
@Override
public double apply(double... args)
{
return Math.abs(args[0]);
}
};
builtinFunctions[INDEX_ACOS] = new Function("acos")
{
@Override
public double apply(double... args)
{
return Math.acos(args[0]);
}
};
builtinFunctions[INDEX_ASIN] = new Function("asin")
{
@Override
public double apply(double... args)
{
return Math.asin(args[0]);
}
};
builtinFunctions[INDEX_ATAN] = new Function("atan")
{
@Override
public double apply(double... args)
{
return Math.atan(args[0]);
}
};
builtinFunctions[INDEX_CBRT] = new Function("cbrt")
{
@Override
public double apply(double... args)
{
return Math.cbrt(args[0]);
}
};
builtinFunctions[INDEX_FLOOR] = new Function("floor")
{
@Override
public double apply(double... args)
{
return Math.floor(args[0]);
}
};
builtinFunctions[INDEX_SINH] = new Function("sinh")
{
@Override
public double apply(double... args)
{
return Math.sinh(args[0]);
}
};
builtinFunctions[INDEX_SQRT] = new Function("sqrt")
{
@Override
public double apply(double... args)
{
return Math.sqrt(args[0]);
}
};
builtinFunctions[INDEX_TANH] = new Function("tanh")
{
@Override
public double apply(double... args)
{
return Math.tanh(args[0]);
}
};
builtinFunctions[INDEX_COSH] = new Function("cosh")
{
@Override
public double apply(double... args)
{
return Math.cosh(args[0]);
}
};
builtinFunctions[INDEX_CEIL] = new Function("ceil")
{
@Override
public double apply(double... args)
{
return Math.ceil(args[0]);
}
};
builtinFunctions[INDEX_POW] = new Function("pow", 2)
{
@Override
public double apply(double... args)
{
return Math.pow(args[0], args[1]);
}
};
builtinFunctions[INDEX_EXP] = new Function("exp", 1)
{
@Override
public double apply(double... args)
{
return Math.exp(args[0]);
}
};
builtinFunctions[INDEX_EXPM1] = new Function("expm1", 1)
{
@Override
public double apply(double... args)
{
return Math.expm1(args[0]);
}
};
builtinFunctions[INDEX_SGN] = new Function("signum", 1)
{
@Override
public double apply(double... args)
{
if (args[0] > 0)
{
return 1;
}
else if (args[0] < 0)
{
return -1;
}
else
{
return 0;
}
}
};
}
/**
* Get the builtin function for a given name
* @param name te name of the function
* @return a Function instance
*/
public static Function getBuiltinFunction(final String name)
{
if (name.equals("sin"))
{
return builtinFunctions[INDEX_SIN];
}
else if (name.equals("cos"))
{
return builtinFunctions[INDEX_COS];
}
else if (name.equals("tan"))
{
return builtinFunctions[INDEX_TAN];
}
else if (name.equals("cot"))
{
return builtinFunctions[INDEX_COT];
}
else if (name.equals("asin"))
{
return builtinFunctions[INDEX_ASIN];
}
else if (name.equals("acos"))
{
return builtinFunctions[INDEX_ACOS];
}
else if (name.equals("atan"))
{
return builtinFunctions[INDEX_ATAN];
}
else if (name.equals("sinh"))
{
return builtinFunctions[INDEX_SINH];
}
else if (name.equals("cosh"))
{
return builtinFunctions[INDEX_COSH];
}
else if (name.equals("tanh"))
{
return builtinFunctions[INDEX_TANH];
}
else if (name.equals("abs"))
{
return builtinFunctions[INDEX_ABS];
}
else if (name.equals("log"))
{
return builtinFunctions[INDEX_LOG];
}
else if (name.equals("log10"))
{
return builtinFunctions[INDEX_LOG10];
}
else if (name.equals("log2"))
{
return builtinFunctions[INDEX_LOG2];
}
else if (name.equals("log1p"))
{
return builtinFunctions[INDEX_LOG1P];
}
else if (name.equals("ceil"))
{
return builtinFunctions[INDEX_CEIL];
}
else if (name.equals("floor"))
{
return builtinFunctions[INDEX_FLOOR];
}
else if (name.equals("sqrt"))
{
return builtinFunctions[INDEX_SQRT];
}
else if (name.equals("cbrt"))
{
return builtinFunctions[INDEX_CBRT];
}
else if (name.equals("pow"))
{
return builtinFunctions[INDEX_POW];
}
else if (name.equals("exp"))
{
return builtinFunctions[INDEX_EXP];
}
else if (name.equals("expm1"))
{
return builtinFunctions[INDEX_EXPM1];
}
else if (name.equals("signum"))
{
return builtinFunctions[INDEX_SGN];
}
else
{
return null;
}
}
}

View File

@ -0,0 +1,161 @@
/*
* Copyright 2014 Frank Asseg
*
* Licensed 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 com.l2jmobius.gameserver.util.exp4j.operator;
/**
* Class representing operators that can be used in an expression
*/
public abstract class Operator
{
/**
* The precedence value for the addition operation
*/
public static final int PRECEDENCE_ADDITION = 500;
/**
* The precedence value for the subtraction operation
*/
public static final int PRECEDENCE_SUBTRACTION = PRECEDENCE_ADDITION;
/**
* The precedence value for the multiplication operation
*/
public static final int PRECEDENCE_MULTIPLICATION = 1000;
/**
* The precedence value for the division operation
*/
public static final int PRECEDENCE_DIVISION = PRECEDENCE_MULTIPLICATION;
/**
* The precedence value for the modulo operation
*/
public static final int PRECEDENCE_MODULO = PRECEDENCE_DIVISION;
/**
* The precedence value for the power operation
*/
public static final int PRECEDENCE_POWER = 10000;
/**
* The precedence value for the unary minus operation
*/
public static final int PRECEDENCE_UNARY_MINUS = 5000;
/**
* The precedence value for the unary plus operation
*/
public static final int PRECEDENCE_UNARY_PLUS = PRECEDENCE_UNARY_MINUS;
/**
* The set of allowed operator chars
*/
public static final char[] ALLOWED_OPERATOR_CHARS =
{
'+',
'-',
'*',
'/',
'%',
'^',
'!',
'#',
'§',
'$',
'&',
';',
':',
'~',
'<',
'>',
'|',
'='
};
protected final int numOperands;
protected final boolean leftAssociative;
protected final String symbol;
protected final int precedence;
/**
* Create a new operator for use in expressions
* @param symbol the symbol of the operator
* @param numberOfOperands the number of operands the operator takes (1 or 2)
* @param leftAssociative set to true if the operator is left associative, false if it is right associative
* @param precedence the precedence value of the operator
*/
public Operator(String symbol, int numberOfOperands, boolean leftAssociative, int precedence)
{
super();
this.numOperands = numberOfOperands;
this.leftAssociative = leftAssociative;
this.symbol = symbol;
this.precedence = precedence;
}
/**
* Check if a character is an allowed operator char
* @param ch the char to check
* @return true if the char is allowed an an operator symbol, false otherwise
*/
public static boolean isAllowedOperatorChar(char ch)
{
for (char allowed : ALLOWED_OPERATOR_CHARS)
{
if (ch == allowed)
{
return true;
}
}
return false;
}
/**
* Check if the operator is left associative
* @return true os the operator is left associative, false otherwise
*/
public boolean isLeftAssociative()
{
return leftAssociative;
}
/**
* Check the precedence value for the operator
* @return the precedence value
*/
public int getPrecedence()
{
return precedence;
}
/**
* Apply the operation on the given operands
* @param args the operands for the operation
* @return the calculated result of the operation
*/
public abstract double apply(double... args);
/**
* Get the operator symbol
* @return the symbol
*/
public String getSymbol()
{
return symbol;
}
/**
* Get the number of operands
* @return the number of operands
*/
public int getNumOperands()
{
return numOperands;
}
}

View File

@ -0,0 +1,135 @@
/*
* Copyright 2014 Frank Asseg
*
* Licensed 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 com.l2jmobius.gameserver.util.exp4j.operator;
public abstract class Operators
{
private static final int INDEX_ADDITION = 0;
private static final int INDEX_SUBTRACTION = 1;
private static final int INDEX_MUTLIPLICATION = 2;
private static final int INDEX_DIVISION = 3;
private static final int INDEX_POWER = 4;
private static final int INDEX_MODULO = 5;
private static final int INDEX_UNARYMINUS = 6;
private static final int INDEX_UNARYPLUS = 7;
private static final Operator[] builtinOperators = new Operator[8];
static
{
builtinOperators[INDEX_ADDITION] = new Operator("+", 2, true, Operator.PRECEDENCE_ADDITION)
{
@Override
public double apply(final double... args)
{
return args[0] + args[1];
}
};
builtinOperators[INDEX_SUBTRACTION] = new Operator("-", 2, true, Operator.PRECEDENCE_ADDITION)
{
@Override
public double apply(final double... args)
{
return args[0] - args[1];
}
};
builtinOperators[INDEX_UNARYMINUS] = new Operator("-", 1, false, Operator.PRECEDENCE_UNARY_MINUS)
{
@Override
public double apply(final double... args)
{
return -args[0];
}
};
builtinOperators[INDEX_UNARYPLUS] = new Operator("+", 1, false, Operator.PRECEDENCE_UNARY_PLUS)
{
@Override
public double apply(final double... args)
{
return args[0];
}
};
builtinOperators[INDEX_MUTLIPLICATION] = new Operator("*", 2, true, Operator.PRECEDENCE_MULTIPLICATION)
{
@Override
public double apply(final double... args)
{
return args[0] * args[1];
}
};
builtinOperators[INDEX_DIVISION] = new Operator("/", 2, true, Operator.PRECEDENCE_DIVISION)
{
@Override
public double apply(final double... args)
{
if (args[1] == 0d)
{
throw new ArithmeticException("Division by zero!");
}
return args[0] / args[1];
}
};
builtinOperators[INDEX_POWER] = new Operator("^", 2, false, Operator.PRECEDENCE_POWER)
{
@Override
public double apply(final double... args)
{
return Math.pow(args[0], args[1]);
}
};
builtinOperators[INDEX_MODULO] = new Operator("%", 2, true, Operator.PRECEDENCE_MODULO)
{
@Override
public double apply(final double... args)
{
if (args[1] == 0d)
{
throw new ArithmeticException("Division by zero!");
}
return args[0] % args[1];
}
};
}
public static Operator getBuiltinOperator(final char symbol, final int numArguments)
{
switch (symbol)
{
case '+':
if (numArguments != 1)
{
return builtinOperators[INDEX_ADDITION];
}
return builtinOperators[INDEX_UNARYPLUS];
case '-':
if (numArguments != 1)
{
return builtinOperators[INDEX_SUBTRACTION];
}
return builtinOperators[INDEX_UNARYMINUS];
case '*':
return builtinOperators[INDEX_MUTLIPLICATION];
case '/':
return builtinOperators[INDEX_DIVISION];
case '^':
return builtinOperators[INDEX_POWER];
case '%':
return builtinOperators[INDEX_MODULO];
default:
return null;
}
}
}

View File

@ -0,0 +1,121 @@
/*
* Copyright 2014 Frank Asseg
*
* Licensed 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 com.l2jmobius.gameserver.util.exp4j.shuntingyard;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Stack;
import com.l2jmobius.gameserver.util.exp4j.function.Function;
import com.l2jmobius.gameserver.util.exp4j.operator.Operator;
import com.l2jmobius.gameserver.util.exp4j.tokenizer.OperatorToken;
import com.l2jmobius.gameserver.util.exp4j.tokenizer.Token;
import com.l2jmobius.gameserver.util.exp4j.tokenizer.Tokenizer;
/**
* Shunting yard implementation to convert infix to reverse polish notation
*/
public class ShuntingYard
{
/**
* Convert a Set of tokens from infix to reverse polish notation
* @param expression the expression to convert
* @param userFunctions the custom functions used
* @param userOperators the custom operators used
* @param variableNames the variable names used in the expression
* @param implicitMultiplication set to fasle to turn off implicit multiplication
* @return a {@link com.l2jmobius.gameserver.util.exp4j.tokenizer.Token} array containing the result
*/
public static Token[] convertToRPN(final String expression, final Map<String, Function> userFunctions, final Map<String, Operator> userOperators, final Set<String> variableNames, final boolean implicitMultiplication)
{
final Stack<Token> stack = new Stack<>();
final List<Token> output = new ArrayList<>();
final Tokenizer tokenizer = new Tokenizer(expression, userFunctions, userOperators, variableNames, implicitMultiplication);
while (tokenizer.hasNext())
{
Token token = tokenizer.nextToken();
switch (token.getType())
{
case Token.TOKEN_NUMBER:
case Token.TOKEN_VARIABLE:
output.add(token);
break;
case Token.TOKEN_FUNCTION:
stack.add(token);
break;
case Token.TOKEN_SEPARATOR:
while (!stack.empty() && (stack.peek().getType() != Token.TOKEN_PARENTHESES_OPEN))
{
output.add(stack.pop());
}
if (stack.empty() || (stack.peek().getType() != Token.TOKEN_PARENTHESES_OPEN))
{
throw new IllegalArgumentException("Misplaced function separator ',' or mismatched parentheses");
}
break;
case Token.TOKEN_OPERATOR:
while (!stack.empty() && (stack.peek().getType() == Token.TOKEN_OPERATOR))
{
OperatorToken o1 = (OperatorToken) token;
OperatorToken o2 = (OperatorToken) stack.peek();
if ((o1.getOperator().getNumOperands() == 1) && (o2.getOperator().getNumOperands() == 2))
{
break;
}
else if ((o1.getOperator().isLeftAssociative() && (o1.getOperator().getPrecedence() <= o2.getOperator().getPrecedence())) || (o1.getOperator().getPrecedence() < o2.getOperator().getPrecedence()))
{
output.add(stack.pop());
}
else
{
break;
}
}
stack.push(token);
break;
case Token.TOKEN_PARENTHESES_OPEN:
stack.push(token);
break;
case Token.TOKEN_PARENTHESES_CLOSE:
while (stack.peek().getType() != Token.TOKEN_PARENTHESES_OPEN)
{
output.add(stack.pop());
}
stack.pop();
if (!stack.isEmpty() && (stack.peek().getType() == Token.TOKEN_FUNCTION))
{
output.add(stack.pop());
}
break;
default:
throw new IllegalArgumentException("Unknown Token type encountered. This should not happen");
}
}
while (!stack.empty())
{
Token t = stack.pop();
if ((t.getType() == Token.TOKEN_PARENTHESES_CLOSE) || (t.getType() == Token.TOKEN_PARENTHESES_OPEN))
{
throw new IllegalArgumentException("Mismatched parentheses detected. Please check the expression");
}
output.add(t);
}
return output.toArray(new Token[output.size()]);
}
}

View File

@ -0,0 +1,30 @@
/*
* Copyright 2014 Frank Asseg
*
* Licensed 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 com.l2jmobius.gameserver.util.exp4j.tokenizer;
/**
* Represents an argument separator in functions i.e: ','
*/
class ArgumentSeparatorToken extends Token
{
/**
* Create a new instance
*/
ArgumentSeparatorToken()
{
super(Token.TOKEN_SEPARATOR);
}
}

View File

@ -0,0 +1,30 @@
/*
* Copyright 2014 Frank Asseg
*
* Licensed 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 com.l2jmobius.gameserver.util.exp4j.tokenizer;
/**
* represents closed parentheses
*/
class CloseParenthesesToken extends Token
{
/**
* Creare a new instance
*/
CloseParenthesesToken()
{
super(Token.TOKEN_PARENTHESES_CLOSE);
}
}

View File

@ -0,0 +1,34 @@
/*
* Copyright 2014 Frank Asseg
*
* Licensed 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 com.l2jmobius.gameserver.util.exp4j.tokenizer;
import com.l2jmobius.gameserver.util.exp4j.function.Function;
public class FunctionToken extends Token
{
private final Function function;
public FunctionToken(final Function function)
{
super(Token.TOKEN_FUNCTION);
this.function = function;
}
public Function getFunction()
{
return function;
}
}

View File

@ -0,0 +1,48 @@
/*
* Copyright 2014 Frank Asseg
*
* Licensed 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 com.l2jmobius.gameserver.util.exp4j.tokenizer;
/**
* Represents a number in the expression
*/
public final class NumberToken extends Token
{
private final double value;
/**
* Create a new instance
* @param value the value of the number
*/
public NumberToken(double value)
{
super(TOKEN_NUMBER);
this.value = value;
}
NumberToken(final char[] expression, final int offset, final int len)
{
this(Double.parseDouble(String.valueOf(expression, offset, len)));
}
/**
* Get the value of the number
* @return the value
*/
public double getValue()
{
return value;
}
}

View File

@ -0,0 +1,24 @@
/*
* Copyright 2014 Frank Asseg
*
* Licensed 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 com.l2jmobius.gameserver.util.exp4j.tokenizer;
class OpenParenthesesToken extends Token
{
OpenParenthesesToken()
{
super(TOKEN_PARENTHESES_OPEN);
}
}

View File

@ -0,0 +1,49 @@
/*
* Copyright 2014 Frank Asseg
*
* Licensed 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 com.l2jmobius.gameserver.util.exp4j.tokenizer;
import com.l2jmobius.gameserver.util.exp4j.operator.Operator;
/**
* Represents an operator used in expressions
*/
public class OperatorToken extends Token
{
private final Operator operator;
/**
* Create a new instance
* @param op the operator
*/
public OperatorToken(Operator op)
{
super(Token.TOKEN_OPERATOR);
if (op == null)
{
throw new IllegalArgumentException("Operator is unknown for token.");
}
this.operator = op;
}
/**
* Get the operator for that token
* @return the operator
*/
public Operator getOperator()
{
return operator;
}
}

View File

@ -0,0 +1,43 @@
/*
* Copyright 2014 Frank Asseg
*
* Licensed 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 com.l2jmobius.gameserver.util.exp4j.tokenizer;
/**
* Abstract class for tokens used by exp4j to tokenize expressions
*/
public abstract class Token
{
public static final short TOKEN_NUMBER = 1;
public static final short TOKEN_OPERATOR = 2;
public static final short TOKEN_FUNCTION = 3;
public static final short TOKEN_PARENTHESES_OPEN = 4;
public static final short TOKEN_PARENTHESES_CLOSE = 5;
public static final short TOKEN_VARIABLE = 6;
public static final short TOKEN_SEPARATOR = 7;
protected final int type;
Token(int type)
{
this.type = type;
}
public int getType()
{
return type;
}
}

View File

@ -0,0 +1,334 @@
/*
* Copyright 2014 Frank Asseg
*
* Licensed 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 com.l2jmobius.gameserver.util.exp4j.tokenizer;
import java.util.Map;
import java.util.Set;
import com.l2jmobius.gameserver.util.exp4j.function.Function;
import com.l2jmobius.gameserver.util.exp4j.function.Functions;
import com.l2jmobius.gameserver.util.exp4j.operator.Operator;
import com.l2jmobius.gameserver.util.exp4j.operator.Operators;
public class Tokenizer
{
private final char[] expression;
private final int expressionLength;
private final Map<String, Function> userFunctions;
private final Map<String, Operator> userOperators;
private final Set<String> variableNames;
private final boolean implicitMultiplication;
private int pos = 0;
private Token lastToken;
public Tokenizer(String expression, final Map<String, Function> userFunctions, final Map<String, Operator> userOperators, final Set<String> variableNames, final boolean implicitMultiplication)
{
this.expression = expression.trim().toCharArray();
this.expressionLength = this.expression.length;
this.userFunctions = userFunctions;
this.userOperators = userOperators;
this.variableNames = variableNames;
this.implicitMultiplication = implicitMultiplication;
}
public Tokenizer(String expression, final Map<String, Function> userFunctions, final Map<String, Operator> userOperators, final Set<String> variableNames)
{
this.expression = expression.trim().toCharArray();
this.expressionLength = this.expression.length;
this.userFunctions = userFunctions;
this.userOperators = userOperators;
this.variableNames = variableNames;
this.implicitMultiplication = true;
}
public boolean hasNext()
{
return this.expression.length > pos;
}
public Token nextToken()
{
char ch = expression[pos];
while (Character.isWhitespace(ch))
{
ch = expression[++pos];
}
if (Character.isDigit(ch) || (ch == '.'))
{
if (lastToken != null)
{
if (lastToken.getType() == Token.TOKEN_NUMBER)
{
throw new IllegalArgumentException("Unable to parse char '" + ch + "' (Code:" + (int) ch + ") at [" + pos + "]");
}
else if (implicitMultiplication && ((lastToken.getType() != Token.TOKEN_OPERATOR) && (lastToken.getType() != Token.TOKEN_PARENTHESES_OPEN) && (lastToken.getType() != Token.TOKEN_FUNCTION) && (lastToken.getType() != Token.TOKEN_SEPARATOR)))
{
// insert an implicit multiplication token
lastToken = new OperatorToken(Operators.getBuiltinOperator('*', 2));
return lastToken;
}
}
return parseNumberToken(ch);
}
else if (isArgumentSeparator(ch))
{
return parseArgumentSeparatorToken(ch);
}
else if (isOpenParentheses(ch))
{
if ((lastToken != null) && implicitMultiplication && ((lastToken.getType() != Token.TOKEN_OPERATOR) && (lastToken.getType() != Token.TOKEN_PARENTHESES_OPEN) && (lastToken.getType() != Token.TOKEN_FUNCTION) && (lastToken.getType() != Token.TOKEN_SEPARATOR)))
{
// insert an implicit multiplication token
lastToken = new OperatorToken(Operators.getBuiltinOperator('*', 2));
return lastToken;
}
return parseParentheses(true);
}
else if (isCloseParentheses(ch))
{
return parseParentheses(false);
}
else if (Operator.isAllowedOperatorChar(ch))
{
return parseOperatorToken(ch);
}
else if (isAlphabetic(ch) || (ch == '_'))
{
// parse the name which can be a setVariable or a function
if ((lastToken != null) && implicitMultiplication && ((lastToken.getType() != Token.TOKEN_OPERATOR) && (lastToken.getType() != Token.TOKEN_PARENTHESES_OPEN) && (lastToken.getType() != Token.TOKEN_FUNCTION) && (lastToken.getType() != Token.TOKEN_SEPARATOR)))
{
// insert an implicit multiplication token
lastToken = new OperatorToken(Operators.getBuiltinOperator('*', 2));
return lastToken;
}
return parseFunctionOrVariable();
}
throw new IllegalArgumentException("Unable to parse char '" + ch + "' (Code:" + (int) ch + ") at [" + pos + "]");
}
private Token parseArgumentSeparatorToken(char ch)
{
this.pos++;
this.lastToken = new ArgumentSeparatorToken();
return lastToken;
}
private boolean isArgumentSeparator(char ch)
{
return ch == ',';
}
private Token parseParentheses(final boolean open)
{
if (open)
{
this.lastToken = new OpenParenthesesToken();
}
else
{
this.lastToken = new CloseParenthesesToken();
}
this.pos++;
return lastToken;
}
private boolean isOpenParentheses(char ch)
{
return (ch == '(') || (ch == '{') || (ch == '[');
}
private boolean isCloseParentheses(char ch)
{
return (ch == ')') || (ch == '}') || (ch == ']');
}
private Token parseFunctionOrVariable()
{
final int offset = this.pos;
int testPos;
int lastValidLen = 1;
Token lastValidToken = null;
int len = 1;
if (isEndOfExpression(offset))
{
this.pos++;
}
testPos = (offset + len) - 1;
while (!isEndOfExpression(testPos) && isVariableOrFunctionCharacter(expression[testPos]))
{
String name = new String(expression, offset, len);
if ((variableNames != null) && variableNames.contains(name))
{
lastValidLen = len;
lastValidToken = new VariableToken(name);
}
else
{
final Function f = getFunction(name);
if (f != null)
{
lastValidLen = len;
lastValidToken = new FunctionToken(f);
}
}
len++;
testPos = (offset + len) - 1;
}
if (lastValidToken == null)
{
throw new UnknownFunctionOrVariableException(new String(expression), pos, len);
}
pos += lastValidLen;
lastToken = lastValidToken;
return lastToken;
}
private Function getFunction(String name)
{
Function f = null;
if (this.userFunctions != null)
{
f = this.userFunctions.get(name);
}
if (f == null)
{
f = Functions.getBuiltinFunction(name);
}
return f;
}
private Token parseOperatorToken(char firstChar)
{
final int offset = this.pos;
int len = 1;
final StringBuilder symbol = new StringBuilder();
Operator lastValid = null;
symbol.append(firstChar);
while (!isEndOfExpression(offset + len) && Operator.isAllowedOperatorChar(expression[offset + len]))
{
symbol.append(expression[offset + len++]);
}
while (symbol.length() > 0)
{
Operator op = this.getOperator(symbol.toString());
if (op == null)
{
symbol.setLength(symbol.length() - 1);
}
else
{
lastValid = op;
break;
}
}
pos += symbol.length();
lastToken = new OperatorToken(lastValid);
return lastToken;
}
private Operator getOperator(String symbol)
{
Operator op = null;
if (this.userOperators != null)
{
op = this.userOperators.get(symbol);
}
if ((op == null) && (symbol.length() == 1))
{
int argc = 2;
if (lastToken == null)
{
argc = 1;
}
else
{
int lastTokenType = lastToken.getType();
if ((lastTokenType == Token.TOKEN_PARENTHESES_OPEN) || (lastTokenType == Token.TOKEN_SEPARATOR))
{
argc = 1;
}
else if (lastTokenType == Token.TOKEN_OPERATOR)
{
final Operator lastOp = ((OperatorToken) lastToken).getOperator();
if ((lastOp.getNumOperands() == 2) || ((lastOp.getNumOperands() == 1) && !lastOp.isLeftAssociative()))
{
argc = 1;
}
}
}
op = Operators.getBuiltinOperator(symbol.charAt(0), argc);
}
return op;
}
private Token parseNumberToken(final char firstChar)
{
final int offset = this.pos;
int len = 1;
this.pos++;
if (isEndOfExpression(offset + len))
{
lastToken = new NumberToken(Double.parseDouble(String.valueOf(firstChar)));
return lastToken;
}
while (!isEndOfExpression(offset + len) && isNumeric(expression[offset + len], (expression[(offset + len) - 1] == 'e') || (expression[(offset + len) - 1] == 'E')))
{
len++;
this.pos++;
}
// check if the e is at the end
if ((expression[(offset + len) - 1] == 'e') || (expression[(offset + len) - 1] == 'E'))
{
// since the e is at the end it's not part of the number and a rollback is necessary
len--;
pos--;
}
lastToken = new NumberToken(expression, offset, len);
return lastToken;
}
private static boolean isNumeric(char ch, boolean lastCharE)
{
return Character.isDigit(ch) || (ch == '.') || (ch == 'e') || (ch == 'E') || (lastCharE && ((ch == '-') || (ch == '+')));
}
public static boolean isAlphabetic(int codePoint)
{
return Character.isLetter(codePoint);
}
public static boolean isVariableOrFunctionCharacter(int codePoint)
{
return isAlphabetic(codePoint) || Character.isDigit(codePoint) || (codePoint == '_') || (codePoint == '.');
}
private boolean isEndOfExpression(int offset)
{
return this.expressionLength <= offset;
}
}

View File

@ -0,0 +1,65 @@
package com.l2jmobius.gameserver.util.exp4j.tokenizer;
/**
* This exception is being thrown whenever {@link Tokenizer} finds unknown function or variable.
* @author Bartosz Firyn (sarxos)
*/
public class UnknownFunctionOrVariableException extends IllegalArgumentException
{
private final String message;
private final String expression;
private final String token;
private final int position;
public UnknownFunctionOrVariableException(String expression, int position, int length)
{
this.expression = expression;
this.token = token(expression, position, length);
this.position = position;
this.message = "Unknown function or variable '" + token + "' at pos " + position + " in expression '" + expression + "'";
}
private static String token(String expression, int position, int length)
{
int len = expression.length();
int end = (position + length) - 1;
if (len < end)
{
end = len;
}
return expression.substring(position, end);
}
@Override
public String getMessage()
{
return message;
}
/**
* @return Expression which contains unknown function or variable
*/
public String getExpression()
{
return expression;
}
/**
* @return The name of unknown function or variable
*/
public String getToken()
{
return token;
}
/**
* @return The position of unknown function or variable
*/
public int getPosition()
{
return position;
}
}

View File

@ -0,0 +1,43 @@
/*
* Copyright 2014 Frank Asseg
*
* Licensed 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 com.l2jmobius.gameserver.util.exp4j.tokenizer;
/**
* represents a setVariable used in an expression
*/
public class VariableToken extends Token
{
private final String name;
/**
* Get the name of the setVariable
* @return the name
*/
public String getName()
{
return name;
}
/**
* Create a new instance
* @param name the name of the setVariable
*/
public VariableToken(String name)
{
super(TOKEN_VARIABLE);
this.name = name;
}
}

View File

@ -7,7 +7,7 @@
<listEntry value="1"/>
</listAttribute>
<stringAttribute key="org.eclipse.debug.core.source_locator_id" value="org.eclipse.jdt.launching.sourceLocator.JavaSourceLookupDirector"/>
<stringAttribute key="org.eclipse.debug.core.source_locator_memento" value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;sourceLookupDirector&gt;&#13;&#10;&lt;sourceContainers duplicates=&quot;false&quot;&gt;&#13;&#10;&lt;container memento=&quot;&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot; standalone=&amp;quot;no&amp;quot;?&amp;gt;&amp;#13;&amp;#10;&amp;lt;archive detectRoot=&amp;quot;true&amp;quot; path=&amp;quot;/L2J_Mobius_Underground/dist/libs/c3p0-0.9.5.2.jar&amp;quot;/&amp;gt;&amp;#13;&amp;#10;&quot; typeId=&quot;org.eclipse.debug.core.containerType.archive&quot;/&gt;&#13;&#10;&lt;container memento=&quot;&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot; standalone=&amp;quot;no&amp;quot;?&amp;gt;&amp;#13;&amp;#10;&amp;lt;archive detectRoot=&amp;quot;true&amp;quot; path=&amp;quot;/L2J_Mobius_Underground/dist/libs/ecj-4.4.2.jar&amp;quot;/&amp;gt;&amp;#13;&amp;#10;&quot; typeId=&quot;org.eclipse.debug.core.containerType.archive&quot;/&gt;&#13;&#10;&lt;container memento=&quot;&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot; standalone=&amp;quot;no&amp;quot;?&amp;gt;&amp;#13;&amp;#10;&amp;lt;archive detectRoot=&amp;quot;true&amp;quot; path=&amp;quot;/L2J_Mobius_Underground/dist/libs/exp4j-0.4.8.jar&amp;quot;/&amp;gt;&amp;#13;&amp;#10;&quot; typeId=&quot;org.eclipse.debug.core.containerType.archive&quot;/&gt;&#13;&#10;&lt;container memento=&quot;&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot; standalone=&amp;quot;no&amp;quot;?&amp;gt;&amp;#13;&amp;#10;&amp;lt;archive detectRoot=&amp;quot;true&amp;quot; path=&amp;quot;/L2J_Mobius_Underground/dist/libs/mail-1.5.2.jar&amp;quot;/&amp;gt;&amp;#13;&amp;#10;&quot; typeId=&quot;org.eclipse.debug.core.containerType.archive&quot;/&gt;&#13;&#10;&lt;container memento=&quot;&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot; standalone=&amp;quot;no&amp;quot;?&amp;gt;&amp;#13;&amp;#10;&amp;lt;archive detectRoot=&amp;quot;true&amp;quot; path=&amp;quot;/L2J_Mobius_Underground/dist/libs/mchange-commons-java-0.2.12.jar&amp;quot;/&amp;gt;&amp;#13;&amp;#10;&quot; typeId=&quot;org.eclipse.debug.core.containerType.archive&quot;/&gt;&#13;&#10;&lt;container memento=&quot;&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot; standalone=&amp;quot;no&amp;quot;?&amp;gt;&amp;#13;&amp;#10;&amp;lt;archive detectRoot=&amp;quot;true&amp;quot; path=&amp;quot;/L2J_Mobius_Underground/dist/libs/mysql-connector-java-5.1.43.jar&amp;quot;/&amp;gt;&amp;#13;&amp;#10;&quot; typeId=&quot;org.eclipse.debug.core.containerType.archive&quot;/&gt;&#13;&#10;&lt;container memento=&quot;&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot; standalone=&amp;quot;no&amp;quot;?&amp;gt;&amp;#13;&amp;#10;&amp;lt;archive detectRoot=&amp;quot;true&amp;quot; path=&amp;quot;/L2J_Mobius_Underground/dist/libs/netty-all-4.1.14.Final.jar&amp;quot;/&amp;gt;&amp;#13;&amp;#10;&quot; typeId=&quot;org.eclipse.debug.core.containerType.archive&quot;/&gt;&#13;&#10;&lt;container memento=&quot;&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot; standalone=&amp;quot;no&amp;quot;?&amp;gt;&amp;#13;&amp;#10;&amp;lt;default/&amp;gt;&amp;#13;&amp;#10;&quot; typeId=&quot;org.eclipse.debug.core.containerType.default&quot;/&gt;&#13;&#10;&lt;/sourceContainers&gt;&#13;&#10;&lt;/sourceLookupDirector&gt;&#13;&#10;"/>
<stringAttribute key="org.eclipse.debug.core.source_locator_memento" value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;sourceLookupDirector&gt;&#13;&#10;&lt;sourceContainers duplicates=&quot;false&quot;&gt;&#13;&#10;&lt;container memento=&quot;&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot; standalone=&amp;quot;no&amp;quot;?&amp;gt;&amp;#13;&amp;#10;&amp;lt;archive detectRoot=&amp;quot;true&amp;quot; path=&amp;quot;/L2J_Mobius_Underground/dist/libs/c3p0-0.9.5.2.jar&amp;quot;/&amp;gt;&amp;#13;&amp;#10;&quot; typeId=&quot;org.eclipse.debug.core.containerType.archive&quot;/&gt;&#13;&#10;&lt;container memento=&quot;&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot; standalone=&amp;quot;no&amp;quot;?&amp;gt;&amp;#13;&amp;#10;&amp;lt;archive detectRoot=&amp;quot;true&amp;quot; path=&amp;quot;/L2J_Mobius_Underground/dist/libs/ecj-4.4.2.jar&amp;quot;/&amp;gt;&amp;#13;&amp;#10;&quot; typeId=&quot;org.eclipse.debug.core.containerType.archive&quot;/&gt;&#13;&#10;&lt;container memento=&quot;&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot; standalone=&amp;quot;no&amp;quot;?&amp;gt;&amp;#13;&amp;#10;&amp;lt;archive detectRoot=&amp;quot;true&amp;quot; path=&amp;quot;/L2J_Mobius_Underground/dist/libs/mail-1.5.2.jar&amp;quot;/&amp;gt;&amp;#13;&amp;#10;&quot; typeId=&quot;org.eclipse.debug.core.containerType.archive&quot;/&gt;&#13;&#10;&lt;container memento=&quot;&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot; standalone=&amp;quot;no&amp;quot;?&amp;gt;&amp;#13;&amp;#10;&amp;lt;archive detectRoot=&amp;quot;true&amp;quot; path=&amp;quot;/L2J_Mobius_Underground/dist/libs/mchange-commons-java-0.2.12.jar&amp;quot;/&amp;gt;&amp;#13;&amp;#10;&quot; typeId=&quot;org.eclipse.debug.core.containerType.archive&quot;/&gt;&#13;&#10;&lt;container memento=&quot;&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot; standalone=&amp;quot;no&amp;quot;?&amp;gt;&amp;#13;&amp;#10;&amp;lt;archive detectRoot=&amp;quot;true&amp;quot; path=&amp;quot;/L2J_Mobius_Underground/dist/libs/mysql-connector-java-5.1.43.jar&amp;quot;/&amp;gt;&amp;#13;&amp;#10;&quot; typeId=&quot;org.eclipse.debug.core.containerType.archive&quot;/&gt;&#13;&#10;&lt;container memento=&quot;&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot; standalone=&amp;quot;no&amp;quot;?&amp;gt;&amp;#13;&amp;#10;&amp;lt;archive detectRoot=&amp;quot;true&amp;quot; path=&amp;quot;/L2J_Mobius_Underground/dist/libs/netty-all-4.1.14.Final.jar&amp;quot;/&amp;gt;&amp;#13;&amp;#10;&quot; typeId=&quot;org.eclipse.debug.core.containerType.archive&quot;/&gt;&#13;&#10;&lt;container memento=&quot;&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot; standalone=&amp;quot;no&amp;quot;?&amp;gt;&amp;#13;&amp;#10;&amp;lt;default/&amp;gt;&amp;#13;&amp;#10;&quot; typeId=&quot;org.eclipse.debug.core.containerType.default&quot;/&gt;&#13;&#10;&lt;/sourceContainers&gt;&#13;&#10;&lt;/sourceLookupDirector&gt;&#13;&#10;"/>
<listAttribute key="org.eclipse.debug.ui.favoriteGroups">
<listEntry value="org.eclipse.debug.ui.launchGroup.debug"/>
</listAttribute>

View File

@ -7,7 +7,7 @@
<listEntry value="1"/>
</listAttribute>
<stringAttribute key="org.eclipse.debug.core.source_locator_id" value="org.eclipse.jdt.launching.sourceLocator.JavaSourceLookupDirector"/>
<stringAttribute key="org.eclipse.debug.core.source_locator_memento" value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;sourceLookupDirector&gt;&#13;&#10;&lt;sourceContainers duplicates=&quot;false&quot;&gt;&#13;&#10;&lt;container memento=&quot;&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot; standalone=&amp;quot;no&amp;quot;?&amp;gt;&amp;#13;&amp;#10;&amp;lt;archive detectRoot=&amp;quot;true&amp;quot; path=&amp;quot;/L2J_Mobius_Underground/dist/libs/c3p0-0.9.5.2.jar&amp;quot;/&amp;gt;&amp;#13;&amp;#10;&quot; typeId=&quot;org.eclipse.debug.core.containerType.archive&quot;/&gt;&#13;&#10;&lt;container memento=&quot;&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot; standalone=&amp;quot;no&amp;quot;?&amp;gt;&amp;#13;&amp;#10;&amp;lt;archive detectRoot=&amp;quot;true&amp;quot; path=&amp;quot;/L2J_Mobius_Underground/dist/libs/ecj-4.4.2.jar&amp;quot;/&amp;gt;&amp;#13;&amp;#10;&quot; typeId=&quot;org.eclipse.debug.core.containerType.archive&quot;/&gt;&#13;&#10;&lt;container memento=&quot;&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot; standalone=&amp;quot;no&amp;quot;?&amp;gt;&amp;#13;&amp;#10;&amp;lt;archive detectRoot=&amp;quot;true&amp;quot; path=&amp;quot;/L2J_Mobius_Underground/dist/libs/exp4j-0.4.8.jar&amp;quot;/&amp;gt;&amp;#13;&amp;#10;&quot; typeId=&quot;org.eclipse.debug.core.containerType.archive&quot;/&gt;&#13;&#10;&lt;container memento=&quot;&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot; standalone=&amp;quot;no&amp;quot;?&amp;gt;&amp;#13;&amp;#10;&amp;lt;archive detectRoot=&amp;quot;true&amp;quot; path=&amp;quot;/L2J_Mobius_Underground/dist/libs/mail-1.5.2.jar&amp;quot;/&amp;gt;&amp;#13;&amp;#10;&quot; typeId=&quot;org.eclipse.debug.core.containerType.archive&quot;/&gt;&#13;&#10;&lt;container memento=&quot;&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot; standalone=&amp;quot;no&amp;quot;?&amp;gt;&amp;#13;&amp;#10;&amp;lt;archive detectRoot=&amp;quot;true&amp;quot; path=&amp;quot;/L2J_Mobius_Underground/dist/libs/mchange-commons-java-0.2.12.jar&amp;quot;/&amp;gt;&amp;#13;&amp;#10;&quot; typeId=&quot;org.eclipse.debug.core.containerType.archive&quot;/&gt;&#13;&#10;&lt;container memento=&quot;&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot; standalone=&amp;quot;no&amp;quot;?&amp;gt;&amp;#13;&amp;#10;&amp;lt;archive detectRoot=&amp;quot;true&amp;quot; path=&amp;quot;/L2J_Mobius_Underground/dist/libs/mysql-connector-java-5.1.43.jar&amp;quot;/&amp;gt;&amp;#13;&amp;#10;&quot; typeId=&quot;org.eclipse.debug.core.containerType.archive&quot;/&gt;&#13;&#10;&lt;container memento=&quot;&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot; standalone=&amp;quot;no&amp;quot;?&amp;gt;&amp;#13;&amp;#10;&amp;lt;archive detectRoot=&amp;quot;true&amp;quot; path=&amp;quot;/L2J_Mobius_Underground/dist/libs/netty-all-4.1.14.Final.jar&amp;quot;/&amp;gt;&amp;#13;&amp;#10;&quot; typeId=&quot;org.eclipse.debug.core.containerType.archive&quot;/&gt;&#13;&#10;&lt;container memento=&quot;&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot; standalone=&amp;quot;no&amp;quot;?&amp;gt;&amp;#13;&amp;#10;&amp;lt;default/&amp;gt;&amp;#13;&amp;#10;&quot; typeId=&quot;org.eclipse.debug.core.containerType.default&quot;/&gt;&#13;&#10;&lt;/sourceContainers&gt;&#13;&#10;&lt;/sourceLookupDirector&gt;&#13;&#10;"/>
<stringAttribute key="org.eclipse.debug.core.source_locator_memento" value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;sourceLookupDirector&gt;&#13;&#10;&lt;sourceContainers duplicates=&quot;false&quot;&gt;&#13;&#10;&lt;container memento=&quot;&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot; standalone=&amp;quot;no&amp;quot;?&amp;gt;&amp;#13;&amp;#10;&amp;lt;archive detectRoot=&amp;quot;true&amp;quot; path=&amp;quot;/L2J_Mobius_Underground/dist/libs/c3p0-0.9.5.2.jar&amp;quot;/&amp;gt;&amp;#13;&amp;#10;&quot; typeId=&quot;org.eclipse.debug.core.containerType.archive&quot;/&gt;&#13;&#10;&lt;container memento=&quot;&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot; standalone=&amp;quot;no&amp;quot;?&amp;gt;&amp;#13;&amp;#10;&amp;lt;archive detectRoot=&amp;quot;true&amp;quot; path=&amp;quot;/L2J_Mobius_Underground/dist/libs/ecj-4.4.2.jar&amp;quot;/&amp;gt;&amp;#13;&amp;#10;&quot; typeId=&quot;org.eclipse.debug.core.containerType.archive&quot;/&gt;&#13;&#10;&lt;container memento=&quot;&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot; standalone=&amp;quot;no&amp;quot;?&amp;gt;&amp;#13;&amp;#10;&amp;lt;archive detectRoot=&amp;quot;true&amp;quot; path=&amp;quot;/L2J_Mobius_Underground/dist/libs/mail-1.5.2.jar&amp;quot;/&amp;gt;&amp;#13;&amp;#10;&quot; typeId=&quot;org.eclipse.debug.core.containerType.archive&quot;/&gt;&#13;&#10;&lt;container memento=&quot;&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot; standalone=&amp;quot;no&amp;quot;?&amp;gt;&amp;#13;&amp;#10;&amp;lt;archive detectRoot=&amp;quot;true&amp;quot; path=&amp;quot;/L2J_Mobius_Underground/dist/libs/mchange-commons-java-0.2.12.jar&amp;quot;/&amp;gt;&amp;#13;&amp;#10;&quot; typeId=&quot;org.eclipse.debug.core.containerType.archive&quot;/&gt;&#13;&#10;&lt;container memento=&quot;&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot; standalone=&amp;quot;no&amp;quot;?&amp;gt;&amp;#13;&amp;#10;&amp;lt;archive detectRoot=&amp;quot;true&amp;quot; path=&amp;quot;/L2J_Mobius_Underground/dist/libs/mysql-connector-java-5.1.43.jar&amp;quot;/&amp;gt;&amp;#13;&amp;#10;&quot; typeId=&quot;org.eclipse.debug.core.containerType.archive&quot;/&gt;&#13;&#10;&lt;container memento=&quot;&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot; standalone=&amp;quot;no&amp;quot;?&amp;gt;&amp;#13;&amp;#10;&amp;lt;archive detectRoot=&amp;quot;true&amp;quot; path=&amp;quot;/L2J_Mobius_Underground/dist/libs/netty-all-4.1.14.Final.jar&amp;quot;/&amp;gt;&amp;#13;&amp;#10;&quot; typeId=&quot;org.eclipse.debug.core.containerType.archive&quot;/&gt;&#13;&#10;&lt;container memento=&quot;&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;UTF-8&amp;quot; standalone=&amp;quot;no&amp;quot;?&amp;gt;&amp;#13;&amp;#10;&amp;lt;default/&amp;gt;&amp;#13;&amp;#10;&quot; typeId=&quot;org.eclipse.debug.core.containerType.default&quot;/&gt;&#13;&#10;&lt;/sourceContainers&gt;&#13;&#10;&lt;/sourceLookupDirector&gt;&#13;&#10;"/>
<listAttribute key="org.eclipse.debug.ui.favoriteGroups">
<listEntry value="org.eclipse.debug.ui.launchGroup.debug"/>
</listAttribute>