/*
* 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.
*/
package org.geotools.xml;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.resource.ResourceSet;
import org.eclipse.xsd.XSDSchema;
import org.eclipse.xsd.util.XSDResourceImpl;
import org.eclipse.xsd.util.XSDSchemaLocator;
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import java.util.Stack;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.xml.namespace.QName;
import org.geotools.feature.NameImpl;
import org.geotools.feature.type.SchemaImpl;
import org.geotools.xs.XS;
import org.opengis.feature.type.AttributeType;
import org.opengis.feature.type.Name;
import org.opengis.feature.type.Schema;
/**
* Xml Schema for a particular namespace.
* <p>
* This class should is subclasses for the xs, gml, filter, sld, etc... schemas.
* Subclasses should be implemented as singletons.
* </p>
* @author Justin Deoliveira, The Open Planning Project
* @since 2.5
*
*
* @source $URL$
*/
public abstract class XSD {
/**
* logging instance
*/
protected static Logger LOGGER = org.geotools.util.logging.Logging.getLogger("org.geotools.xml");
/**
* schema contents
*/
protected XSDSchema schema;
/**
* type schema
*/
protected Schema typeSchema;
/**
* type mapping profile
*/
protected Schema typeMappingProfile;
/**
* dependencies
*/
private Set /*<XSD>*/ dependencies;
protected XSD() {
}
/**
* Sets up the schema which maps xml schema types to attribute types.
*/
protected Schema buildTypeSchema() {
return new SchemaImpl( getNamespaceURI() );
}
/**
* Sets up a profile which uniquely maps a set of java classes to a schema
* element.
*/
protected Schema buildTypeMappingProfile( Schema typeSchema ) {
return typeSchema.profile( Collections.EMPTY_SET );
}
/**
* Convenience method to turn a QName into a Name.
* <p>
* Useful for building type mapping profiles.
* </p>
* @param qName The name to transform.
*/
protected Name name( QName qName ) {
return new NameImpl(qName.getNamespaceURI(), qName.getLocalPart());
}
/**
* Returns the schema containing {@link AttributeType}'s for
* all xml types.
*/
public final Schema getTypeSchema() {
if( typeSchema == null ) {
synchronized ( this ) {
typeSchema = buildTypeSchema();
}
}
return typeSchema;
}
/**
* Returns the sbuset of {@link #getTypeSchema()} which maintains
* a unique java class to xml type mapping.
*/
public final Schema getTypeMappingProfile() {
if ( typeMappingProfile == null ){
synchronized (this) {
typeMappingProfile = buildTypeMappingProfile(getTypeSchema());
}
}
return typeMappingProfile;
}
/**
* The namespace uri of the schema.
*/
public abstract String getNamespaceURI();
/**
* The location on the local disk of the top level .xsd file which defines
* the schema.
*/
public abstract String getSchemaLocation();
/**
* The dependencies of this schema.
*/
public final Set<XSD> getDependencies() {
if (dependencies == null) {
synchronized (this) {
if (dependencies == null) {
dependencies = new LinkedHashSet();
//bootstrap, every xsd depends on XS
dependencies.add(XS.getInstance());
//call subclass hook
addDependencies(dependencies);
}
}
}
return dependencies;
}
/**
* Returns all dependencies , direct and transitive that this xsd
* depends on.
*/
public List<XSD> getAllDependencies() {
return allDependencies();
}
protected List allDependencies() {
LinkedList unpacked = new LinkedList();
Stack stack = new Stack();
stack.addAll(getDependencies());
while (!stack.isEmpty()) {
XSD xsd = (XSD) stack.pop();
if (!equals(xsd) && !unpacked.contains(xsd)) {
unpacked.addFirst(xsd);
stack.addAll(xsd.getDependencies());
}
}
return unpacked;
}
/**
* Subclass hook to add additional dependencies.
*/
protected void addDependencies(Set dependencies) {
}
/**
* Returns the XSD object representing the contents of the schema.
*/
public final XSDSchema getSchema() throws IOException {
if (schema == null) {
synchronized (this) {
if (schema == null) {
LOGGER.fine("building schema for schema: " + getNamespaceURI());
schema = buildSchema();
}
}
}
return schema;
}
/**
* Builds the schema from the .xsd file specified by {@link #getSchemaLocation()}
* <p>
* This method may be extended, but should not be overridden.
* </p>
*/
protected XSDSchema buildSchema() throws IOException {
//grab all the dependencies and create schema locators from the build
// schemas
List locators = new ArrayList();
List resolvers = new ArrayList();
for (Iterator d = allDependencies().iterator(); d.hasNext();) {
XSD dependency = (XSD) d.next();
SchemaLocator locator = dependency.createSchemaLocator();
if (locator != null) {
locators.add(locator);
}
SchemaLocationResolver resolver = dependency.createSchemaLocationResolver();
if (resolver != null) {
resolvers.add(resolver);
}
}
XSDSchemaLocator suppSchemaLocator = getSupplementarySchemaLocator();
if (suppSchemaLocator != null) {
locators.add(suppSchemaLocator);
}
SchemaLocationResolver resolver = createSchemaLocationResolver();
if (resolver != null) {
resolvers.add(resolver);
}
//parse the location of the xsd with all the locators for dependent
// schemas
return Schemas.parse(getSchemaLocation(), locators, resolvers);
}
public SchemaLocator createSchemaLocator() {
return new SchemaLocator(this);
}
public SchemaLocationResolver createSchemaLocationResolver() {
return new SchemaLocationResolver(this);
}
/**
* Returns the qualified name for the specified local part.
*
* @return The QName, built by simply prepending the namespace for this xsd.
*/
public QName qName(String local) {
return new QName(getNamespaceURI(), local);
}
/**
* Implementation of equals, equality is based soley on {@link #getNamespaceURI()}.
*/
public final boolean equals(Object obj) {
if (obj instanceof XSD) {
XSD other = (XSD) obj;
return getNamespaceURI().equals(other.getNamespaceURI());
}
return false;
}
public final int hashCode() {
return getNamespaceURI().hashCode();
}
public String toString() {
return getNamespaceURI();
}
/**
* Optionally, a schema locator that helps locating (other) schema's
* used for includes/imports that might already exist but are not in
* dependencies
* @return Schema Locator
*/
public XSDSchemaLocator getSupplementarySchemaLocator() {
return null;
}
/**
* Remove all references to this schema, and all schemas built in the same resource set
* It is important to call this method for every dynamic schema created that is not needed
* anymore, because references in the static schema's will otherwise keep it alive forever
*/
public void dispose() {
if (schema != null) {
ResourceSet rs = schema.eResource().getResourceSet();
for (Resource r : rs.getResources()) {
if (r instanceof XSDResourceImpl) {
Schemas.dispose(((XSDResourceImpl) r).getSchema());
}
}
schema = null;
}
}
}