Hi again, I was thinking about this again a few minutes ago and thought maybe using an object that implements Map is not the solution, maybe if I use a List implementation, then OGNL will not interpret ".value" as get("value") since you can't do that to a List. Indeed getValue() was called using a List implementation, but during the set phase, setValue() isn't called.
After a bit more exploring, I think I found myself a bit of a workaround/solution. class MyMap implements Map { private Long id; public MyMap(Long id) { this.id = id; } public Object get(Object key) { System.out.println("MyMap@" + id + ".get(" + key + ")"); return new MyMap(id + 1); } public Object put(Object key, Object value) { System.out.println("MyMap@" + id + ".put(" + key + "," + value + ")"); return null; } ... } This code in the jsp seems to work for both get and set <s:textfield name="fieldMap['%{index}']"/> During get >> getIndex() >> getFieldMap() my...@1.get(1) >> getIndex() During set >> getFieldMap() my...@1.put(1,[Ljava.lang.String;@3354a944) Therefore, using my previous example, this should work <s:textfield name="fieldMap['1']"/> or <s:textfield name="fieldMap['CASUAL_1']"/> gets and sets gameConsole of userId = 1, MyMap should be able to look at the key, parses it (sees that it is only 1 id) and gets/sets accordingly <s:textfield name="fieldMap['2_9']"/> or <s:textfield name="fieldMap['AVERAGE_2_9']"/> gets and sets gameName of gameId = 9 of userId = 2, again MyMap should be able to look at the key, parses it (sees that it is 2 ids) and gets/sets accordingly On Tue, May 20, 2014 at 6:26 PM, Christoph Nenning < christoph.nenn...@lex-com.net> wrote: > > Result is > > > > During get > > > > >> getIndex() > > >> getIndex() > > >> getFieldMap() > > my...@1.get(1) > > my...@2.get(1) > > my...@3.get(value) > > >> getIndex() > > >> getIndex() > > > > Ok, getValue() isn't called, I can work with this, I can make all my > keys > > numbers, if I detect a non-number I just route the call to getValue() > > I think since it implements a map, ".value" get interpreted as > get("value"); > > > > During set > > > > >> getFieldMap() > > my...@1.get(1) > > my...@2.get(1) > > > > This I can't work with since setValue() isn't called anywhere. > > > > > > I had bad experience with more complex ONGL expressions like the following > in own applications (other issues with that are registering typeConverters > and validators): > > > <s:textfield name="wonky['1'].value"/> > <s:textfield name="wonky['1']['9'].value"/> > <s:textfield name="wonky['1'].value['9'].value"/> > > > > Usually I define several maps in the action, like this: > > > Map<userId, gameConsole> gamerConsoles > Map<userId, type> gamerConsoleTypes > Map<userId, gameId> gamerGames > > ... > > > Regards, > Christoph > > > > > > > > > My case is logically like this > > > > Imagine these data structure > > > > gamer: > > userId > > gameConsole > > type > > > > game: > > gameId > > gameName > > > > A game is associated to a gameConsole > > There are 2 types of gamers, > > for a type = CASUAL gamer, there are no games associated > > for a type = AVERAGE gamer, you can have games asscociated > > > > Data as follows > > > > userId: 1 > > gameConsole: nintendo > > type: CASUAL > > > > userId: 2 > > gameConsole: playstation > > type: AVERAGE > > gameId: 9, gameName: Gran Turismo > > gameId: 10, gameName: Winning Eleven > > > > Imagine I have a bean class called Wonky (that implements java.util.Map) > > that behaves like this > > > > Type = CASUAL > > Wonky.get(String key) will return a Wonky instance, if you then call > > getValue() on this instance it will return a gameConsole name (e.g. > > nintendo or playstation) > > > > Type = AVERAGE > > Wonky.get(String key) will return a Wonky instance, if you then call > > get(String key) on this instance it will return another Wonky instance, > if > > you then call getValue() on this instance it will return a game name > (e.g > > Gran Turismo or Winning Eleven) > > > > I want to be able to do this on a form > > > > <s:textfield name="wonky['1'].value"/> > > During get: shows the gameConsole of gamer with userId = 1 > > During set: sets the gameConsole for gamer with userId = 1 > > > > <s:textfield name="wonky['1']['9'].value"/> > > During get: shows the game with id = 9 associated with gamer with userId > = 1 > > During set: set the game name with id = 9 associated with gamer with > userId > > = 1 > > > > alternatively, I'm ok with this idea as well > > > > <s:textfield name="wonky['1'].value['9'].value"/> > > > > In this case Wonky behaves like this for Type = AVERAGE > > > > Wonky.get(String key) -> Wonky instance, getValue() -> return this; > > get(String key) -> another Wonky instance, getValue() on that instance > will > > return a game name (e.g Gran Turismo or Winning Eleven) > > > > I did more investigation > > > > Consider this code > > > > class MyMap implements Map { > > private Long id; > > > > public MyMap(Long id) { > > this.id = id; > > } > > > > ... > > > > @Override > > public Object get(Object key) { > > System.out.println("MyMap@" + id + ".get(" + key + ")"); > > return new MyMap(id + 1); > > } > > > > public String getValue() { > > System.out.println("MyMap@" + id + ".getValue()"); > > return Long.toString(id); > > } > > > > public void setValue(String value) { > > System.out.println("MyMap@" + id + ".setValue(" + value + ")"); > > } > > } > > > > On my action class > > > > public MyMap getFieldMap() { > > System.out.println(">> getFieldMap()"); > > MyMap map = new MyMap(1L); > > return map; > > } > > > > public void setFieldMap(MyMap map) { > > } > > > > private Long index = 1L; > > > > public Long getIndex() { > > System.out.println(">> getIndex()"); > > return index; > > } > > > > public void setIndex(Long index) { > > System.out.println(">> setIndex(" + index + ")"); > > this.index = index; > > } > > > > On my jsp > > > > <s:form action="..."> > > <s:textfield name="fieldMap['%{index}']['%{index}'].value"/> > > <s:submit/> > > </s:form> > > > > Result is > > > > During get > > > > >> getIndex() > > >> getIndex() > > >> getFieldMap() > > my...@1.get(1) > > my...@2.get(1) > > my...@3.get(value) > > >> getIndex() > > >> getIndex() > > > > Ok, getValue() isn't called, I can work with this, I can make all my > keys > > numbers, if I detect a non-number I just route the call to getValue() > > I think since it implements a map, ".value" get interpreted as > get("value"); > > > > During set > > > > >> getFieldMap() > > my...@1.get(1) > > my...@2.get(1) > > > > This I can't work with since setValue() isn't called anywhere. > > > > > > > > On Tue, May 20, 2014 at 3:02 PM, Christoph Nenning < > > christoph.nenn...@lex-com.net> wrote: > > > > > > Question: > > > > According to OGNL ( > > > > http://commons.apache.org/proper/commons-ognl/language-guide.html) > under > > > > heading JavaBeans Indexed Properties and OGNL Object Indexed > Properties, > > > > getFieldWithIndex(int index) should be called, but it isn't, same > goes > > > with > > > > getFieldWithKey(String key), why ? > > > > I looked at the latest OGNL source code (not the one I'm using since > I > > > > couldn't find the source code for version 3.0.6) hoping if I could > spot > > > a > > > > method where it tries different get/set methods. > > > > Tried setting breakpoints at OGNLRuntime, ObjectPropertyAccessor but > > > > coulnd't see anything obvious. > > > > > > > > At the moment I have a few workarounds > > > > 1. Use the ones that are working, i.e. getFieldList() and > getFieldMap(), > > > > implementing my own List and Map > > > > > > > > > Another way would be OGNL method call syntax: > > > > > > <s:property value="getFieldWithIndex(1)"/> > > > > > > > > > > > > > > > > > > > 2. Use java.util.List getList() and setList(java.util.List list), > struts > > > > will create a new List and populate accordingly in the order in > which > > > they > > > > were submitted in the form > > > > The issue with this is, if I have a pair or triplet of things that > go > > > hand > > > > in hand, then it gets complex. > > > > e.g. > > > > name and address where address is optional > > > > in the form the user put name1, address1, name2 (with no address), > and > > > > name3, address3 > > > > struts calls setName() with List containing name1, name2, name3 > > > > it also calls setAddress() with List containing address1, address3 > > > > There is no way the code can then figure out that that address3 is > > > actually > > > > associated with name3 > > > > In the past I was able to get around this by supplying another data > > > > structure that tells the code which address belongs to which name > but it > > > > involves JavaScript, and it gets messy quite quickly. > > > > > > > > > > > > struts/OGNL can store mulitple values in a map. You could use name as > key > > > and address as value: > > > > > > address: <s:textfield name="map['name']" /> > > > > > > That works well when you know names during "get phase" (when users > cannot > > > change names on that particular form). > > > If users can also enter names here, you need javascript. > > > > > > Another use case of that map syntax is when the number of input fileds > is > > > dynamic. > > > > > > > > > > > > > > > > 3. "Bypass" struts, in the "get" phase (showing the form) just use > > > straight > > > > html combined with struts tags, during the "set" phase (submitting > the > > > > form), will just get the parameters using HttpServletRequest > > > > e.g. during show form > > > > <input type="text" name="whateverName" value="<s:property > > > > value="whateverValue"/>"/> > > > > instead of > > > > <s:textfield ... /> > > > > > > > > > This is always possible, of course. It means you have to generate > > > parameter names during GET and parse them on POST. > > > > > > Usually I try to avoid this in my applications but there are (rare) > cases > > > I need to do that. > > > If you do so, you have to think of how you do validation. You can > still > > > use struts validation when you generate the same parameter names again > (in > > > validate() method) and use <s:fieldError> tags with the same generated > > > names. > > > > > > > > > > > > > > > > > > Regards, > > > Christoph > > > > > > > > > > > > > > > > > > > > > > > > > > OGNL Indexed and Object Indexed Properties > > > > > > > > Hi, > > > > > > > > I'm wondering why this code is not working. I'm using struts > 2.3.16.1 > > > and > > > > ognl 3.0.6. > > > > > > > > In my action class I have this > > > > > > > > public String[] getFieldArray() { > > > > System.out.println(">> getFieldArray()"); > > > > return null; > > > > } > > > > > > > > public void setFieldArray(String[] array) { > > > > } > > > > > > > > public MyList<String> getFieldList() { > > > > System.out.println(">> getFieldList()"); > > > > return new MyList<String>(); > > > > } > > > > > > > > public void setFieldList(MyList<String> list) { > > > > } > > > > > > > > public MyMap<String, String> getFieldMap() { > > > > System.out.println(">> getFieldMap()"); > > > > return new MyMap<String, String>(); > > > > } > > > > > > > > public void setFieldMap(MyMap<String, String> map) { > > > > > > > > } > > > > > > > > public String getFieldWithIndex(int index) { > > > > System.out.println(">> getFieldWithIndex(" + index + ")"); > > > > return null; > > > > } > > > > > > > > public void setFieldWithIndex(int index, String value) { > > > > System.out.println(">> setFieldWithIndex(" + index + "," + value + > ")"); > > > > } > > > > > > > > public String getFieldWithKey(String key) { > > > > System.out.println(">> getFieldWithKey(" + key + ")"); > > > > return null; > > > > } > > > > > > > > public void setFieldWithKey(String key, String value) { > > > > System.out.println(">> setFieldWithKey(" + key + "," + value + ")"); > > > > } > > > > > > > > Note that MyList and MyMap are as follows > > > > > > > > class MyMap<K, V> implements Map<K, V> { ... } > > > > class MyList<V> implements List<V> { ... } > > > > > > > > I have the get() methods overridden on the those classes > > > > > > > > On my jsp I have this > > > > > > > > <s:property value="fieldArray[1]"/> > > > > <s:property value="fieldList[1]"/> > > > > <s:property value="fieldMap['1']"/> > > > > <s:property value="fieldWithIndex[1]"/> > > > > <s:property value="fieldWithKey['1']"/> > > > > > > > > Result is > > > > > > > > >> getFieldArray() > > > > >> getFieldList() > > > > MyList.get(1) > > > > >> getFieldMap() > > > > MyMap.get(1) > > > > MyMap.get(1) > > > > > > > > Question: > > > > According to OGNL ( > > > > http://commons.apache.org/proper/commons-ognl/language-guide.html) > under > > > > heading JavaBeans Indexed Properties and OGNL Object Indexed > Properties, > > > > getFieldWithIndex(int index) should be called, but it isn't, same > goes > > > with > > > > getFieldWithKey(String key), why ? > > > > I looked at the latest OGNL source code (not the one I'm using since > I > > > > couldn't find the source code for version 3.0.6) hoping if I could > spot > > > a > > > > method where it tries different get/set methods. > > > > Tried setting breakpoints at OGNLRuntime, ObjectPropertyAccessor but > > > > coulnd't see anything obvious. > > > > > > > > At the moment I have a few workarounds > > > > 1. Use the ones that are working, i.e. getFieldList() and > getFieldMap(), > > > > implementing my own List and Map > > > > > > > > 2. Use java.util.List getList() and setList(java.util.List list), > struts > > > > will create a new List and populate accordingly in the order in > which > > > they > > > > were submitted in the form > > > > The issue with this is, if I have a pair or triplet of things that > go > > > hand > > > > in hand, then it gets complex. > > > > e.g. > > > > name and address where address is optional > > > > in the form the user put name1, address1, name2 (with no address), > and > > > > name3, address3 > > > > struts calls setName() with List containing name1, name2, name3 > > > > it also calls setAddress() with List containing address1, address3 > > > > There is no way the code can then figure out that that address3 is > > > actually > > > > associated with name3 > > > > In the past I was able to get around this by supplying another data > > > > structure that tells the code which address belongs to which name > but it > > > > involves JavaScript, and it gets messy quite quickly. > > > > > > > > 3. "Bypass" struts, in the "get" phase (showing the form) just use > > > straight > > > > html combined with struts tags, during the "set" phase (submitting > the > > > > form), will just get the parameters using HttpServletRequest > > > > e.g. during show form > > > > <input type="text" name="whateverName" value="<s:property > > > > value="whateverValue"/>"/> > > > > instead of > > > > <s:textfield ... /> > > > > > > This Email was scanned by Sophos Anti Virus > > > > > This Email was scanned by Sophos Anti Virus >