001: /*
002: * Geotools2 - OpenSource mapping toolkit
003: * http://geotools.org
004: * (C) 2005-2006, GeoTools Project Managment Committee (PMC)
005: *
006: * This library is free software; you can redistribute it and/or
007: * modify it under the terms of the GNU Lesser General Public
008: * License as published by the Free Software Foundation;
009: * version 2.1 of the License.
010: *
011: * This library is distributed in the hope that it will be useful,
012: * but WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014: * Lesser General Public License for more details.
015: *
016: */
017: package org.geotools.data.complex.config;
018:
019: import java.io.IOException;
020: import java.io.InputStream;
021: import java.net.URL;
022: import java.util.ArrayList;
023: import java.util.Arrays;
024: import java.util.Collection;
025: import java.util.Collections;
026: import java.util.HashMap;
027: import java.util.Iterator;
028: import java.util.List;
029: import java.util.Map;
030: import java.util.NoSuchElementException;
031: import java.util.Set;
032: import java.util.Stack;
033: import java.util.Map.Entry;
034: import java.util.logging.Level;
035: import java.util.logging.Logger;
036:
037: import javax.xml.namespace.QName;
038:
039: import org.apache.xml.resolver.Catalog;
040: import org.eclipse.xsd.XSDAttributeDeclaration;
041: import org.eclipse.xsd.XSDAttributeUse;
042: import org.eclipse.xsd.XSDAttributeUseCategory;
043: import org.eclipse.xsd.XSDComplexTypeDefinition;
044: import org.eclipse.xsd.XSDElementDeclaration;
045: import org.eclipse.xsd.XSDSchema;
046: import org.eclipse.xsd.XSDSimpleTypeDefinition;
047: import org.eclipse.xsd.XSDTypeDefinition;
048: import org.geotools.data.feature.adapter.ISOAttributeTypeAdapter;
049: import org.geotools.data.feature.adapter.ISOFeatureTypeAdapter;
050: import org.geotools.feature.iso.Types;
051: import org.geotools.feature.iso.simple.SimpleTypeFactoryImpl;
052: import org.geotools.feature.iso.type.TypeFactoryImpl;
053: import org.geotools.gml3.ApplicationSchemaConfiguration;
054: import org.geotools.gml3.GMLConfiguration;
055: import org.geotools.gml3.GMLSchema;
056: import org.geotools.gml3.bindings.GML;
057: import org.geotools.gml3.bindings.smil.SMIL20;
058: import org.geotools.gml3.bindings.smil.SMIL20LANG;
059: import org.geotools.gml3.smil.SMIL20LANGSchema;
060: import org.geotools.gml3.smil.SMIL20Schema;
061: import org.geotools.xlink.bindings.XLINK;
062: import org.geotools.xml.Binding;
063: import org.geotools.xml.Configuration;
064: import org.geotools.xml.SchemaIndex;
065: import org.geotools.xml.Schemas;
066: import org.geotools.xs.XSSchema;
067: import org.geotools.xs.bindings.XS;
068: import org.opengis.feature.simple.SimpleTypeFactory;
069: import org.opengis.feature.type.AttributeDescriptor;
070: import org.opengis.feature.type.AttributeType;
071: import org.opengis.feature.type.ComplexType;
072: import org.opengis.feature.type.FeatureType;
073: import org.opengis.feature.type.Name;
074: import org.opengis.feature.type.Schema;
075: import org.opengis.feature.type.TypeFactory;
076: import org.opengis.referencing.crs.CoordinateReferenceSystem;
077: import org.opengis.util.InternationalString;
078: import org.xmlpull.v1.XmlPullParser;
079: import org.xmlpull.v1.XmlPullParserException;
080: import org.xmlpull.v1.XmlPullParserFactory;
081:
082: /**
083: * Parses an application schema given by a gtxml {@link Configuration} into a
084: * set of {@link AttributeType}s and {@link AttributeDescriptor}s.
085: * <p>
086: * All the XSD schema locations that comprise the application schema are
087: * obtained from the main {@link Configuration} and its dependencies.
088: * </p>
089: * <p>
090: * Of particular interest might be the {@link ApplicationSchemaConfiguration}
091: * object, which allows to provide the location of the root xsd schema for a
092: * given application schema.
093: * </p>
094: *
095: * @author Gabriel Roldan
096: * @version $Id: EmfAppSchemaReader.java 28577 2008-01-03 15:44:29Z groldan $
097: * @source $URL:
098: * http://svn.geotools.org/geotools/branches/2.4.x/modules/unsupported/community-schemas/community-schema-ds/src/main/java/org/geotools/data/complex/config/EmfAppSchemaReader.java $
099: * @since 2.4
100: */
101: public class EmfAppSchemaReader {
102: private static final Logger LOGGER = org.geotools.util.logging.Logging
103: .getLogger(EmfAppSchemaReader.class.getPackage().getName());
104:
105: /**
106: * Caches the GML 3.1.1 types and its dependencies
107: */
108: private static Map FOUNDATION_TYPES = new HashMap();
109:
110: /**
111: * Caches the GML 3.1.1 top level descriptors and its dependencies
112: */
113: private static Map FOUNDATION_DESCRIPTORS = new HashMap();
114:
115: /**
116: * Contains all the AttributeTypes defined in the application schema and its
117: * imports
118: */
119: private Map/* <Name, AttributeType> */typeRegistry;
120:
121: /**
122: * Contains all the AttributeDescriptors defined in the application schema
123: * and its imports
124: */
125: private Map/* <Name, AttributeDescriptor> */descriptorRegistry;
126:
127: /**
128: * stack of currently being built type names, used by
129: * {@link #createType(Name, XSDTypeDefinition)} to prevent recursive type
130: * definitions by proxy'ing a type that appears to be already being
131: * constructed and thus still not in the type registry.
132: */
133: private Stack processingTypes;
134:
135: private TypeFactory typeFactory;
136:
137: private Catalog oasisCatalog;
138:
139: private EmfAppSchemaReader() {
140: typeFactory = new TypeFactoryImpl();
141: }
142:
143: public TypeFactory getTypeFactory() {
144: return typeFactory;
145: }
146:
147: public Map getTypeRegistry() {
148: return new HashMap(this .typeRegistry);
149: }
150:
151: public Map getDescriptorRegistry() {
152: return new HashMap(this .descriptorRegistry);
153: }
154:
155: public Catalog getCatalog() {
156: return oasisCatalog;
157: }
158:
159: public void setCatalog(final Catalog oasisCatalog) {
160: this .oasisCatalog = oasisCatalog;
161: }
162:
163: /**
164: * Parses the GML schema represented by the <code>configuration</code>'s
165: * {@link Configuration#getSchemaFileURL() schema location} into a set of
166: * {@link AttributeDescriptor} and {@link AttributeType} objects.
167: * <p>
168: * Upon return of this method, its guaranteed that all the globally defined
169: * xml elements in the application schema and its dependencies are available
170: * through {@link #getDescriptorRegistry()}, and all the globally defined
171: * xml types through {@link #getTypeRegistry()}.
172: * </p>
173: *
174: * @param configuration
175: * configuration object used to access the XSDSchema to parse.
176: * This configuration object might contain {@link Binding}s
177: * @throws IOException
178: */
179: public void parse(Configuration configuration) throws IOException {
180: //if there's an Oasis catalog set, use it to resolve schema locations
181: final Catalog catalog = getCatalog();
182: if (catalog != null) {
183: configuration = new OasisCatalogConfigurationWrapper(
184: catalog, configuration);
185: }
186: // find out the schemas involved in the app schema configuration
187: final SchemaIndex appSchemaIndex = Schemas
188: .findSchemas(configuration);
189:
190: // set up the type registry
191: typeRegistry = new HashMap();
192: descriptorRegistry = new HashMap();
193: processingTypes = new Stack();
194:
195: // register the "fundation" gml types already bound to geotools
196: // AttributeTypes
197: if (EmfAppSchemaReader.FOUNDATION_TYPES.isEmpty()) {
198: createFoundationTypes();
199: }
200: typeRegistry.putAll(EmfAppSchemaReader.FOUNDATION_TYPES);
201: descriptorRegistry
202: .putAll(EmfAppSchemaReader.FOUNDATION_DESCRIPTORS);
203:
204: // with the application schemas...
205: XSDSchema[] appSchemas = appSchemaIndex.getSchemas();
206: Map schemas = new HashMap();
207: for (Iterator it = Arrays.asList(appSchemas).iterator(); it
208: .hasNext();) {
209: XSDSchema schema = (XSDSchema) it.next();
210: schemas.put(schema.getTargetNamespace(), schema);
211: }
212:
213: // establish a preferred parsing order so there are the less proxies
214: // possible
215: String[] preferredOrder = { XS.NAMESPACE, XLINK.NAMESPACE,
216: SMIL20.NAMESPACE, SMIL20LANG.NAMESPACE, GML.NAMESPACE };
217: List schemaList = new ArrayList(appSchemas.length);
218: for (int i = 0; i < preferredOrder.length; i++) {
219: String targetNamespace = preferredOrder[i];
220: XSDSchema schema = (XSDSchema) schemas.get(targetNamespace);
221: if (schema != null) {
222: schemaList.add(schema);
223: schemas.remove(targetNamespace);
224: }
225: }
226: schemaList.addAll(schemas.values());
227:
228: // and import them all
229: for (Iterator it = schemaList.iterator(); it.hasNext();) {
230: XSDSchema schema = (XSDSchema) it.next();
231: importSchema(schema);
232: }
233: }
234:
235: /**
236: * Parses the gml schema referenced by <code>location</code> into a set of
237: * {@link AttributeDescriptor} and {@link AttributeType} objects.
238: * <p>
239: * Upon return of this method, its guaranteed that all the globally defined
240: * xml elements in the application schema and its dependencies are available
241: * through {@link #getDescriptorRegistry()}, and all the globally defined
242: * xml types through {@link #getTypeRegistry()}.
243: * </p>
244: *
245: * @param location
246: * the phisical location of the root xsd schema that comprises
247: * the application schema to parse.
248: * @throws IOException
249: * if any non recoverable problem occurs while parsing the
250: * application schema pointed out by <code>location</code> or
251: * one of its dependencies.
252: */
253: public void parse(final URL location) throws IOException {
254:
255: final String nameSpace = findSchemaNamespace(location);
256:
257: final String schemaLocation = location.toExternalForm();
258:
259: final Configuration configuration = new ApplicationSchemaConfiguration(
260: nameSpace, schemaLocation);
261:
262: parse(configuration);
263: }
264:
265: /**
266: * Finds out the targetNamespace of the xsd schema referenced by
267: * <code>location</code>
268: *
269: * @param location
270: * @return
271: * @throws IOException
272: */
273: private String findSchemaNamespace(URL location) throws IOException {
274: String targetNamespace = null;
275: // parse some of the instance document to find out the
276: // schema location
277: if (getCatalog() != null) {
278: String resolvedLocation = getCatalog().resolveSystem(
279: location.toExternalForm());
280: if (resolvedLocation != null) {
281: location = new URL(resolvedLocation);
282: }
283: }
284: InputStream input = location.openStream();
285:
286: // create stream parser
287: XmlPullParser parser = null;
288:
289: try {
290: XmlPullParserFactory factory = XmlPullParserFactory
291: .newInstance();
292: factory.setNamespaceAware(true);
293: factory.setValidating(false);
294:
295: // parse root element
296: parser = factory.newPullParser();
297: parser.setInput(input, "UTF-8");
298: parser.nextTag();
299:
300: // look for schema location
301: for (int i = 0; i < parser.getAttributeCount(); i++) {
302: if ("targetNamespace"
303: .equals(parser.getAttributeName(i))) {
304: targetNamespace = parser.getAttributeValue(i);
305: break;
306: }
307: }
308: // reset input stream
309: parser.setInput(null);
310: } catch (XmlPullParserException e) {
311: String msg = "Cannot find target namespace for schema document "
312: + location;
313: throw (RuntimeException) new RuntimeException(msg)
314: .initCause(e);
315: } finally {
316: input.close();
317: }
318: if (targetNamespace == null) {
319: throw new IllegalArgumentException(
320: "Input document does not specifies a targetNamespace");
321: }
322: return targetNamespace;
323: }
324:
325: private void createFoundationTypes() {
326: synchronized (EmfAppSchemaReader.FOUNDATION_TYPES) {
327: if (!EmfAppSchemaReader.FOUNDATION_TYPES.isEmpty()) {
328: return;
329: }
330: Schema schema;
331: schema = new XSSchema();
332: importSchema(schema);
333:
334: schema = new SMIL20Schema();
335: importSchema(schema);
336:
337: schema = new SMIL20LANGSchema();
338: importSchema(schema);
339:
340: schema = new GMLSchema();
341: importSchema(schema);
342:
343: LOGGER
344: .info("Creating GMLConfiguration to get the prebuilt gml schemas from");
345: GMLConfiguration configuration = new GMLConfiguration();
346: LOGGER
347: .info("Aquiring prebuilt gml schema and its dependencies");
348: SchemaIndex index = Schemas.findSchemas(configuration);
349: XSDSchema[] schemas = index.getSchemas();
350:
351: LOGGER.info("Importing GML schema and dependencies");
352: for (int i = 0; i < schemas.length; i++) {
353: XSDSchema xsdSchema = schemas[i];
354: String targetNamespace = xsdSchema.getTargetNamespace();
355: if (XS.NAMESPACE.equals(targetNamespace)) {
356: LOGGER.finest("Ignoring XS schema parsing");
357: continue;
358: }
359: importSchema(xsdSchema);
360: }
361:
362: EmfAppSchemaReader.FOUNDATION_TYPES.putAll(typeRegistry);
363: EmfAppSchemaReader.FOUNDATION_DESCRIPTORS
364: .putAll(descriptorRegistry);
365: typeRegistry.clear();
366: descriptorRegistry.clear();
367: }
368: }
369:
370: /**
371: * Traverses over the {@link XSDElementDeclaration}s and
372: * {@link XSDTypeDefinition}s in <code>xsdSchema</code>, parses them to
373: * the appropriate {@link AttributeDescriptor}s and {@link AttributeType}s,
374: * and stores the parsed objects into {@link #descriptorRegistry} and
375: * {@link #typeRegistry}, respectively.
376: *
377: * @param xsdSchema
378: */
379: private void importSchema(XSDSchema xsdSchema) {
380: final String targetNamespace = xsdSchema.getTargetNamespace();
381: LOGGER.fine("Importing schema " + targetNamespace);
382:
383: final List xsdTypeDefinitions = xsdSchema.getTypeDefinitions();
384: LOGGER.finer("Importing " + targetNamespace
385: + " type definitions");
386: importXsdTypeDefinitions(xsdTypeDefinitions);
387:
388: final List xsdElementDeclarations = xsdSchema
389: .getElementDeclarations();
390: LOGGER.finer("Importing " + targetNamespace
391: + " element definitions");
392: importElementDeclarations(xsdElementDeclarations);
393: }
394:
395: private void importElementDeclarations(List elementDeclarations) {
396: XSDElementDeclaration elemDecl;
397: for (Iterator it = elementDeclarations.iterator(); it.hasNext();) {
398: elemDecl = (XSDElementDeclaration) it.next();
399: LOGGER.finest("Creating attribute descriptor for "
400: + elemDecl.getQName());
401: AttributeDescriptor descriptor;
402: try {
403: descriptor = createAttributeDescriptor(null, elemDecl);
404: LOGGER.finest("Registering attribute descriptor "
405: + descriptor.getName());
406: register(descriptor);
407: } catch (NoSuchElementException e) {
408: LOGGER.log(Level.WARNING, e.getMessage());
409: }
410: }
411: }
412:
413: private void register(AttributeDescriptor descriptor) {
414: Name name = descriptor.getName();
415: descriptorRegistry.put(name, descriptor);
416: }
417:
418: private void register(AttributeType type) {
419: Name name = type.getName();
420: Object old = typeRegistry.put(name, type);
421: if (old != null) {
422: LOGGER.fine(type.getName() + " replaced by new value.");
423: }
424: }
425:
426: private AttributeDescriptor createAttributeDescriptor(
427: final XSDComplexTypeDefinition container,
428: final XSDElementDeclaration elemDecl) {
429: String targetNamespace = elemDecl.getTargetNamespace();
430: String name = elemDecl.getName();
431: Name elemName = Types.typeName(targetNamespace, name);
432:
433: AttributeType type;
434: try {
435: type = getTypeOf(elemDecl);
436: } catch (NoSuchElementException e) {
437: String msg = "Type not found for " + elemName
438: + " at type container "
439: + container.getTargetNamespace() + "#"
440: + container.getName() + " at "
441: + container.getSchema().getSchemaLocation();
442: NoSuchElementException nse = new NoSuchElementException(msg);
443: nse.initCause(e);
444: throw nse;
445: }
446: int minOccurs = container == null ? 0 : Schemas.getMinOccurs(
447: container, elemDecl);
448: int maxOccurs = container == null ? Integer.MAX_VALUE : Schemas
449: .getMaxOccurs(container, elemDecl);
450: boolean nillable = elemDecl.isNillable();
451:
452: if (maxOccurs == -1) {
453: // this happens when maxOccurs is set to "unbounded"
454: maxOccurs = Integer.MAX_VALUE;
455: }
456: Object defaultValue = null;
457: AttributeDescriptor descriptor = typeFactory
458: .createAttributeDescriptor(type, elemName, minOccurs,
459: maxOccurs, nillable, defaultValue);
460:
461: descriptor.putUserData(XSDElementDeclaration.class, elemDecl);
462:
463: return descriptor;
464: }
465:
466: /**
467: * If the type of elemDecl is annonymous creates a new type with the same
468: * name than the atrribute and returns it. If it is not anonymous, looks it
469: * up on the registry and in case the type does not exists in the registry
470: * uses a proxy.
471: *
472: * @param elemDecl
473: * @return
474: */
475: private AttributeType getTypeOf(XSDElementDeclaration elemDecl) {
476: boolean hasToBeRegistered = false;
477: XSDTypeDefinition typeDefinition;
478:
479: // TODO REVISIT, I'm not sure this is the way to find out if the
480: // element's type is defined in line (an thus no need to register it
481: // as a global type)
482: if (elemDecl.isElementDeclarationReference()) {
483: elemDecl = elemDecl.getResolvedElementDeclaration();
484: }
485: typeDefinition = elemDecl.getAnonymousTypeDefinition();
486: if (typeDefinition == null) {
487: hasToBeRegistered = true;
488: typeDefinition = elemDecl.getTypeDefinition();
489: }
490:
491: if (typeDefinition == null) {
492: throw new NoSuchElementException(
493: "The element declaration "
494: + elemDecl.getTargetNamespace()
495: + "#"
496: + elemDecl.getName()
497: + " has a null type definition, can't continue, fix it on the schema");
498: }
499: AttributeType type;
500: if (hasToBeRegistered) {
501: String targetNamespace = typeDefinition
502: .getTargetNamespace();
503: String name = typeDefinition.getName();
504: Name typeName = Types.typeName(targetNamespace, name);
505: type = getType(typeName);
506: if (type == null) {
507: type = createType(typeName, typeDefinition);
508: register(type);// //////////
509: }
510: } else {
511: String name = elemDecl.getName();
512: String targetNamespace = elemDecl.getTargetNamespace();
513: Name overrideName = Types.typeName(targetNamespace, name);
514: type = createType(overrideName, typeDefinition);
515: }
516: return type;
517: }
518:
519: private AttributeType createProxiedType(final Name assignedName,
520: final XSDTypeDefinition typeDefinition) {
521: AttributeType type;
522: if (null == typeDefinition.getSimpleType()
523: && typeDefinition instanceof XSDComplexTypeDefinition) {
524: boolean isFeatureType = isDerivedFrom(typeDefinition,
525: GML.AbstractFeatureType);
526: if (isFeatureType) {
527: type = new FeatureTypeProxy(assignedName,
528: this .typeRegistry);
529: } else {
530: type = new ComplexTypeProxy(assignedName,
531: this .typeRegistry);
532: }
533: } else {
534: boolean isGeometryType = isDerivedFrom(typeDefinition,
535: GML.AbstractGeometryType);
536: if (isGeometryType) {
537: type = new GeometryTypeProxy(assignedName,
538: this .typeRegistry);
539: } else {
540: type = new AttributeTypeProxy(assignedName,
541: this .typeRegistry);
542: }
543: }
544: return type;
545: }
546:
547: /**
548: * Returns whether <code>typeDefinition</code> has an ancestor named
549: * <code>baseTypeName</code>.
550: *
551: * @param typeDefinition
552: * @param baseTypeName
553: * @return
554: */
555: private boolean isDerivedFrom(
556: final XSDTypeDefinition typeDefinition,
557: final QName baseTypeName) {
558: Name typeName = Types.toName(baseTypeName);
559: // XSDTypeDefinition baseTypeDefinition = Schemas.getBaseTypeDefinition(
560: // typeDefinition, baseTypeName);
561: // boolean isFeatureType = baseTypeDefinition != null;
562: // return isFeatureType;
563: return isDerivedFrom(typeDefinition, typeName);
564: }
565:
566: private AttributeType createType(XSDTypeDefinition typeDefinition) {
567: String targetNamespace = typeDefinition.getTargetNamespace();
568: String name = typeDefinition.getName();
569: Name typeName = Types.typeName(targetNamespace, name);
570: return createType(typeName, typeDefinition);
571: }
572:
573: /**
574: * Creates an {@link AttributeType} that matches the xsd type definition as
575: * much as possible.
576: * <p>
577: * The original type definition given by the {@link XSDTypeDefinition} is
578: * kept as AttributeType's metadata stored as a "user data" property using
579: * <code>XSDTypeDefinition.class</code> as key.
580: * </p>
581: * <p>
582: * If it is a complex attribute, it will contain all the properties declared
583: * in the <code>typeDefinition</code>, as well as all the properties
584: * declared in its super types.
585: * </p>
586: * TODO: handle the case where the extension mechanism is restriction.
587: *
588: * @param assignedName
589: * @param typeDefinition
590: * @return
591: */
592: private AttributeType createType(final Name assignedName,
593: final XSDTypeDefinition typeDefinition) {
594:
595: AttributeType attType;
596: // /////////
597: if (processingTypes.contains(assignedName)) {
598: if (LOGGER.isLoggable(Level.FINE)) {
599: LOGGER.fine("Recursion found for type " + assignedName
600: + ". Proxying it.");
601: }
602: attType = createProxiedType(assignedName, typeDefinition);
603: return attType;
604: }
605: processingTypes.push(assignedName);
606: // //////////
607:
608: final XSDTypeDefinition baseType = typeDefinition.getBaseType();
609:
610: AttributeType super Type = null;
611: if (baseType != null) {
612: String targetNamespace = baseType.getTargetNamespace();
613: String name = baseType.getName();
614: super Type = getType(targetNamespace, name);
615: if (super Type == null) {
616: super Type = createType(baseType);
617: register(super Type);
618: }
619: } else {
620: LOGGER.warning(assignedName + " has no super type");
621: }
622:
623: // if typeDefinition.getSimpleType() != null it means it is a complex
624: // xsd type
625: // with a simple content model, and has some xml attributes declared,
626: // hence the
627: // xsd complex type definition, as simple xsd types can't have
628: // attributes
629: XSDSimpleTypeDefinition simpleType = typeDefinition
630: .getSimpleType();
631:
632: if (simpleType == null
633: && typeDefinition instanceof XSDComplexTypeDefinition) {
634: XSDComplexTypeDefinition complexTypeDef;
635: complexTypeDef = (XSDComplexTypeDefinition) typeDefinition;
636: boolean includeParents = true;
637: List children;
638: children = Schemas.getChildElementDeclarations(
639: typeDefinition, includeParents);
640:
641: final Collection schema = new ArrayList(children.size());
642:
643: XSDElementDeclaration childDecl;
644: AttributeDescriptor descriptor;
645: for (Iterator it = children.iterator(); it.hasNext();) {
646: childDecl = (XSDElementDeclaration) it.next();
647: try {
648: descriptor = createAttributeDescriptor(
649: complexTypeDef, childDecl);
650: schema.add(descriptor);
651: } catch (NoSuchElementException e) {
652: LOGGER.log(Level.WARNING, e.getMessage());
653: throw e;
654: }
655: }
656:
657: attType = createComplexAttributeType(assignedName, schema,
658: complexTypeDef, super Type);
659:
660: } else {
661: Class binding = String.class;
662: boolean isIdentifiable = false;
663: boolean isAbstract = false;
664: Set restrictions = Collections.EMPTY_SET;
665: InternationalString description = null;
666: attType = typeFactory.createAttributeType(assignedName,
667: binding, isIdentifiable, isAbstract, restrictions,
668: super Type, description);
669: }
670:
671: attType.putUserData(XSDTypeDefinition.class, typeDefinition);
672:
673: processingTypes.pop();
674: return attType;
675: }
676:
677: /**
678: * NOTE: to be called only by {@link #createType(Name, XSDTypeDefinition)}
679: *
680: * @param assignedName
681: * @param schema
682: * @param typeDefinition
683: * @param superType
684: * @return
685: */
686: private AttributeType createComplexAttributeType(
687: final Name assignedName, final Collection schema,
688: final XSDComplexTypeDefinition typeDefinition,
689: final AttributeType super Type) {
690:
691: AttributeType abstractFType = getType(GML.NAMESPACE,
692: GML.AbstractFeatureType.getLocalPart());
693: assert abstractFType != null;
694:
695: boolean isFeatureType = isDerivedFrom(typeDefinition,
696: abstractFType.getName());
697: boolean isSimpleContent = isSimpleContent(schema);
698:
699: boolean isAbstract = false;// TODO
700: Set restrictions = Collections.EMPTY_SET;
701: InternationalString description = null; // TODO
702:
703: AttributeType type;
704: if (isFeatureType) {
705: if (isSimpleContent) {
706: SimpleTypeFactory fac = new SimpleTypeFactoryImpl();
707: // let the factory decide
708: CoordinateReferenceSystem crs = null;
709: // let the factory decide
710: AttributeDescriptor defaultGeometry = null;
711: type = fac.createSimpleFeatureType(assignedName,
712: new ArrayList(schema), defaultGeometry, crs,
713: restrictions, description);
714: } else {
715: type = typeFactory.createFeatureType(assignedName,
716: schema, null, null, isAbstract, restrictions,
717: super Type, description);
718:
719: }
720: } else {
721: boolean isIdentifiable = isIdentifiable((XSDComplexTypeDefinition) typeDefinition);
722: type = typeFactory.createComplexType(assignedName, schema,
723: isIdentifiable, isAbstract, restrictions,
724: super Type, description);
725: }
726: return type;
727: }
728:
729: /**
730: * Determines if elements of the given complex type definition are required
731: * to have an identifier by looking for a child element of
732: * <code>typeDefinition</code> of the form
733: * <code><xs:attribute ref="gml:id" use="required" /></code>
734: *
735: * @param typeDefinition
736: * @return
737: */
738: private boolean isIdentifiable(
739: XSDComplexTypeDefinition typeDefinition) {
740: List attributeUses = typeDefinition.getAttributeUses();
741:
742: final String idAttName = GML.id.getLocalPart();
743:
744: for (Iterator it = attributeUses.iterator(); it.hasNext();) {
745: XSDAttributeUse use = (XSDAttributeUse) it.next();
746: XSDAttributeUseCategory useCategory = use.getUse();
747:
748: XSDAttributeDeclaration idAtt = use
749: .getAttributeDeclaration();
750:
751: String targetNamespace = idAtt.getTargetNamespace();
752: String name = idAtt.getName();
753: if (GML.NAMESPACE.equals(targetNamespace)
754: && idAttName.equals(name)) {
755: if (XSDAttributeUseCategory.REQUIRED_LITERAL
756: .equals(useCategory)) {
757: return true;
758: }
759: }
760: }
761: return false;
762: }
763:
764: /**
765: * Returns true if all the AttributeDescriptors contained in
766: * <code>schema</code> are of a simple type and no one has maxOccurs > 1.
767: * <p>
768: * Note this method ignores the attributes from the GML namespace
769: * </p>
770: *
771: * @param schema
772: * @return
773: */
774: private boolean isSimpleContent(Collection schema) {
775: AttributeDescriptor descriptor;
776: for (Iterator it = schema.iterator(); it.hasNext();) {
777: descriptor = (AttributeDescriptor) it.next();
778: if (GML.NAMESPACE.equals(descriptor.getName()
779: .getNamespaceURI())) {
780: continue;
781: }
782: if (descriptor.getMaxOccurs() > 1) {
783: return false;
784: }
785: if (descriptor.type() instanceof ComplexType) {
786: return false;
787: }
788: }
789: return true;
790: }
791:
792: /**
793: * Returns <code>true</code> if <code>typeDefinition</code> is derived
794: * from a type named <code>superTypeName</code>
795: *
796: * @param typeDefinition
797: * @param superTypeName
798: * @return
799: */
800: private boolean isDerivedFrom(XSDTypeDefinition typeDefinition,
801: final Name super TypeName) {
802:
803: XSDTypeDefinition baseType;
804: final String super NS = super TypeName.getNamespaceURI();
805: final String super Name = super TypeName.getLocalPart();
806:
807: String targetNamespace;
808: String name;
809: while ((baseType = typeDefinition.getBaseType()) != null) {
810: targetNamespace = baseType.getTargetNamespace();
811: name = baseType.getName();
812: if (XS.NAMESPACE.equals(targetNamespace)
813: && XS.ANYTYPE.getLocalPart().equals(name)) {
814: return false;
815: }
816: if (super NS.equals(targetNamespace)
817: && super Name.equals(name)) {
818: return true;
819: }
820: typeDefinition = baseType;
821: }
822: return false;
823: }
824:
825: private AttributeType getType(String namespace, String name) {
826: Name typeName = Types.typeName(namespace, name);
827: return getType(typeName);
828: }
829:
830: private AttributeType getType(Name typeName) {
831: AttributeType type = (AttributeType) typeRegistry.get(typeName);
832: return type;
833: }
834:
835: private void importXsdTypeDefinitions(List typeDefinitions) {
836: XSDTypeDefinition typeDef;
837: AttributeType attType;
838: for (Iterator it = typeDefinitions.iterator(); it.hasNext();) {
839: typeDef = (XSDTypeDefinition) it.next();
840: String targetNamespace = typeDef.getTargetNamespace();
841: String name = typeDef.getName();
842:
843: attType = getType(targetNamespace, name);
844: if (attType == null) {
845: LOGGER.finest("Creating attribute type "
846: + typeDef.getQName());
847: attType = createType(typeDef);
848: LOGGER.finest("Registering attribute type "
849: + attType.getName());
850: register(attType);
851: } else {
852: // LOGGER.finer("Ignoring type " +
853: // typeDef.getQName()
854: // + " as it already exists in the registry");
855: }
856: }
857: LOGGER.finer("--- type definitions imported successfully ---");
858: }
859:
860: private void importSchema(Schema schema) {
861: for (Iterator it = schema.entrySet().iterator(); it.hasNext();) {
862: Map.Entry entry = (Entry) it.next();
863: Name key = (Name) entry.getKey();
864: Object value = entry.getValue();
865: if (typeRegistry.containsKey(key)) {
866: LOGGER.finer("Ignoring " + key
867: + " as it already exists. type "
868: + value.getClass().getName());
869: } else {
870: LOGGER.finer("Importing " + key + " of type "
871: + value.getClass().getName());
872: if (value instanceof AttributeType) {
873: AttributeType type = (AttributeType) value;
874: register(type);
875: } else if (value instanceof AttributeDescriptor) {
876: AttributeDescriptor descriptor = (AttributeDescriptor) value;
877: register(descriptor);
878: } else if (value instanceof org.geotools.feature.AttributeType) {
879: org.geotools.feature.AttributeType gtType;
880: gtType = (org.geotools.feature.AttributeType) value;
881: String nsUri = schema.namespace().getURI();
882: AttributeType isoType = ISOAttributeTypeAdapter
883: .adapter(nsUri, gtType);
884: register(isoType);
885: } else if (value instanceof org.geotools.feature.FeatureType) {
886: org.geotools.feature.FeatureType gtType;
887: gtType = (org.geotools.feature.FeatureType) value;
888: FeatureType isoType = new ISOFeatureTypeAdapter(
889: gtType);
890: register(isoType);
891: }
892: }
893: }
894: LOGGER.fine("Schema " + schema.namespace().getURI()
895: + " imported successfully");
896: }
897:
898: public static EmfAppSchemaReader newInstance() {
899: return new EmfAppSchemaReader();
900: }
901:
902: }
|