/*
* GeoTools - The Open Source Java GIS Toolkit
* http://geotools.org
*
* (C) 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.maven.xmlcodegen;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import org.eclipse.xsd.XSDAttributeDeclaration;
import org.eclipse.xsd.XSDComplexTypeDefinition;
import org.eclipse.xsd.XSDElementDeclaration;
import org.eclipse.xsd.XSDParticle;
import org.eclipse.xsd.XSDSchema;
import org.eclipse.xsd.XSDSimpleTypeDefinition;
import org.eclipse.xsd.XSDTypeDefinition;
import org.geotools.feature.NameImpl;
import org.geotools.feature.type.AbstractLazyAttributeTypeImpl;
import org.geotools.feature.type.AbstractLazyComplexTypeImpl;
import org.geotools.util.Utilities;
import org.geotools.xml.Schemas;
import org.opengis.feature.type.AttributeDescriptor;
import org.opengis.feature.type.AttributeType;
import org.opengis.feature.type.ComplexType;
import org.opengis.feature.type.PropertyDescriptor;
/**
* Schema generator that uses subclasses of {@link AbstractLazyAttributeTypeImpl} and
* {@link AbstractLazyComplexTypeImpl} to support cyclically-defined types. Types are sorted by
* name.
*
* <p>
*
* Much of the code that builds the attributes is based on code snippets stolen from
* {@link SchemaGenerator}, written by Justin Deoliveira.
*
* @author Ben Caradoc-Davies (CSIRO Earth Science and Resource Engineering)
* @author Justin Deoliveira
*
*/
public class CycleSchemaGenerator extends SchemaGenerator {
/**
* The depth integer required by the parent API. This class does not use it, so we put the
* maximum value here to defend against accidentally calling the wrong parent method.
*/
private static final int DUMMY_DEPTH = Integer.MAX_VALUE;
/**
* Constructor.
*
* @param schema
* the schema we are building
*/
public CycleSchemaGenerator(XSDSchema schema) {
super(schema);
}
/**
* Build an AttributeType for a simple type.
*
* @see org.geotools.maven.xmlcodegen.SchemaGenerator#createType(org.eclipse.xsd.XSDSimpleTypeDefinition,
* int)
*/
@Override
protected AttributeType createType(final XSDSimpleTypeDefinition xsdType, int depth) {
if (types.containsKey(xsdType)) {
return (AttributeType) types.get(xsdType);
}
// import?
if (!xsdType.getTargetNamespace().equals(schema.getTargetNamespace())) {
return (AttributeType) findType(xsdType);
}
System.err.println("Creating simple type " + name(xsdType));
AttributeType gtType = new AbstractLazyAttributeTypeImpl(name(xsdType), Object.class,
false, false, null, null) {
@Override
public AttributeType buildSuper() {
XSDTypeDefinition baseType = xsdType.getBaseType();
if (baseType != null && baseType.getName() != null && !baseType.equals(xsdType)) {
return createType(baseType, Integer.MAX_VALUE);
} else {
return null;
}
}
};
types.put(xsdType, gtType);
return gtType;
}
/**
* Build an AttributeType for a complex type.
*
* @see org.geotools.maven.xmlcodegen.SchemaGenerator#createType(org.eclipse.xsd.XSDComplexTypeDefinition,
* int)
*/
@Override
protected AttributeType createType(final XSDComplexTypeDefinition xsdType, int depth) {
// already processed?
if (types.containsKey(xsdType)) {
return (AttributeType) types.get(xsdType);
}
// import?
if (!xsdType.getTargetNamespace().equals(schema.getTargetNamespace())) {
return findType(xsdType);
}
System.err.println("Creating complex type " + name(xsdType));
ComplexType gtType = new AbstractLazyComplexTypeImpl(name(xsdType), false,
xsdType.isAbstract(), null, null) {
@Override
public AttributeType buildSuper() {
XSDTypeDefinition baseType = xsdType.getBaseType();
if (baseType != null && baseType.getName() != null && !baseType.equals(xsdType)) {
return createType(baseType, DUMMY_DEPTH);
} else {
return null;
}
}
@SuppressWarnings("unchecked")
@Override
public Collection<PropertyDescriptor> buildDescriptors() {
if (!followComplexTypes) {
// might need to generate shallow schema classes while bootstrapping
return null;
} else {
List<PropertyDescriptor> properties = new ArrayList<PropertyDescriptor>();
for (XSDParticle particle : (List<XSDParticle>) Schemas
.getChildElementParticles(xsdType, false)) {
XSDElementDeclaration element = (XSDElementDeclaration) particle
.getContent();
if (element.isElementDeclarationReference()) {
element = element.getResolvedElementDeclaration();
}
XSDTypeDefinition childType = element.getTypeDefinition();
if (childType == null) {
childType = findGlobalElementXSDType(element);
}
AttributeType gtType = null;
if (childType != null) {
gtType = createType(childType, DUMMY_DEPTH);
} else {
// set to xs:anyType
gtType = xsAnyType();
}
if (gtType == null) {
throw new RuntimeException();
}
int minOccurs = particle.getMinOccurs();
int maxOccurs = particle.getMaxOccurs();
if (maxOccurs == -1) {
maxOccurs = Integer.MAX_VALUE;
}
boolean isNillable = element.isNillable();
// TODO: default value
AttributeDescriptor ad = factory.createAttributeDescriptor(gtType,
new NameImpl(element.getTargetNamespace(), element.getName()),
minOccurs, maxOccurs, isNillable, null);
properties.add(ad);
}
for (XSDAttributeDeclaration attribute : (List<XSDAttributeDeclaration>) Schemas
.getAttributeDeclarations(xsdType, false)) {
if (attribute.isAttributeDeclarationReference()) {
attribute = attribute.getResolvedAttributeDeclaration();
}
XSDSimpleTypeDefinition type = attribute.getTypeDefinition();
if (type == null) {
// look up in global schema
for (XSDAttributeDeclaration ad : schema.getAttributeDeclarations()) {
if (Utilities.equals(ad.getTargetNamespace(),
attribute.getTargetNamespace())
&& Utilities.equals(ad.getName(), attribute.getName())) {
type = ad.getTypeDefinition();
break;
}
}
}
if (type == null) {
throw new RuntimeException(
"Could not locate type definition for attribute "
+ name(attribute) + " of " + getName());
}
if (type.getName() == null) {
// TODO: deal with anonymous attribute types
continue;
}
AttributeType gtType = createType(type, DUMMY_DEPTH);
// TODO: if attribute is required
AttributeDescriptor ad = factory.createAttributeDescriptor(gtType,
name(attribute), 0, 1, true, null);
properties.add(ad);
}
return properties;
}
}
};
types.put(xsdType, gtType);
return gtType;
}
/**
* Return the short class name of the schema template.
*
* @see org.geotools.maven.xmlcodegen.SchemaGenerator#getSchemaClassTemplateName()
*/
@Override
protected String getSchemaClassTemplateName() {
return "CycleSchemaClassTemplate";
}
/**
* Return a list of types sorted by name.
*
* @see org.geotools.maven.xmlcodegen.SchemaGenerator#sort()
*/
@Override
public List<AttributeType> sort() {
List<AttributeType> sorted = new ArrayList<AttributeType>(
(Collection<AttributeType>) types.values());
Collections.sort(sorted, new Comparator<AttributeType>() {
@Override
public int compare(AttributeType at1, AttributeType at2) {
return ((NameImpl) at1.getName()).compareTo((NameImpl) at2.getName());
}
});
return sorted;
}
}