package jamel.data;
import java.security.InvalidParameterException;
import java.util.regex.Pattern;
import jamel.util.JamelObject;
import jamel.util.Simulation;
/**
* The expression factory.
*/
public class ExpressionFactory extends JamelObject {
/**
* Returns a query cleaned from useless parentheses and spaces.
*
* @param query
* the query to be cleaned up.
* @return the cleaned up string.
*/
private static String cleanUp(final String query) {
final String result;
/*if (query.contains(" ")) {
final String str2 = query.replace(" ", "");
result = cleanUp(str2);
} else*/ if (query.startsWith("+")) {
final String str2 = query.substring(1, query.length());
result = cleanUp(str2);
} else if (query.charAt(0) == '(' && query.charAt(query.length() - 1) == ')') {
int count = 1;
for (int i = 1; i < query.length() - 1; i++) {
if (query.charAt(i) == '(') {
count++;
} else if (query.charAt(i) == ')') {
count--;
if (count == 0) {
break;
}
}
}
if (count == 1) {
// Removes the global parentheses.
final String str2 = query.substring(1, query.length() - 1);
result = cleanUp(str2);
} else {
// Nothing to remove.
result = query;
}
} else {
// Nothing to remove.
result = query;
}
return result.trim();
}
/**
* Returns the specified addition.
*
* @param arg1
* the augend.
* @param arg2
* the addend.
* @return the specified addition.
*/
private static Expression getAddition(final Expression arg1, final Expression arg2) {
if (arg1 == null || arg2 == null) {
throw new InvalidParameterException("Null");
}
final Expression result = new Expression() {
@Override
public Double getValue() {
final Double value;
if (arg1.getValue() == null || arg2.getValue() == null) {
value = null;
} else {
value = arg1.getValue() + arg2.getValue();
}
return value;
}
@Override
public String toString() {
return "(" + arg1.toString() + " + " + arg2.toString() + ")";
}
};
return result;
}
/**
* Returns the specified division.
*
* @param arg1
* the dividend.
* @param arg2
* the divisor.
* @return the specified division.
*/
private static Expression getDivision(final Expression arg1, final Expression arg2) {
if (arg1 == null || arg2 == null) {
throw new InvalidParameterException("Null");
}
final Expression result = new Expression() {
@Override
public Double getValue() {
final Double value;
if (arg1.getValue() == null || arg2.getValue() == null || arg2.getValue() == 0) {
value = null;
} else {
value = arg1.getValue() / arg2.getValue();
}
return value;
}
@Override
public String toString() {
return arg1.toString() + " / " + arg2.toString();
}
};
return result;
}
/**
* Returns the specified multiplication.
*
* @param arg1
* the first factor.
* @param arg2
* the second factor.
* @return the specified multiplication.
*/
private static Expression getMultiplication(final Expression arg1, final Expression arg2) {
if (arg1 == null || arg2 == null) {
throw new IllegalArgumentException("Null.");
}
final Expression result = new Expression() {
@Override
public Double getValue() {
final Double value;
if (arg1.getValue() == null || arg2.getValue() == null) {
value = null;
} else {
value = arg1.getValue() * arg2.getValue();
}
return value;
}
@Override
public String toString() {
return arg1.toString() + " * " + arg2.toString();
}
};
return result;
}
/**
* Returns an expression that represents the specified numeric constant.
*
* @param d
* the numeric constant.
* @return an expression that represents the specified numeric constant.
*/
private static Expression getNumeric(final double d) {
final Expression result = new Expression() {
@Override
public Double getValue() {
return d;
}
@Override
public String toString() {
return "" + d;
}
};
return result;
}
/**
* Returns the opposite of the specified expression.
*
* @param arg1
* the specified expression.
* @return the opposite of the specified expression.
*/
private static Expression getOpposite(final Expression arg1) {
if (arg1 == null) {
throw new InvalidParameterException("Null");
}
final Expression result = new Expression() {
@Override
public Double getValue() {
return -arg1.getValue();
}
@Override
public String toString() {
return "- " + arg1.toString();
}
};
return result;
}
/**
* Returns the specified subtraction.
*
* @param arg1
* the minuend.
* @param arg2
* the subtrahend.
* @return the specified subtraction.
*/
private static Expression getSubtraction(final Expression arg1, final Expression arg2) {
if (arg1 == null || arg2 == null) {
throw new InvalidParameterException("Null");
}
final Expression result = new Expression() {
@Override
public Double getValue() {
final Double value;
if (arg1.getValue() == null || arg2.getValue() == null) {
value = null;
} else {
value = arg1.getValue() - arg2.getValue();
}
return value;
}
@Override
public String toString() {
return "(" + arg1.toString() + " - " + arg2.toString() + ")";
}
};
return result;
}
/**
* Returns <code>true</code> if parentheses in the specified query are
* balanced, <code>false</code> otherwise.
*
* @param query
* the query.
* @return <code>true</code> if parentheses in the specified query are
* balanced, <code>false</code> otherwise.
*/
private static boolean isBalanced(String query) {
int count = 0;
for (int i = 0; i < query.length(); i++) {
if (query.charAt(i) == '(') {
count++;
} else if (query.charAt(i) == ')') {
count--;
if (count < 0) {
// Not balanced !
return false;
}
}
}
return count == 0;
}
/**
* Creates a new Expression factory for the specified simulation.
*
* @param simulation
* the parent simulation.
*/
public ExpressionFactory(Simulation simulation) {
super(simulation);
}
/**
* Returns the specified expression.
*
* @param query
* a string that describes the expression to be returned.
* @return the specified expression.
*/
public Expression getExpression(final String query) {
// Jamel.println("getExpression", "\'" + query + "\'");
final Expression result;
if (!isBalanced(query)) {
throw new RuntimeException("Not balanced: " + query);
// TODO Comment traiter cet incident ?
// Il n'est pas dû à Jamel mais au scénario, il faut informer
// clairement l'utilisateur de l'endroit où il s'est planté.
}
final String cleaned = cleanUp(query.replaceAll("(\\p{javaSpaceChar}|\\r|\\n)", ""));
Character operator = null;
Integer position = null;
int count = 0;
for (int i = 0; i < cleaned.length(); i++) {
final char c = cleaned.charAt(i);
if (c == '(') {
count++;
} else if (c == ')') {
count--;
}
else if (count == 0 && i > 0) {
// We are outside parentheses.
// Is this char an operator ?
if (c == '+') {
operator = c;
position = i;
break;
} else if (c == '-') {
final char previous = cleaned.charAt(i - 1);
if (previous != '*' && previous != '/') {
operator = c;
position = i;
break;
}
} else if (c == '*' || c == '/') {
operator = c;
position = i;
}
}
}
if (position != null) {
if (operator == null) {
throw new RuntimeException("Operator is null");
}
final Expression arg1 = getExpression(cleaned.substring(0, position));
final Expression arg2 = getExpression(cleaned.substring(position + 1));
switch (operator) {
case '+':
result = getAddition(arg1, arg2);
break;
case '-':
result = getSubtraction(arg1, arg2);
break;
case '*':
result = getMultiplication(arg1, arg2);
break;
case '/':
result = getDivision(arg1, arg2);
break;
default:
throw new RuntimeException("Unexpected operator: " + operator);
}
} else {
if (cleaned.startsWith("-")) {
result = getOpposite(getExpression(cleaned.substring(1)));
} else if (Pattern.matches("\\d.*", cleaned)) {
result = getNumeric(Double.parseDouble(cleaned));
} else {
result = this.getSimulation().getDataAccess(cleaned);
}
}
return result;
}
}