According to the OGNL spec, setting indexed JavaBean properties using
lijst[0], lijst[1] etc should work. And it does; except not through
struts 2's params interceptor. Try it out:
public class ActionTest extends ActionSupport {
private List<String> someProperty = new ArrayList<String>();
public ActionTest(){
try {
Ognl.setValue("someProperty[0]", this, "Foo");
}
catch(Exception ex){
ex.printStackTrace();
}
}
public String execute(){
return SUCCESS;
}
public String getSomeProperty(int index){
return someProperty.get(index);
}
public void setSomeProperty(int index, String name){
someProperty.set(index, name);
}
}
Set a breakpoint on setSomeProperty and you will find that it calls the
indexed setter exactly as it should. I've been debugging this around a
bit to see why this doesn't work through the Struts 2 params
interceptor, and I found that apparently this is due to the fact that
Struts does not pass the action instance itself as the object to
evaluate the OGNL expression against, but rather a CompoundRoot of the
action and a DefaultTextProvider.
When the OGNL expression "someProperty[0]" is evaluated, OGNL will parse
the expression into an ASTChain of 2 ASTProperty's.
ASTChain.setValueBody is then called to execute the appropriate setter.
What's preventing the indexed setter from being executed is
propertyNode.getIndexedPropertyType(context, target) returning the wrong
index type.
The index type of a property (see ASTProperty.getIndexedPropertyType)
appears to be ultimately determined in
OgnlRuntime.getIndexedPropertyType, which fetches a PropertyDescriptor
for the specified object's class and property (ie. "someProperty" in
this case). The problem here is that, since the object is a
CompoundRoot, a property descriptor for CompoundRoot.class is returned,
which understandably has no property descriptor for "someProperty" since
it has no indexed setter for "someProperty", causing it to return
INDEXED_PROPERTY_NONE (ie. not indexed) instead of
INDEXED_PROPERTY_OBJECT. When the action instance itself is passed as
the object to evaluate against, a property descriptor is fetched for the
actual action class (ActionTest.class), and an
ObjectIndexedPropertyDescriptor is returned for "someProperty" as expected.
So it looks like the root cause is that only the methods of CompoundRoot
are considered for determining any indexed setters, not those of the
objects it contains. I'm sure there are a whole slew of a problems
abound if you would propagate the lookup into the contained objects, and
even if you did, the property descriptors are only assigned once and
then reused, so you'd still only get the descriptors for the first
CompoundRoot object you encounter.
I'm fairly new to the Struts 2 and OGNL codebase and I'm not sure what
the best way to fix it would be, but I hope this might provide some
insight or even serve as a rough bug report.
newton.dave wrote:
Timmos wrote:
Hi,
I figured out how to generate a form with dynamic length. The submitting
part remains a bit tricky, but before you redirect me to OGNL / Type
Conversion pages, I have to say I already read those articles.
This is a part of my generated .jsp (the generate part - I won't include
the
Action code for this):
<s:iterator value="properties" status="stat">
<s:textfield name="lijst[2]" label="%{name}" />
</s:iterator>
As you can see, for testing purposes I hard-coded the index "2", but I
don't
know if this syntax is correct.
This form is to be submitted to DoSearchAction:
public void setLijst(int index, String s) {
lijst[index] = s;
System.out.println(">>>> LIJST!!! >> " + index + ":::" + s);
}
When I run this piece of junk, I get: ognl.OgnlException: target is null
for
setProperty(null, "2", [Ljava.lang.String;@13dd8).
I would like to know what is wrong with the setter / s:textfield name
parameter. What do I have to do to get it working?
(1) If there are multiple parameters w/ the same name the framework
appears to put them into a collection: it may not be necessary to do
*anything*.
(2) IIRC using indexed setters would use the (n) syntax (not [n]), but I
could be remembering wrong. In any case, you don't need to use an
indexed setter, just have an appropriate collection property with a
setter, say:
private List<String> lijst;
public void setLijst(List l) { lijst = l; }
(Or whatever.)
Dave
---------------------------------------------------------------------
To unsubscribe, e-mail: user-unsubscr...@struts.apache.org
For additional commands, e-mail: user-h...@struts.apache.org
Thx. What should the name attributes of my textfield be then, knowing that
"lijst" is my List to set?
<s:textfield value="" name"lijst" /> ? And it should work?