-
Notifications
You must be signed in to change notification settings - Fork 21
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
git-svn-id: https://svn.apache.org/repos/asf/cocoon/trunk/blocks/cocoon-forms/cocoon-forms-impl@409526 13f79535-47bb-0310-9956-ffa450edef68
- Loading branch information
1 parent
072cfb8
commit f3f9db2
Showing
19 changed files
with
1,700 additions
and
36 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
85 changes: 85 additions & 0 deletions
85
src/main/java/org/apache/cocoon/forms/expression/SumFunction.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,85 @@ | ||
/* | ||
* Copyright 1999-2005 The Apache Software Foundation. | ||
* | ||
* 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.apache.cocoon.forms.expression; | ||
|
||
import java.math.BigDecimal; | ||
import java.math.BigInteger; | ||
import java.util.Collection; | ||
import java.util.Iterator; | ||
|
||
import org.outerj.expression.AbstractExpression; | ||
import org.outerj.expression.Expression; | ||
import org.outerj.expression.ExpressionContext; | ||
import org.outerj.expression.ExpressionException; | ||
|
||
/** | ||
* Sum function. This function returns the sum of all of its argument, but | ||
* it accepts Collections or Iterators as arguments. When it finds such an | ||
* argument it iterates on all it's values, and try to sum them as well. It | ||
* accepts String and any instance of Number. | ||
*/ | ||
public class SumFunction extends AbstractExpression { | ||
|
||
public Object evaluate(ExpressionContext context) throws ExpressionException { | ||
BigDecimal result = new BigDecimal("0"); | ||
for(int i = 0; i < arguments.size(); i++) { | ||
Expression function = (Expression)arguments.get(i); | ||
Object ret = function.evaluate(context); | ||
if (ret instanceof Collection) { | ||
ret = ((Collection)ret).iterator(); | ||
} | ||
if (ret instanceof Iterator) { | ||
Iterator iter = (Iterator)ret; | ||
while (iter.hasNext()) { | ||
Object p = iter.next(); | ||
BigDecimal db = null; | ||
if (p instanceof BigDecimal) { | ||
db =(BigDecimal)p; | ||
} else if (p instanceof Long) { | ||
db = new BigDecimal(((Long)p).longValue()); | ||
} else if (p instanceof Integer) { | ||
db = new BigDecimal(((Integer)p).intValue()); | ||
} else if (p instanceof Double) { | ||
db = new BigDecimal(((Double)p).doubleValue()); | ||
} else if (p instanceof Float) { | ||
db = new BigDecimal(((Float)p).floatValue());; | ||
} else if (p instanceof BigInteger) { | ||
db = new BigDecimal((BigInteger)p); | ||
} else if (p instanceof Number) { | ||
db = new BigDecimal(((Number)p).doubleValue()); | ||
} else if (p instanceof String) { | ||
db = new BigDecimal((String)p); | ||
} else { | ||
throw new IllegalArgumentException("Cannot sum an argument of type " + p.getClass().getName()); | ||
} | ||
result = result.add(db); | ||
} | ||
} else { | ||
result = result.add((BigDecimal)function.evaluate(context)); | ||
} | ||
} | ||
return result; | ||
} | ||
|
||
public Class getResultType() { | ||
return BigDecimal.class; | ||
} | ||
|
||
public String getDescription() { | ||
return "Summatory"; | ||
} | ||
|
||
} |
200 changes: 200 additions & 0 deletions
200
src/main/java/org/apache/cocoon/forms/formmodel/CalculatedField.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,200 @@ | ||
/* | ||
* Copyright 1999-2005 The Apache Software Foundation. | ||
* | ||
* 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.apache.cocoon.forms.formmodel; | ||
|
||
import java.util.Iterator; | ||
import java.util.List; | ||
|
||
import org.apache.cocoon.forms.datatype.Datatype; | ||
import org.apache.cocoon.forms.datatype.convertor.ConversionResult; | ||
import org.apache.cocoon.forms.event.RepeaterEvent; | ||
import org.apache.cocoon.forms.event.RepeaterListener; | ||
import org.apache.cocoon.forms.event.ValueChangedEvent; | ||
import org.apache.cocoon.forms.event.ValueChangedListener; | ||
import org.apache.cocoon.forms.event.ValueChangedListenerEnabled; | ||
import org.apache.cocoon.forms.util.WidgetFinder; | ||
|
||
import com.ibm.icu.math.BigDecimal; | ||
|
||
|
||
/** | ||
* A field which calculates its value. | ||
* | ||
* <p>A calculated field is useful to create fields containing a sum, or a percentage, or any other | ||
* value derived from other fields in the form.</p> | ||
* | ||
* <p>The way the field calculates its value is determined by its | ||
* {@link org.apache.cocoon.forms.formmodel.CalculatedFieldAlgorithm}. | ||
* The algorithm is also responsible for determining which other form widgets will trigger | ||
* a value calculation for this field. | ||
* </p> | ||
* | ||
* @version $Id$ | ||
*/ | ||
public class CalculatedField extends Field { | ||
|
||
private CalculatedFieldDefinition definition = null; | ||
private CalculatedFieldAlgorithm algorithm = null; | ||
|
||
private WidgetFinder finder = null; | ||
private RecalculateValueListener mockListener = new RecalculateValueListener(); | ||
|
||
private boolean needRecaulculate = false; | ||
private boolean initialized = false; | ||
private boolean calculating = false; | ||
|
||
|
||
/** | ||
* @param definition | ||
*/ | ||
protected CalculatedField(CalculatedFieldDefinition definition) { | ||
super(definition); | ||
|
||
this.definition = definition; | ||
this.algorithm = definition.getAlgorithm(); | ||
} | ||
|
||
public void initialize() { | ||
super.initialize(); | ||
Iterator triggers = this.algorithm.getTriggerWidgets(); | ||
this.finder = new WidgetFinder(this.getParent(), triggers, true); | ||
this.finder.addRepeaterListener(new InstallHandlersListener()); | ||
installHandlers(); | ||
|
||
this.initialized = true; | ||
} | ||
|
||
/** | ||
* Installs handlers on other widgets. This both forces other widget to | ||
* submit the form when their values change, and also gives this field | ||
* a good optimization on calls to its algorithm. | ||
*/ | ||
protected void installHandlers() { | ||
List adds = this.finder.getNewAdditions(); | ||
for (Iterator iter = adds.iterator(); iter.hasNext();) { | ||
Widget widget = (Widget) iter.next(); | ||
if (widget instanceof ValueChangedListenerEnabled) { | ||
((ValueChangedListenerEnabled)widget).addValueChangedListener(mockListener); | ||
} | ||
} | ||
} | ||
|
||
protected void readFromRequest(String newEnteredValue) { | ||
// Never read a calculated field from request. | ||
} | ||
|
||
public Object getValue() { | ||
// Need to calculate if the following is true. | ||
// - We are not already calculating (to avoid stack overflow) | ||
// - We need to recaulculate. | ||
if (!calculating && needRecaulculate) { | ||
calculating = true; | ||
try { | ||
super.setValue(recalculate()); | ||
} finally { | ||
calculating = false; | ||
} | ||
} | ||
return super.getValue(); | ||
} | ||
|
||
/** | ||
* Calls the algorithm to perform a recaulculation. | ||
* @return The calculated value for this field. | ||
*/ | ||
protected Object recalculate() { | ||
Object ret = this.algorithm.calculate(this.getForm(), this.getParent(), this.getDatatype()); | ||
needRecaulculate = false; | ||
try { | ||
ret = convert(ret, this.getDatatype()); | ||
} catch (Exception e) { | ||
// FIXME : log the conversion error | ||
} | ||
return ret; | ||
} | ||
|
||
/** | ||
* Tries to convert the return value of the algorithm to the right value for this field datatype. | ||
* @param ret The return value fo the algorithm. | ||
* @param datatype The target datatype. | ||
* @return A converted value, or the given ret value if no conversion was possible. | ||
*/ | ||
protected Object convert(Object ret, Datatype datatype) throws Exception { | ||
// First try object to object conversion | ||
Class target = datatype.getTypeClass(); | ||
if (ret instanceof Number) { | ||
// Try to convert the number back to what expected | ||
Number number = (Number)ret; | ||
if (target.equals(BigDecimal.class)) { | ||
return number; | ||
} else if (target.equals(Double.class)) { | ||
ret = new Double(number.doubleValue()); | ||
} else if (target.equals(Float.class)) { | ||
ret = new Float(number.floatValue()); | ||
} else if (target.equals(Integer.class)) { | ||
ret = new Integer(number.intValue()); | ||
} else if (target.equals(Long.class)) { | ||
ret = new Long(number.longValue()); | ||
} else if (target.equals(String.class)) { | ||
ret = number.toString(); | ||
} | ||
return ret; | ||
} else if (ret instanceof String) { | ||
if (Number.class.isAssignableFrom(target)) { | ||
// Try to build a new number parsing the string. | ||
ret = target.getConstructor(new Class[] { String.class }).newInstance(new Object[] { ret }); | ||
} | ||
return ret; | ||
} | ||
// Finally try to use the convertor | ||
ConversionResult result = this.getDatatype().convertFromString(ret.toString(), getForm().getLocale()); | ||
if (result.isSuccessful()) { | ||
ret = result.getResult(); | ||
} | ||
return ret; | ||
} | ||
|
||
|
||
/** | ||
* This listener is added to trigger fields, so that we know when they have been modified AND they are | ||
* automatically submitted. | ||
*/ | ||
class RecalculateValueListener implements ValueChangedListener { | ||
public void valueChanged(ValueChangedEvent event) { | ||
needRecaulculate = true; | ||
getValue(); | ||
} | ||
} | ||
|
||
/** | ||
* This listener is installed on the WidgetFinder to know when some repeater | ||
* involved in our calculations gets modified. | ||
*/ | ||
class InstallHandlersListener implements RepeaterListener { | ||
public void repeaterModified(RepeaterEvent event) { | ||
needRecaulculate = true; | ||
installHandlers(); | ||
getValue(); | ||
} | ||
} | ||
|
||
/** | ||
* @return Returns the algorithm. | ||
*/ | ||
public CalculatedFieldAlgorithm getAlgorithm() { | ||
return algorithm; | ||
} | ||
} |
Oops, something went wrong.