/*
* GeoTools - The Open Source Java GIS Toolkit
* http://geotools.org
*
* (C) 2009-2011, Open Source Geospatial Foundation (OSGeo)
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*/
package org.geotools.filter.expression;
import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.geotools.data.complex.config.EmfAppSchemaReader;
import org.geotools.data.complex.config.FeatureTypeRegistry;
import org.geotools.factory.Hints;
import org.geotools.feature.AttributeImpl;
import org.geotools.feature.ComplexAttributeImpl;
import org.geotools.feature.FeatureImpl;
import org.geotools.feature.NameImpl;
import org.geotools.feature.Types;
import org.geotools.feature.type.AttributeDescriptorImpl;
import org.geotools.feature.type.AttributeTypeImpl;
import org.geotools.feature.type.ComplexTypeImpl;
import org.geotools.feature.type.UniqueNameFeatureTypeFactoryImpl;
import org.geotools.filter.AttributeExpressionImpl;
import org.geotools.filter.identity.FeatureIdImpl;
import org.geotools.test.AppSchemaTestSupport;
import org.geotools.xml.SchemaIndex;
import org.junit.Test;
import org.opengis.feature.Feature;
import org.opengis.feature.Property;
import org.opengis.feature.type.AttributeDescriptor;
import org.opengis.feature.type.AttributeType;
import org.opengis.feature.type.ComplexType;
import org.opengis.feature.type.FeatureType;
import org.opengis.feature.type.FeatureTypeFactory;
import org.opengis.feature.type.Name;
import org.opengis.feature.type.PropertyDescriptor;
import org.xml.sax.Attributes;
import org.xml.sax.helpers.NamespaceSupport;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
/**
* This is to demonstrate evaluating XPaths as attribute expressions when complex
* attributes/features are passed in, instead of simple features. This is necessary since complex
* features could contain nested properties, and we should be able to get properties of any level
* from the features.
*
* @author Rini Angreani (CSIRO Earth Science and Resource Engineering)
*
*
*
* @source $URL$
*/
public class FeaturePropertyAccessorTest extends AppSchemaTestSupport {
/**
* Mock name space prefix
*/
static final String EG = "urn:cgi:xmlns:Example:1.0";
static final Name SIMPLE_ATTRIBUTE = new NameImpl(EG, "simpleAttribute");
static final Name COMPLEX_ATTRIBUTE = new NameImpl(EG, "complexAttribute");
static final Name ROOT_ATTRIBUTE = new NameImpl(EG, "rootAttribute");
static final Name SINGLE_LEAF_ATTRIBUTE = new NameImpl(EG, "singleLeafAttribute");
static final Name MULTI_LEAF_ATTRIBUTE = new NameImpl(EG, "multiLeafAttribute");
/**
* Mock name space
*/
static final NamespaceSupport NAMESPACES = new NamespaceSupport() {
{
declarePrefix("eg", EG);
}
};
private static final String GSMLNS = "http://www.cgi-iugs.org/xml/GeoSciML/2";
private static final String XLINKNS = "http://www.w3.org/1999/xlink";
private static final String schemaBase = "/test-data/";
/**
* Gsml name space
*/
static final NamespaceSupport GSMLNAMESPACES = new NamespaceSupport() {
{
declarePrefix("gsml", GSMLNS);
declarePrefix("xlink", XLINKNS);
}
};
@Test
public void testComplexFeature() {
FeatureType fType = createFeatureType();
AttributeDescriptor complexDesc = (AttributeDescriptor)fType.getDescriptor(COMPLEX_ATTRIBUTE);
ComplexType complexAttType = (ComplexType) complexDesc.getType();
AttributeDescriptor rootDesc = (AttributeDescriptor) complexAttType.getDescriptor(ROOT_ATTRIBUTE) ;
ComplexType rootAttType = (ComplexType) rootDesc.getType();
// feature properties
Collection<Property> properties = new ArrayList<Property>(fType.getDescriptors().size());
/**
* Build the feature properties
*/
// eg:simpleAttribute
AttributeDescriptor simpleAttributeDesc = (AttributeDescriptor) fType.getDescriptor(SIMPLE_ATTRIBUTE);
AttributeImpl simpleAttribute = new AttributeImpl("simple value", simpleAttributeDesc, null);
properties.add(simpleAttribute);
// eg:complexAttribute/eg:rootAttribute[1]
Collection<Property> rootPropertiesOne = new ArrayList<Property>();
AttributeDescriptor multiLeafDesc = (AttributeDescriptor) rootAttType.getDescriptor(MULTI_LEAF_ATTRIBUTE);
// eg:complexAttribute/eg:rootAttribute[1]/eg:multiLeafAttribute[1]
AttributeImpl leafOne = new AttributeImpl("multi leaf value 1", multiLeafDesc, null);
rootPropertiesOne.add(leafOne);
// eg:complexAttribute/eg:rootAttribute[1]/eg:multiLeafAttribute[2]
AttributeImpl leafTwo = new AttributeImpl("multi leaf value 2", multiLeafDesc, null);
rootPropertiesOne.add(leafTwo);
// eg:complexAttribute/eg:rootAttribute[1]/eg:singleLeafAttribute
AttributeDescriptor singleLeafDesc = (AttributeDescriptor) rootAttType.getDescriptor(SINGLE_LEAF_ATTRIBUTE);
AttributeImpl singleLeaf = new AttributeImpl("single leaf value", singleLeafDesc, null);
rootPropertiesOne.add(singleLeaf);
//NC- add, test xml-attribute
Map<Name, Object> userData = new HashMap<Name, Object>();
singleLeaf.getUserData().put(Attributes.class, userData);
userData.put(Types.typeName(EG, "att"), "test attribute");
AttributeImpl rootOne = new ComplexAttributeImpl(rootPropertiesOne, rootDesc, null);
// eg:complexAttribute/eg:rootAttribute[2]
Collection<Property> rootPropertiesTwo = new ArrayList<Property>();
// eg:complexAttribute/eg:rootAttribute[2]/eg:multiLeafAttribute[1]
rootPropertiesTwo.add(leafOne);
rootPropertiesTwo.add(singleLeaf);
AttributeImpl rootTwo = new ComplexAttributeImpl(rootPropertiesTwo, rootDesc, null);
// eg:complexAttribute/eg:rootAttribute[3]
Collection<Property> rootPropertiesThree = new ArrayList<Property>();
// eg:complexAttribute/eg:rootAttribute[3]/eg:singleLeafAttribute
rootPropertiesThree.add(singleLeaf);
AttributeImpl rootThree = new ComplexAttributeImpl(rootPropertiesThree, rootDesc, null);
Collection<Property> complexProperties = new ArrayList<Property>(2);
complexProperties.add(rootOne);
complexProperties.add(rootTwo);
complexProperties.add(rootThree);
AttributeImpl complexAttribute = new ComplexAttributeImpl(complexProperties,
complexDesc, null);
properties.add(complexAttribute);
Feature feature = new FeatureImpl(properties, fType, new FeatureIdImpl("test1"));
/**
* Test evaluating complex feature
*/
// test evaluating simple property
AttributeExpressionImpl ex = new AttributeExpressionImpl("eg:simpleAttribute", new Hints(
FeaturePropertyAccessorFactory.NAMESPACE_CONTEXT, NAMESPACES));
assertEquals(simpleAttribute, ex.evaluate(feature));
// test evaluating complex property
ex = new AttributeExpressionImpl("eg:complexAttribute", new Hints(
FeaturePropertyAccessorFactory.NAMESPACE_CONTEXT, NAMESPACES));
assertEquals(complexAttribute, ex.evaluate(feature));
// test multi-valued nested properties
ex = new AttributeExpressionImpl("eg:complexAttribute/eg:rootAttribute", new Hints(
FeaturePropertyAccessorFactory.NAMESPACE_CONTEXT, NAMESPACES));
// no index would return array of all features
Object o = ex.evaluate(feature);
assertTrue( o instanceof List );
assertEquals(3, ((List) o).size());
assertEquals(rootOne, ((List) o).get(0));
assertEquals(rootTwo, ((List) o).get(1));
assertEquals(rootThree, ((List) o).get(2));
//test nested on multi-valued attributes
ex = new AttributeExpressionImpl("eg:complexAttribute/eg:rootAttribute/eg:singleLeafAttribute", new Hints(
FeaturePropertyAccessorFactory.NAMESPACE_CONTEXT, NAMESPACES));
// no index would return array of all features
o = ex.evaluate(feature);
assertTrue( o instanceof List );
assertEquals(3, ((List) o).size());
assertEquals(singleLeaf, ((List) o).get(0));
assertEquals(singleLeaf, ((List) o).get(1));
assertEquals(singleLeaf, ((List) o).get(2));
// index specified
ex = new AttributeExpressionImpl("eg:complexAttribute/eg:rootAttribute[1]", new Hints(
FeaturePropertyAccessorFactory.NAMESPACE_CONTEXT, NAMESPACES));
assertEquals(rootOne, ex.evaluate(feature));
ex = new AttributeExpressionImpl("eg:complexAttribute/eg:rootAttribute[2]", new Hints(
FeaturePropertyAccessorFactory.NAMESPACE_CONTEXT, NAMESPACES));
assertEquals(rootTwo, ex.evaluate(feature));
// single valued nested property
ex = new AttributeExpressionImpl(
"eg:complexAttribute/eg:rootAttribute[3]/eg:singleLeafAttribute", new Hints(
FeaturePropertyAccessorFactory.NAMESPACE_CONTEXT, NAMESPACES));
assertEquals(singleLeaf, ex.evaluate(feature));
// null values
ex = new AttributeExpressionImpl(
"eg:complexAttribute/eg:rootAttribute[3]/eg:multiLeafAttribute", new Hints(
FeaturePropertyAccessorFactory.NAMESPACE_CONTEXT, NAMESPACES));
assertEquals(null, ex.evaluate(feature));
// deeper nesting
ex = new AttributeExpressionImpl(
"eg:complexAttribute/eg:rootAttribute[2]/eg:multiLeafAttribute", new Hints(
FeaturePropertyAccessorFactory.NAMESPACE_CONTEXT, NAMESPACES));
assertEquals(leafOne, ex.evaluate(feature));
// property index doesn't exist
ex = new AttributeExpressionImpl(
"eg:complexAttribute/eg:rootAttribute[2]/eg:multiLeafAttribute[2]", new Hints(
FeaturePropertyAccessorFactory.NAMESPACE_CONTEXT, NAMESPACES));
assertEquals(null, ex.evaluate(feature));
// expect an exception when object supplied is not a complex attribute
boolean exceptionThrown = false;
try {
ex.setLenient(false); //NC -added, only exception if lenient off
assertEquals(null, ex.evaluate(singleLeaf));
} catch (Exception e) {
exceptionThrown = true;
}
if (!exceptionThrown) {
fail("Expecting Exception since object passed in is not a complex attribute.");
}
// invalid property
ex = new AttributeExpressionImpl("randomAttribute", new Hints(
FeaturePropertyAccessorFactory.NAMESPACE_CONTEXT, NAMESPACES));
assertEquals(null, ex.evaluate(feature));
assertEquals(null, ex.evaluate(fType));
// NC - test xml attribute
ex = new AttributeExpressionImpl(
"eg:complexAttribute/eg:rootAttribute[3]/eg:singleLeafAttribute/@eg:att", new Hints(
FeaturePropertyAccessorFactory.NAMESPACE_CONTEXT, NAMESPACES));
assertEquals("test attribute", ex.evaluate(feature));
// NC - test descriptor functionality
ex = new AttributeExpressionImpl("eg:simpleAttribute", new Hints(
FeaturePropertyAccessorFactory.NAMESPACE_CONTEXT, NAMESPACES));
assertEquals( simpleAttributeDesc, ex.evaluate(fType));
ex = new AttributeExpressionImpl("eg:complexAttribute", new Hints(
FeaturePropertyAccessorFactory.NAMESPACE_CONTEXT, NAMESPACES));
assertEquals( complexDesc, ex.evaluate(fType));
// test nested properties
ex = new AttributeExpressionImpl("eg:complexAttribute/eg:rootAttribute", new Hints(
FeaturePropertyAccessorFactory.NAMESPACE_CONTEXT, NAMESPACES));
assertEquals( rootDesc, ex.evaluate(fType));
ex = new AttributeExpressionImpl(
"eg:complexAttribute/eg:rootAttribute/eg:singleLeafAttribute", new Hints(
FeaturePropertyAccessorFactory.NAMESPACE_CONTEXT, NAMESPACES));
assertEquals(singleLeafDesc, ex.evaluate(fType));
ex = new AttributeExpressionImpl(
"eg:complexAttribute/eg:rootAttribute/eg:multiLeafAttribute", new Hints(
FeaturePropertyAccessorFactory.NAMESPACE_CONTEXT, NAMESPACES));
assertEquals(multiLeafDesc, ex.evaluate(fType));
}
public static FeatureType createFeatureType() {
FeatureTypeFactory tfac = new UniqueNameFeatureTypeFactoryImpl();
Name fName = new NameImpl(EG, "complexFeatureType");
Collection<PropertyDescriptor> schema = new ArrayList<PropertyDescriptor>();
// add simple attribute
AttributeType attType = new AttributeTypeImpl(SIMPLE_ATTRIBUTE, String.class, false, false,
Collections.EMPTY_LIST, null, null);
AttributeDescriptor attDesc = new AttributeDescriptorImpl(attType, SIMPLE_ATTRIBUTE, 0, 1,
true, null);
schema.add(attDesc);
// build nested attributes
Collection<PropertyDescriptor> rootProperties = new ArrayList<PropertyDescriptor>();
attType = new AttributeTypeImpl(SINGLE_LEAF_ATTRIBUTE, String.class, false, false,
Collections.EMPTY_LIST, null, null);
attDesc = new AttributeDescriptorImpl(attType, SINGLE_LEAF_ATTRIBUTE, 0, 1, true, null);
rootProperties.add(attDesc);
attType = new AttributeTypeImpl(MULTI_LEAF_ATTRIBUTE, String.class, false, false,
Collections.EMPTY_LIST, null, null);
attDesc = new AttributeDescriptorImpl(attType, MULTI_LEAF_ATTRIBUTE, 0, -1, true, null);
rootProperties.add(attDesc);
attType = new ComplexTypeImpl(ROOT_ATTRIBUTE, rootProperties, false, false,
Collections.EMPTY_LIST, null, null);
attDesc = new AttributeDescriptorImpl(attType, ROOT_ATTRIBUTE, 0, -1, true, null);
Collection<PropertyDescriptor> nestedProperties = new ArrayList<PropertyDescriptor>(1);
nestedProperties.add(attDesc);
// add nested properties to complex attribute
attType = new ComplexTypeImpl(COMPLEX_ATTRIBUTE, nestedProperties, false, false,
Collections.EMPTY_LIST, null, null);
attDesc = new AttributeDescriptorImpl(attType, COMPLEX_ATTRIBUTE, 0, -1, true, null);
// add to feature schema
schema.add(attDesc);
return tfac.createFeatureType(fName, schema, null, false, Collections.EMPTY_LIST, null,
null);
}
/**
* Load schema
*
* @param location
* schema location path that can be found through getClass().getResource()
* @return
*/
private SchemaIndex loadSchema(final String location) throws IOException {
EmfAppSchemaReader reader = EmfAppSchemaReader.newInstance();
final URL catalogLocation = getClass().getResource(schemaBase + "mappedPolygons.oasis.xml");
reader.setResolver(catalogLocation);
return reader.parse(new URL(location));
}
/**
* Tests getting descriptor from GeoSciML type, supporting polymorphism
*
* @throws Exception
*/
@Test
public void testPolymorphism() throws Exception {
SchemaIndex schemaIndex = loadSchema("http://schemas.opengis.net/GeoSciML/Gsml.xsd");
FeatureTypeRegistry typeRegistry = new FeatureTypeRegistry();
try {
typeRegistry.addSchemas(schemaIndex);
Name typeName = Types.typeName(GSMLNS, "MappedFeatureType");
ComplexType mf = (ComplexType) typeRegistry.getAttributeType(typeName);
assertNotNull(mf);
assertTrue(mf instanceof FeatureType);
AttributeExpressionImpl ex = new AttributeExpressionImpl("gsml:specification/gsml:GeologicUnit/gsml:preferredAge/gsml:GeologicEvent/gsml:eventAge/gsml:CGI_TermRange/gsml:upper/gsml:CGI_TermValue/gsml:value",
new Hints(FeaturePropertyAccessorFactory.NAMESPACE_CONTEXT, GSMLNAMESPACES));
Object o = ex.evaluate(mf);
assertNotNull(o);
assertTrue(o instanceof PropertyDescriptor);
ex = new AttributeExpressionImpl("gsml:specification/gsml:GeologicUnit/gsml:composition/gsml:CompositionPart/gsml:lithology/@xlink:href",
new Hints(FeaturePropertyAccessorFactory.NAMESPACE_CONTEXT, GSMLNAMESPACES));
o = ex.evaluate(mf);
assertNotNull(o);
assertTrue(o.equals (Types.typeName(XLINKNS, "href")));
ex = new AttributeExpressionImpl("gsml:specification/gsml:GeologicUnit/gsml:composition/gsml:CompositionPart/gsml:lithology/@foo:bar",
new Hints(FeaturePropertyAccessorFactory.NAMESPACE_CONTEXT, GSMLNAMESPACES));
o = ex.evaluate(mf);
assertNull(o);
}
finally {
typeRegistry.disposeSchemaIndexes();
}
}
}