001: //$HeadURL: https://svn.wald.intevation.org/svn/deegree/base/trunk/src/org/deegree/model/feature/GMLFeatureCollectionDocument.java $
002: /*---------------- FILE HEADER ------------------------------------------
003:
004: This file is part of deegree.
005: Copyright (C) 2001-2008 by:
006: EXSE, Department of Geography, University of Bonn
007: http://www.giub.uni-bonn.de/deegree/
008: lat/lon GmbH
009: http://www.lat-lon.de
010:
011: This library is free software; you can redistribute it and/or
012: modify it under the terms of the GNU Lesser General Public
013: License as published by the Free Software Foundation; either
014: version 2.1 of the License, or (at your option) any later version.
015:
016: This library is distributed in the hope that it will be useful,
017: but WITHOUT ANY WARRANTY; without even the implied warranty of
018: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
019: Lesser General Public License for more details.
020:
021: You should have received a copy of the GNU Lesser General Public
022: License along with this library; if not, write to the Free Software
023: Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
024:
025: Contact:
026:
027: Andreas Poth
028: lat/lon GmbH
029: Aennchenstraße 19
030: 53177 Bonn
031: Germany
032: E-Mail: poth@lat-lon.de
033:
034: Prof. Dr. Klaus Greve
035: Department of Geography
036: University of Bonn
037: Meckenheimer Allee 166
038: 53115 Bonn
039: Germany
040: E-Mail: greve@giub.uni-bonn.de
041:
042: ---------------------------------------------------------------------------*/
043: package org.deegree.model.feature;
044:
045: import java.net.URI;
046: import java.net.URISyntaxException;
047: import java.util.ArrayList;
048: import java.util.Collection;
049: import java.util.Iterator;
050: import java.util.List;
051:
052: import org.deegree.datatypes.QualifiedName;
053: import org.deegree.framework.util.IDGenerator;
054: import org.deegree.framework.xml.ElementList;
055: import org.deegree.framework.xml.XMLParsingException;
056: import org.deegree.framework.xml.XMLTools;
057: import org.deegree.ogcbase.CommonNamespaces;
058: import org.w3c.dom.Element;
059: import org.w3c.dom.Text;
060:
061: /**
062: * Parser and wrapper class for GML feature collections.
063: * <p>
064: * Extends {@link GMLFeatureDocument}, as a feature collection is a feature in the GML type hierarchy.
065: * <p>
066: *
067: * TODO Remove hack for xlinked feature members (should be easy after fixing model package).
068: *
069: * @author <a href="mailto:schneider@lat-lon.de">Markus Schneider </a>
070: * @author last edited by: $Author: rbezema $
071: *
072: * @version $Revision: 10536 $, $Date: 2008-03-10 04:01:29 -0700 (Mon, 10 Mar 2008) $
073: *
074: * @see GMLFeatureDocument
075: */
076: public class GMLFeatureCollectionDocument extends GMLFeatureDocument {
077:
078: private static final long serialVersionUID = -6923435144671685710L;
079:
080: private Collection<String> xlinkedMembers = new ArrayList<String>();
081:
082: private boolean namespaceAware = false;
083:
084: /**
085: * Creates a new instance of <code>GMLFeatureCollectionDocument</code>.
086: * <p>
087: * Simple types encountered during parsing are "guessed", i.e. the parser tries to convert the values to double,
088: * integer, calendar, etc. However, this may lead to unwanted results, e.g. a property value of "054604" is
089: * converted to "54604".
090: * </p>
091: * <p>
092: * Note, the featurecollection Document created with this constructor will return wfs-1.1 bound FeatureCollections.
093: * If you want to return the same namespace bound feature collection as the incoming feature collection, please use
094: * the {@link #GMLFeatureCollectionDocument(boolean, boolean)}
095: * </p>
096: */
097: public GMLFeatureCollectionDocument() {
098: super ();
099: }
100:
101: /**
102: * Creates a new instance of <code>GMLFeatureCollectionDocument</code>.
103: * <p>
104: * Note, the featurecollection Document created with this constructor will return wfs-1.1 bound FeatureCollections.
105: * If you want to return the same namespace bound feature collection as the incoming feature collection, please use
106: * the {@link #GMLFeatureCollectionDocument(boolean, boolean)}
107: * </p>
108: *
109: * @param guessSimpleTypes
110: * set to true, if simple types should be "guessed" during parsing
111: */
112: public GMLFeatureCollectionDocument(boolean guessSimpleTypes) {
113: super (guessSimpleTypes);
114: }
115:
116: /**
117: * Creates a new instance of <code>GMLFeatureCollectionDocument</code>.
118: * <p>
119: * Instead of the other constructors, this one will be namespace aware of the incoming featureCollection. This
120: * means, that the incoming top root element will hold it's namespace binding and will not automatically be
121: * overwritten with the wfs:1.1 namespace binding.
122: * </p>
123: *
124: * @param guessSimpleTypes
125: * set to true, if simple types should be "guessed" during parsing
126: * @param namespaceAware
127: * if true, the returned FeatureCollection will have the same namespace binding as the incoming
128: * FeatureCollection document. If set to false this constructor equals the
129: * {@link #GMLFeatureCollectionDocument(boolean)}.
130: */
131: public GMLFeatureCollectionDocument(boolean guessSimpleTypes,
132: boolean namespaceAware) {
133: this (guessSimpleTypes);
134: namespaceAware = true;
135: }
136:
137: /**
138: * Returns the object representation of the underlying feature collection document.
139: *
140: * @return object representation of the underlying feature collection document.
141: * @throws XMLParsingException
142: */
143: public FeatureCollection parse() throws XMLParsingException {
144: FeatureCollection fc = parse(this .getRootElement());
145: resolveXLinkReferences();
146: addXLinkedMembers(fc);
147: return fc;
148: }
149:
150: /**
151: * Ugly hack that adds the "xlinked" feature members to the feature collection.
152: *
153: * TODO remove this
154: *
155: * @param fc
156: * @throws XMLParsingException
157: */
158: private void addXLinkedMembers(FeatureCollection fc)
159: throws XMLParsingException {
160: Iterator<String> iter = this .xlinkedMembers.iterator();
161: while (iter.hasNext()) {
162: String fid = iter.next();
163: Feature feature = this .featureMap.get(fid);
164: if (feature == null) {
165: String msg = Messages.format(
166: "ERROR_XLINK_NOT_RESOLVABLE", fid);
167: throw new XMLParsingException(msg);
168: }
169: fc.add(feature);
170: }
171: }
172:
173: /**
174: * Returns the object representation for the given feature collection element.
175: *
176: * @return object representation for the given feature collection element.
177: * @throws XMLParsingException
178: */
179: private FeatureCollection parse(Element element)
180: throws XMLParsingException {
181:
182: String fcId = parseFeatureId(element);
183: // generate id if necessary (use feature type name + a unique number as id)
184: if ("".equals(fcId)) {
185: fcId = element.getLocalName();
186: fcId += IDGenerator.getInstance().generateUniqueID();
187: }
188:
189: String srsName = XMLTools.getNodeAsString(element,
190: "gml:boundedBy/*[1]/@srsName", nsContext, null);
191:
192: ElementList el = XMLTools.getChildElements(element);
193: List<Feature> list = new ArrayList<Feature>(el.getLength());
194:
195: for (int i = 0; i < el.getLength(); i++) {
196: Feature member = null;
197: Element propertyElement = el.item(i);
198: String propertyName = propertyElement.getNodeName();
199:
200: if (!propertyName.endsWith("boundedBy")
201: && !propertyName.endsWith("name")
202: && !propertyName.endsWith("description")) {
203: // the first child of a feature member must always be a feature
204: Element featureElement = XMLTools.getChildElements(
205: el.item(i)).item(0);
206: if (featureElement == null) {
207: // check if feature content is xlinked
208: // TODO remove this ugly hack
209: Text xlinkHref = (Text) XMLTools.getNode(
210: propertyElement, "@xlink:href/text()",
211: nsContext);
212: if (xlinkHref == null) {
213: String msg = Messages.format(
214: "ERROR_INVALID_FEATURE_PROPERTY",
215: propertyName);
216: throw new XMLParsingException(msg);
217: }
218: String href = xlinkHref.getData();
219: if (!href.startsWith("#")) {
220: String msg = Messages.format(
221: "ERROR_EXTERNAL_XLINK_NOT_SUPPORTED",
222: href);
223: throw new XMLParsingException(msg);
224: }
225: String fid = href.substring(1);
226: this .xlinkedMembers.add(fid);
227: } else {
228: try {
229: member = parseFeature(featureElement, srsName);
230: list.add(member);
231: } catch (Exception e) {
232: throw new XMLParsingException(
233: "Error creating feature instance from element '"
234: + featureElement.getLocalName()
235: + "': " + e.getMessage(), e);
236: }
237: }
238: }
239: }
240:
241: Feature[] features = list.toArray(new Feature[list.size()]);
242: FeatureCollection fc = null;
243: if (namespaceAware) {
244: String prefix = element.getPrefix();
245: String namespaceURI = element.getNamespaceURI();
246: if (prefix != null && !"".equals(prefix.trim())) {
247: String tmp = element.lookupNamespaceURI(prefix);
248: if (tmp != null && !"".equals(tmp.trim())) {
249: namespaceURI = tmp;
250: }
251: }
252: if (namespaceURI == null
253: || "".equals(namespaceURI.trim())
254: || CommonNamespaces.WFSNS.toASCIIString().equals(
255: namespaceURI)) {
256: fc = FeatureFactory.createFeatureCollection(fcId,
257: features);
258: } else {
259: QualifiedName name = null;
260: URI ns = null;
261: try {
262: ns = new URI(namespaceURI);
263: name = new QualifiedName(prefix, element
264: .getLocalName(), ns);
265: } catch (URISyntaxException e) {
266: // a failure while creating the namespace uri, the name will be null and the wfs:FeatureCollection
267: // will be the default, just to be safe.
268: }
269: fc = FeatureFactory.createFeatureCollection(fcId,
270: features, name);
271: }
272: } else {
273: // the old (default) behavior, just use the wfs-namespace for all feature collections.
274: fc = FeatureFactory.createFeatureCollection(fcId, features);
275: }
276: String nof = element.getAttribute("numberOfFeatures");
277: if (nof == null) {
278: nof = "" + features.length;
279: }
280: fc.setAttribute("numberOfFeatures", nof);
281: return fc;
282: }
283: }
|