• Post Reply Bookmark Topic Watch Topic
  • New Topic
programming forums Java Mobile Certification Databases Caching Books Engineering Micro Controllers OS Languages Paradigms IDEs Build Tools Frameworks Application Servers Open Source This Site Careers Other Pie Elite all forums
this forum made possible by our volunteer staff, including ...
Marshals:
  • Campbell Ritchie
  • Jeanne Boyarsky
  • Ron McLeod
  • Paul Clapham
  • Liutauras Vilda
Sheriffs:
  • paul wheaton
  • Rob Spoor
  • Devaka Cooray
Saloon Keepers:
  • Stephan van Hulst
  • Tim Holloway
  • Carey Brown
  • Frits Walraven
  • Tim Moores
Bartenders:
  • Mikalai Zaikin

Struts logic:iterate weirdness

 
Greenhorn
Posts: 9
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
<logic:iterate name="multiForm" id="foo" property="keys" indexId="index" >
<tr><td>
<html:text name="multiForm" property='<%="keysIndexed["+index+"]"%>' />
</td></tr>
</logic:iterate>


public class MultiFieldForm extends ValidatorForm {

private List keys = new ArrayList();

public List getKeys() {
return keys;
}
public void setKeys(List keys) {
this.keys = keys;
}
public String getKeysIndexed(int index) {
return (String) keys.get(index);
}

public void setKeysIndexed(int index, String value) {
keys.add(index,value);
}
}


The above code throws an ArrayIndexOutofBoundException after 3 entries
 
Ranch Hand
Posts: 113
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Hi floyd,

I am trying something similar as of this.
I really don't understand the concept of iterating collection in form bean using logic:iterate.

In you logic:iterate you have used name="multiForm". Does this do not point you action form?

I would really appreciate if you could explain this concept.

Thank you for your time
[ December 08, 2004: Message edited by: s penumudi ]
 
pie sneak
Posts: 4727
Mac VI Editor Ruby
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
The reason for the issue is because the submissions do not happen in order of index.

ArrayList's add method allows you to specify an index as high as one beyond the current highest index. If it tries an add with index of 5 on an ArrayList with size 2, an ArrayIndexOutOfBoundsException is thrown.

Add this output line in your code to see that the indexes are not done in order.
public void setKeysIndexed(int index, String value) {
System.out.println(index);
keys.add(index,value);
}

You'll have to do something like this for it to work:

[ December 08, 2004: Message edited by: Marc Peabody ]
 
Ranch Hand
Posts: 3271
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Pink Floyd,

Welcome to the Ranch. I hope you enjoy it here. However, we have a standard naming policy here and we try to avoid obviously fictitious names (such as Pink Floyd). I'd appreciate it if you'd take a moment to read that naming policy and update your Publicly Displayed Name here.

Thanks.
 
Sheriff
Posts: 17644
300
Mac Android IntelliJ IDE Eclipse IDE Spring Debian Java Ubuntu Linux
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
MP: You'll have to do something like this for it to work:


To avoid the for loop and the call to remove() before add(), you could just use ensureCapacity(int) and set(int, Object).
 
Jon Baxter
Greenhorn
Posts: 9
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
It looks like the problem is deep within BeanUtils.populate(..) method which
is what is called ultimately for mapping request parameters to bean properties.

The BeanUtils.populate(..) method is called in the processPopulate() method
of the RequestProcessor class (See Struts src) and well before the execute(..) method of the action is called. This populate method is what calls your indexed getter/setter on your form.


Yes, the order of the parameters submitted is random, and this random order
throws off BeanUtils.populate(..). To illustrate..


<pre>

public class BeanUtilTest extends TestCase {

private int INPUT_NUM = 10;
public void testPopulateMethod() {

HashMap p = new HashMap();

for (int i = 0; i < INPUT_NUM; ++i) {
String key = "item[" + i + "].test";
String[] val = new String[] { String.valueOf(i) };
p.put(key, val);
}

System.out.println(p);

Bean bean = new Bean();
try {
BeanUtils.populate(bean, p);
} catch (Exception e) {
e.printStackTrace();
}

System.out.println(bean);
}

}


