001: /*
002: * GeoTools - OpenSource mapping toolkit
003: * http://geotools.org
004: * (C) 2002-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: package org.geotools.maven.xmlcodegen;
017:
018: import org.eclipse.xsd.XSDComplexTypeDefinition;
019: import org.eclipse.xsd.XSDElementDeclaration;
020: import org.eclipse.xsd.XSDFactory;
021: import org.eclipse.xsd.XSDSchema;
022: import org.eclipse.xsd.XSDSimpleTypeDefinition;
023: import org.eclipse.xsd.XSDTypeDefinition;
024: import org.eclipse.xsd.util.XSDConstants;
025: import org.eclipse.xsd.util.XSDUtil;
026: import org.geotools.feature.AttributeType;
027: import org.geotools.feature.AttributeTypeFactory;
028: import org.geotools.feature.type.SchemaImpl;
029: import org.geotools.graph.build.GraphGenerator;
030: import org.geotools.graph.build.basic.BasicDirectedGraphGenerator;
031: import org.geotools.graph.structure.Graph;
032: import org.geotools.graph.structure.Graphable;
033: import org.geotools.graph.traverse.GraphTraversal;
034: import org.geotools.graph.traverse.GraphWalker;
035: import org.geotools.graph.traverse.basic.BasicGraphTraversal;
036: import org.geotools.graph.traverse.standard.DirectedDepthFirstTopologicalIterator;
037: import org.geotools.graph.util.graph.CycleDetector;
038: import org.geotools.graph.util.graph.DirectedCycleDetector;
039: import org.geotools.xml.Schemas;
040: import org.opengis.feature.type.Name;
041: import org.opengis.feature.type.Schema;
042: import org.opengis.feature.type.TypeName;
043: import java.io.IOException;
044: import java.util.ArrayList;
045: import java.util.Collection;
046: import java.util.Collections;
047: import java.util.HashMap;
048: import java.util.HashSet;
049: import java.util.Iterator;
050: import java.util.List;
051: import java.util.Map;
052: import java.util.Map.Entry;
053: import java.util.Set;
054: import java.util.Stack;
055: import java.util.logging.Logger;
056:
057: /**
058: * Parses an XML schema to procuce an instance of
059: * {@link org.opengis.feature.type.Schema}.
060: *
061: * <p>
062: *
063: * </p>
064: *
065: * @author Justin Deoliveira, The Open Planning Project, jdeolive@openplans.org
066: *
067: */
068: public class SchemaGenerator extends AbstractGenerator {
069: /**
070: * The xsd schema from which gt types will be
071: * generated.
072: */
073: XSDSchema schema;
074:
075: /**
076: * Mapping from XSD types to Geotools types.
077: */
078: HashMap /*<XSDTypeDefinition,AttributeType>*/types;
079:
080: /**
081: * Factory used to build geotools types.
082: */
083:
084: //TypeFactory factory;
085: /**
086: * Flag indicating wether simple types should be processed.
087: */
088: boolean simpleTypes;
089:
090: /**
091: * Flag indiciating wether complex types should be processed.
092: */
093: boolean complexTypes;
094:
095: /**
096: * Flag indicating wether to follow type references within
097: * complex type definitions.
098: */
099: boolean followComplexTypes;
100:
101: /**
102: * Mapping of schemas imported by the schema being processed, indexed by
103: * namespace.
104: */
105: HashMap /*<String,Schema>*/imports;
106:
107: /**
108: * Logger
109: */
110: Logger logger = org.geotools.util.logging.Logging
111: .getLogger("org.geotools.xml");
112:
113: public SchemaGenerator(XSDSchema schema /*, TypeFactory factory*/) {
114: this .schema = schema;
115: //this.factory = factory;
116: types = new HashMap();
117: simpleTypes = true;
118: complexTypes = true;
119: followComplexTypes = true;
120: imports = new HashMap();
121: }
122:
123: /**
124: * @return The parsed xml schema.
125: */
126: public XSDSchema getSchema() {
127: return schema;
128: }
129:
130: /**
131: * @param complexTypes Flag indicating wether or not to process complex
132: * types in the supplied schema.
133: */
134: public void setComplexTypes(boolean complexTypes) {
135: this .complexTypes = complexTypes;
136: }
137:
138: /**
139: * @param simpleTypes Flag indicating wether or not to process complex
140: * types in the supplied schema.
141: */
142: public void setSimpleTypes(boolean simpleTypes) {
143: this .simpleTypes = simpleTypes;
144: }
145:
146: /**
147: * Indicates to generator wether to follow the type definitons of
148: * complex types.
149: * <p>
150: * Warning, setting this flag to <code>true</code> will result in all
151: * generated complex types being empty.
152: * </p>
153: */
154: public void setFollowComplexTypes(boolean followComplexTypes) {
155: this .followComplexTypes = followComplexTypes;
156: }
157:
158: /**
159: * Provide an explicit mapping from an XSD type
160: * @param namespace
161: * @param name
162: */
163: public void addTypeMapping(String namespace, String name,
164: AttributeType gtType) {
165: if (namespace == null) {
166: namespace = schema.getTargetNamespace();
167: }
168: assert name != null;
169:
170: //find the type in the xsd schema
171: List typeDefs = schema.getTypeDefinitions();
172:
173: for (Iterator itr = typeDefs.iterator(); itr.hasNext();) {
174: XSDTypeDefinition xsdType = (XSDTypeDefinition) itr.next();
175: String tns = xsdType.getTargetNamespace();
176: String tn = xsdType.getName();
177:
178: if (namespace.equals(tns) && name.equals(tn)) {
179: types.put(xsdType, gtType);
180:
181: return;
182: }
183: }
184:
185: throw new IllegalArgumentException("Type: [" + namespace + ","
186: + name + "] not found");
187: }
188:
189: /**
190: * Adds an imported schema to be used for type lookups.
191: */
192: public void addImport(Schema imported) {
193: imports.put(imported.namespace().getURI(), imported);
194: }
195:
196: /**
197: * Returns an imported schema for a particular namespace.
198: *
199: * @return The imported schema, or null if non exists.
200: */
201: public Schema getImport(String namespace) {
202: return (Schema) imports.get(namespace);
203: }
204:
205: /**
206: * @return The collection of schemas imported by the schema being generated.
207: */
208: public Collection getImports() {
209: return imports.values();
210: }
211:
212: /**
213: * @param type Geotools attribute type.
214: *
215: * @return the XSD type associated with <code>type</code>.
216: */
217: public XSDTypeDefinition getXSDType(AttributeType type) {
218: for (Iterator itr = types.entrySet().iterator(); itr.hasNext();) {
219: Map.Entry entry = (Entry) itr.next();
220: XSDTypeDefinition xsdType = (XSDTypeDefinition) entry
221: .getKey();
222: AttributeType gtType = (AttributeType) entry.getValue();
223:
224: if (gtType.equals(type)) {
225: return xsdType;
226: }
227: }
228:
229: return null;
230: }
231:
232: /**
233: * Generates the Geotools schema from the XML schema.
234: */
235: public void generate() throws Exception {
236: List typeDefs = GeneratorUtils.allTypes(schema);
237:
238: //process simple types
239: if (simpleTypes) {
240: logger.info("Generting simple types");
241: for (Iterator itr = typeDefs.iterator(); itr.hasNext();) {
242: XSDTypeDefinition xsdType = (XSDTypeDefinition) itr
243: .next();
244:
245: if (xsdType.getName() == null) {
246: continue;
247: }
248:
249: if (!xsdType.getTargetNamespace().equals(
250: schema.getTargetNamespace())) {
251: continue;
252: }
253:
254: if (xsdType instanceof XSDSimpleTypeDefinition) {
255: logger.info(xsdType.getName());
256: createType((XSDSimpleTypeDefinition) xsdType);
257: }
258: }
259: }
260:
261: //process complex types
262: if (complexTypes) {
263: logger.info("Generting complex types");
264: for (Iterator itr = typeDefs.iterator(); itr.hasNext();) {
265: XSDTypeDefinition xsdType = (XSDTypeDefinition) itr
266: .next();
267:
268: if (xsdType.getName() == null) {
269: continue;
270: }
271:
272: if (!xsdType.getTargetNamespace().equals(
273: schema.getTargetNamespace())) {
274: continue;
275: }
276:
277: if (xsdType instanceof XSDComplexTypeDefinition) {
278: logger.info(xsdType.getName());
279: createType((XSDComplexTypeDefinition) xsdType);
280: }
281: }
282: }
283:
284: Schema gtSchema = new SchemaImpl(schema.getTargetNamespace());
285:
286: for (Iterator itr = types.values().iterator(); itr.hasNext();) {
287: AttributeType gtType = (AttributeType) itr.next();
288: gtSchema.put(new org.geotools.feature.Name(schema
289: .getTargetNamespace(), gtType.getName()), gtType);
290: }
291:
292: Object[] input = new Object[] { gtSchema,
293: Schemas.getTargetPrefix(schema), this };
294: String result = execute("SchemaClassTemplate", input);
295: String className = Schemas.getTargetPrefix(schema)
296: .toUpperCase()
297: + "Schema";
298:
299: write(result, className);
300: }
301:
302: /**
303: * Returns a list of the types in the generated schema sorted
304: * as follows:
305: * <p>
306: * <ul>
307: * <li>If A is a super type of B, then A appears in list before B.
308: * <li>If B is complex and A is referenced from the type definition
309: * of B, then A appears in the list before B.
310: * </ul>
311: * </p>
312: */
313:
314: // public List sort() {
315: // //build a directed graph representing dependencies among types
316: // GraphGenerator gg = new BasicDirectedGraphGenerator();
317: //
318: // for (Iterator itr = types.values().iterator(); itr.hasNext();) {
319: // AttributeType type = (AttributeType) itr.next();
320: // AttributeType superType = type.getSuper();
321: //
322: // if (superType != null) {
323: // //add edge type -> parent
324: // gg.add(new Object[]{type,superType});
325: // }
326: //
327: // if (type instanceof ComplexType) {
328: // ComplexType cType = (ComplexType) type;
329: //
330: // //add an edge for each descriptor
331: // Collection atts = cType.attributes();
332: // for (Iterator aitr = atts.iterator(); aitr.hasNext();) {
333: // AttributeDescriptor ad = (AttributeDescriptor) aitr.next();
334: // gg.add(new Object[]{type,ad.getType()});
335: // }
336: // }
337: // }
338: //
339: // Graph graph = gg.getGraph();
340: //
341: // //test the graph for cycles
342: // CycleDetector cycleDetector = new DirectedCycleDetector(graph);
343: // if (cycleDetector.containsCycle()) {
344: // logger.info("Cycle found");
345: // return null;
346: // }
347: //
348: //
349: // //no cycles, perform a topological sorting of the graph
350: // DirectedDepthFirstTopologicalIterator iterator =
351: // new DirectedDepthFirstTopologicalIterator();
352: //
353: // final ArrayList sorted = new ArrayList();
354: // GraphWalker walker = new GraphWalker() {
355: //
356: // public int visit(Graphable element, GraphTraversal traversal) {
357: // AttributeType type = (AttributeType) element.getObject();
358: //
359: // //only add if in this schema
360: // if (type.getName().getNamespaceURI().equals(schema.getTargetNamespace())) {
361: // sorted.add(element.getObject());
362: // }
363: //
364: // return GraphTraversal.CONTINUE;
365: // }
366: //
367: // public void finish() { }
368: // };
369: //
370: // GraphTraversal traversal =
371: // new BasicGraphTraversal(graph,walker,iterator);
372: // traversal.init();
373: // traversal.traverse();
374: //
375: // assert sorted.size() == types.size();
376: // Collections.reverse(sorted);
377: //
378: // return sorted;
379: // }
380: // private AttributeType createType(XSDTypeDefinition xsdType) {
381: // if (xsdType instanceof XSDSimpleTypeDefinition) {
382: // return createType((XSDSimpleTypeDefinition)xsdType);
383: // }
384: // else {
385: // return createType((XSDComplexTypeDefinition)xsdType);
386: // }
387: // }
388: private AttributeType createType( /*XSDSimpleTypeDefinition*/
389: XSDTypeDefinition xsdType) {
390: if (types.containsKey(xsdType)) {
391: return (AttributeType) types.get(xsdType);
392: }
393:
394: //import?
395: if (!xsdType.getTargetNamespace().equals(
396: schema.getTargetNamespace())) {
397: return (AttributeType) findType(xsdType);
398: }
399:
400: //first build super type
401: // AttributeType superType = null;
402: // XSDTypeDefinition baseType = xsdType.getBaseType();
403: //
404: // if ((baseType != null) && !baseType.equals(xsdType)) {
405: // if (baseType.getName() != null) {
406: // //ignore unamed types
407: // //superType = createType((XSDSimpleTypeDefinition)baseType);
408: // superType = createType(baseType);
409: // assert superType != null;
410: // }
411: // }
412:
413: //TODO: actually derive valus from type
414: // AttributeType gtType = factory.createAttributeType(
415: // name(xsdType), Object.class, false, false, Collections.EMPTY_SET,
416: // superType, null
417: // );
418: AttributeType gtType = AttributeTypeFactory.newAttributeType(
419: xsdType.getName(), Object.class);
420: types.put(xsdType, gtType);
421:
422: return gtType;
423: }
424:
425: // private AttributeType createType(XSDComplexTypeDefinition xsdType) {
426: // //already processed?
427: // if (types.containsKey(xsdType)) {
428: // return (AttributeType) types.get(xsdType);
429: // }
430: //
431: // //import?
432: // if (!xsdType.getTargetNamespace().equals(schema.getTargetNamespace())) {
433: // return findType(xsdType);
434: // }
435: //
436: // //first build super type
437: // AttributeType/*ComplexType*/ superType = null;
438: // XSDTypeDefinition baseType = xsdType.getBaseType();
439: // if (baseType != null && !baseType.equals(xsdType)) {
440: // if (baseType.getName() != null) {
441: // //ignore unamed types
442: // superType = createType(/*(XSDComplexTypeDefinition)*/baseType);
443: // assert superType != null;
444: // }
445: // }
446: //
447: // // now build child types
448: // ArrayList properties = new ArrayList();
449: // if (followComplexTypes) {
450: // List children = Schemas.getChildElementDeclarations(xsdType);
451: // for (Iterator itr = children.iterator(); itr.hasNext();) {
452: // XSDElementDeclaration element =
453: // (XSDElementDeclaration) itr.next();
454: // XSDTypeDefinition childType = element.getTypeDefinition();
455: //
456: // AttributeType gtType = createType(childType);
457: // assert gtType != null;
458: //
459: // String uri = element.getTargetNamespace();
460: // String name = element.getName();
461: //
462: // //TODO: minOccurs
463: // //TODO: maxOccurs
464: // //TODO: nillable
465: // AttributeDescriptor ad = factory.createAttributeDescriptor(
466: // gtType, new org.geotools.util.Name(uri,name),1,1,false
467: // );
468: // properties.add(ad);
469: // }
470: // }
471: //
472: //
473: // //TODO: isIdentifiable
474: // //TODO: restrictions
475: // //TODO: description
476: // ComplexType gtType = factory.createComplexType(
477: // name(xsdType), properties, false, xsdType.isAbstract(),
478: // Collections.EMPTY_SET, superType, null
479: // );
480: // types.put(xsdType,gtType);
481: // return gtType;
482: // }
483: private AttributeType findType(XSDTypeDefinition xsdType) {
484: Name name = name(xsdType);
485:
486: if (imports != null) {
487: for (Iterator itr = imports.values().iterator(); itr
488: .hasNext();) {
489: Schema imported = (Schema) itr.next();
490:
491: if (imported.containsKey(name)) {
492: return (AttributeType) imported.get(name);
493: }
494: }
495: }
496:
497: throw new IllegalStateException(
498: "Could not find imported type: " + name);
499: }
500:
501: /**
502: * Convenience method for gettign the name of a type.
503: */
504: private Name name(XSDTypeDefinition type) {
505: return new org.geotools.feature.Name(type.getTargetNamespace(),
506: type.getName());
507: }
508:
509: public static void main(String[] args) throws Exception {
510: XSDSchema schema = XSDUtil
511: .getSchemaForSchema(XSDConstants.SCHEMA_FOR_SCHEMA_URI_2001);
512:
513: SchemaGenerator generator = new SchemaGenerator(schema);
514: generator.setComplexTypes(false);
515: generator.setFollowComplexTypes(false);
516: generator.setSimpleTypes(true);
517:
518: generator.generate();
519: }
520: }
|