This page captures ideas for improving expression handling over the course of 2.4.x development.
- Literal Expressions
- Property Expressions
- Evaulation Context
- Geometry Filter
Filter Brainstorming at Refractions:
- Filter-Brainstorming (2006_11_08)
Literal expressions (and the Filter/Expression) system are "semi-typed". Literal values can be represented as plain text or an xml fragment; but they are interpreted based on context.
This problem breaks down into two:
- Simple – an "Extention point" needs to be process string literals into a varity of contexts (like date, or date range)
- Complex – this depends on an more capable XML parser, on the bright side the Literal will be a real POJO (or a ComplexAttribute) and special evaualtion code will not be needed.
- support for more then one format (ie Date)
- need to consider printing as well (when making requests to external services)
Gabriel has mentioned in email a Jakarta Commons package suitable for handling the String constants.
See http://jira.codehaus.org/browse/GEOT-602 for more background.
Need examples of time range
Literal Evaulation Options
The first thing to note is that handing Literals and Context may be the same problem:
- For Literal: we need a String literal interpreted according to context
- For Context: we need an Expression result interpreted according to context
If we are "Eager" we need to ensure that we handle Literals correctly at parse time, if we are lazy we can put this off to evaualtion time. Give our uses cases (handling the same literal as both an Integer and String) my money is on the lazy approach.
Alternative 0: Current Implementation
The current implementation(s):
AbstractExpression - number() and friends
- uses constructor reflection (search for a constructor that takes a string parameter)
AttributeType.parse( Object )
- is type and context aware, not effecitvely used during filter encoding
Alternative 1: Use Apache Commons ConvertUtils
With this approach, we simply register instances of Converter and things will be happy.
ConvertUtils is somewhat limited:
- It only allows a single converter to be registered under a class (so more then one Date format cannot be used)
- It only does parsing, not encoding
We have reports of the implementation being based on singletons and generall not being suitable.
Alternative 2: Roll our own Interface
This approach also has the benefit of being able to wrap around ConvertUtils, or any other library that performs literal parsing, for instance java.beans.PropertyEditor. The interface might look something like:
Creating an implementation based on ConvertUtils:
And one based on PropertyEditorManager:
When dealing with a Date object and more then one Converter is avilable how can one choose between them?
If using the extension point, the first you get. The only way to counter act this is for the client to create a factory directly, or supply some kind of hint, liks for instance a date format.
Property expressions need to be able to extract information from a number of sources:
- Bean (ie POJOs)
- Maps & Lists
And they need to implement a subset of XPath (as documented by the Filter specification.
JXPath provides us with both needs.
Property Evaulation Options
This option was implemented
Here are the links to JXPath:
And we will need something like the following to hook it up:
And of course a Factory...
Full XPath support is only one test .. so lets try for the following implementations:
- SimpleFeaturePropertyAccessor – recognizes "name" as Feature attribute
- FeaturePropertyAccessor - extends the above with support for "@gml.id" as FeatureID
- BeanPropertyAccessor – Implementation that recognizes "name" as Bean property
- XPathPropertyAccesor – Implementation using JXPath out of the box with bean and collections support
- XPathFeatureAccessor – Implementation using JXPath configured to navigate feature model
- pros: works, can engage PropertyHandler based on type and xpath
- cons: xpath is not the be all and end all, review CSW2.0 to look for other accessors
We also need to evaulate our expression into the "context" it is expected. As an example when making a query against a datastore with an Interger "size" attribute, an expression will need to be sure to provide an Integer.
Here are a couple options for providing "context" to our existing filter evaulation interfaces.
Context Push / Class Context Alternative (GeoAPI Change)
This option was implemented
Add an additional method to Expression which takes a Class parameter:
The addition being the Class hint parameter, which as it states is a hint to the expression about which type of object to return.
This requires each and every expression (including all functions) to be able to type morph.
Context Pull / Value Object Alternative (GeoAPI Change)
This option not implemented
We can have the evaulate method return a Value Object (basically a value wrapper) rather then an explicit value:
We can then use Value to "unpack" the value into the type required for the context:
This approach matches the problem in that it let's us put off typing until it is needed (the same value may be used as a "compariable, string, and interger"), but it is not very cool for those expecting a real value out of evaualte( Object ), breaking out another method is an option.
Context Pull / Lazy Value Alternative (Geotools Change)
This option incomplete
To pull this off, expressions will now behave lazily, holding onto just a String, which can be a simple value, or something more complicated like an xml fragement.
As described in http://jira.codehaus.org/browse/GEOT-602, break out an extension point.
Cannot recommend the use of a String internally, Style objects like Stroke often use both "#FFFFFF" and Color.WHITE in time critical code). Since this approach is similar, I would recommend simply making use of the above ExpressionValue interface internally for both Cache and Morph.
Evaulation API Options
Since type morphing is mostly covered in the preceeding section on literal I am going to focus on the API differences between context push and context pull.
Context Push Code Examples
Let's start by isolating the type morphing into a single utility class for implementators:
Putting together the proposed API with ExpressionValue we have:
- Pro: works, easy to read
- Con: no ability to support Caching
- Con: not sure class will always provide enough context, consider Date Literals perhaps additional URI hint is needed?
Context Pull Code Example
Here is what changing Expression to use ExpressionValue would look like:
Putting this together we have:
- Pro: ExpressionValue available to those that need it, may allow support for URI based date format selection etc...
- Pro: Useful Cache for uses like Stroke (where an expression is used as both "#FFFFFF" and Color.WHITE )
- Con: Another class visible to end users (never a good thing)
As we get more and more implementations of Geometry (beyond the basic JTS) it would be nice to keep the filter love flowing.
This is a similar problem to that delth with by Property Expression (in that we need to be sure we have an implementation on hand to deal with the provided content. This time rather then simple access we need to be able to:
- Apply a Filter to the provided "Geometry"
- Morph between Geometry implementations
The morph part is well covered by the #Evaluation Context, an initial implementation based on WKT can morph from everything into JTS Geometry - where the functions work. More advanced implementations like Oracle SDO Geometry, or the PostGIS Geometry classes, can take the WKB route and not lose accuracy.
The Applying a Filter part can be handled using the same stratagy as Property Expresion a small interface that does what is needed.
All of the above examples need to work with the cross product of:
- JTS Geometry
- GeoAPI Geometry
- Oracle Geometry
- PostGIS Geometry
Geometry Filter Alternatives
Geometry Converter to JTS
Using a WKT (or WKB) Converer to allow the Evaluation Context solution to convert to JTS would let the existing Spatial Filter Implementations work out of the box with no further effort. They would "pull" the Geometry implementation into a JTS Geometry and then proceed form there.
Please note reprojection is not covered as part of this conversion "pull"; although it could be considered part of the process (our pull method simply takes a class but that can be changed).
Letting the Geometry implementations exist at the same level as JTS (and with greater range of expression would be a good thing.
Example SpatialStratagy implementation:
Our implementations would need to look up a Spatial Stratagy using the factory dance as usual. A SpatialStratagy implementation for each Geometry implementation would be quick to implement and allow for optimized performance. Adding in WKB Converters would let implementations work together without data loss.