/* * GeoTools - The Open Source Java GIS Toolkit * http://geotools.org * * (C) 2007-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.data.complex.config; import java.io.IOException; import java.io.InputStream; import java.io.StringReader; import java.net.URL; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.logging.Level; import java.util.logging.Logger; import org.apache.commons.digester.Digester; import org.geotools.data.complex.AppSchemaDataAccessFactory; import org.geotools.data.complex.AppSchemaDataAccessRegistry; import org.geotools.util.InterpolationProperties; import org.xml.sax.SAXException; /** * Digester to consume the app-schema {@link AppSchemaDataAccessFactory} configuration file. * * @author Gabriel Roldan (Axios Engineering) * @author Rini Angreani (CSIRO Earth Science and Resource Engineering) * @author Ben Caradoc-Davies (CSIRO Earth Science and Resource Engineering) * @author Russell Petty (GeoScience Victoria) * @version $Id$ * * * @source $URL$ * @since 2.4 */ public class XMLConfigDigester { /** DOCUMENT ME! */ private static final Logger LOGGER = org.geotools.util.logging.Logging .getLogger(XMLConfigDigester.class.getPackage().getName()); /** Namespace URI for the AppSchemaDataAccess configuration files */ private static final String CONFIG_NS_URI = "http://www.geotools.org/app-schema"; /** * Properties */ protected InterpolationProperties properties; /** * Creates a new XMLConfigReader object. */ public XMLConfigDigester() { this (AppSchemaDataAccessRegistry.getAppSchemaProperties()); } /** * Creates a new XMLConfigReader object. * * @param properties Properties to use for interpolation */ public XMLConfigDigester(InterpolationProperties properties) { this.properties = properties; } /** * Parses a complex datastore configuration file in xml format into a * {@link AppSchemaDataAccessDTO} * * @param dataStoreConfigUrl * config file location * * @return a DTO object representing the datastore's configuration * * @throws IOException * if an error occurs parsing the file */ public AppSchemaDataAccessDTO parse(URL dataStoreConfigUrl) throws IOException { AppSchemaDataAccessDTO config = digest(dataStoreConfigUrl); return config; } /** * DOCUMENT ME! * * @param dataStoreConfigUrl * DOCUMENT ME! * * @return DOCUMENT ME! * * @throws IOException * DOCUMENT ME! * @throws NullPointerException * DOCUMENT ME! */ private AppSchemaDataAccessDTO digest(final URL dataStoreConfigUrl) throws IOException { if (dataStoreConfigUrl == null) { throw new NullPointerException("datastore config url"); } // read mapping file into configString and interpolate properties InputStream configStream = null; String configString = null; try { configStream = dataStoreConfigUrl.openStream(); if (configStream == null) { throw new IOException("Can't open datastore config file " + dataStoreConfigUrl); } else { configString = properties.interpolate(InterpolationProperties.readAll(configStream)); } } finally { if (configStream != null) { configStream.close(); } } XMLConfigDigester.LOGGER.fine("parsing complex datastore config: " + dataStoreConfigUrl.toExternalForm()); Digester digester = new Digester(); XMLConfigDigester.LOGGER.fine("digester created"); // URL schema = getClass() // .getResource("../test-data/AppSchemaDataAccess.xsd"); // digester.setSchema(schema.toExternalForm()); digester.setValidating(false); digester.setNamespaceAware(true); digester.setRuleNamespaceURI(XMLConfigDigester.CONFIG_NS_URI); // digester.setRuleNamespaceURI(OGC_NS_URI); AppSchemaDataAccessDTO configDto = new AppSchemaDataAccessDTO(); configDto.setBaseSchemasUrl(dataStoreConfigUrl.toExternalForm()); digester.push(configDto); try { setNamespacesRules(digester); setIncludedTypesRules(digester); setSourceDataStoresRules(digester); setTargetSchemaUriRules(digester); setTypeMappingsRules(digester); } catch (Exception e) { e.printStackTrace(); XMLConfigDigester.LOGGER.log(Level.SEVERE, "setting digester properties: ", e); throw new IOException("Error setting digester properties: " + e.getMessage()); } try { digester.parse(new StringReader(configString)); } catch (SAXException e) { e.printStackTrace(); XMLConfigDigester.LOGGER.log(Level.SEVERE, "parsing " + dataStoreConfigUrl, e); IOException ioe = new IOException("Can't parse complex datastore config. "); ioe.initCause(e); throw ioe; } AppSchemaDataAccessDTO config = (AppSchemaDataAccessDTO) digester.getRoot(); return config; } private void setTypeMappingsRules(Digester digester) { final String mappings = "AppSchemaDataAccess/typeMappings"; digester.addObjectCreate(mappings, XMLConfigDigester.CONFIG_NS_URI, HashSet.class); final String typeMapping = mappings + "/FeatureTypeMapping"; digester.addObjectCreate(typeMapping, XMLConfigDigester.CONFIG_NS_URI, TypeMapping.class); digester.addCallMethod(typeMapping + "/mappingName", "setMappingName", 1); digester.addCallParam(typeMapping + "/mappingName", 0); digester.addCallMethod(typeMapping + "/sourceDataStore", "setSourceDataStore", 1); digester.addCallParam(typeMapping + "/sourceDataStore", 0); digester.addCallMethod(typeMapping + "/sourceType", "setSourceTypeName", 1); digester.addCallParam(typeMapping + "/sourceType", 0); digester.addCallMethod(typeMapping + "/targetElement", "setTargetElementName", 1); digester.addCallParam(typeMapping + "/targetElement", 0); digester.addCallMethod(typeMapping + "/itemXpath", "setItemXpath", 1); digester.addCallParam(typeMapping + "/itemXpath", 0); // isXmlDataStore is a flag to denote that AppSchema needs to process // the data from the datastore differently as it returns xml rather than features. digester.addCallMethod(typeMapping + "/isXmlDataStore", "setXmlDataStore", 1); digester.addCallParam(typeMapping + "/isXmlDataStore", 0); // create attribute mappings final String attMappings = typeMapping + "/attributeMappings"; digester.addObjectCreate(attMappings, XMLConfigDigester.CONFIG_NS_URI, ArrayList.class); final String attMap = attMappings + "/AttributeMapping"; digester.addObjectCreate(attMap, XMLConfigDigester.CONFIG_NS_URI, AttributeMapping.class); digester.addCallMethod(attMap + "/label", "setLabel", 1); digester.addCallParam(attMap + "/label", 0); digester.addCallMethod(attMap + "/parentLabel", "setParentLabel", 1); digester.addCallParam(attMap + "/parentLabel", 0); digester.addCallMethod(attMap + "/targetQueryString", "setTargetQueryString", 1); digester.addCallParam(attMap + "/targetQueryString", 0); digester.addCallMethod(attMap + "/instancePath", "setInstancePath", 1); digester.addCallParam(attMap + "/instancePath", 0); digester.addCallMethod(attMap + "/isMultiple", "setMultiple", 1); digester.addCallParam(attMap + "/isMultiple", 0); digester.addCallMethod(attMap + "/targetAttribute", "setTargetAttributePath", 1); digester.addCallParam(attMap + "/targetAttribute", 0); digester.addCallMethod(attMap + "/targetAttributeNode", "setTargetAttributeSchemaElement", 1); digester.addCallParam(attMap + "/targetAttributeNode", 0); digester.addCallMethod(attMap + "/idExpression/OCQL", "setIdentifierExpression", 1); digester.addCallParam(attMap + "/idExpression/OCQL", 0); digester.addCallMethod(attMap + "/sourceExpression/OCQL", "setSourceExpression", 1); digester.addCallParam(attMap + "/sourceExpression/OCQL", 0); digester.addCallMethod(attMap + "/idExpression/inputAttribute", "setIdentifierPath", 1); digester.addCallParam(attMap + "/idExpression/inputAttribute", 0); // if the source is a data access, then the input is in XPath expression digester.addCallMethod(attMap + "/sourceExpression/inputAttribute", "setInputAttributePath", 1); digester.addCallParam(attMap + "/sourceExpression/inputAttribute", 0); // for feature chaining: this refers to the nested feature type digester.addCallMethod(attMap + "/sourceExpression/linkElement", "setLinkElement", 1); digester.addCallParam(attMap + "/sourceExpression/linkElement", 0); // for feature chaining: this refers to the nested feature attribute digester.addCallMethod(attMap + "/sourceExpression/linkField", "setLinkField", 1); digester.addCallParam(attMap + "/sourceExpression/linkField", 0); digester.addCallMethod(attMap + "/ClientProperty", "putClientProperty", 2); digester.addCallParam(attMap + "/ClientProperty/name", 0); digester.addCallParam(attMap + "/ClientProperty/value", 1); // add the AttributeMapping to the list digester.addSetNext(attMap, "add"); // set attribute mappings digester.addSetNext(attMappings, "setAttributeMappings"); // add the TypeMapping to the Set digester.addSetNext(typeMapping, "add"); // set the TypeMapping on AppSchemaDataAccessDTO digester.addSetNext(mappings, "setTypeMappings"); } private void setTargetSchemaUriRules(Digester digester) { final String targetSchemas = "AppSchemaDataAccess/targetTypes"; digester.addBeanPropertySetter("AppSchemaDataAccess/catalog"); // digester.addCallMethod(targetSchemas + "/Catalog", "setCatalog", 1); // digester.addCallParam(targetSchemas + "/Catalog", 0); digester.addObjectCreate(targetSchemas, XMLConfigDigester.CONFIG_NS_URI, ArrayList.class); final String schema = targetSchemas + "/FeatureType/schemaUri"; digester.addCallMethod(schema, "add", 1); digester.addCallParam(schema, 0); // set the list of XSD file uris on AppSchemaDataAccessDTO digester.addSetNext(targetSchemas, "setTargetSchemasUris"); } private void setSourceDataStoresRules(Digester digester) { final String dataStores = "AppSchemaDataAccess/sourceDataStores"; digester.addObjectCreate(dataStores, XMLConfigDigester.CONFIG_NS_URI, ArrayList.class); // create a SourceDataStore for each DataStore tag digester.addObjectCreate(dataStores + "/DataStore", XMLConfigDigester.CONFIG_NS_URI, SourceDataStore.class); digester.addCallMethod(dataStores + "/DataStore/id", "setId", 1); digester.addCallParam(dataStores + "/DataStore/id", 0); digester.addObjectCreate(dataStores + "/DataStore/parameters", XMLConfigDigester.CONFIG_NS_URI, HashMap.class); digester.addCallMethod(dataStores + "/DataStore/parameters/Parameter", "put", 2); digester.addCallParam(dataStores + "/DataStore/parameters/Parameter/name", 0); digester.addCallParam(dataStores + "/DataStore/parameters/Parameter/value", 1); digester.addSetNext(dataStores + "/DataStore/parameters", "setParams"); // isDataAccess is a flag to denote that we want to connect to the data access // that is connected to the data store specified digester.addCallMethod(dataStores + "/DataStore/isDataAccess", "setDataAccess", 1); digester.addCallParam(dataStores + "/DataStore/isDataAccess", 0); // add the SourceDataStore to the list digester.addSetNext(dataStores + "/DataStore", "add"); // set the list of SourceDataStores for ComlexDataStoreDTO digester.addSetNext(dataStores, "setSourceDataStores"); } private void setNamespacesRules(Digester digester) { final String ns = "AppSchemaDataAccess/namespaces"; digester.addObjectCreate(ns, XMLConfigDigester.CONFIG_NS_URI, HashMap.class); digester.addCallMethod(ns + "/Namespace", "put", 2); digester.addCallParam(ns + "/Namespace/prefix", 0); digester.addCallParam(ns + "/Namespace/uri", 1); digester.addSetNext(ns, "setNamespaces"); } private void setIncludedTypesRules(Digester digester) { final String includes = "AppSchemaDataAccess/includedTypes"; digester.addObjectCreate(includes, XMLConfigDigester.CONFIG_NS_URI, ArrayList.class); // point to related type config path relative to this mapping file location final String includePath = includes + "/Include"; digester.addCallMethod(includePath, "add", 1); digester.addCallParam(includePath, 0); digester.addSetNext(includes, "setIncludedTypes"); } }