--- /dev/null
+/*
+ Copyright 2011 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 de.congrace.exp4j;
+
+import java.util.Set;
+import java.util.Stack;
+
+/**
+ * Translate a mathematical expression in human readable infix notation to a
+ * Reverse Polish Notation (postfix) expression for easier parsing. by
+ * implementing the shunting yard algorithm by dijkstra
+ *
+ * @author fas@congrace.de
+ */
+class InfixTranslator {
+
+ private static String substituteUnaryOperators(String expr) {
+ final StringBuilder exprBuilder = new StringBuilder(expr.length());
+ final char[] data = expr.toCharArray();
+ char lastChar = ' ';
+ for (int i = 0; i < expr.length(); i++) {
+ if (exprBuilder.length() > 0) {
+ lastChar = exprBuilder.charAt(exprBuilder.length() - 1);
+ }
+ final char c = data[i];
+ switch (c) {
+ case '+':
+ if (i > 0 && lastChar != '(' && !(OperatorToken.isOperator(lastChar))) {
+ exprBuilder.append(c);
+ }
+ break;
+ case '-':
+ if (i > 0 && lastChar != '(' && !(OperatorToken.isOperator(lastChar))) {
+ exprBuilder.append(c);
+ } else {
+ exprBuilder.append('#');
+ }
+ break;
+ default:
+ if (!Character.isWhitespace(c)) {
+ exprBuilder.append(c);
+ }
+ }
+ }
+ return exprBuilder.toString();
+ }
+
+ /**
+ * Delegation method for simple expression without variables or custom
+ * functions
+ *
+ * @param infixExpression
+ * the infix expression to be translated
+ * @return translated RNP postfix expression
+ * @throws UnparsableExpressionException
+ * when the expression is invalid
+ * @throws UnknownFunctionException
+ * when an unknown function has been used in the input.
+ */
+ static String toPostfixExpression(String infixExpression) throws UnparsableExpressionException, UnknownFunctionException {
+ return toPostfixExpression(infixExpression, null, null);
+ }
+
+ /**
+ * implement the shunting yard algorithm
+ *
+ * @param infixExpression
+ * the human readable expression which should be translated to
+ * RPN
+ * @param variableNames
+ * the variable names used in the expression
+ * @param customFunctions
+ * the CustomFunction implementations used
+ * @return the expression in postfix format
+ * @throws UnparsableExpressionException
+ * if the expression could not be translated to RPN
+ * @throws UnknownFunctionException
+ * if an unknown function was encountered
+ */
+ static String toPostfixExpression(String infixExpression, String[] variableStrings, Set<CustomFunction> customFunctions)
+ throws UnparsableExpressionException, UnknownFunctionException {
+ infixExpression = substituteUnaryOperators(infixExpression);
+ final Token[] tokens = new Tokenizer(variableStrings, customFunctions).tokenize(infixExpression);
+ final StringBuilder output = new StringBuilder(tokens.length);
+ final Stack<Token> operatorStack = new Stack<Token>();
+ for (final Token token : tokens) {
+ token.mutateStackForInfixTranslation(operatorStack, output);
+ }
+ // all tokens read, put the rest of the operations on the output;
+ while (operatorStack.size() > 0) {
+ output.append(operatorStack.pop().getValue()).append(" ");
+ }
+ return output.toString().trim();
+ }
+}