org.jdice.calc.AbstractCalculator Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of jcalc Show documentation
Show all versions of jcalc Show documentation
Fluent Java API for easier work with numbers, writing formula and calculations in Java.
The newest version!
/*
* Copyright 2014 Davor Sauer
*
* 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 org.jdice.calc;
import java.text.ParseException;
import java.util.Iterator;
import java.util.LinkedList;
import org.jdice.calc.internal.BindExtensionProvider;
import org.jdice.calc.internal.Bracket;
import org.jdice.calc.internal.CList;
import org.jdice.calc.internal.CListListener;
import org.jdice.calc.internal.CacheExtension;
import org.jdice.calc.internal.FunctionData;
import org.jdice.calc.internal.InfixParser;
import org.jdice.calc.internal.PostfixCalculator;
import org.jdice.calc.internal.UseExtension;
/**
* Abstract class which is extended by concrete calculator (e.g. {@link Calculator}
*
* @author Davor Sauer
*
* @param
*/
public abstract class AbstractCalculator {
/**
* Detect changes in infix expression
*/
private CList infix = new CList(new CListListener() {
@Override
public void change() {
isInfixChanged = true;
}
});
private boolean isInfixChanged = true;
private InfixParser infixParser;
private final PostfixCalculator postfixCalculator = new PostfixCalculator();
private CList postfix = new CList();
private Num lastCalculatedValue;
private LinkedList calculatingSteps;
private Properties properties;
/**
* User defined extensions in scope of instance
*/
private UseExtension useExtensions;
private static boolean isImplExtRegistered = false;
private AbstractCalculator parentCalculator;
private AbstractCalculator childCalculator;
private boolean isBind = false;
private boolean isUnbind = false;
private boolean trackSteps = false;
/**
* Read implemented extensions by subclass
*
*/
private void detectImplmentedExtension() {
if (isImplExtRegistered == false) {
Object o = getThis();
Class thisClass = o.getClass();
// superclass interfaces
Class[] declared = thisClass.getSuperclass().getInterfaces();
for (Class declare : declared) {
detectImplmentedExtension(declare);
}
// subclass interfaces
declared = thisClass.getInterfaces();
for (Class declare : declared) {
detectImplmentedExtension(declare);
}
isImplExtRegistered = true;
}
}
/**
* Register defined operation or function class to global cache
*
* @param declare
*/
private void detectImplmentedExtension(Class declare) {
Class c = BindExtensionProvider.getExtension(declare);
if (c == null && declare.isAnnotationPresent(BindExtension.class)) {
BindExtension impl = (BindExtension) declare.getAnnotation(BindExtension.class);
if (impl != null)
c = impl.implementation();
BindExtensionProvider.bind(declare, c);
}
if (c != null) {
if (Operator.class.isAssignableFrom(c))
CacheExtension.setOperator(c);
if (Function.class.isAssignableFrom(c))
CacheExtension.setFunction(c);
}
}
/**
* Return reference of subclass
* @return
*/
protected abstract CALC getThis();
/**
* Use custom {@link Operator} or {@link Function} inside scope of this calculation, which can be used during parsing expression.
* With using custom extension it's possible to override existing extension from global scope.
* Because during calculation API first scan scoped (locally used) extensions and after that search in global extension from {@link CacheExtension}
*
* @param operationClass
* @return
*/
public CALC use(Class extends Extension> operationClass) {
if (useExtensions == null)
useExtensions = new UseExtension();
if (Operator.class.isAssignableFrom(operationClass))
useExtensions.registerOperator(operationClass.asSubclass(Operator.class));
if (Function.class.isAssignableFrom(operationClass))
useExtensions.registerFunction(operationClass.asSubclass(Function.class));
return getThis();
}
/**
* List registered local scoped operations.
*
* @return
*/
public UseExtension getUsedExtensions() {
return this.useExtensions;
}
/**
* Append value to expression
*
* @param value
* @return
*/
public CALC val(Object value) {
Num val = null;
if (value instanceof Num)
val = (Num)value;
else
val = new Num(value);
infix.add(val);
return getThis();
}
/**
* Append value to expression
*
* @param value custom type value
* @param converter class for convert custom type to {@link Num}
* @return
*/
public CALC val(Object value, Class extends NumConverter> converter) {
infix.add(new Num(value, converter));
return getThis();
}
/**
* Append String value to expression that will be parsed to {@link Num} with defined decimal separator
*
*
* @param value String representation of number
* @param decimalSeparator used in string representation of number
* @return
*/
public CALC val(String value, char decimalSeparator) {
infix.add(new Num(value, decimalSeparator));
return getThis();
}
/**
* Append operator to expression
* @param operator
* @return
*/
public final CALC operator(Class extends Operator> operator) {
infix.add(operator);
return getThis();
}
/**
* Append operator and number to expression
* @param operator
* @param value
* @return
*/
protected final CALC operator(Class extends Operator> operator, Object value) {
Num tmp = null;
if (value instanceof Num)
tmp = (Num)value;
else
tmp = new Num(value);
infix.add(CacheExtension.getOperator(operator));
infix.add(tmp);
return getThis();
}
/**
* Append operator and parsed String value with custom decimal separator used in String representation of value
* @param operator
* @param value
* @param decimalSeparator
* @return
*/
protected final CALC operator(Class extends Operator> operator, String value, char decimalSeparator) {
return operator(operator, new Num(value, decimalSeparator));
}
/**
* Append function with value to expression.
*
*
* e.g. Abs.class, -5 => abs(-5)
*
* @param function
* @param values can accept any object that {@link Num} can work with
* @return
* @see {@link Function}
*/
public final CALC function(Class extends Function> function, Object... values) {
Function fn = CacheExtension.getFunction(function);
FunctionData fd = new FunctionData(fn, values);
this.infix.addFunction(fd);
return getThis();
}
/**
* Parse and append given expression to existing expression
*
* @param expression
* @return
* @throws ParseException
*/
public final CALC expression(String expression) throws ParseException {
detectImplmentedExtension();
if (infixParser == null)
infixParser = new InfixParser();
CList infix = infixParser.parse(useExtensions, getProperties(), expression);
expression(infix, false);
return getThis();
}
/**
* Parse and append given expression to existing expression
* String representation of expression that will be parsed with unknown variables.
* It is possible to define name of Num with {@link Num#setName(String)} then name will be matched with name of unknown variable.
* Otherwise unknown variable will be matched by declared order.
*
*
* e.g. X + 5 - (2 * X - Y)
*
* @param expression
* @param values that match unknown variable by name or by order
* @return {@link AbstractCalculator}
* @throws ParseException
*/
public final CALC expression(String expression, Object... values) throws ParseException {
detectImplmentedExtension();
if (infixParser == null)
infixParser = new InfixParser();
CList infix = infixParser.parse(useExtensions, getProperties(), expression, values);
expression(infix, false);
return getThis();
}
/**
*
* Copy expression from given calculator into this expression within or without brackets
*
* @param expression
* @param withinBrackets
* @return
*/
public CALC expression(AbstractCalculator expression, boolean withinBrackets) {
expression(expression.infix, withinBrackets);
return getThis();
}
/**
*
* Append copy of given infix expression into this expression within or without brackets
*
* @param infix
* @param withinBrackets
* @return
*/
private final CALC expression(CList infix, boolean withinBrackets) {
if (withinBrackets)
this.infix.add(Bracket.OPEN);
Iterator
© 2015 - 2025 Weber Informatics LLC | Privacy Policy