|
Author: adrianc
Date: Thu Jun 14 12:36:25 2012 New Revision: 1350214 URL: http://svn.apache.org/viewvc?rev=1350214&view=rev Log: Overhauled Mini-language <calculate> element. The overhaul includes: removing unnecessary object creation, make the class thread-safe, add syntax validation, and misc code cleanups. Modified: ofbiz/trunk/framework/minilang/dtd/simple-methods-v2.xsd ofbiz/trunk/framework/minilang/src/org/ofbiz/minilang/method/otherops/Calculate.java Modified: ofbiz/trunk/framework/minilang/dtd/simple-methods-v2.xsd URL: http://svn.apache.org/viewvc/ofbiz/trunk/framework/minilang/dtd/simple-methods-v2.xsd?rev=1350214&r1=1350213&r2=1350214&view=diff ============================================================================== --- ofbiz/trunk/framework/minilang/dtd/simple-methods-v2.xsd (original) +++ ofbiz/trunk/framework/minilang/dtd/simple-methods-v2.xsd Thu Jun 14 12:36:25 2012 @@ -4284,141 +4284,187 @@ under the License. <xs:element name="calculate" substitutionGroup="OtherOperations"> <xs:annotation> <xs:documentation> - The calculate tag performs the specified calculation and puts the result in an object in the field of the specified map (see the calculate element attribute descriptions). - The type of the object can be specified with the type attribute, defaults to Double. - - The calculate tag can contain calcop and number tags, and the calcop tag can also contain these two tags to enable nested calculations. - - The operator specifies the operation to perform on the given field and nested calcops and numbers. - It must be one of the following: get | add | subtract | multiply | divide | negative. + Performs an arithmetic calculation and puts the result in the specified field. + Deprecated - use the set element. </xs:documentation> </xs:annotation> <xs:complexType> <xs:choice minOccurs="0" maxOccurs="unbounded"> - <xs:element ref="calcop"> - <xs:annotation> - <xs:documentation> - This tag is used to apply an operator in the calculation. - It can have calcop and number tags nested under it, making it also act like a parenthesis. - It has three attributes: operator, map-name, and field-name. Operator and field-name are required. - </xs:documentation> - </xs:annotation> - </xs:element> - <xs:element ref="number"/> + <xs:element ref="calcop" /> + <xs:element ref="number" /> </xs:choice> - <xs:attributeGroup ref="attlist.calculate"/> + <xs:attribute ref="field" /> + <xs:attribute name="rounding-mode"> + <xs:annotation> + <xs:documentation> + Rounding mode for BigDecimal calculation, primarily for divide operation. + Defaults to "HalfEven". + </xs:documentation> + </xs:annotation> + <xs:simpleType> + <xs:restriction base="xs:token"> + <xs:enumeration value="Ceiling"> + <xs:annotation> + <xs:documentation> + Round towards positive infinity. + </xs:documentation> + </xs:annotation> + </xs:enumeration> + <xs:enumeration value="Floor"> + <xs:annotation> + <xs:documentation> + Round towards negative infinity. + </xs:documentation> + </xs:annotation> + </xs:enumeration> + <xs:enumeration value="Up"> + <xs:annotation> + <xs:documentation> + Round towards "nearest neighbor" unless both neighbors are equidistant, in which case round up. + </xs:documentation> + </xs:annotation> + </xs:enumeration> + <xs:enumeration value="Down"> + <xs:annotation> + <xs:documentation> + Round towards "nearest neighbor" unless both neighbors are equidistant, in which case round down. + </xs:documentation> + </xs:annotation> + </xs:enumeration> + <xs:enumeration value="HalfUp"> + <xs:annotation> + <xs:documentation> + Round towards "nearest neighbor" unless both neighbors are equidistant, in which case round up. + </xs:documentation> + </xs:annotation> + </xs:enumeration> + <xs:enumeration value="HalfDown"> + <xs:annotation> + <xs:documentation> + Round towards "nearest neighbor" unless both neighbors are equidistant, in which case round down. + </xs:documentation> + </xs:annotation> + </xs:enumeration> + <xs:enumeration value="HalfEven"> + <xs:annotation> + <xs:documentation> + Round towards the "nearest neighbor" unless both neighbors are equidistant, in which case, round towards the even neighbor. + </xs:documentation> + </xs:annotation> + </xs:enumeration> + <xs:enumeration value="Unnecessary"> + <xs:annotation> + <xs:documentation> + Asserts that the requested operation has an exact result, hence no rounding is necessary. + </xs:documentation> + </xs:annotation> + </xs:enumeration> + <xs:enumeration value="${roundingMode}"> + <xs:annotation> + <xs:documentation> + Convention for variable name for this attribute, in cases where it is determined at run-time. + </xs:documentation> + </xs:annotation> + </xs:enumeration> + </xs:restriction> + </xs:simpleType> + </xs:attribute> + <xs:attribute type="xs:string" name="decimal-scale"> + <xs:annotation> + <xs:documentation> + Initial scale to use for the internal BigDecimal. Defaults to "2". + </xs:documentation> + </xs:annotation> + </xs:attribute> + <xs:attribute type="xs:string" name="decimal-format"> + <xs:annotation> + <xs:documentation> + Decimal format to use for conversion to string. + </xs:documentation> + </xs:annotation> + </xs:attribute> + <xs:attribute name="type"> + <xs:annotation> + <xs:documentation> + Data type of the calculation result. Defaults to "BigDecimal". + </xs:documentation> + </xs:annotation> + <xs:simpleType> + <xs:restriction base="xs:token"> + <xs:enumeration value="String" /> + <xs:enumeration value="Double" /> + <xs:enumeration value="Float" /> + <xs:enumeration value="Long" /> + <xs:enumeration value="Integer" /> + <xs:enumeration value="BigDecimal" /> + </xs:restriction> + </xs:simpleType> + </xs:attribute> </xs:complexType> </xs:element> - <xs:attributeGroup name="attlist.calculate"> - <xs:attribute type="xs:string" name="field" use="required"> - <xs:annotation><xs:documentation>The name (key) of the map (or env if map-name is empty) field to use.</xs:documentation></xs:annotation> - </xs:attribute> - <xs:attribute name="type" default="BigDecimal"> - <xs:simpleType> - <xs:restriction base="xs:token"> - <xs:enumeration value="String"/> - <xs:enumeration value="Double"/> - <xs:enumeration value="Float"/> - <xs:enumeration value="Long"/> - <xs:enumeration value="Integer"/> - <xs:enumeration value="BigDecimal"/> - </xs:restriction> - </xs:simpleType> - </xs:attribute> - <xs:attribute name="rounding-mode" default="HalfEven"> - <xs:annotation> - <xs:documentation> - Rounding mode for BigDecimal calculation, primarily for divide operation. - </xs:documentation> - </xs:annotation> - <xs:simpleType> - <xs:restriction base="xs:token"> - <xs:enumeration value="Ceiling"><xs:annotation><xs:documentation>Rounding mode to round towards positive infinity</xs:documentation></xs:annotation></xs:enumeration> - <xs:enumeration value="Floor"><xs:annotation><xs:documentation>Rounding mode to round towards negative infinity</xs:documentation></xs:annotation></xs:enumeration> - <xs:enumeration value="Up"><xs:annotation><xs:documentation>Rounding mode to round towards "nearest neighbor" unless both neighbors are equidistant, in which case round up</xs:documentation></xs:annotation></xs:enumeration> - <xs:enumeration value="Down"><xs:annotation><xs:documentation>Rounding mode to round towards "nearest neighbor" unless both neighbors are equidistant, in which case round down</xs:documentation></xs:annotation></xs:enumeration> - <xs:enumeration value="HalfUp"><xs:annotation><xs:documentation>Rounding mode to round towards "nearest neighbor" unless both neighbors are equidistant, in which case round up</xs:documentation></xs:annotation></xs:enumeration> - <xs:enumeration value="HalfDown"><xs:annotation><xs:documentation>Rounding mode to round towards "nearest neighbor" unless both neighbors are equidistant, in which case round down</xs:documentation></xs:annotation></xs:enumeration> - <xs:enumeration value="HalfEven"><xs:annotation><xs:documentation>Rounding mode to round towards the "nearest neighbor" unless both neighbors are equidistant, in which case, round towards the even neighbor</xs:documentation></xs:annotation></xs:enumeration> - <xs:enumeration value="Unnecessary"><xs:annotation><xs:documentation>Rounding mode to assert that the requested operation has an exact result, hence no rounding is necessary</xs:documentation></xs:annotation></xs:enumeration> - <xs:enumeration value="${roundingMode}"><xs:annotation><xs:documentation>Convention for variable name for this attribute, in cases where it is determined at run-time.</xs:documentation></xs:annotation></xs:enumeration> - </xs:restriction> - </xs:simpleType> - </xs:attribute> - <xs:attribute type="xs:string" name="decimal-scale" default="2"> - <xs:annotation><xs:documentation>Initial scale to use for the internal BigDecimal. Defaults to 2 for monetary calculations.</xs:documentation></xs:annotation> - </xs:attribute> - <xs:attribute type="xs:string" name="decimal-format"/> - </xs:attributeGroup> <xs:element name="calcop"> <xs:annotation> <xs:documentation> - The calcop tag has an operator: get, add, subtract, multiply, divide, and negative. + A basic arithmetic operation. + Supports these operations: get, add, subtract, multiply, divide, and negate. So add, subtract, multiply, and divide are just the basic arithmetic operations. </xs:documentation> </xs:annotation> <xs:complexType> <xs:choice minOccurs="0" maxOccurs="unbounded"> - <xs:element ref="calcop"/> - <xs:element ref="number"/> + <xs:element ref="calcop" /> + <xs:element ref="number" /> </xs:choice> - <xs:attributeGroup ref="attlist.calcop"/> + <xs:attribute name="operator" use="required"> + <xs:simpleType> + <xs:restriction base="xs:token"> + <xs:enumeration value="get"> + <xs:annotation> + <xs:documentation> + Gets the positive value. + </xs:documentation> + </xs:annotation> + </xs:enumeration> + <xs:enumeration value="add" /> + <xs:enumeration value="subtract" /> + <xs:enumeration value="multiply" /> + <xs:enumeration value="divide" /> + <xs:enumeration value="negative"> + <xs:annotation> + <xs:documentation> + Negates the value + </xs:documentation> + </xs:annotation> + </xs:enumeration> + </xs:restriction> + </xs:simpleType> + </xs:attribute> + <xs:attribute type="xs:string" name="field"> + <xs:annotation> + <xs:documentation> + The name of the field to use. + </xs:documentation> + </xs:annotation> + </xs:attribute> </xs:complexType> </xs:element> - <xs:attributeGroup name="attlist.calcop"> - <xs:attribute name="operator" use="required"> - <xs:simpleType> - <xs:restriction base="xs:token"> - <xs:enumeration value="get"> - <xs:annotation> - <xs:documentation> - Gets the positive value. - </xs:documentation> - </xs:annotation> - </xs:enumeration> - <xs:enumeration value="add"/> - <xs:enumeration value="subtract"/> - <xs:enumeration value="multiply"/> - <xs:enumeration value="divide"/> - <xs:enumeration value="negative"> - <xs:annotation> - <xs:documentation> - Negates the value - </xs:documentation> - </xs:annotation> - </xs:enumeration> - </xs:restriction> - </xs:simpleType> - </xs:attribute> - <xs:attribute type="xs:string" name="field"> - <xs:annotation> - <xs:documentation> - The name (key) of the map field to use. - </xs:documentation> - </xs:annotation> - </xs:attribute> - </xs:attributeGroup> <xs:element name="number"> <xs:annotation> <xs:documentation> This is used to put a numeric constant (a number) into the calculation. It has one attribute: value. This must be a properly formatted number or an error will result. - May use flexible string. </xs:documentation> </xs:annotation> <xs:complexType> - <xs:attributeGroup ref="attlist.number"/> + <xs:attribute type="xs:string" name="value" use="required"> + <xs:annotation> + <xs:documentation> + Literal or flexible string. + </xs:documentation> + </xs:annotation> + </xs:attribute> </xs:complexType> </xs:element> - <xs:attributeGroup name="attlist.number"> - <xs:attribute type="xs:string" name="value" use="required"> - <xs:annotation> - <xs:documentation> - Literal or flexible string. - </xs:documentation> - </xs:annotation> - </xs:attribute> - </xs:attributeGroup> <xs:element name="set-calendar" substitutionGroup="EnvOperations"> <xs:annotation> <xs:documentation> @@ -4427,13 +4473,6 @@ under the License. </xs:annotation> <xs:complexType> <xs:attribute ref="field" /> - <xs:attribute type="xs:string" name="from-field"> - <xs:annotation> - <xs:documentation> - Deprecated - use the from attribute. - </xs:documentation> - </xs:annotation> - </xs:attribute> <xs:attribute type="xs:string" name="from"> <xs:annotation> <xs:documentation> @@ -4445,6 +4484,13 @@ under the License. </xs:documentation> </xs:annotation> </xs:attribute> + <xs:attribute type="xs:string" name="from-field"> + <xs:annotation> + <xs:documentation> + Deprecated - use the from attribute. + </xs:documentation> + </xs:annotation> + </xs:attribute> <xs:attribute type="xs:string" name="value"> <xs:annotation> <xs:documentation> @@ -4455,19 +4501,19 @@ under the License. </xs:documentation> </xs:annotation> </xs:attribute> - <xs:attribute type="xs:string" name="default-value"> + <xs:attribute type="xs:string" name="default"> <xs:annotation> <xs:documentation> - Deprecated - use the default attribute. + A default value that is used when the from attribute evaluates to null or empty. + <br/><br/> + Optional. Attribute types: constant, ${expression}. </xs:documentation> </xs:annotation> </xs:attribute> - <xs:attribute type="xs:string" name="default"> + <xs:attribute type="xs:string" name="default-value"> <xs:annotation> <xs:documentation> - A default value that is used when the from attribute evaluates to null or empty. - <br/><br/> - Optional. Attribute types: constant, ${expression}. + Deprecated - use the default attribute. </xs:documentation> </xs:annotation> </xs:attribute> Modified: ofbiz/trunk/framework/minilang/src/org/ofbiz/minilang/method/otherops/Calculate.java URL: http://svn.apache.org/viewvc/ofbiz/trunk/framework/minilang/src/org/ofbiz/minilang/method/otherops/Calculate.java?rev=1350214&r1=1350213&r2=1350214&view=diff ============================================================================== --- ofbiz/trunk/framework/minilang/src/org/ofbiz/minilang/method/otherops/Calculate.java (original) +++ ofbiz/trunk/framework/minilang/src/org/ofbiz/minilang/method/otherops/Calculate.java Thu Jun 14 12:36:25 2012 @@ -22,26 +22,24 @@ import java.math.BigDecimal; import java.text.DecimalFormat; import java.util.List; import java.util.Locale; -import java.util.Map; -import javolution.util.FastMap; - -import org.ofbiz.base.util.Debug; -import org.ofbiz.base.util.ObjectType; -import org.ofbiz.base.util.UtilValidate; import org.ofbiz.base.util.UtilXml; +import org.ofbiz.base.util.collections.FlexibleMapAccessor; +import org.ofbiz.base.util.string.FlexibleStringExpander; +import org.ofbiz.minilang.MiniLangElement; import org.ofbiz.minilang.MiniLangException; +import org.ofbiz.minilang.MiniLangRuntimeException; import org.ofbiz.minilang.MiniLangUtil; +import org.ofbiz.minilang.MiniLangValidate; import org.ofbiz.minilang.SimpleMethod; -import org.ofbiz.minilang.method.ContextAccessor; import org.ofbiz.minilang.method.MethodContext; import org.ofbiz.minilang.method.MethodOperation; import org.w3c.dom.Element; /** - * Calculates a result based on nested calcops. + * Implements the <calculate> element. */ -public class Calculate extends MethodOperation { +public final class Calculate extends MethodOperation { public static final String module = Calculate.class.getName(); @@ -51,36 +49,40 @@ public class Calculate extends MethodOpe public static final int TYPE_INTEGER = 4; public static final int TYPE_STRING = 5; public static final int TYPE_BIG_DECIMAL = 6; - public static final BigDecimal ZERO = BigDecimal.ZERO; - Calculate.SubCalc calcops[]; - String decimalFormatString; - String decimalScaleString; - ContextAccessor<Object> fieldAcsr; - ContextAccessor<Map<String, Object>> mapAcsr; - String roundingModeString; - String typeString; + private final Calculate.SubCalc calcops[]; + private final FlexibleStringExpander decimalFormatFse; + private final FlexibleStringExpander decimalScaleFse; + private final FlexibleMapAccessor<Object> fieldFma; + private final FlexibleStringExpander roundingModeFse; + private final FlexibleStringExpander typeFse; public Calculate(Element element, SimpleMethod simpleMethod) throws MiniLangException { super(element, simpleMethod); - // the schema for this element now just has the "field" attribute, though the old "field-name" and "map-name" pair is still supported - this.fieldAcsr = new ContextAccessor<Object>(element.getAttribute("field"), element.getAttribute("field-name")); - this.mapAcsr = new ContextAccessor<Map<String, Object>>(element.getAttribute("map-name")); - decimalScaleString = element.getAttribute("decimal-scale"); - decimalFormatString = element.getAttribute("decimal-format"); - typeString = element.getAttribute("type"); - roundingModeString = element.getAttribute("rounding-mode"); + if (MiniLangValidate.validationOn()) { + MiniLangValidate.handleError("<calculate> element is deprecated (use <set>)", simpleMethod, element); + MiniLangValidate.attributeNames(simpleMethod, element, "field", "decimal-scale", "decimal-format", "rounding-mode", "type"); + MiniLangValidate.requiredAttributes(simpleMethod, element, "field"); + MiniLangValidate.expressionAttributes(simpleMethod, element, "field"); + MiniLangValidate.childElements(simpleMethod, element, "calcop", "number"); + } + this.fieldFma = FlexibleMapAccessor.getInstance(element.getAttribute("field")); + this.decimalFormatFse = FlexibleStringExpander.getInstance(element.getAttribute("decimal-format")); + this.decimalScaleFse = FlexibleStringExpander.getInstance(element.getAttribute("decimal-scale")); + this.roundingModeFse = FlexibleStringExpander.getInstance(element.getAttribute("rounding-mode")); + this.typeFse = FlexibleStringExpander.getInstance(element.getAttribute("type")); List<? extends Element> calcopElements = UtilXml.childElementList(element); calcops = new Calculate.SubCalc[calcopElements.size()]; int i = 0; for (Element calcopElement : calcopElements) { String nodeName = calcopElement.getNodeName(); if ("calcop".equals(nodeName)) { - calcops[i] = new Calculate.CalcOp(calcopElement); + calcops[i] = new CalcOp(calcopElement, simpleMethod); } else if ("number".equals(nodeName)) { - calcops[i] = new Calculate.NumberOp(calcopElement); + calcops[i] = new NumberOp(calcopElement, simpleMethod); } else { - Debug.logError("Error: calculate operation with type " + nodeName, module); + MiniLangValidate.handleError("Invalid calculate sub-element.", simpleMethod, calcopElement); + calcops[i] = new InvalidOp(calcopElement, simpleMethod); } i++; } @@ -88,7 +90,7 @@ public class Calculate extends MethodOpe @Override public boolean exec(MethodContext methodContext) throws MiniLangException { - String typeString = methodContext.expandString(this.typeString); + String typeString = typeFse.expandString(methodContext.getEnvMap()); int type; if ("Double".equals(typeString)) { type = Calculate.TYPE_DOUBLE; @@ -105,7 +107,7 @@ public class Calculate extends MethodOpe } else { type = Calculate.TYPE_BIG_DECIMAL; } - String roundingModeString = methodContext.expandString(this.roundingModeString); + String roundingModeString = roundingModeFse.expandString(methodContext.getEnvMap()); int roundingMode; if ("Ceiling".equals(roundingModeString)) { roundingMode = BigDecimal.ROUND_CEILING; @@ -127,29 +129,21 @@ public class Calculate extends MethodOpe // default to HalfEven, reduce cumulative errors roundingMode = BigDecimal.ROUND_HALF_EVEN; } - String decimalScaleString = methodContext.expandString(this.decimalScaleString); + String decimalScaleString = decimalScaleFse.expandString(methodContext.getEnvMap()); int decimalScale = 2; - if (UtilValidate.isNotEmpty(decimalScaleString)) { + if (!decimalScaleString.isEmpty()) { decimalScale = Integer.valueOf(decimalScaleString).intValue(); } - String decimalFormatString = methodContext.expandString(this.decimalFormatString); + String decimalFormatString = decimalFormatFse.expandString(methodContext.getEnvMap()); DecimalFormat df = null; - if (UtilValidate.isNotEmpty(decimalFormatString)) { + if (!decimalFormatString.isEmpty()) { df = new DecimalFormat(decimalFormatString); } - BigDecimal resultValue = ZERO; - resultValue = resultValue.setScale(decimalScale, roundingMode); + BigDecimal resultValue = BigDecimal.ZERO.setScale(decimalScale, roundingMode); for (Calculate.SubCalc calcop : calcops) { resultValue = resultValue.add(calcop.calcValue(methodContext, decimalScale, roundingMode)); - // Debug.logInfo("main total so far: " + resultValue, module); } resultValue = resultValue.setScale(decimalScale, roundingMode); - /* - * the old thing that did conversion to string and back, may want to use somewhere sometime...: for now just doing the setScale above (before and after calc ops) try { resultValue = new - * BigDecimal(df.format(resultValue)); } catch (ParseException e) { String errorMessage = "Unable to format [" + formatString + "] result [" + resultValue + "]"; Debug.logError(e, - * errorMessage, module); if (methodContext.getMethodType() == MethodContext.EVENT) { methodContext.putEnv(simpleMethod.getEventErrorMessageName(), errorMessage); } else if - * (methodContext.getMethodType() == MethodContext.SERVICE) { methodContext.putEnv(simpleMethod.getServiceErrorMessageName(), errorMessage); } return false; } - */ Object resultObj = null; switch (type) { case TYPE_DOUBLE: @@ -168,7 +162,7 @@ public class Calculate extends MethodOpe break; case TYPE_STRING: // run the decimal-formatting - if (df != null && resultValue.compareTo(ZERO) > 0) { + if (df != null && resultValue.compareTo(BigDecimal.ZERO) > 0) { resultObj = df.format(resultValue); } else { resultObj = resultValue.toString(); @@ -178,71 +172,90 @@ public class Calculate extends MethodOpe resultObj = resultValue; break; } - - if (!mapAcsr.isEmpty()) { - Map<String, Object> toMap = mapAcsr.get(methodContext); - if (toMap == null) { - if (Debug.verboseOn()) - Debug.logVerbose("Map not found with name " + mapAcsr + ", creating new map", module); - toMap = FastMap.newInstance(); - mapAcsr.put(methodContext, toMap); - } - fieldAcsr.put(toMap, resultObj, methodContext); - } else { - fieldAcsr.put(methodContext, resultObj); - } - + fieldFma.put(methodContext.getEnvMap(), resultObj); return true; } @Override public String expandedString(MethodContext methodContext) { - // TODO: something more than a stub/dummy - return this.rawString(); + return FlexibleStringExpander.expandString(toString(), methodContext.getEnvMap()); } @Override public String rawString() { - // TODO: add all attributes and other info - return "<calculate field-name=\"" + this.fieldAcsr + "\" map-name=\"" + this.mapAcsr + "\"/>"; + return toString(); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder("<set "); + sb.append("field=\"").append(this.fieldFma).append("\" "); + if (!this.roundingModeFse.isEmpty()) { + sb.append("rounding-mode=\"").append(this.roundingModeFse).append("\" "); + } + if (!this.decimalScaleFse.isEmpty()) { + sb.append("decimal-scale=\"").append(this.decimalScaleFse).append("\" "); + } + if (!this.decimalFormatFse.isEmpty()) { + sb.append("decimal-format=\"").append(this.decimalFormatFse).append("\" "); + } + if (!typeFse.isEmpty()) { + sb.append("type=\"").append(this.typeFse).append("\" "); + } + sb.append("/>"); + return sb.toString(); } - protected static class CalcOp implements SubCalc { - public static final int OPERATOR_ADD = 1; - public static final int OPERATOR_DIVIDE = 4; - public static final int OPERATOR_MULTIPLY = 3; - public static final int OPERATOR_NEGATIVE = 5; - public static final int OPERATOR_SUBTRACT = 2; - - Calculate.SubCalc calcops[]; - ContextAccessor<Object> fieldAcsr; - ContextAccessor<Map<String, ? extends Object>> mapAcsr; - String operatorStr; - - public CalcOp(Element element) { - // the schema for this element now just has the "field" attribute, though the old "field-name" and "map-name" pair is still supported - this.fieldAcsr = new ContextAccessor<Object>(element.getAttribute("field"), element.getAttribute("field-name")); - this.mapAcsr = new ContextAccessor<Map<String, ? extends Object>>(element.getAttribute("map-name")); - operatorStr = element.getAttribute("operator"); + /** + * Interface for <calculate> sub-element implementations. + */ + public interface SubCalc { + BigDecimal calcValue(MethodContext methodContext, int scale, int roundingMode) throws MiniLangException; + } + + /** + * Implements the <calcop> element. + */ + public final class CalcOp extends MiniLangElement implements SubCalc { + private static final int OPERATOR_ADD = 1; + private static final int OPERATOR_DIVIDE = 4; + private static final int OPERATOR_MULTIPLY = 3; + private static final int OPERATOR_NEGATIVE = 5; + private static final int OPERATOR_SUBTRACT = 2; + + private final Calculate.SubCalc calcops[]; + private final FlexibleMapAccessor<Object> fieldFma; + private final FlexibleStringExpander operatorFse; + + private CalcOp(Element element, SimpleMethod simpleMethod) throws MiniLangException { + super(element, simpleMethod); + if (MiniLangValidate.validationOn()) { + MiniLangValidate.attributeNames(simpleMethod, element, "field", "operator"); + MiniLangValidate.requiredAttributes(simpleMethod, element, "field"); + MiniLangValidate.expressionAttributes(simpleMethod, element, "field"); + MiniLangValidate.childElements(simpleMethod, element, "calcop", "number"); + } + this.fieldFma = FlexibleMapAccessor.getInstance(element.getAttribute("field")); + this.operatorFse = FlexibleStringExpander.getInstance(element.getAttribute("operator")); List<? extends Element> calcopElements = UtilXml.childElementList(element); calcops = new Calculate.SubCalc[calcopElements.size()]; int i = 0; - for (Element calcopElement : calcopElements) { - String nodeName = calcopElement.getNodeName(); if ("calcop".equals(calcopElement.getNodeName())) { - calcops[i] = new Calculate.CalcOp(calcopElement); + calcops[i] = new Calculate.CalcOp(calcopElement, simpleMethod); } else if ("number".equals(calcopElement.getNodeName())) { - calcops[i] = new Calculate.NumberOp(calcopElement); + calcops[i] = new Calculate.NumberOp(calcopElement, simpleMethod); } else { - Debug.logError("Error: calculate operation unknown with type " + nodeName, module); + MiniLangValidate.handleError("Invalid calculate sub-element.", simpleMethod, calcopElement); + calcops[i] = new InvalidOp(calcopElement, simpleMethod); } i++; } } - public BigDecimal calcValue(MethodContext methodContext, int scale, int roundingMode) { - String operatorStr = methodContext.expandString(this.operatorStr); + @Override + public BigDecimal calcValue(MethodContext methodContext, int scale, int roundingMode) throws MiniLangException { + String operatorStr = operatorFse.expandString(methodContext.getEnvMap()); int operator = CalcOp.OPERATOR_ADD; if ("get".equals(operatorStr)) { operator = CalcOp.OPERATOR_ADD; @@ -257,45 +270,26 @@ public class Calculate extends MethodOpe } else if ("negative".equals(operatorStr)) { operator = CalcOp.OPERATOR_NEGATIVE; } - BigDecimal resultValue = ZERO; - resultValue = resultValue.setScale(scale, roundingMode); + BigDecimal resultValue = BigDecimal.ZERO.setScale(scale, roundingMode); boolean isFirst = true; - // if a fieldAcsr was specified, get the field from the map or result and use it as the initial value - if (!fieldAcsr.isEmpty()) { - Object fieldObj = null; - if (!mapAcsr.isEmpty()) { - Map<String, ? extends Object> fromMap = mapAcsr.get(methodContext); - if (fromMap == null) { - if (Debug.verboseOn()) - Debug.logVerbose("Map not found with name " + mapAcsr + ", creating new map", module); - fromMap = FastMap.newInstance(); - mapAcsr.put(methodContext, fromMap); - } - fieldObj = fieldAcsr.get(fromMap, methodContext); - } else { - fieldObj = fieldAcsr.get(methodContext); - } - if (fieldObj != null) { - if (fieldObj instanceof Double) { - resultValue = new BigDecimal(((Double) fieldObj).doubleValue()); - } else if (fieldObj instanceof Long) { - resultValue = BigDecimal.valueOf(((Long) fieldObj).longValue()); - } else if (fieldObj instanceof Float) { - resultValue = new BigDecimal(((Float) fieldObj).floatValue()); - } else if (fieldObj instanceof Integer) { - resultValue = BigDecimal.valueOf(((Integer) fieldObj).longValue()); - } else if (fieldObj instanceof String) { - resultValue = new BigDecimal((String) fieldObj); - } else if (fieldObj instanceof BigDecimal) { - resultValue = (BigDecimal) fieldObj; - } - if (operator == OPERATOR_NEGATIVE) - resultValue = resultValue.negate(); - isFirst = false; - } else { - if (Debug.infoOn()) - Debug.logInfo("Field not found with field-name " + fieldAcsr + ", and map-name " + mapAcsr + "using a default of 0", module); + Object fieldObj = fieldFma.get(methodContext.getEnvMap()); + if (fieldObj != null) { + if (fieldObj instanceof Double) { + resultValue = new BigDecimal(((Double) fieldObj).doubleValue()); + } else if (fieldObj instanceof Long) { + resultValue = BigDecimal.valueOf(((Long) fieldObj).longValue()); + } else if (fieldObj instanceof Float) { + resultValue = new BigDecimal(((Float) fieldObj).floatValue()); + } else if (fieldObj instanceof Integer) { + resultValue = BigDecimal.valueOf(((Integer) fieldObj).longValue()); + } else if (fieldObj instanceof String) { + resultValue = new BigDecimal((String) fieldObj); + } else if (fieldObj instanceof BigDecimal) { + resultValue = (BigDecimal) fieldObj; } + if (operator == OPERATOR_NEGATIVE) + resultValue = resultValue.negate(); + isFirst = false; } for (SubCalc calcop : calcops) { if (isFirst) { @@ -325,42 +319,62 @@ public class Calculate extends MethodOpe } } - public static final class CalculateFactory implements Factory<Calculate> { - public Calculate createMethodOperation(Element element, SimpleMethod simpleMethod) throws MiniLangException { - return new Calculate(element, simpleMethod); - } - - public String getName() { - return "calculate"; - } - } - - protected static class NumberOp implements SubCalc { - String valueStr; - - public NumberOp(Element element) { - valueStr = element.getAttribute("value"); + /** + * Implements the <number> element. + */ + public final class NumberOp extends MiniLangElement implements SubCalc { + + private final FlexibleStringExpander valueFse; + + private NumberOp(Element element, SimpleMethod simpleMethod) throws MiniLangException { + super(element, simpleMethod); + if (MiniLangValidate.validationOn()) { + MiniLangValidate.attributeNames(simpleMethod, element, "value"); + MiniLangValidate.requiredAttributes(simpleMethod, element, "value"); + MiniLangValidate.noChildElements(simpleMethod, element); + } + valueFse = FlexibleStringExpander.getInstance(element.getAttribute("value")); } - public BigDecimal calcValue(MethodContext methodContext, int scale, int roundingMode) { - String valueStr = methodContext.expandString(this.valueStr); + @Override + public BigDecimal calcValue(MethodContext methodContext, int scale, int roundingMode) throws MiniLangException { + String valueStr = valueFse.expandString(methodContext.getEnvMap()); Locale locale = methodContext.getLocale(); if (locale == null) locale = Locale.getDefault(); - BigDecimal value; try { - BigDecimal parseVal = (BigDecimal) MiniLangUtil.convertType(valueStr, java.math.BigDecimal.class, locale, null, null); - value = parseVal.setScale(scale, roundingMode); + BigDecimal parsedVal = (BigDecimal) MiniLangUtil.convertType(valueStr, java.math.BigDecimal.class, locale, null, null); + return parsedVal.setScale(scale, roundingMode); } catch (Exception e) { - Debug.logError(e, "Could not parse the number string: " + valueStr, module); - throw new IllegalArgumentException("Could not parse the number string: " + valueStr); + throw new MiniLangRuntimeException("Exception thrown while parsing value attribute: " + e.getMessage(), this); } - return value; } + } + private final class InvalidOp extends MiniLangElement implements SubCalc { + + private InvalidOp(Element element, SimpleMethod simpleMethod) throws MiniLangException { + super(element, simpleMethod); + } + + @Override + public BigDecimal calcValue(MethodContext methodContext, int scale, int roundingMode) throws MiniLangException { + throw new MiniLangRuntimeException("Invalid calculate sub-element.", this); + } } - protected static interface SubCalc { - public BigDecimal calcValue(MethodContext methodContext, int scale, int roundingMode); + /** + * A factory for the <calculate> element. + */ + public static final class CalculateFactory implements Factory<Calculate> { + @Override + public Calculate createMethodOperation(Element element, SimpleMethod simpleMethod) throws MiniLangException { + return new Calculate(element, simpleMethod); + } + + @Override + public String getName() { + return "calculate"; + } } } |
| Free forum by Nabble | Edit this page |
