/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package reportgen.math.complex;
import reportgen.math.reference.operator.MathExpressionOperatorRef;
import reportgen.math.MathExpressionList;
import org.jdom.Element;
import reportgen.utils.ReportException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Stack;
import reportgen.prototype.context.Context;
import reportgen.prototype.context.group.ContextGroup;
import reportgen.math.ContextFilter;
import reportgen.math.MathExpression;
import reportgen.math.MathExpressionOperand;
import reportgen.math.reference.operator.variants.binary.MathOperatorBinary;
import reportgen.prototype.UsedReportableType;
import reportgen.prototype.stream.SQLStream;
/**
* Составное математическое выражение состоит из одного
* или нескольких математических элементов.
* Все элементы должны быть разделены оператором.
* Все элементы в выражении должны быть одного типа.
*
* @author axe
*/
abstract public class MathExpressionComplex extends MathExpressionOperand
implements ContextFilter {
protected Stack<MathExpression> stack = new Stack<MathExpression>();
protected final MathExpressionList children;
public MathExpressionComplex(Context context) {
super(context);
children = new MathExpressionListEx();
}
public MathExpressionComplex(Element element, Context context) throws ReportException {
super(element, context);
this.children = new MathExpressionListEx(element, context, this);
}
/**
*
* @return
*/
@Override
protected void toXML(Element root) {
children.toXML(root);
}
@Override
public Context getChildContext(ContextGroup group) {
return getParentContext();
}
public MathExpressionList getChildren() {
return children;
}
public boolean isEmpty() {
return children.size() == 0;
}
/**
* Собирает все использованные свойства в запросе, для того что бы загрузить
* для них данные из отчета
* @param set
*/
@Override
public void buildUsedSet(UsedReportableType cls, Set set) {
children.buildUsedSet(cls, set);
}
/**
*
* @param sql
* @throws reportgen.ren.exception.ReportException
*/
@Override
public void appendToQuery(SQLStream sql, Map model) throws ReportException {
children.appendToQuery(sql, model);
}
@Override
public Class getCls() throws ReportException {
if(isNeedRecalcStack()) {
stack = buildStack();
}
Stack calcstack = (Stack) stack.clone();
Class eax = ((MathExpressionOperand) calcstack.pop()).getCls();
while (!calcstack.empty()) {
Object express1 = calcstack.pop();
Object express2 = calcstack.pop();
if (express2 instanceof MathExpressionOperatorRef) {
MathExpressionOperatorRef operator = (MathExpressionOperatorRef) express2;
Class op2Class = getExpressionClass(express1);
MathOperatorBinary opcode = (MathOperatorBinary) operator.getRef();
eax = opcode.checkAvailiable(eax, op2Class);
} else {
MathExpressionOperatorRef operator = (MathExpressionOperatorRef) calcstack.pop();
Class op1Class = getExpressionClass(express1);
Class op2Class = getExpressionClass(express2);
MathOperatorBinary opcode = (MathOperatorBinary) operator.getRef();
Class res = opcode.checkAvailiable(op1Class, op2Class);
calcstack.push(res);
}
}
return eax;
}
/**
*
* @param constants
* @return
* @throws reportgen.ren.exception.ReportException
*/
@Override
public Object getValue(Map constants) throws ReportException {
if(isNeedRecalcStack()) {
stack = buildStack();
}
Stack calcstack = (Stack) stack.clone();
Object eax = ((MathExpressionOperand) calcstack.pop()).getValue(constants);
while (!calcstack.empty()) {
Object express1 = calcstack.pop();
Object express2 = calcstack.pop();
if (express2 instanceof MathExpressionOperatorRef) {
MathExpressionOperatorRef operator = (MathExpressionOperatorRef) express2;
Object op2Value = getExpressionValue(express1, constants);
MathOperatorBinary opcode = (MathOperatorBinary) operator.getRef();
eax = opcode.getValue(eax, op2Value);
} else {
MathExpressionOperatorRef operator = (MathExpressionOperatorRef) calcstack.pop();
Object op1Value = getExpressionValue(express1, constants);
Object op2Value = getExpressionValue(express2, constants);
MathOperatorBinary opcode = (MathOperatorBinary) operator.getRef();
Object res = opcode.getValue(op1Value, op2Value);
calcstack.push(res);
}
}
return eax;
}
/**
*
* @throws reportgen.ren.exception.ReportException
*/
protected Stack<MathExpression> buildStack() throws ReportException {
ArrayList<MathExpression> elements = new ArrayList<MathExpression>();
for(int i=0; i<children.size(); i++) {
elements.add(children.get(i));
}
return buildStack(elements, 0, elements.size()-1, new Stack<MathExpression>());
}
/**
*
* @param entity
* @return
*/
@Override
public final boolean isContain(Object object) {
if(super.isContain(object)) {
return true;
}
return children.isContain(object);
}
@Override
public boolean containsMissingVariables() {
return children.containsMissingVariables();
}
protected void clearStack() {
stack.clear();
}
protected boolean isNeedRecalcStack() {
return stack.isEmpty();
}
/**
*
* @return
*/
@Override
public String toString() {
if (children.size() == 0) {
return super.toString();
}
return children.toString();
}
/**
*
* @throws reportgen.ren.exception.ReportException
*/
@Override
public void validate() throws ReportException {
super.validate();
children.validate();
getCls();
}
/**
*
* @param elem
* @param stack
* @return
* @throws reportgen.ren.exception.ReportException
*/
final protected Stack<MathExpression> buildStack(List<MathExpression> elem, int beg, int end,
Stack<MathExpression> stack) throws ReportException {
if(beg > end) {
throw new ReportException("Пустое выражение");
}
//get min priority operand
int pos = -1;
int priority = Integer.MAX_VALUE;
MathExpressionOperatorRef operator = null;
for(int j=end; j>=beg; j--) {
MathExpression element = elem.get(j);
if(element instanceof MathExpressionOperatorRef) {
MathExpressionOperatorRef iOperator = (MathExpressionOperatorRef) element;
int prior = iOperator.getRef().getPriority();
if(prior < priority) {
priority = prior;
pos = j;
operator = iOperator;
}
}
}
if(pos == -1) {
if(end == beg) {
//it allways be operand
stack.add(elem.get(0));
return stack;
}
throw new ReportException("Выражение составлено неверно, пропущен оператор");
}
if(pos == 0) {
throw new ReportException("Перед оператором '" + operator + "' должен быть операнд");
} else if(pos+1 == elem.size()) {
throw new ReportException("После оператора '" + operator + "' должен быть операнд");
}
int leftOperandPos = pos-1;
int rightOperandPos = pos+1;
MathExpression leftOperand = elem.get(leftOperandPos);
MathExpression rightOperand = elem.get(rightOperandPos);
if(!(leftOperand instanceof MathExpressionOperand)) {
throw new ReportException("Перед оператором '" + operator + "' должен быть операнд");
} else if (!(rightOperand instanceof MathExpressionOperand)) {
throw new ReportException("После оператора '" + operator + "' должен быть операнд");
}
stack.add(operator);
//right part
if(rightOperandPos == end) {
stack.add(rightOperand);
} else {
stack = buildStack(elem, rightOperandPos, end, stack);
}
//left part
if(leftOperandPos == beg) {
stack.add(leftOperand);
} else {
stack = buildStack(elem, beg, leftOperandPos, stack);
}
return stack;
}
/**
*
* @param express
* @return
* @throws reportgen.ren.exception.ReportException
*/
static private Class getExpressionClass(Object express) throws ReportException {
if(express instanceof MathExpressionOperand) {
MathExpressionOperand operand = (MathExpressionOperand) express;
return operand.getCls();
} else if(express instanceof Class) {
return (Class) express;
}
throw new ReportException("Ожидается операнд: " + express);
}
/**
*
* @param express
* @param constants
* @return
* @throws reportgen.ren.exception.ReportException
*/
static private Object getExpressionValue(Object express, Map constants)
throws ReportException {
if(express instanceof MathExpressionOperand) {
MathExpressionOperand operand = (MathExpressionOperand) express;
return operand.getValue(constants);
}
return express;
}
private class MathExpressionListEx extends MathExpressionList {
public MathExpressionListEx() {
}
public MathExpressionListEx(Element root, Context context, ContextFilter cf) throws ReportException {
super(root, context, cf);
}
@Override
protected void onAdd(MathExpression value) {
super.onAdd(value);
clearStack();
}
@Override
protected void onRemove(MathExpression value) throws ReportException {
super.onRemove(value);
clearStack();
}
@Override
protected void onMove() {
super.onMove();
clearStack();
}
}
}