Re: [JXPath] Problem adding objects to an ArrayList Variable
Hi Dimitri, Hmmm. This seems a bit strange to me. It works for Maps (using put instead of add). If I change the context definition to this: Object o = new Object (); JXPathContext context = JXPathContext.newContext( o ); context.getVariables().declareVariable(myList, new ArrayList() ); context.getVariables().declareVariable(myMap, new HashMap() ); FunctionLibrary library = new FunctionLibrary (); library.addFunctions( new ClassFunctions ( List.class, list ) ); library.addFunctions( new ClassFunctions ( Map.class, map ) ); context.setFunctions( library ); Then I can produce the following results: getValue(list:size($myList)) returns: 0 getValue(list:add($myList, 'hello')) returns: true getValue(list:size($myList)) returns: 0 getValue($myList) returns: [] getValue(map:size($myMap)) returns: 0 getValue(map:put($myMap, 'key', 'value')) returns: null getValue(map:size($myMap)) returns: 1 getValue($myMap) returns: {key=value} So you can see that the Map is mutable. I'm not sure how difficult it is to allow for mutable Lists, but if I can't do things like: getValue(list:add($myList, 'hello')) returns: true I think it might be a show stopper for me. I am almost certainly trying to use JXPath in a way that was not intended. I am trying to build a 2 way data binding mapping structure which I think JXPath is excellent for, but I would also like to add some easy micro-scripting, which is maybe not possible with JXPath. One other thing, can you explain to me why the returned collections should be immutable? Is it necessary to enforce that behaviour? Thanks for your help, Adam. - Original Message - From: Dmitri Plotnikov [EMAIL PROTECTED] To: Jakarta Commons Developers List [EMAIL PROTECTED] Sent: Thursday, February 06, 2003 9:47 PM Subject: Re: [JXPath] Problem adding objects to an ArrayList Variable Adam, JXPath is in fact incapable of doing what you want it to do. Before a collection is passed to an extension function, it undergoes some conversions, which make it unmodifiable. JXPath should of course throw an exception in this case. I have added that exception and will check that new code in tonight. The rationale for this behavior is that from JXPath's perspective an extension function cannot take a collection by reference. Arguments of an extension function are either primitive values or node sets, which are really results of search. JXPath finds all nodes matching the path, loads them into a list and then passes the list to the function. Consider for example this: context.getValue(size(//foo[bar = 3])) From this example it should be clear that the argument of the function is in fact an unmodifiable collection - results of search. We could of course do something special for variables, but then the extension function's code would have to be aware of the difference between variables and general node sets. I think we should avoid this implicit difference. What you could do to add a node to the collection is call createPathAndSetValue() with a path like $myList[24]. Thanks for bringing this issue up - I should document it. - Dmitri --- Adam J Chesney [EMAIL PROTECTED] wrote: Hi, Attached is a small test program that simply defines a Context like this: Object o = new Object (); JXPathContext context = JXPathContext.newContext( o ); context.getVariables().declareVariable(myList, new ArrayList() ); FunctionLibrary library = new FunctionLibrary (); library.addFunctions( new ClassFunctions ( List.class, list ) ); context.setFunctions( library ); The output from executing several XPath expressions on it is thus: getValue(list:size($myList)) returns: 0 getValue(list:add($myList, 'hello')) returns: true getValue(list:size($myList)) returns: 0 getValue($myList) returns: [] I would expect the String 'hello' to be added to the list, and therefore the second call to size should return 1. Am I doing something wrong here? Cheers, Adam = Tel: (+44) 117 908 1254 (Direct) Mobile (+44) 7780 962 961 email: [EMAIL PROTECTED] This communication is intended solely for the addressee and is confidential. If you are not the intended recipient, any disclosure, copying, distribution or any action taken or omitted to be taken in reliance on it, is prohibited and may be unlawful. Although this e-mail and any attachments are believed to be free of any virus, or any other defect which might affect any computer or IT system into which they are received and opened, it is the responsibility of the recipient to ensure that they are virus free and no responsibility is accepted by Multicom Products Limited for any loss or damage arising in any way from receipt or use thereof. package test; import org.apache.commons.jxpath.*; import java.util.*; /** * pTitle: /p * pDescription: /p
Re: [JXPath] Problem adding objects to an ArrayList Variable
Adam, Hi Dimitri, Hmmm. This seems a bit strange to me. It works for Maps (using put instead of add). Sure, a Map is not a Collection. If I change the context definition to this: Object o = new Object (); JXPathContext context = JXPathContext.newContext( o ); context.getVariables().declareVariable(myList, new ArrayList() ); context.getVariables().declareVariable(myMap, new HashMap() ); FunctionLibrary library = new FunctionLibrary (); library.addFunctions( new ClassFunctions ( List.class, list ) ); library.addFunctions( new ClassFunctions ( Map.class, map ) ); context.setFunctions( library ); Then I can produce the following results: getValue(list:size($myList)) returns: 0 getValue(list:add($myList, 'hello')) returns: true getValue(list:size($myList)) returns: 0 getValue($myList) returns: [] getValue(map:size($myMap)) returns: 0 getValue(map:put($myMap, 'key', 'value')) returns: null getValue(map:size($myMap)) returns: 1 getValue($myMap) returns: {key=value} So you can see that the Map is mutable. I'm not sure how difficult it is to allow for mutable Lists, but if I can't do things like: getValue(list:add($myList, 'hello')) returns: true There is a sort-of round about way of getting to a list by reference. Create a wrapper for it and pass that wrapper to your own custom extension function, which would then add an element to the list. I think it might be a show stopper for me. I am almost certainly trying to use JXPath in a way that was not intended. I am trying to build a 2 way data binding mapping structure which I think JXPath is excellent for, but I would also like to add some easy micro-scripting, which is maybe not possible with JXPath. Right, I think you might be stretching JXPath's capabilities. The getValue() method is supposed to be a query, that is a request without side effects. One other thing, can you explain to me why the returned collections should be immutable? Is it necessary to enforce that behaviour? I make the collection immutable simply to get it to throw an exception. If the collection is mutable, operations changing it are successful, but since the changes are made to some temporary collection, not the original one you want to change. Thanks for your help, Adam. I hope this does not make jxpath completely unusable for you. - Dmitri - Original Message - From: Dmitri Plotnikov [EMAIL PROTECTED] To: Jakarta Commons Developers List [EMAIL PROTECTED] Sent: Thursday, February 06, 2003 9:47 PM Subject: Re: [JXPath] Problem adding objects to an ArrayList Variable Adam, JXPath is in fact incapable of doing what you want it to do. Before a collection is passed to an extension function, it undergoes some conversions, which make it unmodifiable. JXPath should of course throw an exception in this case. I have added that exception and will check that new code in tonight. The rationale for this behavior is that from JXPath's perspective an extension function cannot take a collection by reference. Arguments of an extension function are either primitive values or node sets, which are really results of search. JXPath finds all nodes matching the path, loads them into a list and then passes the list to the function. Consider for example this: context.getValue(size(//foo[bar = 3])) From this example it should be clear that the argument of the function is in fact an unmodifiable collection - results of search. We could of course do something special for variables, but then the extension function's code would have to be aware of the difference between variables and general node sets. I think we should avoid this implicit difference. What you could do to add a node to the collection is call createPathAndSetValue() with a path like $myList[24]. Thanks for bringing this issue up - I should document it. - Dmitri --- Adam J Chesney [EMAIL PROTECTED] wrote: Hi, Attached is a small test program that simply defines a Context like this: Object o = new Object (); JXPathContext context = JXPathContext.newContext( o ); context.getVariables().declareVariable(myList, new ArrayList() ); FunctionLibrary library = new FunctionLibrary (); library.addFunctions( new ClassFunctions ( List.class, list ) ); context.setFunctions( library ); The output from executing several XPath expressions on it is thus: getValue(list:size($myList)) returns: 0 getValue(list:add($myList, 'hello')) returns: true getValue(list:size($myList)) returns: 0 getValue($myList) returns: [] I would expect the String 'hello' to be added to the list, and therefore the second call to size should return 1. Am I doing something wrong here? Cheers, Adam = Tel: (+44) 117 908 1254 (Direct) Mobile (+44) 7780 962 961 email: [EMAIL PROTECTED
Re: [JXPath] Problem adding objects to an ArrayList Variable
Dimitri, snip Hmmm. This seems a bit strange to me. It works for Maps (using put instead of add). Sure, a Map is not a Collection. Good point. :) snip So you can see that the Map is mutable. I'm not sure how difficult it is to allow for mutable Lists, but if I can't do things like: getValue(list:add($myList, 'hello')) returns: true There is a sort-of round about way of getting to a list by reference. Create a wrapper for it and pass that wrapper to your own custom extension function, which would then add an element to the list. Yes, that is a possibility. I think it might be a show stopper for me. I am almost certainly trying to use JXPath in a way that was not intended. I am trying to build a 2 way data binding mapping structure which I think JXPath is excellent for, but I would also like to add some easy micro-scripting, which is maybe not possible with JXPath. Right, I think you might be stretching JXPath's capabilities. The getValue() method is supposed to be a query, that is a request without side effects. Thought so. However, it would make it a very cool tool indeed if it could be used like that, no? It seems that JXPath is very close to allowing me to do this. One other thing, can you explain to me why the returned collections should be immutable? Is it necessary to enforce that behaviour? I make the collection immutable simply to get it to throw an exception. If the collection is mutable, operations changing it are successful, but since the changes are made to some temporary collection, not the original one you want to change. I understand that if you are performing a query or a slice or something (within an XPath) that you must have to create a temporary collection to hold the result, however I still don't see why, if you are referencing a whole collection (i.e. $myList), a copy is made before operating on it. I still don't see the fundemental difference between this: getValue(map:put($myMap, 'key', 'value')) returns: null which calls 'put' on my original Map: and this: getValue(list:add($myList, 'hello')) returns: true which calls 'add' on a temporary copy of my original list, Why do we create a temporary structure just because the target of the ExtensionFunction is a Collection? Is it just because of the implementation details, or is it by design? Sorry for being a pain! :) BTW - I have hacked a patch that allows XPaths like this: context.createPathAndSetValue($myList[-1], 'hello'); which now adds to the List. I had to do this because when I tried this: context.createPathAndSetValue($myList[last()+1], 'hello'); I got an exception: org.apache.commons.jxpath.JXPathException: Cannot set value for xpath: $myList[last()+1] at org.apache.commons.jxpath.ri.JXPathContextReferenceImpl.setValue(JXPathConte xtReferenceImpl.java:412) I would have expected that my Factory would have been invoked in order to expand the List, but it never got called. Cheers, Adam. Thanks for your help, Adam. I hope this does not make jxpath completely unusable for you. - Dmitri - Original Message - From: Dmitri Plotnikov [EMAIL PROTECTED] To: Jakarta Commons Developers List [EMAIL PROTECTED] Sent: Thursday, February 06, 2003 9:47 PM Subject: Re: [JXPath] Problem adding objects to an ArrayList Variable Adam, JXPath is in fact incapable of doing what you want it to do. Before a collection is passed to an extension function, it undergoes some conversions, which make it unmodifiable. JXPath should of course throw an exception in this case. I have added that exception and will check that new code in tonight. The rationale for this behavior is that from JXPath's perspective an extension function cannot take a collection by reference. Arguments of an extension function are either primitive values or node sets, which are really results of search. JXPath finds all nodes matching the path, loads them into a list and then passes the list to the function. Consider for example this: context.getValue(size(//foo[bar = 3])) From this example it should be clear that the argument of the function is in fact an unmodifiable collection - results of search. We could of course do something special for variables, but then the extension function's code would have to be aware of the difference between variables and general node sets. I think we should avoid this implicit difference. What you could do to add a node to the collection is call createPathAndSetValue() with a path like $myList[24]. Thanks for bringing this issue up - I should document it. - Dmitri --- Adam J Chesney [EMAIL PROTECTED] wrote: Hi, Attached is a small test program that simply defines a Context like this: Object o = new Object (); JXPathContext context = JXPathContext.newContext( o ); context.getVariables
Re: [JXPath] Problem adding objects to an ArrayList Variable
Adam, --- Adam J Chesney [EMAIL PROTECTED] wrote: Dimitri, snip I think it might be a show stopper for me. I am almost certainly trying to use JXPath in a way that was not intended. I am trying to build a 2 way data binding mapping structure which I think JXPath is excellent for, but I would also like to add some easy micro-scripting, which is maybe not possible with JXPath. Right, I think you might be stretching JXPath's capabilities. The getValue() method is supposed to be a query, that is a request without side effects. Thought so. However, it would make it a very cool tool indeed if it could be used like that, no? It seems that JXPath is very close to allowing me to do this. One other thing, can you explain to me why the returned collections should be immutable? Is it necessary to enforce that behaviour? I make the collection immutable simply to get it to throw an exception. If the collection is mutable, operations changing it are successful, but since the changes are made to some temporary collection, not the original one you want to change. I understand that if you are performing a query or a slice or something (within an XPath) that you must have to create a temporary collection to hold the result, however I still don't see why, if you are referencing a whole collection (i.e. $myList), a copy is made before operating on it. I still don't see the fundemental difference between this: getValue(map:put($myMap, 'key', 'value')) returns: null which calls 'put' on my original Map: and this: getValue(list:add($myList, 'hello')) returns: true which calls 'add' on a temporary copy of my original list, Why do we create a temporary structure just because the target of the ExtensionFunction is a Collection? Is it just because of the implementation details, or is it by design? Sorry for being a pain! :) No, no, this is a legitimate complaint. Collections have a special status in XPath. XPath works with objects of four kinds: string, boolean, number and node set (aka collection). This why they are treated differently. Like I said earlier, we could make an exception for a variable, but other cases are more complicated. Consider for example this: add(//foo, 'hello') Let say jxpath finds a bean that has a property called foo and it happens to be a collection. Intuitively, it should then call the add method on that collection. But unfortunately it cannot, because there might be another bean somewhere else in three that also has a property called foo. It needs to combine all values from the first list with all values from the second list and continue search. Only when the whole tree has been examined, can it call add on the result. BTW - I have hacked a patch that allows XPaths like this: context.createPathAndSetValue($myList[-1], 'hello'); which now adds to the List. I had to do this because when I tried this: context.createPathAndSetValue($myList[last()+1], 'hello'); I got an exception: org.apache.commons.jxpath.JXPathException: Cannot set value for xpath: $myList[last()+1] at org.apache.commons.jxpath.ri.JXPathContextReferenceImpl.setValue(JXPathConte xtReferenceImpl.java:412) I would have expected that my Factory would have been invoked in order to expand the List, but it never got called. Thanks for bringing this up. I should make the exception message more concrete. The message should say: createPathAndSetValue can only be called with a 'simple' path, that is a path using only the child and attribute axes and no context-dependent predicates Since last() is a context dependent predicate, the invocation does not work. Have you considered using Jexl instead of JXPath? JXPath is intended primarily for mixed models containing both beans and XML. After all XPath itself is only defined for XML. Jexl does not have to deal with the burden of non-java bean models like DOM and JDOM, so its handling of beans is more intuitive. Cheers, Adam. Regards, - Dmitri Thanks for your help, Adam. I hope this does not make jxpath completely unusable for you. - Dmitri - Original Message - From: Dmitri Plotnikov [EMAIL PROTECTED] To: Jakarta Commons Developers List [EMAIL PROTECTED] Sent: Thursday, February 06, 2003 9:47 PM Subject: Re: [JXPath] Problem adding objects to an ArrayList Variable Adam, JXPath is in fact incapable of doing what you want it to do. Before a collection is passed to an extension function, it undergoes some conversions, which make it unmodifiable. JXPath should of course throw an exception in this case. I have added that exception and will check that new code in tonight. The rationale for this behavior is that from JXPath's perspective an extension function cannot take a collection by reference. Arguments
Re: [JXPath] Problem adding objects to an ArrayList Variable
Adam, JXPath is in fact incapable of doing what you want it to do. Before a collection is passed to an extension function, it undergoes some conversions, which make it unmodifiable. JXPath should of course throw an exception in this case. I have added that exception and will check that new code in tonight. The rationale for this behavior is that from JXPath's perspective an extension function cannot take a collection by reference. Arguments of an extension function are either primitive values or node sets, which are really results of search. JXPath finds all nodes matching the path, loads them into a list and then passes the list to the function. Consider for example this: context.getValue(size(//foo[bar = 3])) From this example it should be clear that the argument of the function is in fact an unmodifiable collection - results of search. We could of course do something special for variables, but then the extension function's code would have to be aware of the difference between variables and general node sets. I think we should avoid this implicit difference. What you could do to add a node to the collection is call createPathAndSetValue() with a path like $myList[24]. Thanks for bringing this issue up - I should document it. - Dmitri --- Adam J Chesney [EMAIL PROTECTED] wrote: Hi, Attached is a small test program that simply defines a Context like this: Object o = new Object (); JXPathContext context = JXPathContext.newContext( o ); context.getVariables().declareVariable(myList, new ArrayList() ); FunctionLibrary library = new FunctionLibrary (); library.addFunctions( new ClassFunctions ( List.class, list ) ); context.setFunctions( library ); The output from executing several XPath expressions on it is thus: getValue(list:size($myList)) returns: 0 getValue(list:add($myList, 'hello')) returns: true getValue(list:size($myList)) returns: 0 getValue($myList) returns: [] I would expect the String 'hello' to be added to the list, and therefore the second call to size should return 1. Am I doing something wrong here? Cheers, Adam = Tel: (+44) 117 908 1254 (Direct) Mobile (+44) 7780 962 961 email: [EMAIL PROTECTED] This communication is intended solely for the addressee and is confidential. If you are not the intended recipient, any disclosure, copying, distribution or any action taken or omitted to be taken in reliance on it, is prohibited and may be unlawful. Although this e-mail and any attachments are believed to be free of any virus, or any other defect which might affect any computer or IT system into which they are received and opened, it is the responsibility of the recipient to ensure that they are virus free and no responsibility is accepted by Multicom Products Limited for any loss or damage arising in any way from receipt or use thereof. package test; import org.apache.commons.jxpath.*; import java.util.*; /** * pTitle: /p * pDescription: /p * pCopyright: Copyright (c) 2003/p * pCompany: /p * @author unascribed * @version 1.0 */ public class Test2 { public static void main(String[] args) { try { Object o = new Object (); JXPathContext context = JXPathContext.newContext( o ); context.getVariables().declareVariable(myList, new ArrayList() ); FunctionLibrary library = new FunctionLibrary (); library.addFunctions( new ClassFunctions ( List.class, list ) ); context.setFunctions( library ); getValue (context, list:size($myList)); getValue (context, list:add($myList, 'hello')); getValue (context, list:size($myList)); getValue (context, $myList); } catch (Exception e1) { e1.printStackTrace(); } } public static Object getValue ( JXPathContext context, String xpath ) { Object obj = context.getValue( xpath ); System.out.println(getValue(\ + xpath + \) returns: + obj ); return obj; } } - To unsubscribe, e-mail: [EMAIL PROTECTED] For additional commands, e-mail: [EMAIL PROTECTED] __ Do you Yahoo!? Yahoo! Mail Plus - Powerful. Affordable. Sign up now. http://mailplus.yahoo.com - To unsubscribe, e-mail: [EMAIL PROTECTED] For additional commands, e-mail: [EMAIL PROTECTED]