/** * 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.v2_0_0.catalog.converter.impl; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.Serializable; import java.io.StringReader; import java.io.UnsupportedEncodingException; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import javax.xml.transform.Result; import javax.xml.transform.Source; import javax.xml.transform.TransformerException; import javax.xml.transform.TransformerFactory; import javax.xml.transform.TransformerFactoryConfigurationError; import javax.xml.transform.dom.DOMSource; import javax.xml.transform.stream.StreamResult; import org.codice.ddf.libs.geo.util.GeospatialUtil; import org.codice.ddf.spatial.ogc.catalog.common.converter.XmlNode; import org.codice.ddf.spatial.ogc.wfs.catalog.converter.FeatureConverter; import org.codice.ddf.spatial.ogc.wfs.catalog.converter.impl.AbstractFeatureConverter; import org.codice.ddf.spatial.ogc.wfs.catalog.mapper.MetacardMapper; import org.codice.ddf.spatial.ogc.wfs.v2_0_0.catalog.common.Wfs20Constants; import org.geotools.xml.Configuration; import org.geotools.xml.Parser; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.w3c.dom.Document; import org.xml.sax.InputSource; import org.xml.sax.SAXException; import com.thoughtworks.xstream.io.HierarchicalStreamReader; import com.vividsolutions.jts.geom.Coordinate; import com.vividsolutions.jts.geom.CoordinateFilter; import com.vividsolutions.jts.geom.Geometry; import com.vividsolutions.jts.io.WKTWriter; import ddf.catalog.data.AttributeType.AttributeFormat; public abstract class AbstractFeatureConverterWfs20 extends AbstractFeatureConverter implements FeatureConverter { private static final Logger LOGGER = LoggerFactory.getLogger(AbstractFeatureConverterWfs20.class); private static final String XML_PARSE_FAILURE = "Failed to parse GML based XML into a Document."; private static final String CREATE_TRANSFORMER_FAILURE = "Failed to create Transformer."; private static final String GML_FAILURE = "Failed to transform GML.\n"; public AbstractFeatureConverterWfs20() { } public AbstractFeatureConverterWfs20(MetacardMapper metacardMapper) { super(metacardMapper); } protected Serializable getValueForMetacardAttribute(AttributeFormat attributeFormat, HierarchicalStreamReader reader) { Serializable ser = null; switch (attributeFormat) { case BOOLEAN: ser = Boolean.valueOf(reader.getValue()); break; case DOUBLE: ser = Double.valueOf(reader.getValue()); break; case FLOAT: ser = Float.valueOf(reader.getValue()); break; case INTEGER: ser = Integer.valueOf(reader.getValue()); break; case LONG: ser = Long.valueOf(reader.getValue()); break; case SHORT: ser = Short.valueOf(reader.getValue()); break; case XML: case STRING: ser = reader.getValue(); break; case GEOMETRY: XmlNode node = new XmlNode(reader); String geometryXml = node.toString(); Geometry geo = null; geo = (Geometry) readGml(geometryXml); LOGGER.debug("coordinateOrder = {}", coordinateOrder); if (GeospatialUtil.LAT_LON_ORDER.equals(coordinateOrder)) { swapCoordinates(geo); } if (geo != null) { WKTWriter wktWriter = new WKTWriter(); ser = wktWriter.write(geo); LOGGER.debug("wkt = {}", ser); } break; case BINARY: try { ser = reader.getValue() .getBytes(UTF8_ENCODING); } catch (UnsupportedEncodingException e) { LOGGER.debug("Error encoding the binary value into the metacard.", e); } break; case DATE: ser = parseDateFromXml(reader); break; default: break; } return ser; } protected Object readGml(String xml) { LOGGER.debug("readGml() input XML: {}", xml); //Add namespace into XML for processing DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance(); try { dbFactory.setFeature("http://apache.org/xml/features/nonvalidating/load-dtd-grammar", false); dbFactory.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false); } catch (ParserConfigurationException e) { LOGGER.debug("Unable to configure features on document builder.", e); } DocumentBuilder dBuilder = null; Document doc = null; Object gml = null; InputStream xmlIs = null; //Check if GML 3.2.1 namespace exist on XML chunk try { dBuilder = dbFactory.newDocumentBuilder(); InputSource is = new InputSource(); is.setCharacterStream(new StringReader(xml)); doc = dBuilder.parse(is); } catch (ParserConfigurationException | SAXException | IOException e) { LOGGER.debug(XML_PARSE_FAILURE); } if (null != doc) { String[] namePrefix = doc.getDocumentElement() .getNodeName() .split(":"); String prefix = ""; if (namePrefix.length < 2) { LOGGER.debug("Incoming XML has no GML prefix"); } else { prefix = ":" + namePrefix[0]; } String xmlNs = doc.getDocumentElement() .getAttribute("xmlns" + prefix); if (xmlNs.equals(Wfs20Constants.GML_3_2_NAMESPACE)) { LOGGER.debug("Namespace already exists."); } else { doc.createElementNS(Wfs20Constants.GML_3_2_NAMESPACE, doc.getDocumentElement() .getNodeName()); } //Convert DOM to InputStream ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); Source xmlSource = new DOMSource(doc); Result outputTarget = new StreamResult(outputStream); try { TransformerFactory.newInstance() .newTransformer() .transform(xmlSource, outputTarget); } catch (TransformerException | TransformerFactoryConfigurationError e) { LOGGER.debug(CREATE_TRANSFORMER_FAILURE); } xmlIs = new ByteArrayInputStream(outputStream.toByteArray()); //Parse XML into a Geometry object Configuration configurationG = new org.geotools.gml3.v3_2.GMLConfiguration(); Parser parser = new org.geotools.xml.Parser(configurationG); parser.setStrict(false); parser.setValidating(false); parser.setFailOnValidationError(false); parser.setForceParserDelegate(false); try { gml = parser.parse(xmlIs); } catch (IOException | SAXException | ParserConfigurationException e) { LOGGER.debug("{} {}", GML_FAILURE, xml); } } return gml; } private void swapCoordinates(Geometry geo) { LOGGER.debug("Swapping Lat/Lon Coords to Lon/Lat using Geometry"); geo.apply(new CoordinateFilter() { @Override public void filter(Coordinate coordinate) { double x = coordinate.x; double y = coordinate.y; coordinate.y = x; coordinate.x = y; } }); geo.geometryChanged(); } }