/*
* 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 net.objecthunter.exp4j;
import java.util.*;
import net.objecthunter.exp4j.function.Function;
import net.objecthunter.exp4j.operator.Operator;
import net.objecthunter.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;
/**
* 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;
this.userOperators = new HashMap<String, Operator>(4);
this.userFunctions = new HashMap<String, Function>(4);
this.variableNames = new HashSet<String>(4);
}
/**
* Add a {@link net.objecthunter.exp4j.function.Function} implementation available for use in the expression
* @param function the custom {@link net.objecthunter.exp4j.function.Function} implementation that should be available for use in the expression.
* @return the ExpressionBuilder instance
*/
public ExpressionBuilder function(Function function) {
this.userFunctions.put(function.getName(), function);
return this;
}
/**
* Add multiple {@link net.objecthunter.exp4j.function.Function} implementations available for use in the expression
* @param functions the custom {@link net.objecthunter.exp4j.function.Function} implementations
* @return the ExpressionBuilder instance
*/
public ExpressionBuilder functions(Function... functions) {
for (Function f : functions) {
this.userFunctions.put(f.getName(), f);
}
return this;
}
/**
* Add multiple {@link net.objecthunter.exp4j.function.Function} implementations available for use in the expression
* @param functions A {@link java.util.List} of custom {@link net.objecthunter.exp4j.function.Function} implementations
* @return the ExpressionBuilder instance
*/
public ExpressionBuilder functions(List<Function> functions) {
for (Function f : functions) {
this.userFunctions.put(f.getName(), f);
}
return this;
}
public ExpressionBuilder variables(Set<String> variableNames) {
this.variableNames.addAll(variableNames);
return this;
}
public ExpressionBuilder variables(String ... variableNames) {
Collections.addAll(this.variableNames, variableNames);
return this;
}
public ExpressionBuilder variable(String variableName) {
this.variableNames.add(variableName);
return this;
}
/**
* Add an {@link net.objecthunter.exp4j.operator.Operator} which should be available for use in the expression
* @param operator the custom {@link net.objecthunter.exp4j.operator.Operator} to add
* @return the ExpressionBuilder instance
*/
public ExpressionBuilder operator(Operator operator) {
this.checkOperatorSymbol(operator);
this.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 net.objecthunter.exp4j.operator.Operator} implementations which should be available for use in the expression
* @param operators the set of custom {@link net.objecthunter.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 net.objecthunter.exp4j.operator.Operator} implementations which should be available for use in the expression
* @param operators the {@link java.util.List} of custom {@link net.objecthunter.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");
}
return new Expression(ShuntingYard.convertToRPN(this.expression, this.userFunctions, this.userOperators, this.variableNames),
this.userFunctions.keySet());
}
}