package org.geotools.data.complex;
/*
* GeoTools - The Open Source Java GIS Toolkit
* http://geotools.org
*
* (C) 2002-2008, 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.
*/
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.net.URI;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import junit.framework.TestCase;
import org.geotools.data.FeatureSource;
import org.geotools.data.Query;
import org.geotools.data.complex.AppSchemaDataAccess;
import org.geotools.data.complex.DataAccessRegistry;
import org.geotools.data.complex.FeatureChainingTest;
import org.geotools.data.complex.config.AppSchemaDataAccessConfigurator;
import org.geotools.data.complex.config.AppSchemaDataAccessDTO;
import org.geotools.data.complex.config.XMLConfigDigester;
import org.geotools.data.complex.xml.XmlResponse;
import org.geotools.data.ws.WSDataStoreFactory;
import org.geotools.data.ws.WS_DataStore;
import org.geotools.data.ws.protocol.ws.WSProtocol;
import org.geotools.feature.ComplexAttributeImpl;
import org.geotools.feature.FeatureCollection;
import org.geotools.feature.FeatureIterator;
import org.geotools.feature.NameImpl;
import org.geotools.feature.Types;
import org.geotools.filter.FilterFactoryImplNamespaceAware;
import org.geotools.gml3.bindings.GML3EncodingUtils;
import org.geotools.util.Converters;
import org.jdom.Document;
import org.jdom.JDOMException;
import org.jdom.input.SAXBuilder;
import org.opengis.feature.Attribute;
import org.opengis.feature.ComplexAttribute;
import org.opengis.feature.Feature;
import org.opengis.feature.type.FeatureType;
import org.opengis.feature.type.Name;
import org.opengis.filter.Filter;
import org.opengis.filter.FilterFactory;
import org.xml.sax.Attributes;
import org.xml.sax.helpers.NamespaceSupport;
/**
*
* @author Russell Petty
* @version $Id$
*
*
* @source $URL$
* DataAccessFinderTest.java $
*/
public class XmlDataStoreTest extends TestCase {
private static final String MOCK_DS_PARAM_KEY = "DATA_FILE_DIRECTORY_PARAM_KEY";
private static final SAXBuilder sax = new SAXBuilder();
private static final String schemaBase = "/test-data/";
private static final String CGINS = "urn:cgi:xmlns:CGI:Utilities:1.0";
private final int MAX_FEATURES = 1;
private AppSchemaDataAccess mappingDataStore;
private Name typeName;
/**
* Filter factory instance
*/
static FilterFactory ff;
protected void setUp() throws Exception {
super.setUp();
setFilterFactory();
buildXmlBackedDataAccess();
typeName = Types.typeName(FeatureChainingTest.GSMLNS, "GeologicUnit");
}
protected void tearDown() throws Exception {
super.tearDown();
DataAccessRegistry.unregisterAndDisposeAll();
}
public void testDataStoreCreated() throws Exception {
assertNotNull(mappingDataStore);
FeatureType mappedFeatureType = mappingDataStore.getSchema(typeName);
assertNotNull(mappedFeatureType);
}
public void testFilterTranslation() throws Exception {
//tests that the translation of the filter from GeoSciML to the xml datasorce works.
Filter inputFilter = ff.equals(ff.property("gml:name"), ff
.literal("Unit Name1233811724109 UC1233811724109 description name"));
FeatureCollection features = getFeatures(Query.DEFAULT_MAX, inputFilter);
assertEquals(1, size(features));
// check that it returns the right feature
FeatureIterator iterator = features.features();
Feature f = iterator.next();
assertEquals(f.getIdentifier().toString(), "lithostratigraphic.unit.1679161041155866313");
iterator.close();
}
public void testFeatureCounting() throws Exception {
Filter inputFilter = ff.like(ff.property("gml:name"), "*description name*");
FeatureCollection features = getFeatures(Query.DEFAULT_MAX, inputFilter);
assertEquals(3, size(features));
// check feature ids
FeatureIterator iterator = features.features();
Feature f = iterator.next();
assertEquals(f.getIdentifier().toString(), "lithostratigraphic.unit.1679161021439131319");
assertTrue(iterator.hasNext());
f = iterator.next();
assertEquals(f.getIdentifier().toString(), "lithostratigraphic.unit.1679161041155866313");
assertTrue(iterator.hasNext());
f = iterator.next();
assertEquals(f.getIdentifier().toString(), "lithostratigraphic.unit.1679161021439938381");
iterator.close();
// now with maxFeatures = 1, it should only return the first one
features = getFeatures(MAX_FEATURES, inputFilter);
assertEquals(MAX_FEATURES, size(features));
iterator = features.features();
f = iterator.next();
assertEquals(f.getIdentifier().toString(), "lithostratigraphic.unit.1679161021439131319");
iterator.close();
}
public void testNoElementsReturned() throws Exception {
final Filter filter = ff.equals(ff.property("gml:name"), ff
.literal("invalid name"));
FeatureCollection features = getFeatures(MAX_FEATURES, filter);
assertEquals(0, size(features));
List<Feature> results = new ArrayList<Feature>();
FeatureIterator it = features.features();
for (; it.hasNext();) {
results.add((Feature) it.next());
}
it.close();
assertEquals(0, results.size());
}
public void testFeaturesCreatedCorrectly() throws Exception {
final Name GeologicUnitName = new NameImpl(FeatureChainingTest.GSMLNS, "GeologicUnit");
final Name GeologicUnitType = new NameImpl(FeatureChainingTest.GSMLNS, "GeologicUnitType");
List<Feature> results = new ArrayList<Feature>();
FeatureCollection features = getFeatures(Query.DEFAULT_MAX, Filter.INCLUDE);
FeatureIterator it = features.features();
for (; it.hasNext();) {
results.add((Feature) it.next());
}
it.close();
final int EXPECTED_SIZE = 3;
assertEquals(EXPECTED_SIZE, results.size());
// ***************************************************************************************
// Check that all features returned are GUs and have the correct id.
// ***************************************************************************************
final String[] ids = new String[] { "lithostratigraphic.unit.1679161021439131319",
"lithostratigraphic.unit.1679161041155866313",
"lithostratigraphic.unit.1679161021439938381" };
for (int i = 0; i < EXPECTED_SIZE; i++) {
Feature f = results.get(i);
assertEquals(ids[i], f.getIdentifier().getID());
assertEquals(GeologicUnitName, f.getName());
assertEquals(GeologicUnitType, f.getDescriptor().getType().getName());
// ***************************************************************************************
// Test an attribute with constant value (as opposed to an xpath)
// ***************************************************************************************
List<Attribute> attPurposeList = getAttributesForProperty(f, "purpose");
assertEquals(1, attPurposeList.size());
Attribute at = attPurposeList.get(0);
assertEquals("CONSTANT", getValueForAttribute(at));
assertFalse(at.getUserData().containsKey(Attributes.class));
// ***************************************************************************************
// Test an attribute with null value (ie unset) but User data is an xpath
// ***************************************************************************************
List<Attribute> attGUTypeList = getAttributesForProperty(f, "rank");
assertEquals(1, attGUTypeList.size());
at = attGUTypeList.get(0);
assertEquals("", getValueForAttribute(at));
assertEquals(1, ((Map<Object, Object>) at.getUserData().get(Attributes.class)).size());
assertEquals("urn:cgi:classifier:GSV:LithostratigraphicUnitRank:formation",
getUserDataForAttribute(at, new NameImpl("http://www.w3.org/1999/xlink", "href")));
// ***************************************************************************************
// Test that a nested complex type is created correctly
// ***************************************************************************************
List<Attribute> attObMethList = getAttributesForProperty(f, "observationMethod");
assertEquals(1, attObMethList.size());
at = attObMethList.get(0);
assertEquals(null, at.getIdentifier());
assertEquals(new NameImpl(FeatureChainingTest.GSMLNS, "observationMethod"), at.getName());
assertEquals(new NameImpl(FeatureChainingTest.GSMLNS, "CGI_TermValuePropertyType"), at.getDescriptor()
.getType().getName());
ComplexAttribute ob = getNestedComplexValueForAttribute(at);
assertEquals(null, ob.getIdentifier());
assertEquals(new NameImpl(FeatureChainingTest.GSMLNS, "CGI_TermValue"), ob.getName());
assertEquals(new NameImpl(FeatureChainingTest.GSMLNS, "CGI_TermValueType"), ob.getDescriptor().getType()
.getName());
ComplexAttribute ob2 = getNestedComplexValueForAttribute(ob);
assertEquals(null, ob2.getIdentifier());
assertEquals(new NameImpl(FeatureChainingTest.GSMLNS, "value"), ob2.getName());
assertEquals(new NameImpl(CGINS, "CodeWithAuthorityType"), ob2
.getDescriptor().getType().getName());
assertEquals("CONSTANT", getValueForAttribute(ob2));
assertEquals("gsv:NameSpace", getUserDataForAttribute(ob2, new NameImpl("codeSpace")));
}
Feature feature = results.get(0);
// ***************************************************************************************
// Test an attribute that occurs once, set via xpaths. Also has no userData
// ***************************************************************************************
List<Attribute> attDescList = getAttributesForProperty(feature, "description");
assertEquals(1, attDescList.size());
Attribute at = attDescList.get(0);
assertEquals("Test description 1", getValueForAttribute(at));
assertFalse(at.getUserData().containsKey(Attributes.class));
// ***************************************************************************************
// Test an attribute with two values. Also has userData, set with constants instead of
// xpaths
// ***************************************************************************************
List<Attribute> attNameList = getAttributesForProperty(feature, "name");
assertEquals(2, attNameList.size());
// First one has a value and a single attribute--codeSpace
at = attNameList.get(0);
assertEquals("Unit Name1248396531312 UC1248396531312 description name",
getValueForAttribute(at));
assertEquals(1, ((Map) at.getUserData().get(Attributes.class)).size());
assertEquals("gsv:NameSpace", getUserDataForAttribute(at, new NameImpl("codeSpace")));
// Second one has a value and a single attribute--codeSpace
at = attNameList.get(1);
assertEquals("urn:cgi:feature:GSV:1679161021439131319", getValueForAttribute(at));
assertTrue(at.getUserData() != null && ((Map) at.getUserData().get(Attributes.class)).size() == 1);
assertEquals("gsv:NameSpace", getUserDataForAttribute(at, new NameImpl("codeSpace")));
// second feature
feature = results.get(1);
attDescList = getAttributesForProperty(feature, "description");
assertEquals(1, attDescList.size());
at = attDescList.get(0);
assertEquals("Test description 1", getValueForAttribute(at));
assertFalse(at.getUserData().containsKey(Attributes.class));
attNameList = getAttributesForProperty(feature, "name");
assertEquals(2, attNameList.size());
at = attNameList.get(0);
assertEquals("Unit Name1233811724109 UC1233811724109 description name",
getValueForAttribute(at));
assertEquals(1, ((Map) at.getUserData().get(Attributes.class)).size());
assertEquals("gsv:NameSpace", getUserDataForAttribute(at, new NameImpl("codeSpace")));
at = attNameList.get(1);
assertEquals("urn:cgi:feature:GSV:1679161041155866313", getValueForAttribute(at));
assertTrue(at.getUserData() != null && ((Map) at.getUserData().get(Attributes.class)).size() == 1);
assertEquals("gsv:NameSpace", getUserDataForAttribute(at, new NameImpl("codeSpace")));
// third feature
feature = results.get(2);
attDescList = getAttributesForProperty(feature, "description");
assertEquals(1, attDescList.size());
at = attDescList.get(0);
assertEquals("Test description 2", getValueForAttribute(at));
assertFalse(at.getUserData().containsKey(Attributes.class));
attNameList = getAttributesForProperty(feature, "name");
assertEquals(2, attNameList.size());
at = attNameList.get(0);
assertEquals("Unit Name1248396020281 UC1248396020281 description name 2",
getValueForAttribute(at));
assertEquals(1, ((Map) at.getUserData().get(Attributes.class)).size());
assertEquals("gsv:NameSpace", getUserDataForAttribute(at, new NameImpl("codeSpace")));
at = attNameList.get(1);
assertEquals("urn:cgi:feature:GSV:1679161021439938381", getValueForAttribute(at));
assertTrue(at.getUserData() != null && ((Map) at.getUserData().get(Attributes.class)).size() == 1);
assertEquals("gsv:NameSpace", getUserDataForAttribute(at, new NameImpl("codeSpace")));
}
private String getValueForAttribute(Attribute sv) {
if (sv instanceof ComplexAttribute) {
Object value = GML3EncodingUtils.getSimpleContent((ComplexAttribute) sv);
if (value != null) {
return Converters.convert(value, String.class);
}
}
String value = null;
Object ob = sv.getValue();
if (ob instanceof String) {
value = (String) ob;
} else {
List values = (List) ob;
if (values != null && values.size() > 0) {
value = values.get(0).toString();
}
}
return value;
}
private ComplexAttributeImpl getNestedComplexValueForAttribute(Attribute sv) {
Object value = null;
Object ob = sv.getValue();
List values = (List) ob;
if (values != null && values.size() > 0) {
value = values.get(0);
}
return (ComplexAttributeImpl) value;
}
private String getUserDataForAttribute(Attribute sv, Name key) {
String value = null;
Map<Object, Object> userData = (Map<Object, Object>) sv.getUserData();
if (userData.containsKey(Attributes.class)) {
Map map = (Map) userData.get(Attributes.class);
value = (String) map.get(key);
}
return value;
}
private List<Attribute> getAttributesForProperty(Feature feature, String propertyName) {
List<Attribute> returnValues = new ArrayList<Attribute>();
Collection cs = feature.getProperties(propertyName);
Iterator it = cs.iterator();
while (it.hasNext()) {
Attribute sv = (Attribute) it.next();
returnValues.add(sv);
}
return returnValues;
}
public static class MockXmlDataStoreFactory extends WSDataStoreFactory {
public boolean isAvailable() {
return true;
}
@Override
public boolean canProcess(final Map params) {
return params.get(MOCK_DS_PARAM_KEY) != null;
}
@Override
public XmlDataStore createDataStore(Map params) throws IOException {
XmlDataStore ds = null;
Map<String, Object> wsParams = new HashMap<String, Object>();
wsParams.put("WSDataStoreFactory:GET_CONNECTION_URL",
"http://d00109:8080/xaware/XADocSoapServlet");
wsParams.put("WSDataStoreFactory:TIMEOUT", new Integer(30000));
wsParams.put("WSDataStoreFactory:TEMPLATE_DIRECTORY", getClass()
.getResource(schemaBase));
wsParams.put("WSDataStoreFactory:TEMPLATE_NAME", "request.ftl");
wsParams.put("WSDataStoreFactory:CAPABILITIES_FILE_LOCATION", getClass().getResource(
schemaBase + "ws_capabilities_equals_removed.xml"));
org.geotools.data.ws.XmlDataStore wsStore = super.createDataStore(wsParams);
ds = new XmlDataStore(((WS_DataStore) wsStore).getProtocol());
// additional parameter because we don't actually have a web service, but pretend
// the output has been written in an xml file
ds.setFileName((String) params.get(MOCK_DS_PARAM_KEY));
return ds;
}
}
public static final class XmlDataStore extends WS_DataStore {
public XmlDataStore(WSProtocol protocol) {
super(protocol);
}
private String fileName;
public void setFileName(String fileName) {
this.fileName = fileName;
}
@Override
public XmlResponse getXmlReader(Query query) throws IOException {
if (Filter.EXCLUDE.equals(query.getFilter())) {
return null; //empty response
}
Query callQuery = new Query(query);
Filter[] filters = getProtocol().splitFilters(query.getFilter());
Filter supportedFilter = filters[0];
Filter postFilter = filters[1];
callQuery.setFilter(supportedFilter);
Document doc = getXmlResponse();
List<Integer> validFeatureIndex = determineValidFeatures(postFilter, doc, query
.getMaxFeatures());
return new XmlResponse(doc, validFeatureIndex);
}
public XmlResponse getXmlReader(Query query, String xpath, String value) throws IOException {
Document doc = getXmlResponse();
List<Integer> validFeatureIndex = determineValidFeatures(xpath, value, doc, query
.getMaxFeatures());
return new XmlResponse(doc, validFeatureIndex);
}
private Document getXmlResponse() throws IOException {
Document doc = null;
BufferedInputStream dos = null;
try {
File outFile = new File(fileName);
dos = new BufferedInputStream(new FileInputStream(outFile));
doc = sax.build(dos);
} catch (JDOMException e1) {
throw new RuntimeException("error reading xml from file ", e1);
} finally {
if (dos != null) {
dos.close();
}
}
return doc;
}
};
private Query namedQuery(Filter filter, int count) throws Exception {
return new Query("GeologicUnit", new URI(FeatureChainingTest.GSMLNS), filter, count,
new String[] {}, "test");
}
/**
* Set filter factory with name spaces
*/
private void setFilterFactory() {
NamespaceSupport namespaces = new NamespaceSupport();
namespaces.declarePrefix("gsml", FeatureChainingTest.GSMLNS);
namespaces.declarePrefix("gml", FeatureChainingTest.GMLNS);
ff = new FilterFactoryImplNamespaceAware(namespaces);
}
private void buildXmlBackedDataAccess() throws IOException {
URL url = getClass().getResource(schemaBase + "xmlDataAccessConfig.xml");
assertNotNull(url);
AppSchemaDataAccessDTO config = new XMLConfigDigester().parse(url);
Set mappings = AppSchemaDataAccessConfigurator.buildMappings(config);
mappingDataStore = new AppSchemaDataAccess(mappings);
}
private String getExpectedFilter() {
final String prefix = "/soapenv:Envelope/soapenv:Body/qaz:getGeologicalFeaturesByFilterStringResponse/qaz:out/qaz:item/";
return "[[ "
+ prefix
+ "gss:formattedName = Unit Name1233811724109 UC1233811724109 description name ] OR "
+ "[ "
+ prefix
+ "gss:urn[@domain='GSV'] = Unit Name1233811724109 UC1233811724109 description name ]]";
}
private String getReversedExpectedFilter() {
final String prefix = "/soapenv:Envelope/soapenv:Body/qaz:getGeologicalFeaturesByFilterStringResponse/qaz:out/qaz:item/";
return "[[ "
+ prefix
+ "gss:urn[@domain='GSV'] = Unit Name1233811724109 UC1233811724109 description name ] OR "
+ "[ "
+ prefix
+ "gss:formattedName = Unit Name1233811724109 UC1233811724109 description name ]]";
}
private FeatureCollection getFeatures(final int maxFeatures, Filter inputFilter)
throws Exception {
FeatureSource fSource = (FeatureSource) mappingDataStore.getFeatureSource(typeName);
FeatureCollection features = (FeatureCollection) fSource.getFeatures(namedQuery(
inputFilter, new Integer(maxFeatures)));
return features;
}
private int size(FeatureCollection<FeatureType, Feature> features) {
int size = 0;
for (Iterator i = features.iterator(); i.hasNext(); i.next()) {
size++;
}
return size;
}
}