package org.geotools; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.URL; import java.nio.charset.Charset; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.Date; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Map.Entry; import javax.xml.namespace.QName; import javax.xml.parsers.ParserConfigurationException; import javax.xml.transform.TransformerException; import net.opengis.wfs.FeatureCollectionType; import net.opengis.wfs.WfsFactory; import org.eclipse.xsd.XSDComplexTypeDefinition; import org.eclipse.xsd.XSDCompositor; import org.eclipse.xsd.XSDDerivationMethod; import org.eclipse.xsd.XSDElementDeclaration; import org.eclipse.xsd.XSDFactory; import org.eclipse.xsd.XSDForm; import org.eclipse.xsd.XSDImport; import org.eclipse.xsd.XSDModelGroup; import org.eclipse.xsd.XSDParticle; import org.eclipse.xsd.XSDSchema; import org.eclipse.xsd.XSDTypeDefinition; import org.eclipse.xsd.util.XSDConstants; import org.eclipse.xsd.util.XSDResourceImpl; import org.geotools.data.DataUtilities; import org.geotools.data.simple.SimpleFeatureCollection; import org.geotools.data.simple.SimpleFeatureIterator; import org.geotools.feature.AttributeTypeBuilder; import org.geotools.feature.DefaultFeatureCollection; import org.geotools.feature.FeatureCollections; import org.geotools.feature.NameImpl; import org.geotools.feature.simple.SimpleFeatureBuilder; import org.geotools.feature.simple.SimpleFeatureTypeBuilder; import org.geotools.feature.type.SchemaImpl; import org.geotools.gml.producer.FeatureTransformer; import org.geotools.gtxml.GTXML; import org.geotools.referencing.CRS; import org.geotools.xml.Configuration; import org.geotools.xml.Encoder; import org.geotools.xml.Parser; import org.geotools.xml.ParserDelegate; import org.geotools.xml.StreamingParser; import org.geotools.xml.XSD; import org.geotools.xml.XSDParserDelegate; import org.geotools.xs.XS; import org.geotools.xs.XSConfiguration; import org.geotools.xs.XSSchema; import org.opengis.feature.simple.SimpleFeature; import org.opengis.feature.simple.SimpleFeatureType; 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.Name; import org.opengis.feature.type.PropertyDescriptor; import org.opengis.feature.type.Schema; import org.opengis.referencing.crs.CoordinateReferenceSystem; import org.xml.sax.SAXException; import com.vividsolutions.jts.geom.Geometry; /** * UtilityClass for encoding GML content. * <p> * This utility class uses a range of GeoTools technologies as required; if you would like finer * grain control over the encoding process please review the source code of this class and take your * own measures. * <p> * * @source $URL: http://svn.osgeo.org/geotools/trunk/modules/library/xml/src/main/java/org/geotools/GML.java $ */ public class GML { /** Version of encoder to use */ public static enum Version { GML2, GML3, WFS1_0, WFS1_1 } private Charset encoding = Charset.forName("UTF-8"); private URL baseURL; /** GML Configuration to use */ private Configuration gmlConfiguration; private String gmlNamespace; private String gmlLocation; /** * Schema or profile used to map between Java classes and XML elements. */ private List<Schema> schemaList = new ArrayList<Schema>(); String prefix = null; String namespace = null; private final Version version; private boolean legacy; private CoordinateReferenceSystem crs; /** * Construct a GML utility class to work with the indicated version of GML. * <p> * Note that when working with GML2 you need to supply additional information prior to use (in * order to indicate where for XSD file is located). * * @param version * Version of GML to use */ public GML(Version version) { this.version = version; init(); } /** * Engage legacy support for GML2. * <p> * The GML2 support for FeatureTransformer is much faster then that provided by the GTXML * parser/encoder. This speed is at the expense of getting the up front configuration exactly * correct (something you can only tell when parsing the produced result!). Setting this value * to false will use the same GMLConfiguration employed when parsing and has less risk of * producing invalid content. * * @param legacy */ public void setLegacy(boolean legacy) { this.legacy = legacy; } /** * Set the target namespace for the encoding. * * @param prefix * @param namespace */ public void setNamespace(String prefix, String namespace) { this.prefix = prefix; this.namespace = namespace; } /** * Set the encoding to use. * * @param encoding */ public void setEncoding(Charset encoding) { this.encoding = encoding; } /** * Base URL to use when encoding */ public void setBaseURL(URL baseURL) { this.baseURL = baseURL; } /** * Coordinate reference system to use when decoding. * <p> * In a few cases (such as decoding a SimpleFeatureType) the file format does not include the * required CooridinateReferenceSystem and you are asked to supply it. * * @param crs */ public void setCoordinateReferenceSystem(CoordinateReferenceSystem crs) { this.crs = crs; } /** * Set up out of the box configuration for GML encoding. * <ul> * <li>GML2</li> * <li>GML3</li> * </ul> * The following are not avialable yet: * <ul> * <li>gml3.2 - not yet available</li> * <li>wfs1.0 - not yet available</li> * <li>wfs1.1 - not yet available</li> * </ul> * * @param version */ protected void init() { List<Schema> schemas = new ArrayList<Schema>(); schemas.add(new XSSchema().profile()); // encoding of common java types Schema hack = new SchemaImpl(XS.NAMESPACE); AttributeTypeBuilder builder = new AttributeTypeBuilder(); builder.setName("date"); builder.setBinding(Date.class); hack.put(new NameImpl(XS.DATETIME), builder.buildType()); schemas.add(hack); // GML 2 // if (Version.GML2 == version) { gmlNamespace = org.geotools.gml2.GML.NAMESPACE; gmlLocation = "gml/2.1.2/feature.xsd"; gmlConfiguration = new org.geotools.gml2.GMLConfiguration(); schemas.add(new org.geotools.gml2.GMLSchema().profile()); } if (Version.WFS1_0 == version) { gmlNamespace = org.geotools.gml2.GML.NAMESPACE; gmlLocation = "gml/2.1.2/feature.xsd"; gmlConfiguration = new org.geotools.wfs.v1_0.WFSConfiguration(); schemas.add(new org.geotools.gml2.GMLSchema().profile()); } // GML 3 // if (Version.GML3 == version) { gmlNamespace = org.geotools.gml3.GML.NAMESPACE; gmlLocation = "gml/3.1.1/base/gml.xsd"; gmlConfiguration = new org.geotools.gml3.GMLConfiguration(); schemas.add(new org.geotools.gml3.GMLSchema().profile()); } if (Version.WFS1_1 == version) { gmlNamespace = org.geotools.gml3.GML.NAMESPACE; gmlLocation = "gml/3.1.1/base/gml.xsd"; gmlConfiguration = new org.geotools.wfs.v1_1.WFSConfiguration(); schemas.add(new org.geotools.gml3.GMLSchema().profile()); } schemaList = schemas; } private Entry<Name, AttributeType> searchSchemas(Class<?> binding) { // sort by isAssignable so we get the most specific match possible // Comparator<Entry<Name, AttributeType>> sort = new Comparator<Entry<Name, AttributeType>>() { public int compare(Entry<Name, AttributeType> o1, Entry<Name, AttributeType> o2) { Class<?> binding1 = o1.getValue().getBinding(); Class<?> binding2 = o2.getValue().getBinding(); if (binding1.equals(binding2)) { return 0; } if (binding1.isAssignableFrom(binding2)) { return 1; } else { return 0; } } }; List<Entry<Name, AttributeType>> match = new ArrayList<Entry<Name, AttributeType>>(); // process the listed profiles recording all available matches for (Schema profile : schemaList) { for (Entry<Name, AttributeType> entry : profile.entrySet()) { AttributeType type = entry.getValue(); if (type.getBinding().isAssignableFrom(binding)) { match.add(entry); } } } Collections.sort(match, sort); Iterator<Entry<Name, AttributeType>> iter = match.iterator(); if (iter.hasNext()) { Entry<Name, AttributeType> entry = iter.next(); return entry; } else { return null; // no binding found that matches } } @SuppressWarnings("unchecked") public void encode(OutputStream out, SimpleFeatureCollection collection) throws IOException { if (version == Version.GML2) { if (legacy) { encodeLegacyGML2(out, collection); } else { throw new IllegalStateException( "Cannot encode a feature collection using GML2 (only WFS)"); } } if (version == Version.WFS1_0) { Encoder e = new Encoder(new org.geotools.wfs.v1_0.WFSConfiguration()); e.getNamespaces().declarePrefix(prefix, namespace); e.setIndenting(true); FeatureCollectionType featureCollectionType = WfsFactory.eINSTANCE .createFeatureCollectionType(); featureCollectionType.getFeature().add(collection); e.encode(featureCollectionType, org.geotools.wfs.WFS.FeatureCollection, out); } if (version == Version.WFS1_1) { Encoder e = new Encoder(new org.geotools.wfs.v1_1.WFSConfiguration()); e.getNamespaces().declarePrefix(prefix, namespace); e.setIndenting(true); FeatureCollectionType featureCollectionType = WfsFactory.eINSTANCE .createFeatureCollectionType(); featureCollectionType.getFeature().add(collection); e.encode(featureCollectionType, org.geotools.wfs.WFS.FeatureCollection, out); } } private void encodeLegacyGML2(OutputStream out, SimpleFeatureCollection collection) throws IOException { final SimpleFeatureType TYPE = collection.getSchema(); FeatureTransformer transform = new FeatureTransformer(); transform.setIndentation(4); transform.setGmlPrefixing(true); if (prefix != null && namespace != null) { transform.getFeatureTypeNamespaces().declareDefaultNamespace(prefix, namespace); transform.addSchemaLocation(prefix, namespace); // transform.getFeatureTypeNamespaces().declareDefaultNamespace("", namespace ); } if (TYPE.getName().getNamespaceURI() != null && TYPE.getUserData().get("prefix") != null) { String typeNamespace = TYPE.getName().getNamespaceURI(); String typePrefix = (String) TYPE.getUserData().get("prefix"); transform.getFeatureTypeNamespaces().declareNamespace(TYPE, typePrefix, typeNamespace); } else if (prefix != null && namespace != null) { // ignore namespace URI in feature type transform.getFeatureTypeNamespaces().declareNamespace(TYPE, prefix, namespace); } else { // hopefully that works out for you then } // we probably need to do a wfs feaure collection here? transform.setCollectionPrefix(null); transform.setCollectionNamespace(null); // other configuration transform.setCollectionBounding(true); transform.setEncoding(encoding); // configure additional feature namespace lookup transform.getFeatureNamespaces(); String srsName = CRS.toSRS(TYPE.getCoordinateReferenceSystem()); if (srsName != null) { transform.setSrsName(srsName); } try { transform.transform(collection, out); } catch (TransformerException e) { throw (IOException) new IOException("Failed to encode feature collection:" + e) .initCause(e); } } /** * Encode the provided SimpleFeatureType into an XSD file, using a target namespace * <p> * When encoding the simpleFeatureType: * <ul> * <li>target prefix/namespace can be provided by prefix and namespace parameters. This is the * default for the entire XSD document.</li> * <li>simpleFeatureType.geName().getNamespaceURI() is used for the simpleFeatureType itself, * providing simpleFeatureType.getUserData().get("prefix") is defined</li> * </ul> * * @param simpleFeatureType * To be encoded as an XSD document * @param prefix * Prefix to use for for target namespace * @param namespace * Target namespace */ public void encode(OutputStream out, SimpleFeatureType simpleFeatureType) throws IOException { XSDSchema xsd = xsd(simpleFeatureType); XSDResourceImpl.serialize(out, xsd.getElement(), encoding.name()); } /** * Decode a typeName from the provided schemaLocation. * <p> * The XMLSchema does not include CoordinateReferenceSystem we need to ask you to supply this * information. * * @param schemaLocation * @param typeName * @return SimpleFeatureType * @throws IOException */ public SimpleFeatureType decodeSimpleFeatureType(URL schemaLocation, Name typeName) throws IOException { if (Version.WFS1_1 == version) { final QName featureName = new QName(typeName.getNamespaceURI(), typeName.getLocalPart()); String namespaceURI = featureName.getNamespaceURI(); String uri = schemaLocation.toExternalForm(); Configuration wfsConfiguration = new org.geotools.gml3.ApplicationSchemaConfiguration( namespaceURI, uri); FeatureType parsed = GTXML.parseFeatureType(wfsConfiguration, featureName, crs); return DataUtilities.simple(parsed); } if (Version.WFS1_0 == version) { final QName featureName = new QName(typeName.getNamespaceURI(), typeName.getLocalPart()); String namespaceURI = featureName.getNamespaceURI(); String uri = schemaLocation.toExternalForm(); XSD xsd = new org.geotools.gml2.ApplicationSchemaXSD(namespaceURI, uri); Configuration configuration = new Configuration(xsd) { { addDependency(new XSConfiguration()); addDependency(gmlConfiguration); // use our GML configuration } protected void registerBindings(java.util.Map bindings) { // we have no special bindings } }; FeatureType parsed = GTXML.parseFeatureType(configuration, featureName, crs); return DataUtilities.simple(parsed); } return null; } public SimpleFeatureCollection decodeFeatureCollection(InputStream in) throws IOException, SAXException, ParserConfigurationException { if (Version.GML2 == version || Version.WFS1_0 == version || Version.GML2 == version || Version.GML3 == version || Version.WFS1_0 == version || Version.WFS1_1 == version) { Parser parser = new Parser(gmlConfiguration); Object obj = parser.parse(in); SimpleFeatureCollection collection = toFeatureCollection(obj); return collection; } return null; } /** * Convert parse results into a SimpleFeatureCollection. * * @param obj SimpleFeatureCollection, Collection<?>, SimpleFeature, etc... * @return SimpleFeatureCollection of the results */ private SimpleFeatureCollection toFeatureCollection(Object obj) { if (obj == null) { return null; // not available? } if (obj instanceof SimpleFeatureCollection) { return (SimpleFeatureCollection) obj; } if (obj instanceof Collection<?>) { Collection<?> collection = (Collection<?>) obj; SimpleFeatureCollection simpleFeatureCollection = simpleFeatureCollection(collection); return simpleFeatureCollection; } if (obj instanceof SimpleFeature) { SimpleFeature feature = (SimpleFeature) obj; return DataUtilities.collection(feature); } if (obj instanceof FeatureCollectionType) { FeatureCollectionType collectionType = (FeatureCollectionType) obj; for (Object entry : collectionType.getFeature()) { SimpleFeatureCollection collection = toFeatureCollection(entry); if (entry != null) { return collection; } } return null; // nothing found } else { throw new ClassCastException(obj.getClass() + " produced when FeatureCollection expected" + " check schema use of AbstractFeatureCollection"); } } /** * Allow the parsing of features as a stream; the returned iterator can be used to step through * the inputstream of content one feature at a time without loading everything into memory. * <p> * The schema used by the XML is consulted to determine what element extends AbstractFeature. * * @param in * @return Iterator that can be used to parse features one at a time * @throws SAXException * @throws ParserConfigurationException * @throws IOException */ public SimpleFeatureIterator decodeFeatureIterator(InputStream in) throws IOException, ParserConfigurationException, SAXException { return decodeFeatureIterator(in, null); } /** * Allow the parsing of features as a stream; the returned iterator can be used to step through * the inputstream of content one feature at a time without loading everything into memory. * <p> * The use of an elementName is optional; and can be used as a workaround in cases where the * schema is not available or correctly defined. The returned elements are wrapped up as a * Feature if needed. This mehtod can be used to retrive only the Geometry elements from a GML * docuemnt. * * @param in * InputStream used as a source of SimpleFeature content * @param xpath * Optional xpath used to indicate simple feature element; the schema will be checked * for an entry that extends AbstratFeatureType * @return * @throws SAXException * @throws ParserConfigurationException */ public SimpleFeatureIterator decodeFeatureIterator(InputStream in, QName elementName) throws IOException, ParserConfigurationException, SAXException { if (Version.GML2 == version || Version.GML3 == version || Version.WFS1_0 == version || Version.WFS1_1 == version) { // ParserDelegate parserDelegate = new XSDParserDelegate( gmlConfiguration ); StreamingParser parser; if (elementName != null) { parser = new StreamingParser(gmlConfiguration, in, elementName); } else { parser = new StreamingParser(gmlConfiguration, in, SimpleFeature.class); } return iterator(parser); } return null; } /** * Go through collection contents and morph contents into SimpleFeatures as required. * * @param collection * @return SimpleFeatureCollection */ private SimpleFeatureCollection simpleFeatureCollection(Collection<?> collection) { SimpleFeatureCollection featureCollection = FeatureCollections.newCollection(); SimpleFeatureType schema = null; for (Object obj : collection) { if (schema == null) { schema = simpleType(obj); } SimpleFeature feature = simpleFeature(obj, schema); featureCollection.add(feature); } return featureCollection; } /** * Used to wrap up a StreamingParser as a Iterator<SimpleFeature>. * <p> * This iterator is actually forgiving; and willing to "morph" content into a SimpleFeature if * needed. * <ul> * <li>SimpleFeature - is returned as is * <li> * * @param parser * @return */ protected SimpleFeatureIterator iterator(final StreamingParser parser) { return new SimpleFeatureIterator() { SimpleFeatureType schema; Object next; public boolean hasNext() { if (next != null) { return true; } next = parser.parse(); return next != null; } public SimpleFeature next() { if (next == null) { next = parser.parse(); } if (next != null) { try { if (schema == null) { schema = simpleType(next); } SimpleFeature feature = simpleFeature(next, schema); return feature; } finally { next = null; // we have tried processing this one now } } else { return null; // nothing left } } public void close() { schema = null; } }; } protected SimpleFeatureType simpleType(Object obj) { if (obj instanceof SimpleFeature) { SimpleFeature feature = (SimpleFeature) obj; return feature.getFeatureType(); } if (obj instanceof Map<?, ?>) { Map<?, ?> map = (Map<?, ?>) obj; SimpleFeatureTypeBuilder build = new SimpleFeatureTypeBuilder(); build.setName("Unknown"); for (Map.Entry<?, ?> entry : map.entrySet()) { String key = (String) entry.getKey(); Object value = entry.getValue(); Class<?> binding = value == null ? Object.class : value.getClass(); if (value instanceof Geometry) { Geometry geom = (Geometry) value; Object srs = geom.getUserData(); if (srs instanceof CoordinateReferenceSystem) { build.add(key, binding, (CoordinateReferenceSystem) srs); } else if (srs instanceof Integer) { build.add(key, binding, (Integer) srs); } else if (srs instanceof String) { build.add(key, binding, (String) srs); } else { build.add(key, binding); } } else { build.add(key, binding); } } SimpleFeatureType schema = build.buildFeatureType(); return schema; } if (obj instanceof Geometry) { Geometry geom = (Geometry) obj; Class<?> binding = geom.getClass(); Object srs = geom.getUserData(); SimpleFeatureTypeBuilder build = new SimpleFeatureTypeBuilder(); build.setName("Unknown"); if (srs instanceof CoordinateReferenceSystem) { build.add("the_geom", binding, (CoordinateReferenceSystem) srs); } else if (srs instanceof Integer) { build.add("the_geom", binding, (Integer) srs); } else if (srs instanceof String) { build.add("the_geom", binding, (String) srs); } else { build.add("the_geom", binding); } build.setDefaultGeometry("the_geom"); SimpleFeatureType schema = build.buildFeatureType(); return schema; } return null; } /** * Morph provided obj to a SimpleFeature if possible. * * @param obj * @param schema * @return SimpleFeature, or null if not possible */ protected SimpleFeature simpleFeature(Object obj, SimpleFeatureType schema) { if (schema == null) { schema = simpleType(obj); } if (obj instanceof SimpleFeature) { return (SimpleFeature) obj; } if (obj instanceof Map<?, ?>) { Map<?, ?> map = (Map<?, ?>) obj; Object values[] = new Object[schema.getAttributeCount()]; for (int i = 0; i < schema.getAttributeCount(); i++) { AttributeDescriptor descriptor = schema.getDescriptor(i); String key = descriptor.getLocalName(); Object value = map.get(key); values[i] = value; } SimpleFeature feature = SimpleFeatureBuilder.build(schema, values, null); return feature; } if (obj instanceof Geometry) { Geometry geom = (Geometry) obj; SimpleFeatureBuilder build = new SimpleFeatureBuilder(schema); build.set(schema.getGeometryDescriptor().getName(), geom); SimpleFeature feature = build.buildFeature(null); return feature; } return null; // not available as a feature! } @SuppressWarnings("unchecked") protected XSDSchema xsd(SimpleFeatureType simpleFeatureType) throws IOException { XSDFactory factory = XSDFactory.eINSTANCE; XSDSchema xsd = factory.createXSDSchema(); xsd.setSchemaForSchemaQNamePrefix("xsd"); xsd.getQNamePrefixToNamespaceMap().put("xsd", XSDConstants.SCHEMA_FOR_SCHEMA_URI_2001); xsd.setElementFormDefault(XSDForm.get(XSDForm.QUALIFIED)); if (baseURL == null) { throw new IllegalStateException("Please setBaseURL prior to encoding"); } if (prefix != null || namespace != null) { xsd.setTargetNamespace(namespace); xsd.getQNamePrefixToNamespaceMap().put(prefix, namespace); } if (simpleFeatureType.getName().getNamespaceURI() != null && simpleFeatureType.getUserData().get("prefix") != null) { String providedNamespace = simpleFeatureType.getName().getNamespaceURI(); String providedPrefix = (String) simpleFeatureType.getUserData().get("prefix"); xsd.getQNamePrefixToNamespaceMap().put(providedPrefix, providedNamespace); } if (simpleFeatureType.getUserData().get("schemaURI") != null) { throw new IllegalArgumentException("Unable to support app-schema supplied types"); } // import GML import XSDImport gml = factory.createXSDImport(); gml.setNamespace(gmlNamespace); gml.setSchemaLocation(baseURL.toString() + "/" + gmlLocation); gml.setResolvedSchema(gmlConfiguration.getXSD().getSchema()); xsd.getContents().add(gml); xsd.getQNamePrefixToNamespaceMap().put("gml", gmlNamespace); xsd.getQNamePrefixToNamespaceMap().put("gml", "http://www.opengis.net/gml"); XSDElementDeclaration element = factory.createXSDElementDeclaration(); element.setName(simpleFeatureType.getTypeName()); XSDElementDeclaration _FEATURE = xsd.resolveElementDeclaration(gmlNamespace, "_Feature"); element.setSubstitutionGroupAffiliation(_FEATURE); XSDComplexTypeDefinition ABSTRACT_FEATURE_TYPE = xsd.resolveComplexTypeDefinition( gmlNamespace, "AbstractFeatureType"); XSDComplexTypeDefinition featureType = xsd(xsd, simpleFeatureType, ABSTRACT_FEATURE_TYPE); // package up and add to xsd element.setTypeDefinition(featureType); xsd.getContents().add(element); xsd.updateElement(); return xsd; } /** * Build the XSD definition for the provided type. * <p> * The generated definition is recorded in the XSDSchema prior to being returned. * * @param xsd * The XSDSchema being worked on * @param type * ComplexType to capture as an encoding, usually a SimpleFeatureType * @param L_TYPE * definition to use as the base type, or null * @return XSDComplexTypeDefinition generated for the provided type */ @SuppressWarnings("unchecked") protected XSDComplexTypeDefinition xsd(XSDSchema xsd, ComplexType type, final XSDComplexTypeDefinition BASE_TYPE) { XSDFactory factory = XSDFactory.eINSTANCE; XSDComplexTypeDefinition definition = factory.createXSDComplexTypeDefinition(); definition.setName(type.getName().getLocalPart()); definition.setDerivationMethod(XSDDerivationMethod.EXTENSION_LITERAL); if (BASE_TYPE != null) { definition.setBaseTypeDefinition(BASE_TYPE); } List<String> skip = Collections.emptyList(); if ("AbstractFeatureType".equals(BASE_TYPE.getName())) { // should look at ABSTRACT_FEATURE_TYPE to determine contents to skip skip = Arrays.asList(new String[] { "nounds", "description", "boundedBy" }); } // attributes XSDModelGroup attributes = factory.createXSDModelGroup(); attributes.setCompositor(XSDCompositor.SEQUENCE_LITERAL); Name anyName = new NameImpl(XS.NAMESPACE, XS.ANYTYPE.getLocalPart()); for (PropertyDescriptor descriptor : type.getDescriptors()) { if (descriptor instanceof AttributeDescriptor) { AttributeDescriptor attributeDescriptor = (AttributeDescriptor) descriptor; if (skip.contains(attributeDescriptor.getLocalName())) { continue; } XSDElementDeclaration attribute = factory.createXSDElementDeclaration(); attribute.setName(attributeDescriptor.getLocalName()); attribute.setNillable(attributeDescriptor.isNillable()); Name name = attributeDescriptor.getType().getName(); // return the first match. if (!anyName.equals(name)) { AttributeType attributeType = attributeDescriptor.getType(); if (attributeType instanceof ComplexType) { ComplexType complexType = (ComplexType) attributeType; // any complex contents must resolve (we cannot encode against // an abstract type for example) if (xsd.resolveTypeDefinition(name.getNamespaceURI(), name.getLocalPart()) == null) { // not yet added; better add it into the mix xsd(xsd, complexType, null); } } else { Class<?> binding = attributeType.getBinding(); Entry<Name, AttributeType> entry = searchSchemas(binding); if (entry == null) { throw new IllegalStateException("No type for " + attribute.getName() + " (" + binding.getName() + ")"); } name = entry.getKey(); } } XSDTypeDefinition attributeDefinition = xsd.resolveTypeDefinition(name .getNamespaceURI(), name.getLocalPart()); attribute.setTypeDefinition(attributeDefinition); XSDParticle particle = factory.createXSDParticle(); particle.setMinOccurs(attributeDescriptor.getMinOccurs()); particle.setMaxOccurs(attributeDescriptor.getMaxOccurs()); particle.setContent(attribute); attributes.getContents().add(particle); } } // set up fatureType with attributes XSDParticle contents = factory.createXSDParticle(); contents.setContent(attributes); definition.setContent(contents); xsd.getContents().add(definition); return definition; } }