I was wondering if it's possible to invoke a method on an object in the template properties? Basically, I have a SAX parser that dynamically builds my properties for a template, which is essentially nested HashMaps and ArrayLists. There is an element I need to iterate over in a foreach loop, and I need to insure that it is a Collection containing one or more HashMaps. In my properties builder, if there is a recurring group of xml elements, then a HashMap is made for each element and they are all placed in a Collection which is added to the template properties. If there is only one ocurrance of this group, the HashMap is created and placed in the template properties. For example:
<group>
<field1 />
<field2 />
</group>
creates a single HashMap {field1="", field2=""} and places the HashMap in the template properties with the key "group".
Creates an ArrayList of two HashMaps and places them in the template properties with the name "group" ([{field1="", field2=""}, {field1="", field2=""}].
I found a solution using Velocity which allows me to invode a method from my foreach statement by creating a Helper object which has a method, insureCollection which tests an Object to see if it's a Collection; if so the Collection is returned, if not, the Object (a HashMap for me) is returned in a Collection:
#foreach( $g in ${helper.insureCollection(${group})} )
Field 1: $g.field1
Field 2: $g.field2
#end
Is there a way to do this with the print package of UJAC? Without placing the HashMap in a Collection the loop iterates over the HashMap and gives me one element at a time. I would prefer my properties builder to remain agnostic of data types and not require additional syntax for groups that appear once but need to be placed in a Collection.
I apologize if this is unclear, I appreciate your help.
Thanks,
Matt
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
this means to extend the ExpressionInterpreter, which is the very heart of UJAC. So far, I haven't planned to implement something like this. The ExpressionInterpreter syntax allows expressions in the form ${object operation operand} where operations are bound to the type of the processed object. I have to think about how I can realize your requested feature, without changing the current architcture too much.
Best regards,
Christian
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
Matt, I think I've found a solution for your specific problem. You need to do this:
a) Checkout the latest version from CVS, because I had to chance a few things to make this work.
b) Define a simple class, lets call it MattHelper ;-)
c) Create a type handler class for this class, which extends org.ujac.util.exi.type.BaseType. Let's call this cone MattHelperType. This could be implemented like this:
public class MattHelperType extends BaseType {
class EnsureCollectionOperation implements ExpressionOperation {
public Object evaluate(ExpressionTuple expr, Map params, Object bean) throws ExpressionException {
Operand operand = expr.getOperand();
if (operand == null) {
throw new NoOperandException("No operand given for operation: " + expr.getOperation() + " on object " + expr.getObject() + "!");
}
Object operandValue = interpreter.evalOperand(operand, params, bean);
if (operandValue instanceof Collection) {
return operandValue;
}
List collection = new ArrayList();
collection.add(operandValue);
return collection;
}
}
public MattHelperType(ExpressionInterpreter interpreter) {
super(interpreter);
addOperation("ensureCollection", new EnsureCollectionOperation());
}
public Class getType() {
return MattHelper.class;
}
}
d) Create an instance of the ExpressionInterpreter and register the new type implementation with it using the method 'registerTypeHandler'.
f) Set the expression interpreter instance at your DocumentPrinter instance using the method 'setExpressionInterpreter'.
e) Add an instance of your new MattHelper class to the document properties using the key 'helper'.
Now it should be possible to execute the expression
${helper ensureCollection group}
where group is the name of the property, which holds your data structure.
I haven't checked every single step completely but it should work more or less like this.
Best regards,
Christian
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
Wow! Thanks a lot, Christian. I did exactly what you said and it worked great! Thanks for building a tool that is flexible enough that it can be easily adapted to accomodate various situations.
Again, thank you very much for your time, help, and quick response.
--matt
If you would like to refer to this comment somewhere else in this project, copy and paste the following link:
I was wondering if it's possible to invoke a method on an object in the template properties? Basically, I have a SAX parser that dynamically builds my properties for a template, which is essentially nested HashMaps and ArrayLists. There is an element I need to iterate over in a foreach loop, and I need to insure that it is a Collection containing one or more HashMaps. In my properties builder, if there is a recurring group of xml elements, then a HashMap is made for each element and they are all placed in a Collection which is added to the template properties. If there is only one ocurrance of this group, the HashMap is created and placed in the template properties. For example:
<group>
<field1 />
<field2 />
</group>
creates a single HashMap {field1="", field2=""} and places the HashMap in the template properties with the key "group".
<group>
<field1 />
<field2 />
</group>
<group>
<field1 />
<field2 />
</group>
Creates an ArrayList of two HashMaps and places them in the template properties with the name "group" ([{field1="", field2=""}, {field1="", field2=""}].
I found a solution using Velocity which allows me to invode a method from my foreach statement by creating a Helper object which has a method, insureCollection which tests an Object to see if it's a Collection; if so the Collection is returned, if not, the Object (a HashMap for me) is returned in a Collection:
#foreach( $g in ${helper.insureCollection(${group})} )
Field 1: $g.field1
Field 2: $g.field2
#end
Is there a way to do this with the print package of UJAC? Without placing the HashMap in a Collection the loop iterates over the HashMap and gives me one element at a time. I would prefer my properties builder to remain agnostic of data types and not require additional syntax for groups that appear once but need to be placed in a Collection.
I apologize if this is unclear, I appreciate your help.
Thanks,
Matt
Hi Matt,
this means to extend the ExpressionInterpreter, which is the very heart of UJAC. So far, I haven't planned to implement something like this. The ExpressionInterpreter syntax allows expressions in the form ${object operation operand} where operations are bound to the type of the processed object. I have to think about how I can realize your requested feature, without changing the current architcture too much.
Best regards,
Christian
Matt, I think I've found a solution for your specific problem. You need to do this:
a) Checkout the latest version from CVS, because I had to chance a few things to make this work.
b) Define a simple class, lets call it MattHelper ;-)
c) Create a type handler class for this class, which extends org.ujac.util.exi.type.BaseType. Let's call this cone MattHelperType. This could be implemented like this:
public class MattHelperType extends BaseType {
class EnsureCollectionOperation implements ExpressionOperation {
public Object evaluate(ExpressionTuple expr, Map params, Object bean) throws ExpressionException {
Operand operand = expr.getOperand();
if (operand == null) {
throw new NoOperandException("No operand given for operation: " + expr.getOperation() + " on object " + expr.getObject() + "!");
}
Object operandValue = interpreter.evalOperand(operand, params, bean);
if (operandValue instanceof Collection) {
return operandValue;
}
List collection = new ArrayList();
collection.add(operandValue);
return collection;
}
}
public MattHelperType(ExpressionInterpreter interpreter) {
super(interpreter);
addOperation("ensureCollection", new EnsureCollectionOperation());
}
public Class getType() {
return MattHelper.class;
}
}
d) Create an instance of the ExpressionInterpreter and register the new type implementation with it using the method 'registerTypeHandler'.
f) Set the expression interpreter instance at your DocumentPrinter instance using the method 'setExpressionInterpreter'.
e) Add an instance of your new MattHelper class to the document properties using the key 'helper'.
Now it should be possible to execute the expression
${helper ensureCollection group}
where group is the name of the property, which holds your data structure.
I haven't checked every single step completely but it should work more or less like this.
Best regards,
Christian
Wow! Thanks a lot, Christian. I did exactly what you said and it worked great! Thanks for building a tool that is flexible enough that it can be easily adapted to accomodate various situations.
Again, thank you very much for your time, help, and quick response.
--matt