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.gml2.bindings;
017:
018: import org.eclipse.xsd.XSDElementDeclaration;
019: import org.eclipse.xsd.XSDParticle;
020: import java.net.URI;
021: import java.util.ArrayList;
022: import java.util.Collection;
023: import java.util.Iterator;
024: import java.util.List;
025: import com.vividsolutions.jts.geom.Envelope;
026: import com.vividsolutions.jts.geom.GeometryCollection;
027: import org.opengis.referencing.FactoryException;
028: import org.opengis.referencing.NoSuchAuthorityCodeException;
029: import org.opengis.referencing.crs.CoordinateReferenceSystem;
030: import org.geotools.feature.AttributeType;
031: import org.geotools.feature.AttributeTypeFactory;
032: import org.geotools.feature.DefaultFeatureTypeFactory;
033: import org.geotools.feature.Feature;
034: import org.geotools.feature.FeatureType;
035: import org.geotools.feature.FeatureTypeBuilder;
036: import org.geotools.feature.GeometryAttributeType;
037: import org.geotools.gml2.FeatureTypeCache;
038: import org.geotools.referencing.CRS;
039: import org.geotools.util.Converters;
040: import org.geotools.xml.Binding;
041: import org.geotools.xml.BindingWalkerFactory;
042: import org.geotools.xml.ElementInstance;
043: import org.geotools.xml.Node;
044: import org.geotools.xml.Schemas;
045: import org.geotools.xml.impl.BindingWalker;
046:
047: /**
048: * Utility methods used by gml2 bindings when parsing.
049: *
050: * @author Justin Deoliveira, The Open Planning Project, jdeolive@openplans.org
051: *
052: */
053: public class GML2ParsingUtils {
054: /**
055: * Utility method to implement Binding.parse for a binding which parses
056: * into A feature.
057: *
058: * @param instance The instance being parsed.
059: * @param node The parse tree.
060: * @param value The value from the last binding in the chain.
061: * @param ftCache The feature type cache.
062: * @param bwFactory Binding walker factory.
063: *
064: * @return A feature type.
065: */
066: public static Feature parseFeature(ElementInstance instance,
067: Node node, Object value, FeatureTypeCache ftCache,
068: BindingWalkerFactory bwFactory) throws Exception {
069: //get the definition of the element
070: XSDElementDeclaration decl = instance.getElementDeclaration();
071:
072: //special case, if the declaration is abstract it is probably "_Feautre"
073: // which means we are parsing an elemetn which could not be found in the
074: // schema, so instaed of using the element declaration to build the
075: // type, just use the node given to us
076: FeatureType fType = null;
077:
078: if (!decl.isAbstract()) {
079: //first look in cache
080: fType = ftCache.get(decl.getTargetNamespace(), decl
081: .getName());
082:
083: if (fType == null) {
084: //build from element declaration
085: fType = GML2ParsingUtils.featureType(decl, bwFactory);
086: ftCache.put(fType);
087: }
088: } else {
089: // first look in cache
090: fType = ftCache.get(node.getComponent().getNamespace(),
091: node.getComponent().getName());
092:
093: if (fType == null) {
094: //build from node
095: fType = GML2ParsingUtils.featureType(node);
096: ftCache.put(fType);
097: }
098: }
099:
100: //fid
101: String fid = (String) node.getAttributeValue("fid");
102:
103: if (fid == null) {
104: //look for id
105: fid = (String) node.getAttributeValue("id");
106: }
107:
108: //create feature
109: return GML2ParsingUtils.feature(fType, fid, node);
110: }
111:
112: /**
113: * Turns a parse node instance into a geotools feature type.
114: * <p>
115: * For each child element and attribute of the node a geotools attribute
116: * type is created. AttributeType#getName() is derived from the name of
117: * the child element / attribute. Attribute#getType() is derived from the
118: * class of the value of the child element / attribute.
119: * </p>
120: * <p>
121: * Attribute types for the mandatory properties of any gml feature type
122: * (description,name,boundedBy) are also created.
123: * </p>
124: * @param node The parse node / tree for the feature.
125: *
126: * @return A geotools feature type
127: */
128: public static FeatureType featureType(Node node) throws Exception {
129: FeatureTypeBuilder ftBuilder = new DefaultFeatureTypeFactory();
130: ftBuilder.setName(node.getComponent().getName());
131: ftBuilder.setNamespace(new URI(node.getComponent()
132: .getNamespace()));
133:
134: //mandatory gml attributes
135: if (!node.hasChild("description")) {
136: ftBuilder.addType(AttributeTypeFactory.newAttributeType(
137: "description", String.class));
138: }
139:
140: if (!node.hasChild("name")) {
141: ftBuilder.addType(AttributeTypeFactory.newAttributeType(
142: "name", String.class));
143: }
144:
145: if (!node.hasChild("boundedBy")) {
146: ftBuilder.addType(AttributeTypeFactory.newAttributeType(
147: "boundedBy", Envelope.class));
148: }
149:
150: //application schema defined attributes
151: for (Iterator c = node.getChildren().iterator(); c.hasNext();) {
152: Node child = (Node) c.next();
153: String name = child.getComponent().getName();
154: Object valu = child.getValue();
155:
156: ftBuilder.addType(AttributeTypeFactory.newAttributeType(
157: name, (valu != null) ? valu.getClass()
158: : Object.class));
159: }
160:
161: return ftBuilder.getFeatureType();
162: }
163:
164: /**
165: * Turns a xml type definition into a geotools feature type.
166: *
167: * @param type
168: * The xml schema tupe.
169: *
170: * @return The corresponding geotools feature type.
171: */
172: public static FeatureType featureType(
173: XSDElementDeclaration element,
174: BindingWalkerFactory bwFactory) throws Exception {
175: FeatureTypeBuilder ftBuilder = new DefaultFeatureTypeFactory();
176: ftBuilder.setName(element.getName());
177: ftBuilder.setNamespace(new URI(element.getTargetNamespace()));
178:
179: // build the feaure type by walking through the elements of the
180: // actual xml schema type
181: List children = Schemas.getChildElementParticles(element
182: .getType(), true);
183:
184: for (Iterator itr = children.iterator(); itr.hasNext();) {
185: XSDParticle particle = (XSDParticle) itr.next();
186: XSDElementDeclaration property = (XSDElementDeclaration) particle
187: .getContent();
188:
189: if (property.isElementDeclarationReference()) {
190: property = property.getResolvedElementDeclaration();
191: }
192:
193: final ArrayList bindings = new ArrayList();
194: BindingWalker.Visitor visitor = new BindingWalker.Visitor() {
195: public void visit(Binding binding) {
196: bindings.add(binding);
197: }
198: };
199:
200: bwFactory.walk(property, visitor);
201:
202: if (bindings.isEmpty()) {
203: // could not find a binding, use the defaults
204: throw new RuntimeException(
205: "Could not find binding for "
206: + property.getQName());
207: }
208:
209: // get hte last binding in the chain to execute
210: Binding last = ((Binding) bindings.get(bindings.size() - 1));
211: Class theClass = last.getType();
212:
213: if (theClass == null) {
214: throw new RuntimeException(
215: "binding declares null type: "
216: + last.getTarget());
217: }
218:
219: // get the attribute properties
220: int min = particle.getMinOccurs();
221: int max = particle.getMaxOccurs();
222:
223: // create the type
224: AttributeType type = AttributeTypeFactory.newAttributeType(
225: property.getName(), theClass, true, null, null,
226: null, min, max);
227: ftBuilder.addType(type);
228:
229: //do a check for default geometry
230: if (type instanceof GeometryAttributeType) {
231: //only set if non-gml, we do this because of "gml:location",
232: // we dont want that to be the default if the user has another
233: // geometry attribute
234: if (!GML.NAMESPACE
235: .equals(property.getTargetNamespace())) {
236: ftBuilder
237: .setDefaultGeometry((GeometryAttributeType) type);
238: }
239: }
240: }
241:
242: return ftBuilder.getFeatureType();
243: }
244:
245: public static Feature feature(FeatureType fType, String fid,
246: Node node) throws Exception {
247: Object[] attributes = new Object[fType.getAttributeCount()];
248:
249: for (int i = 0; i < fType.getAttributeCount(); i++) {
250: AttributeType attType = fType.getAttributeType(i);
251: Object attValue = node.getChildValue(attType.getName());
252:
253: if ((attValue != null)
254: && !attType.getType().isAssignableFrom(
255: attValue.getClass())) {
256: //type mismatch, to try convert
257: Object converted = Converters.convert(attValue, attType
258: .getType());
259:
260: if (converted != null) {
261: attValue = converted;
262: }
263: }
264:
265: attributes[i] = attValue;
266: }
267:
268: //create the feature
269: return fType.create(attributes, fid);
270: }
271:
272: public static CoordinateReferenceSystem crs(Node node) {
273: if (node.getAttribute("srsName") != null) {
274: Object o = node.getAttributeValue("srsName");
275: String srs = null;
276:
277: if (o instanceof URI) {
278: URI uri = (URI) node.getAttributeValue("srsName");
279:
280: //TODO: JD, this is a hack until GEOT-1136 has been resolved
281: if ("http".equals(uri.getScheme())
282: && "www.opengis.net".equals(uri.getAuthority())
283: && "/gml/srs/epsg.xml".equals(uri.getPath())
284: && (uri.getFragment() != null)) {
285: try {
286: return CRS.decode("EPSG:" + uri.getFragment());
287: } catch (Exception e) {
288: //no nothing, will fail belows
289: }
290: }
291:
292: srs = uri.toString();
293: } else {
294: srs = o.toString();
295: }
296:
297: try {
298: return CRS.decode(srs.toString());
299: } catch (Exception e) {
300: throw new RuntimeException("Could not create crs: "
301: + srs, e);
302: }
303: }
304:
305: return null;
306: }
307:
308: /**
309: * Wraps the elements of a geometry collection in a normal collection.
310: */
311: public static Collection asCollection(GeometryCollection gc) {
312: ArrayList members = new ArrayList();
313:
314: for (int i = 0; i < gc.getNumGeometries(); i++) {
315: members.add(gc.getGeometryN(i));
316: }
317:
318: return members;
319: }
320: }
|