You are viewing a plain text version of this content. The canonical link for it is here.
Posted to user@struts.apache.org by foo bar <li...@gmail.com> on 2014/05/20 09:43:31 UTC

OGNL Indexed and Object Indexed Properties

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 ... />

Re: OGNL Indexed and Object Indexed Properties

Posted by foo bar <li...@gmail.com>.
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()
MyMap@1.get(1)
>> getIndex()

During set

>> getFieldMap()
MyMap@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.Nenning@lex-com.net> wrote:

> > Result is
> >
> > During get
> >
> > >> getIndex()
> > >> getIndex()
> > >> getFieldMap()
> > MyMap@1.get(1)
> > MyMap@2.get(1)
> > MyMap@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()
> > MyMap@1.get(1)
> > MyMap@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()
> > MyMap@1.get(1)
> > MyMap@2.get(1)
> > MyMap@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()
> > MyMap@1.get(1)
> > MyMap@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.Nenning@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
>

Re: OGNL Indexed and Object Indexed Properties

Posted by Christoph Nenning <Ch...@lex-com.net>.
> Result is
> 
> During get
> 
> >> getIndex()
> >> getIndex()
> >> getFieldMap()
> MyMap@1.get(1)
> MyMap@2.get(1)
> MyMap@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()
> MyMap@1.get(1)
> MyMap@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()
> MyMap@1.get(1)
> MyMap@2.get(1)
> MyMap@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()
> MyMap@1.get(1)
> MyMap@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.Nenning@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

Re: OGNL Indexed and Object Indexed Properties

Posted by foo bar <li...@gmail.com>.
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()
MyMap@1.get(1)
MyMap@2.get(1)
MyMap@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()
MyMap@1.get(1)
MyMap@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.Nenning@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
>

Antwort: OGNL Indexed and Object Indexed Properties

Posted by Christoph Nenning <Ch...@lex-com.net>.
> 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