/**
* Copyright (c) Codice Foundation
* <p>
* This is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser
* General Public License as published by the Free Software Foundation, either version 3 of the
* License, or any later version.
* <p>
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
* even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details. A copy of the GNU Lesser General Public License
* is distributed along with this program and can be found at
* <http://www.gnu.org/licenses/lgpl.html>.
**/
package org.codice.ddf.spatial.ogc.wfs.v1_0_0.catalog.converter.impl;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import javax.xml.XMLConstants;
import javax.xml.namespace.QName;
import org.codice.ddf.spatial.ogc.catalog.common.converter.XmlNode;
import org.codice.ddf.spatial.ogc.wfs.catalog.common.WfsConstants;
import org.codice.ddf.spatial.ogc.wfs.catalog.common.WfsFeatureCollection;
import org.codice.ddf.spatial.ogc.wfs.catalog.common.WfsQnameBuilder;
import org.codice.ddf.spatial.ogc.wfs.catalog.converter.FeatureConverter;
import org.codice.ddf.spatial.ogc.wfs.v1_0_0.catalog.common.Wfs10Constants;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.thoughtworks.xstream.converters.Converter;
import com.thoughtworks.xstream.converters.MarshallingContext;
import com.thoughtworks.xstream.converters.UnmarshallingContext;
import com.thoughtworks.xstream.io.HierarchicalStreamReader;
import com.thoughtworks.xstream.io.HierarchicalStreamWriter;
import com.vividsolutions.jts.geom.Geometry;
import com.vividsolutions.jts.geom.GeometryCollection;
import com.vividsolutions.jts.geom.GeometryFactory;
import ddf.catalog.data.Metacard;
import ddf.catalog.data.impl.MetacardImpl;
/**
* This class works in conjunction with XStream to convert a {@link WfsFeatureCollection} to XML
* according to the GML 3.2.1 spec. It will also convert respective XML into a
* {@link WfsFeatureCollection}.
*
*/
public class FeatureCollectionConverterWfs10 implements Converter {
private static final Logger LOGGER =
LoggerFactory.getLogger(FeatureCollectionConverterWfs10.class);
protected String featureMember = "";
protected Map<String, String> prefixToUriMapping = new HashMap<String, String>();
private String contextRoot;
private Map<String, FeatureConverter> featureConverterMap =
new HashMap<String, FeatureConverter>();
public FeatureCollectionConverterWfs10() {
featureMember = "featureMember";
prefixToUriMapping.put(Wfs10Constants.XSI_PREFIX,
XMLConstants.W3C_XML_SCHEMA_INSTANCE_NS_URI);
prefixToUriMapping.put(Wfs10Constants.WFS_NAMESPACE_PREFIX, Wfs10Constants.WFS_NAMESPACE);
prefixToUriMapping.put(Wfs10Constants.GML_PREFIX, Wfs10Constants.GML_NAMESPACE);
}
@Override
public boolean canConvert(Class clazz) {
if (!WfsFeatureCollection.class.isAssignableFrom(clazz)) {
LOGGER.debug("Cannot convert: {}", clazz.getName());
}
return WfsFeatureCollection.class.isAssignableFrom(clazz);
}
@Override
public void marshal(Object value, HierarchicalStreamWriter writer, MarshallingContext context) {
WfsFeatureCollection wfc = (WfsFeatureCollection) value;
String schemaLoc = generateSchemaLocationFromMetacards(wfc.getFeatureMembers(),
prefixToUriMapping);
for (Entry<String, String> entry : prefixToUriMapping.entrySet()) {
writer.addAttribute(XMLConstants.XMLNS_ATTRIBUTE + ":" + entry.getKey(),
entry.getValue());
}
writer.addAttribute(WfsConstants.ATTRIBUTE_SCHEMA_LOCATION, schemaLoc);
Geometry allGeometry = getBounds(wfc.getFeatureMembers());
if (!allGeometry.isEmpty()) {
XmlNode.writeEnvelope(WfsConstants.GML_PREFIX + ":" + "boundedBy",
context,
writer,
allGeometry.getEnvelopeInternal());
}
for (Metacard mc : wfc.getFeatureMembers()) {
writer.startNode(WfsConstants.GML_PREFIX + ":" + featureMember);
context.convertAnother(mc);
writer.endNode();
}
}
public void setContextRoot(String contextRoot) {
if (null != contextRoot) {
this.contextRoot = contextRoot;
}
}
private String generateSchemaLocationFromMetacards(List<Metacard> metacards,
Map<String, String> prefixToUriMapping) {
StringBuilder descFeatureService = new StringBuilder();
descFeatureService.append(contextRoot)
.append("/wfs?service=wfs&request=DescribeFeatureType&version=1.0.0&typeName=");
StringBuilder schemaLocation = new StringBuilder();
Set<QName> qnames = new HashSet<QName>();
for (Metacard metacard : metacards) {
qnames.add(WfsQnameBuilder.buildQName(metacard.getMetacardType()
.getName(), metacard.getContentTypeName()));
}
for (QName qname : qnames) {
prefixToUriMapping.put(qname.getPrefix(), qname.getNamespaceURI());
schemaLocation.append(qname.getNamespaceURI())
.append(" ")
.append(descFeatureService)
.append(qname.getPrefix())
.append(":")
.append(qname.getLocalPart())
.append(" ");
}
return schemaLocation.toString();
}
private Geometry getBounds(List<Metacard> metacards) {
if (metacards != null) {
List<Geometry> geometries = new ArrayList<Geometry>();
for (Metacard card : metacards) {
if (null != card.getLocation()) {
Geometry geo = XmlNode.readGeometry(card.getLocation());
if (null != geo) {
geometries.add(geo);
}
}
}
Geometry allGeometry = new GeometryCollection(geometries.toArray(new Geometry[0]),
new GeometryFactory());
return allGeometry;
} else {
LOGGER.debug("List of metacards was null.");
return null;
}
}
@Override
public Object unmarshal(HierarchicalStreamReader reader, UnmarshallingContext context) {
WfsFeatureCollection featureCollection = new WfsFeatureCollection();
while (reader.hasMoreChildren()) {
reader.moveDown();
String nodeName = reader.getNodeName();
// Its important to note that the reader appears to drop the
// namespace.
if (featureMember.equals(nodeName)) {
reader.moveDown();
// lookup the converter for this featuretype
featureCollection.getFeatureMembers()
.add((Metacard) context.convertAnother(null,
MetacardImpl.class,
featureConverterMap.get(reader.getNodeName())));
reader.moveUp();
}
reader.moveUp();
}
return featureCollection;
}
public void setFeatureConverterMap(Map<String, FeatureConverter> featureConverterMap) {
this.featureConverterMap = featureConverterMap;
}
}