Re: [JXPath] Problem adding objects to an ArrayList Variable

2003-02-07 Thread Adam J Chesney
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

2003-02-07 Thread Dmitri Plotnikov
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

2003-02-07 Thread Adam J Chesney
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

2003-02-07 Thread Dmitri Plotnikov
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

2003-02-06 Thread Dmitri Plotnikov
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]