001: /* Copyright (c) 2001 - 2007 TOPP - www.openplans.org. All rights reserved.
002: * This code is licensed under the GPL 2.0 license, availible at the root
003: * application directory.
004: */
005: package org.geoserver.wfs.xml;
006:
007: import org.eclipse.xsd.XSDComplexTypeDefinition;
008: import org.eclipse.xsd.XSDCompositor;
009: import org.eclipse.xsd.XSDDerivationMethod;
010: import org.eclipse.xsd.XSDElementDeclaration;
011: import org.eclipse.xsd.XSDFactory;
012: import org.eclipse.xsd.XSDForm;
013: import org.eclipse.xsd.XSDImport;
014: import org.eclipse.xsd.XSDModelGroup;
015: import org.eclipse.xsd.XSDParticle;
016: import org.eclipse.xsd.XSDSchema;
017: import org.eclipse.xsd.XSDSchemaContent;
018: import org.eclipse.xsd.XSDTypeDefinition;
019: import org.eclipse.xsd.util.XSDConstants;
020: import org.geoserver.ows.util.RequestUtils;
021: import org.geoserver.ows.util.ResponseUtils;
022: import org.geoserver.platform.GeoServerResourceLoader;
023: import org.geoserver.wfs.WFS;
024: import org.geotools.feature.AttributeType;
025: import org.geotools.feature.FeatureType;
026: import org.geotools.gml2.GMLConfiguration;
027: import org.geotools.xml.Configuration;
028: import org.geotools.xml.Schemas;
029: import org.opengis.feature.type.Name;
030: import org.vfny.geoserver.global.Data;
031: import org.vfny.geoserver.global.FeatureTypeInfo;
032: import java.io.File;
033: import java.io.IOException;
034: import java.util.ArrayList;
035: import java.util.HashMap;
036: import java.util.Iterator;
037: import java.util.List;
038: import java.util.Map;
039: import java.util.logging.Level;
040: import java.util.logging.Logger;
041:
042: /**
043: * Builds a {@link org.eclipse.xsd.XSDSchema} from {@link FeatureTypeInfo}
044: * metadata objects.
045: * <p>
046: *
047: * </p>
048: *
049: * @author Justin Deoliveira, The Open Planning Project
050: *
051: */
052: public abstract class FeatureTypeSchemaBuilder {
053: /** logging instance */
054: static Logger logger = org.geotools.util.logging.Logging
055: .getLogger("org.geoserver.wfs");
056:
057: /** wfs configuration */
058: WFS wfs;
059:
060: /** the catalog */
061: Data catalog;
062:
063: /** resource loader */
064: GeoServerResourceLoader resourceLoader;
065:
066: /**
067: * profiles used for type mapping.
068: */
069: protected List profiles;
070:
071: /**
072: * gml schema stuff
073: */
074: protected String gmlNamespace;
075: protected String gmlSchemaLocation;
076: protected String baseType;
077: protected String substitutionGroup;
078: protected String describeFeatureTypeBase;
079: protected String gmlPrefix;
080: protected Configuration xmlConfiguration;
081:
082: protected FeatureTypeSchemaBuilder(WFS wfs, Data catalog,
083: GeoServerResourceLoader resourceLoader) {
084: this .wfs = wfs;
085: this .catalog = catalog;
086: this .resourceLoader = resourceLoader;
087:
088: profiles = new ArrayList();
089: profiles.add(new XSProfile());
090: }
091:
092: public XSDSchema build(FeatureTypeInfo featureTypeInfo,
093: String baseUrl) throws IOException {
094: return build(new FeatureTypeInfo[] { featureTypeInfo }, baseUrl);
095: }
096:
097: public XSDSchema build(FeatureTypeInfo[] featureTypeInfos,
098: String baseUrl) throws IOException {
099: XSDFactory factory = XSDFactory.eINSTANCE;
100: XSDSchema schema = factory.createXSDSchema();
101: schema.setSchemaForSchemaQNamePrefix("xsd");
102: schema.getQNamePrefixToNamespaceMap().put("xsd",
103: XSDConstants.SCHEMA_FOR_SCHEMA_URI_2001);
104: schema.setElementFormDefault(XSDForm.get(XSDForm.QUALIFIED));
105:
106: //group the feature types by namespace
107: HashMap ns2featureTypeInfos = new HashMap();
108:
109: for (int i = 0; i < featureTypeInfos.length; i++) {
110: String prefix = featureTypeInfos[i].getNameSpace()
111: .getPrefix();
112: List l = (List) ns2featureTypeInfos.get(prefix);
113:
114: if (l == null) {
115: l = new ArrayList();
116: }
117:
118: l.add(featureTypeInfos[i]);
119:
120: ns2featureTypeInfos.put(prefix, l);
121: }
122:
123: if (baseUrl == null)
124: baseUrl = wfs.getSchemaBaseURL();
125:
126: if (!baseUrl.endsWith("wfs") && !baseUrl.endsWith("wfs?")) {
127: baseUrl = ResponseUtils.appendPath(baseUrl, "wfs");
128: }
129:
130: if (ns2featureTypeInfos.entrySet().size() == 1) {
131: //import gml schema
132: XSDImport imprt = factory.createXSDImport();
133: imprt.setNamespace(gmlNamespace);
134:
135: imprt.setSchemaLocation(ResponseUtils.appendPath(baseUrl,
136: "schemas/" + gmlSchemaLocation));
137:
138: XSDSchema gmlSchema = gmlSchema();
139: imprt.setResolvedSchema(gmlSchema);
140:
141: schema.getContents().add(imprt);
142:
143: String targetPrefix = (String) ns2featureTypeInfos.keySet()
144: .iterator().next();
145: String targetNamespace = catalog.getNameSpace(targetPrefix)
146: .getURI();
147:
148: schema.setTargetNamespace(targetNamespace);
149: //schema.getQNamePrefixToNamespaceMap().put( null, targetNamespace );
150: schema.getQNamePrefixToNamespaceMap().put(targetPrefix,
151: targetNamespace);
152: schema.getQNamePrefixToNamespaceMap().put(gmlPrefix,
153: gmlNamespace);
154: schema.getQNamePrefixToNamespaceMap().put("gml",
155: "http://www.opengis.net/gml");
156:
157: //all types in same namespace, write out the schema
158: for (int i = 0; i < featureTypeInfos.length; i++) {
159: buildSchemaContent(featureTypeInfos[i], schema, factory);
160: }
161: } else {
162: //different namespaces, write out import statements
163: for (Iterator i = ns2featureTypeInfos.entrySet().iterator(); i
164: .hasNext();) {
165: Map.Entry entry = (Map.Entry) i.next();
166: String prefix = (String) entry.getKey();
167: List types = (List) entry.getValue();
168:
169: StringBuffer queryString = new StringBuffer(
170: describeFeatureTypeBase);
171: queryString.append("&typeName=");
172:
173: for (Iterator t = types.iterator(); t.hasNext();) {
174: FeatureTypeInfo type = (FeatureTypeInfo) t.next();
175: queryString.append(type.getName());
176:
177: if (t.hasNext()) {
178: queryString.append(",");
179: }
180: }
181:
182: String schemaLocation = ResponseUtils
183: .appendQueryString(baseUrl, queryString
184: .toString());
185: String namespace = catalog.getNameSpace(prefix)
186: .getURI();
187:
188: XSDImport imprt = factory.createXSDImport();
189: imprt.setNamespace(namespace);
190: imprt.setSchemaLocation(schemaLocation);
191:
192: schema.getContents().add(imprt);
193: }
194: }
195:
196: return schema;
197: }
198:
199: void buildSchemaContent(FeatureTypeInfo featureTypeMeta,
200: XSDSchema schema, XSDFactory factory) throws IOException {
201: //look if the schema for the type is already defined
202: String prefix = featureTypeMeta.getNameSpace().getPrefix();
203: String name = featureTypeMeta.getNativeTypeName();
204:
205: File schemaFile = null;
206:
207: try {
208: schemaFile = resourceLoader.find("featureTypes/" + prefix
209: + "_" + name + "/schema.xsd");
210: } catch (IOException e1) {
211: }
212:
213: if (schemaFile != null) {
214: //schema file found, parse it and lookup the complex type
215: List resolvers = Schemas
216: .findSchemaLocationResolvers(xmlConfiguration);
217: XSDSchema ftSchema = null;
218:
219: try {
220: ftSchema = Schemas.parse(schemaFile.getAbsolutePath(),
221: null, resolvers);
222: } catch (IOException e) {
223: logger.log(Level.WARNING, "Unable to parse schema: "
224: + schemaFile.getAbsolutePath(), e);
225: }
226:
227: if (ftSchema != null) {
228: //add the contents of this schema to the schema being built
229: //look up the complex type
230: List contents = ftSchema.getContents();
231:
232: for (Iterator i = contents.iterator(); i.hasNext();) {
233: XSDSchemaContent content = (XSDSchemaContent) i
234: .next();
235: content.setElement(null);
236: }
237:
238: schema.getContents().addAll(contents);
239: schema.updateElement();
240:
241: return;
242: }
243: }
244:
245: //build the type manually
246: FeatureType featureType = featureTypeMeta.getFeatureType();
247: XSDComplexTypeDefinition complexType = factory
248: .createXSDComplexTypeDefinition();
249: complexType.setName(featureType.getTypeName() + "Type");
250:
251: complexType
252: .setDerivationMethod(XSDDerivationMethod.EXTENSION_LITERAL);
253: complexType.setBaseTypeDefinition(schema
254: .resolveComplexTypeDefinition(gmlNamespace, baseType));
255:
256: XSDModelGroup group = factory.createXSDModelGroup();
257: group.setCompositor(XSDCompositor.SEQUENCE_LITERAL);
258:
259: AttributeType[] attributes = featureType.getAttributeTypes();
260:
261: for (int i = 0; i < attributes.length; i++) {
262: AttributeType attribute = attributes[i];
263:
264: if (filterAttributeType(attribute)) {
265: continue;
266: }
267:
268: XSDElementDeclaration element = factory
269: .createXSDElementDeclaration();
270: element.setName(attribute.getName());
271: element.setNillable(attribute.isNillable());
272:
273: Class binding = attribute.getType();
274: Name typeName = findTypeName(binding);
275:
276: if (typeName == null) {
277: throw new NullPointerException(
278: "Could not find a type for property: "
279: + attribute.getName() + " of type: "
280: + binding.getName());
281: }
282:
283: XSDTypeDefinition type = schema
284: .resolveTypeDefinition(typeName.getNamespaceURI(),
285: typeName.getLocalPart());
286: element.setTypeDefinition(type);
287:
288: XSDParticle particle = factory.createXSDParticle();
289: particle.setMinOccurs(attribute.getMinOccurs());
290: particle.setMaxOccurs(attribute.getMaxOccurs());
291: particle.setContent(element);
292: group.getContents().add(particle);
293: }
294:
295: XSDParticle particle = factory.createXSDParticle();
296: particle.setContent(group);
297:
298: complexType.setContent(particle);
299:
300: schema.getContents().add(complexType);
301:
302: XSDElementDeclaration element = factory
303: .createXSDElementDeclaration();
304: element.setName(name);
305:
306: element.setSubstitutionGroupAffiliation(schema
307: .resolveElementDeclaration(gmlNamespace,
308: substitutionGroup));
309: element.setTypeDefinition(complexType);
310:
311: schema.getContents().add(element);
312: schema.updateElement();
313:
314: schema.updateElement();
315: }
316:
317: Name findTypeName(Class binding) {
318: for (Iterator p = profiles.iterator(); p.hasNext();) {
319: TypeMappingProfile profile = (TypeMappingProfile) p.next();
320: Name name = profile.name(binding);
321:
322: if (name != null) {
323: return name;
324: }
325: }
326:
327: return null;
328: }
329:
330: protected abstract XSDSchema gmlSchema();
331:
332: protected boolean filterAttributeType(AttributeType attribute) {
333: return "name".equals(attribute.getName())
334: || "description".equals(attribute.getName())
335: || "boundedBy".equals(attribute.getName());
336: }
337:
338: public static final class GML2 extends FeatureTypeSchemaBuilder {
339: /**
340: * Cached gml2 schema
341: */
342: private static XSDSchema gml2Schema;
343:
344: public GML2(WFS wfs, Data catalog,
345: GeoServerResourceLoader resourceLoader) {
346: super (wfs, catalog, resourceLoader);
347:
348: profiles.add(new GML2Profile());
349: gmlNamespace = org.geotools.gml2.bindings.GML.NAMESPACE;
350: gmlSchemaLocation = "gml/2.1.2/feature.xsd";
351: baseType = "AbstractFeatureType";
352: substitutionGroup = "_Feature";
353: describeFeatureTypeBase = "request=DescribeFeatureType&version=1.1.0";
354: gmlPrefix = "gml";
355: xmlConfiguration = new GMLConfiguration();
356: }
357:
358: protected XSDSchema gmlSchema() {
359: if (gml2Schema == null) {
360: gml2Schema = xmlConfiguration.schema();
361: }
362:
363: return gml2Schema;
364: }
365: }
366:
367: public static final class GML3 extends FeatureTypeSchemaBuilder {
368: /**
369: * Cached gml3 schema
370: */
371: private static XSDSchema gml3Schema;
372:
373: public GML3(WFS wfs, Data catalog,
374: GeoServerResourceLoader resourceLoader) {
375: super (wfs, catalog, resourceLoader);
376:
377: profiles.add(new GML3Profile());
378:
379: gmlNamespace = org.geotools.gml3.bindings.GML.NAMESPACE;
380: gmlSchemaLocation = "gml/3.1.1/base/gml.xsd";
381: baseType = "AbstractFeatureType";
382: substitutionGroup = "_Feature";
383: describeFeatureTypeBase = "request=DescribeFeatureType&version=1.1.0";
384: gmlPrefix = "gml";
385: xmlConfiguration = new org.geotools.gml3.GMLConfiguration();
386: }
387:
388: protected XSDSchema gmlSchema() {
389: if (gml3Schema == null) {
390: gml3Schema = xmlConfiguration.schema();
391: }
392:
393: return gml3Schema;
394: }
395:
396: protected boolean filterAttributeType(AttributeType attribute) {
397: return super .filterAttributeType(attribute)
398: || "metaDataProperty".equals(attribute.getName())
399: || "location".equals(attribute.getName());
400: }
401: }
402: }
|