public class Bean {

private ArrayList items = new ArrayList();

public ArrayList getItems() {
return items;
}

public void setItems(ArrayList items) {
this.items = items;
}

public Item getItem(int index) {
if (index >= items.size()) {
Object o = new Item();
items.add(o);
return (Item)o;
}
return (Item) items.get(index);
}

public void setItem(int index, Object o) {
items.add(o);
}

public String toString() {
return items.toString();
}


public class Item {

private String test;

public Object getTest() {
return test;
}
public void setTest(Object test) {
this.test = (String)test;
}

public String toString() { return test; }
}

</pre>

When you run this the result is..[0, 1, 2, 7, 4, 5].

When you convert HashMap p = new HashMap() to

LinkedHashMap p = new LinkedHashMap() to

the result is..[0, 1, 2, 3, 4, 5, 6, 7, 8, 9] (as you would expect).

The thing is the Apache code uses java.util.HashMap for storing the request parameters.

At this point.. the behaviour of BeanUtils.populate(..) is erratic. I don't
see how modifying the indexed getter/setters in your form is going to make
any difference.

List based indexed properties in Struts are broken period.!
 
Jon Baxter
Greenhorn
Posts: 9
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Modified the Bean class to maintain the order in a TreeMap.


public class Bean {

private TreeMap _items = new TreeMap();

public ArrayList getItems() {
ArrayList l = new ArrayList();
for (Iterator ii = _items.entrySet().iterator(); ii.hasNext() {
l.add(ii.next());
}
return l;
}

public void setItems(ArrayList items) {
for (int i = 0; i < items.size(); ++i) {
_items.put(new Integer(i), items.get(i));
}
}

public Item getItem(int index) {
System.out.println("Getter called " + index);

Integer ii = new Integer(index);
if (!_items.containsKey(ii)) {
_items.put(new Integer(index), new Item());
}
return (Item) _items.get(ii);
}


public String toString() {
return _items.toString();
}

}

This looks like it fixed it.
 
Junilu Lacar
Sheriff
Posts: 17644
300
Mac Android IntelliJ IDE Eclipse IDE Spring Debian Java Ubuntu Linux
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Originally posted by Jon Baxter:
List based indexed properties in Struts are broken period.!



Hmmm...I wouldn't be too quick to jump to that conclusion. It really doesn't matter what the order of submission is, all parameters will eventually get processed anyway. The key is how you implement your getter and setter.

Try this version of your getter/setter:

private ArrayList items = new ArrayList();

public Item getItem(int index) {
return (Item) items.get(index);
}

public void setItem(int index, Object o) {
items.ensureCapacity(index);
items.set(index, o);
}
 
Greenhorn
Posts: 20
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
First off, let break this down. From the first discussion thread you have this code:

1 <logic:iterate name="multiForm" id="foo" property="keys" indexId="index" >
2 <tr><td>
3 <html:text name="multiForm" property='<%="keysIndexed["+index+"]"%>' />
4 </td></tr>
5 </logic:iterate>

I see that you are trying to leverage Struts Forms.
First line looks good. Make sure that the form "name" specified above matches the name specifed in struts-config.xml.

Third line is completely wrong.
(1)You are using the Struts Form bean name as your input textfield name. Also, what line 3 is saying that I am creating input textfield(s) with property of "keysIndexed" name. Which the property "keyIndexed" name MUST be included in the MultiFieldForm as gettters and setters. Why? When you add the Struts tag "<html:text property="somename"/>", it binds the getter and setter method to that input textfield.

(2) Unless you use Scriptlets to access the form bean, in struts you cannot access a method that has PARAMETER VALUE. getBean1(), getBean2()... Getting a method with int parameter values will not be called.

(3) To access the Form bean, this scriptlet does nothing to access the Struts Form Bean. "<%="keysIndexed["+index+"]"%>" , this is not correct. This is the correct syntax: <bean:write name="foo" property="someMethod"/>
The "id" is specified in the first line to reference the object. The bean tags are used to access the methods in the form

(4)<logic:iterate> can be very frustrating if not used properly. Especially if you are looping throught a LIST. There is a special way to loop through a list of objects.

Hope that helps. Sorry for being short winded. Doing this at work.
 
Junilu Lacar
Sheriff
Posts: 17644
300
Mac Android IntelliJ IDE Eclipse IDE Spring Debian Java Ubuntu Linux
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Originally posted by Joseph Hatton:
First off, let break this down. From the first discussion thread you have this code:

1 <logic:iterate name="multiForm" id="foo" property="keys" indexId="index" >
2 <tr><td>
3 <html:text name="multiForm" property='<%="keysIndexed["+index+"]"%>' />
4 </td></tr>
5 </logic:iterate>



>> Third line is completely wrong.

Agreed, there's something wrong but it's not in the way the attribute values are specified. It's in the way the getters and setters are implemented (see my reply above). Specifying name="multiForm" is redundant but there's nothing functionally/logically wrong with doing so.

You are using the Struts Form bean name as your input textfield name.

Actually, the "name" attribute indicates the attribute key of the bean that will be used to set the "value" attribute of the rendered HTML input tag. The "property" attribute renders an HTML input tag "name" attribute.

in struts you cannot access a method that has PARAMETER VALUE. getBean1(), getBean2()... Getting a method with int parameter values will not be called.

Not quite true. Struts tags can be used to access indexed and mapped properties. These properties can be backed by an array, List, or Map.
 
Joseph Hatton
Greenhorn
Posts: 20
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Junilu Lacar,

Thanks for the correction. I should have mention that normally, you cannot access parameter values unless it is properly mappe in the Struts-config.xml form. However, Index parameter ONLY applies to String Arrays. You cannot do that to list of Person object, Address object or Any Bean objects. I should have stated clearly my intentions.
 
Marc Peabody
pie sneak
Posts: 4727
Mac VI Editor Ruby
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator


At this point.. the behaviour of BeanUtils.populate(..) is erratic. I don't see how modifying the indexed getter/setters in your form is going to make any difference.

List based indexed properties in Struts are broken period.!


Jon,
You would have seen it, had you tried it. Just because you gave up on it does not mean it is "broken period".
I don't understand what stopped you from inserting 2 lines of code to try what was recommended other than some presumptuous doubt. I'm further perplexed that you wrote an entire test case to prove what could have been proven with the single line of System.out code I recommended.
OK, I'm done venting now.
 
With a little knowledge, a cast iron skillet is non-stick and lasts a lifetime.
reply
    Bookmark Topic Watch Topic
  • New Topic