001: package org.geotools.filter.expression;
002:
003: import java.util.Enumeration;
004:
005: import org.apache.commons.jxpath.JXPathContext;
006: import org.apache.commons.jxpath.JXPathIntrospector;
007: import org.apache.commons.jxpath.ri.JXPathContextReferenceImpl;
008: import org.geotools.factory.Hints;
009: import org.geotools.factory.Hints.Key;
010: import org.geotools.feature.IllegalAttributeException;
011: import org.geotools.feature.iso.AttributeImpl;
012: import org.geotools.feature.iso.ComplexAttributeImpl;
013: import org.geotools.feature.iso.FeatureImpl;
014: import org.geotools.feature.iso.attribute.BooleanAttribute;
015: import org.geotools.feature.iso.attribute.GeometricAttribute;
016: import org.geotools.feature.iso.attribute.NumericAttribute;
017: import org.geotools.feature.iso.attribute.TemporalAttribute;
018: import org.geotools.feature.iso.attribute.TextualAttribute;
019: import org.geotools.feature.iso.simple.SimpleFeatureImpl;
020: import org.geotools.feature.iso.type.AttributeDescriptorImpl;
021: import org.geotools.feature.iso.type.FeatureTypeImpl;
022: import org.geotools.feature.iso.xpath.AttributeDescriptorPropertyHandler;
023: import org.geotools.feature.iso.xpath.AttributeNodePointerFactory;
024: import org.geotools.feature.iso.xpath.AttributePropertyHandler;
025: import org.opengis.feature.Attribute;
026: import org.opengis.feature.ComplexAttribute;
027: import org.opengis.feature.Feature;
028: import org.opengis.feature.GeometryAttribute;
029: import org.opengis.feature.type.AttributeDescriptor;
030: import org.opengis.feature.type.AttributeType;
031: import org.opengis.feature.type.ComplexType;
032: import org.opengis.feature.type.FeatureType;
033: import org.xml.sax.helpers.NamespaceSupport;
034:
035: import com.vividsolutions.jts.geom.Geometry;
036:
037: /**
038: * Creates a namespace aware property accessor for ISO Features.
039: * <p>
040: * The created accessor handles a small subset of xpath expressions, a
041: * non-nested "name" which corresponds to a feature attribute, and "@id",
042: * corresponding to the feature id.
043: * </p>
044: * <p>
045: * THe property accessor may be run against {@link org.geotools.feature.Feature},
046: * or against {@link org.geotools.feature.FeatureType}. In the former case the
047: * feature property value is returned, in the latter the feature property type
048: * is returned.
049: * </p>
050: *
051: * @author Justin Deoliveira, The Open Planning Project
052: * @author Gabriel Roldan, Axios Engineering
053: *
054: */
055: public class FeaturePropertyAccessorFactory implements
056: PropertyAccessorFactory {
057:
058: /**
059: * {@link Hints} key used to pass namespace context to
060: * {@link #createPropertyAccessor(Class, String, Class, Hints)} in the form
061: * of a {@link NamespaceSupport} instance with the prefix/namespaceURI
062: * mappings
063: */
064: public static final Key NAMESPACE_CONTEXT = new Hints.Key(
065: org.xml.sax.helpers.NamespaceSupport.class);
066:
067: static {
068: // unfortunatley, jxpath only works against concreate classes
069: // JXPathIntrospector.registerDynamicClass(DefaultFeature.class,
070: // FeaturePropertyHandler.class);
071: JXPathContextReferenceImpl
072: .addNodePointerFactory(new AttributeNodePointerFactory());
073: }
074:
075: /** Single instnace is fine - we are not stateful */
076: static PropertyAccessor ATTRIBUTE_ACCESS = new FeaturePropertyAccessor();
077:
078: static PropertyAccessor DEFAULT_GEOMETRY_ACCESS = new DefaultGeometryFeaturePropertyAccessor();
079:
080: static PropertyAccessor FID_ACCESS = new FidFeaturePropertyAccessor();
081:
082: public PropertyAccessor createPropertyAccessor(Class type,
083: String xpath, Class target, Hints hints) {
084:
085: if (xpath == null)
086: return null;
087:
088: if (!ComplexAttribute.class.isAssignableFrom(type)
089: && !ComplexType.class.isAssignableFrom(type)
090: && !AttributeDescriptor.class.isAssignableFrom(type))
091: return null; // we only work with simple feature
092:
093: if ("".equals(xpath) && target == Geometry.class)
094: return DEFAULT_GEOMETRY_ACCESS;
095:
096: // check for fid access
097: if (xpath.matches("@(\\w+:)?id"))
098: return FID_ACCESS;
099:
100: // check for simple property access
101: // if (xpath.matches("(\\w+:)?(\\w+)")) {
102: NamespaceSupport namespaces = null;
103: if (hints != null) {
104: namespaces = (NamespaceSupport) hints
105: .get(FeaturePropertyAccessorFactory.NAMESPACE_CONTEXT);
106: }
107: if (namespaces == null) {
108: return ATTRIBUTE_ACCESS;
109: } else {
110: return new FeaturePropertyAccessor(namespaces);
111: }
112: // }
113:
114: // return null;
115: }
116:
117: /**
118: * We strip off namespace prefix, we need new feature model to do this
119: * property
120: * <ul>
121: * <li>BEFORE: foo:bar
122: * <li>AFTER: bar
123: * </ul>
124: *
125: * @param xpath
126: * @return xpath with any XML prefixes removed
127: */
128: static String stripPrefix(String xpath) {
129: int split = xpath.indexOf(":");
130: if (split != -1) {
131: return xpath.substring(split + 1);
132: }
133: return xpath;
134: }
135:
136: /**
137: * Access to Feature Identifier.
138: *
139: * @author Jody Garnett, Refractions Research Inc.
140: */
141: static class FidFeaturePropertyAccessor implements PropertyAccessor {
142:
143: public boolean canHandle(Object object, String xpath,
144: Class target) {
145: // we only work against feature, not feature type
146: return object instanceof Attribute
147: && xpath.matches("@(\\w+:)?id");
148: }
149:
150: public Object get(Object object, String xpath, Class target) {
151: Attribute feature = (Attribute) object;
152: return feature.getID();
153: }
154:
155: public void set(Object object, String xpath, Object value,
156: Class target) throws IllegalAttributeException {
157: throw new IllegalAttributeException(
158: "feature id is immutable");
159: }
160: }
161:
162: static class DefaultGeometryFeaturePropertyAccessor implements
163: PropertyAccessor {
164:
165: public boolean canHandle(Object object, String xpath,
166: Class target) {
167: if (!"".equals(xpath))
168: return false;
169:
170: if (target != Geometry.class
171: || target != GeometryAttribute.class)
172: return false;
173:
174: return (object instanceof Feature || object instanceof FeatureType);
175: }
176:
177: public Object get(Object object, String xpath, Class target) {
178: if (object instanceof Feature) {
179: return ((Feature) object).getDefaultGeometry();
180: }
181: if (object instanceof FeatureType) {
182: return ((FeatureType) object).getDefaultGeometry();
183: }
184:
185: return null;
186: }
187:
188: public void set(Object object, String xpath, Object value,
189: Class target) throws IllegalAttributeException {
190:
191: if (object instanceof Feature) {
192: final Feature f = (Feature) object;
193: GeometryAttribute geom;
194: if (value instanceof GeometryAttribute) {
195: geom = (GeometryAttribute) value;
196: f.setDefaultGeometry(geom);
197: } else if (value instanceof Geometry) {
198: geom = f.getDefaultGeometry();
199: geom.setValue(value);
200: } else {
201: throw new IllegalArgumentException(
202: "Argument is not a geometry: " + value);
203: }
204: }
205: if (object instanceof FeatureType) {
206: throw new IllegalAttributeException(
207: "feature type is immutable");
208: }
209: }
210: }
211:
212: static class FeaturePropertyAccessor implements PropertyAccessor {
213: static {
214: // TODO: use a wrapper public class for Feature in order to
215: // support any implementation. Reason being that JXPath works
216: // over concrete classes and hence we cannot set it up over the
217: // interface
218: JXPathIntrospector.registerDynamicClass(FeatureImpl.class,
219: AttributePropertyHandler.class);
220: JXPathIntrospector.registerDynamicClass(
221: SimpleFeatureImpl.class,
222: AttributePropertyHandler.class);
223: JXPathIntrospector.registerDynamicClass(
224: ComplexAttributeImpl.class,
225: AttributePropertyHandler.class);
226: JXPathIntrospector
227: .registerDynamicClass(AttributeImpl.class,
228: AttributePropertyHandler.class);
229: JXPathIntrospector.registerDynamicClass(
230: GeometricAttribute.class,
231: AttributePropertyHandler.class);
232: JXPathIntrospector.registerDynamicClass(
233: BooleanAttribute.class,
234: AttributePropertyHandler.class);
235: JXPathIntrospector.registerDynamicClass(
236: NumericAttribute.class,
237: AttributePropertyHandler.class);
238: JXPathIntrospector.registerDynamicClass(
239: TemporalAttribute.class,
240: AttributePropertyHandler.class);
241: JXPathIntrospector.registerDynamicClass(
242: TextualAttribute.class,
243: AttributePropertyHandler.class);
244:
245: JXPathIntrospector.registerDynamicClass(
246: AttributeDescriptorImpl.class,
247: AttributeDescriptorPropertyHandler.class);
248: JXPathIntrospector.registerDynamicClass(
249: FeatureTypeImpl.class,
250: AttributeDescriptorPropertyHandler.class);
251: }
252:
253: private NamespaceSupport namespaces;
254:
255: public FeaturePropertyAccessor() {
256: namespaces = new NamespaceSupport();
257: }
258:
259: public FeaturePropertyAccessor(NamespaceSupport namespaces) {
260: this .namespaces = namespaces;
261: }
262:
263: public boolean canHandle(Object object, String xpath,
264: Class target) {
265: // xpath = stripPrefix(xpath);
266:
267: return object instanceof Attribute
268: || object instanceof AttributeType
269: || object instanceof AttributeDescriptor;
270:
271: }
272:
273: public Object get(Object object, String xpath, Class target) {
274: // xpath = stripPrefix(xpath);
275:
276: JXPathContext context = JXPathContext.newContext(object);
277: context.setLenient(true);
278: Enumeration declaredPrefixes = namespaces
279: .getDeclaredPrefixes();
280: while (declaredPrefixes.hasMoreElements()) {
281: String prefix = (String) declaredPrefixes.nextElement();
282: String uri = namespaces.getURI(prefix);
283: context.registerNamespace(prefix, uri);
284: }
285:
286: Object value = context.getValue(xpath);
287:
288: return value;
289: }
290:
291: public void set(Object object, String xpath, Object value,
292: Class target) throws IllegalAttributeException {
293: // xpath = stripPrefix(xpath);
294:
295: if (object instanceof FeatureType) {
296: throw new IllegalAttributeException(
297: "feature type is immutable");
298: }
299:
300: JXPathContext context = JXPathContext.newContext(object);
301: context.setLenient(true);
302: context.setValue(xpath, value);
303:
304: assert value == context.getValue(xpath);
305: }
306: }
307:
308: }
|