So, I finally succeeded. Here's what I've done:
- I created custom PropertyPointer subclass called CustomPropertyPointer
- I created BeanPropertyPointer subclass CustomBeanPointer that overrides the
getPropertyPointer() method to return my custom property pointer
- I created a custom NodePointerFactory that returns a CustomBeanPointer for
the bean in question (class Foo) called CustomPointerFactory
That's the easy version. It was a bit more difficult though...
In BeanPropertyPointer, everything is based on the method getPropertyDescriptors(). This method returns an array of java.beans.PropertyDescriptor. As my read method is not a standard bean read method, it cannot be described by a PropertyDescriptor. So I reimplemented BeanPropertyPointer (now named CustomPropertyPointer) to be based on a new method getPropertyDescs() that returns an array of PropertyDescs:
public interface PropertyDesc {
public String getName();
public int getLength(Object bean, Object baseValue);
public Object getBaseValue(Object bean);
public Object getValue(Object bean, int index);
public boolean isCollection(Object baseValue);
}I then created an implementation BeanPropertyDesc that is based on a PropertyDescriptor. This interface allowed me to implement the custom behavior. It's implemented in TestPropertyDesc.
The getValue method of this implementation looks like:
public Object getValue(Object bean, int index) {
Root root = (Root) bean;
List list = getMapping();
return root.getC((String) list.get(index));
}The mapping is a simple list that maps from index to a String in this case. The mapping is set on CustomPropertyFactory an is passed down all the way through CustomBeanPointer to CustomPropertyPointer to TestPropertyDesc.
The Root class has the following method: +getC(key:String):C
With the above custom pointers it's now possible to access C objects by index like:
/c[2]
Why this fuss? Simply because there is no notion of ordering (e.g. Root does not have a list of C, it has a set of C) in the model of my application and I don't want to add an ordering. The ordering is only important for the output. In my real case, instances of C (Text) are accessed by language (Language).
If you are interested in the implementation example, you can have a glance here:
http://sim.iserver.ch:81/repos/private/jxpath/trunk/
As I'm pretty new to jxpath I might have implemented it way to complicated. Let me know, I'm interested in any simplifications...
Simon
Dmitri Plotnikov wrote:
Simon,
A possible alternative to using custom NodePointers in this situation could be a custom Function.
Step 1. You implement a static method that does pretty much what you have in your example:
public class MyFunc { public static Bar barByLang(Foo foo, String ln) { Language lang = (Language)mapping.get(ln); return foo.getBar(lang); } }
Step 2. Register the MyFunc class with JXPathContext.
Step 3. Use the function like this context.getValue("barByLang(/foo, 'fr')");
Also see http://jakarta.apache.org/commons/jxpath/users-guide.html#Extension%20Functions
Now, if you insist on custom implementation of NodePointer, choose the right superclass. For example, BeanPropertyPointer might be the right one.
I hope this helps
- Dmitri
----- Original Message ----- From: "Simon Raess" <[EMAIL PROTECTED]>
To: "Jakarta Commons Users List" <[EMAIL PROTECTED]>
Sent: Thursday, June 17, 2004 5:32 PM
Subject: [jxpath] Custom NodePointer implementation
hi
I hope to get some ideas how to implement the following:
Let's say I have a class Foo that can contain several Bar instances. The Bar instances are accessed by a key, let's say a Language object. So I have the following methods in Foo:
public Bar getBar(Language l); public Set getLanguages();
In the model of my application I don't want to have an absolute ordering of Bar objects, that is I don't want to expose a method like
public Bar getBar(int index);
or similar. My idea is to have an external object impose an ordering on the Bar objects (that is an ordering on languages). The point is, that the ordering may change at runtime. What that basically means is, that I'll have a mapping from a int key to a Language. For example the mapping would contain:
Language int key de 0 fr 1 en 2
So my idea is to create a custom NodePointer that receives such a mapping definition (so it knows about the ordering of languages). The following xpath expression (with the above mapping definition) should return the text with language fr:
foo/bar[2]
This should be translated (by the custom NodePointer) to a call to:
int index = ...; Foo foo = ...; List mapping = ...; Language lang = (Language)mapping.get(index); Bar fr_bar = foo.getBar(lang);
The following xpath
foo/bar
should probably be converted to something like
List list = foo.getBar();
I'm still unconfident about the jxpath internals, so could please somebody give me some pointers how I could achieve that? I'm I on the right tracks? Which methods from NodePointer class must I override (except the abstract ones, of course)? Is there a simple example that shows how to implement a NodePointer for objects without using any reflection, introspection, ...? Is there anything else I should consider? Please ask me if I missed some important information!
best regards Simon
--------------------------------------------------------------------- To unsubscribe, e-mail: [EMAIL PROTECTED] For additional commands, e-mail: [EMAIL PROTECTED]
--------------------------------------------------------------------- To unsubscribe, e-mail: [EMAIL PROTECTED] For additional commands, e-mail: [EMAIL PROTECTED]
--------------------------------------------------------------------- To unsubscribe, e-mail: [EMAIL PROTECTED] For additional commands, e-mail: [EMAIL PROTECTED]
