/*
* Geotoolkit - An Open Source Java GIS Toolkit
* http://www.geotoolkit.org
*
* (C) 2009-2015, Geomatys
*
* This library 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;
* version 2.1 of the License.
*
* This library 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.
*/
package org.geotoolkit.feature.xml.jaxp;
import com.vividsolutions.jts.geom.Geometry;
import java.io.File;
import java.net.MalformedURLException;
import java.net.URL;
import java.io.IOException;
import java.io.StringReader;
import java.net.URI;
import java.net.URISyntaxException;
import java.nio.file.Path;
import java.util.*;
import java.util.Map.Entry;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Unmarshaller;
import javax.xml.namespace.QName;
import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamReader;
import org.geotoolkit.data.FeatureStoreUtilities;
import org.geotoolkit.data.FeatureCollection;
import org.geotoolkit.feature.xml.Utils;
import org.geotoolkit.feature.xml.XmlFeatureReader;
import org.geotoolkit.feature.xml.jaxb.JAXBEventHandler;
import org.geotoolkit.feature.xml.jaxb.JAXBFeatureTypeReader;
import org.geotoolkit.geometry.isoonjts.spatialschema.geometry.JTSGeometry;
import org.geotoolkit.geometry.isoonjts.spatialschema.geometry.aggregate.JTSMultiCurve;
import org.geotoolkit.geometry.jts.JTSEnvelope2D;
import org.geotoolkit.internal.jaxb.JTSWrapperMarshallerPool;
import org.geotoolkit.internal.jaxb.LineStringPosListType;
import org.geotoolkit.internal.jaxb.PolygonType;
import org.apache.sis.util.ObjectConverters;
import org.apache.sis.xml.MarshallerPool;
import org.apache.sis.xml.Namespaces;
import org.geotoolkit.xml.StaxStreamReader;
import org.opengis.util.GenericName;
import static javax.xml.stream.events.XMLEvent.*;
import net.iharder.Base64;
import org.apache.sis.feature.FeatureExt;
import org.apache.sis.internal.feature.AttributeConvention;
import org.geotoolkit.geometry.isoonjts.spatialschema.geometry.geometry.JTSLineString;
import org.geotoolkit.geometry.jts.JTS;
import org.geotoolkit.gml.GeometrytoJTS;
import org.geotoolkit.gml.xml.AbstractGeometry;
import org.geotoolkit.gml.xml.GMLMarshallerPool;
import org.opengis.util.FactoryException;
import org.apache.sis.util.Numbers;
import org.apache.sis.util.Utilities;
import org.geotoolkit.data.FeatureReader;
import org.geotoolkit.data.FeatureStoreRuntimeException;
import org.apache.sis.util.logging.Logging;
import org.geotoolkit.nio.IOUtilities;
import org.geotoolkit.util.NamesExt;
import org.opengis.feature.Attribute;
import org.opengis.feature.AttributeType;
import org.opengis.feature.Feature;
import org.opengis.feature.FeatureAssociationRole;
import org.opengis.feature.FeatureType;
import org.opengis.feature.Operation;
import org.opengis.feature.PropertyNotFoundException;
import org.opengis.feature.PropertyType;
import org.opengis.referencing.crs.CoordinateReferenceSystem;
import org.w3c.dom.Document;
/**
*
* @author Guilhem Legal (Geomatys)
* @author Johann Sorel (Geomatys)
*/
public class JAXPStreamFeatureReader extends StaxStreamReader implements XmlFeatureReader {
private static final JAXBEventHandler JAXBLOGGER = new JAXBEventHandler();
public static final String READ_EMBEDDED_FEATURE_TYPE = "readEmbeddedFeatureType";
public static final String SKIP_UNEXPECTED_PROPERTY_TAGS = "skipUnexpectedPropertyTags";
public static final String BINDING_PACKAGE = "bindingPackage";
protected static final Logger LOGGER = Logging.getLogger("org.geotoolkit.feature.xml.jaxp");
private Unmarshaller unmarshaller;
public static final String LONGITUDE_FIRST = "longitudeFirst";
/**
* GML namespace for this class.
*/
private static final String GML = "http://www.opengis.net/gml";
protected List<FeatureType> featureTypes;
private URI base = null;
//benchmarked 07/04/2015 : reduce by 10% reading time
private final Map<QName,GenericName> nameCache = new HashMap<QName,GenericName>(){
@Override
public GenericName get(Object key) {
GenericName n = super.get(key);
if(n==null){
n = Utils.getNameFromQname((QName) key);
put((QName)key, n);
}
return n;
}
};
private final Map<String,String> schemaLocations = new HashMap<>();
public JAXPStreamFeatureReader() {
this(new ArrayList<FeatureType>());
}
public JAXPStreamFeatureReader(final FeatureType featureType) {
this(Arrays.asList(featureType));
}
public JAXPStreamFeatureReader(final List<FeatureType> featureTypes) {
this.featureTypes = featureTypes;
this.properties.put(READ_EMBEDDED_FEATURE_TYPE, false);
}
/**
* XSD Schema locations.
* Will be filled only if reading feature type was asked.
*/
public Map<String, String> getSchemaLocations() {
return schemaLocations;
}
/**
* {@inheritDoc }
*/
@Override
public void setFeatureType(final FeatureType featureType) {
this.featureTypes = Arrays.asList(featureType);
}
@Override
public void dispose() {
// do nothing
if(unmarshaller!=null){
getPool().recycle(unmarshaller);
unmarshaller = null;
}
}
@Override
public Object read(final Object xml) throws IOException, XMLStreamException {
setInput(xml);
return read();
}
@Override
public FeatureReader readAsStream(final Object xml) throws IOException, XMLStreamException {
setInput(xml);
return new JAXPStreamIterator();
}
@Override
public void setInput(Object input) throws IOException, XMLStreamException {
super.setInput(input);
if (input instanceof URL) {
try {
base = ((URL) input).toURI();
} catch (URISyntaxException ex) {
throw new IOException(ex);
}
} else if (input instanceof URI) {
base = (URI) input;
} else if (input instanceof Path) {
base = ((Path) input).toUri();
} else if (input instanceof File) {
base = ((File) input).toURI();
} else {
base = null;
}
if (unmarshaller == null) {
try {
unmarshaller = getPool().acquireUnmarshaller();
unmarshaller.setEventHandler(JAXBLOGGER);
} catch (JAXBException ex) {
throw new IOException(ex.getMessage(), ex);
}
}
}
public URI getInput(){
return base;
}
/**
* Start to read An object from the XML datasource.
* @return A feature or featureCollection described in the XML stream.
*/
private Object read() throws XMLStreamException {
while (reader.hasNext()) {
final int event = reader.getEventType();
//we are looking for the root mark
if (event == START_ELEMENT) {
readFeatureTypes();
final GenericName name = nameCache.get(reader.getName());
String id = "no-gml-id";
for(int i=0,n=reader.getAttributeCount();i<n;i++){
final QName attName = reader.getAttributeName(i);
//search and id property from any namespace
if("id".equals(attName.getLocalPart()) && attName.getNamespaceURI().startsWith(GML)){
id = reader.getAttributeValue(i);
}
}
final StringBuilder expectedFeatureType = new StringBuilder();
if (name.tip().toString().equals("FeatureCollection")) {
final Object coll = readFeatureCollection(id);
if (coll == null) {
if (featureTypes.size() == 1) {
return FeatureStoreUtilities.collection(id, featureTypes.get(0));
} else {
return FeatureStoreUtilities.collection(id, null);
}
}
return coll;
} else if (name.tip().toString().equals("Transaction")) {
return extractFeatureFromTransaction();
} else {
for (FeatureType ft : featureTypes) {
if (ft.getName().equals(name)) {
return readFeature(ft);
}
expectedFeatureType.append(ft.getName()).append('\n');
}
}
throw new IllegalArgumentException("The xml does not describe the same type of feature: \n " +
"Expected: " + expectedFeatureType.toString() + '\n' +
"But was: " + name);
}
reader.next();
}
return null;
}
private void readFeatureTypes(){
// we search an embedded featureType description
String schemaLocation = reader.getAttributeValue(Namespaces.XSI, "schemaLocation");
if (isReadEmbeddedFeatureType() && schemaLocation != null) {
final JAXBFeatureTypeReader featureTypeReader = new JAXBFeatureTypeReader();
schemaLocation = schemaLocation.trim();
final String[] urls = schemaLocation.split(" ");
for (int i = 0; i < urls.length; i++) {
final String namespace = urls[i];
if (!(namespace.equalsIgnoreCase("http://www.opengis.net/gml") || namespace.equalsIgnoreCase("http://www.opengis.net/wfs")) && i + 1 < urls.length) {
final String fturl = urls[i + 1];
schemaLocations.put(namespace, fturl);
try {
final URI uri = Utils.resolveURI(base, fturl);
List<FeatureType> fts = (List<FeatureType>) featureTypeReader.read(IOUtilities.open(uri));
for (FeatureType ft : fts) {
if (!featureTypes.contains(ft)) {
featureTypes.add(ft);
}
}
} catch (MalformedURLException | URISyntaxException ex) {
LOGGER.log(Level.WARNING, null, ex);
} catch (IOException | JAXBException ex) {
LOGGER.log(Level.WARNING, null, ex);
}
i = i + 2;
} else if(namespace.equalsIgnoreCase("http://www.opengis.net/gml") || namespace.equalsIgnoreCase("http://www.opengis.net/wfs")) {
i++;
}
}
}
}
private Object readFeatureCollection(final String id) throws XMLStreamException {
FeatureCollection collection = null;
while (reader.hasNext()) {
int event = reader.next();
//we are looking for the root mark
if (event == START_ELEMENT) {
final GenericName name = nameCache.get(reader.getName());
String fid = null;
if (reader.getAttributeCount() > 0) {
fid = reader.getAttributeValue(0);
}
if (name.tip().toString().equals("featureMember") || name.tip().toString().equals("featureMembers")) {
continue;
} else if (name.tip().toString().equals("boundedBy")) {
while (reader.hasNext()) {
event = reader.next();
if (event == START_ELEMENT) {
break;
}
}
String srsName = null;
if (reader.getAttributeCount() > 0) {
srsName = reader.getAttributeValue(0);
}
final JTSEnvelope2D bounds = readBounds(srsName);
} else {
if (fid == null) {
LOGGER.info("Missing feature id : generating a random one");
fid = UUID.randomUUID().toString();
}
boolean find = false;
StringBuilder expectedFeatureType = new StringBuilder();
for (FeatureType ft : featureTypes) {
if (ft.getName().equals(name)) {
if (collection == null) {
collection = FeatureStoreUtilities.collection(id, ft);
}
collection.add((Feature)readFeature(ft));
find = true;
}
expectedFeatureType.append(ft.getName()).append('\n');
}
if (!find) {
throw new IllegalArgumentException("The xml does not describe the same type of feature: \n "
+ "Expected: " + expectedFeatureType.toString() + '\n'
+ "But was: " + name);
}
}
}
}
return collection;
}
private Feature readFeature(final FeatureType featureType) throws XMLStreamException {
return readFeature(featureType, featureType.getName());
}
private Feature readFeature(final FeatureType featureType, final GenericName tagName) throws XMLStreamException {
final Feature feature = featureType.newInstance();
/*
* We create a map and a collection because we can encounter two cases :
* - The case featureType defines a property with max occur > 1.
* - The case featureType defines a property with max occur = 1, and its
* value instance of collection or map.
* We store all encountered name with its linked property in the map, so
* at each value parsed, we can add it in the existing property if its
* value is a list or map. The collection is the final property store,
* we add the all the created properties in it (so we can put multiple
* properties with the same name).
*/
final List<Entry<Operation,Object>> ops = new ArrayList<>();
//read attributes
final int nbAtts = reader.getAttributeCount();
for(int i=0;i<nbAtts;i++){
final QName attName = reader.getAttributeName(i);
try {
final AttributeType pd = (AttributeType) featureType.getProperty("@"+attName.getLocalPart());
final String attVal = reader.getAttributeValue(i);
final Object val = ObjectConverters.convert(attVal, pd.getValueClass());
feature.setPropertyValue(pd.getName().toString(), val);
} catch(PropertyNotFoundException ex) {
//do nothing
}
}
boolean doNext = true;
//read a real complex type
while (!doNext || reader.hasNext()) {
if(doNext){
reader.next();
}
doNext = true;
int event = reader.getEventType();
if (event == START_ELEMENT) {
final GenericName propName = nameCache.get(reader.getName());
// we skip the boundedby attribute if it's present
if ("boundedBy".equals(propName.tip().toString())) {
toTagEnd("boundedBy");
continue;
}
final String nameAttribute = reader.getAttributeValue(null, "name");
final PropertyType propertyType;
try {
propertyType = featureType.getProperty(propName.toString());
}catch(PropertyNotFoundException ex) {
if (Boolean.TRUE.equals(this.properties.get(SKIP_UNEXPECTED_PROPERTY_TAGS))) {
toTagEnd(propName.tip().toString());
continue;
} else {
try {
//convert the content as a dom node
final AttributeType pd = (AttributeType) featureType.getProperty("_any");
final Document doc = readAsDom(propName.tip().toString());
feature.setPropertyValue(pd.getName().toString(), doc);
doNext = false;
continue;
}catch(PropertyNotFoundException e) {
throw new IllegalArgumentException("Unexpected attribute:" + propName + " not found in :\n" + featureType);
}
}
}
if(propertyType instanceof Operation){
final Operation opType = (Operation) propertyType;
final PropertyType resultType = (PropertyType) opType.getResult();
final Object value = readPropertyValue(resultType,false);
ops.add(new AbstractMap.SimpleImmutableEntry<>((Operation)propertyType,value));
if(resultType.getName().equals(propertyType.getName())){
//we are already on the next element here, jaxb ate one
doNext = false;
}
continue;
}
//read attributes
if (propertyType instanceof AttributeType) {
final AttributeType<?> attType = (AttributeType) propertyType;
final int nbPropAtts = reader.getAttributeCount();
if (nbPropAtts>0) {
final Attribute att = (Attribute) feature.getProperty(propName.toString());
for(int i=0;i<nbPropAtts;i++){
final QName qname = reader.getAttributeName(i);
final GenericName attName = nameCache.get(new QName(qname.getNamespaceURI(), "@"+qname.getLocalPart()));
final AttributeType<?> charType = attType.characteristics().get(attName.toString());
if (charType!=null) {
final String attVal = reader.getAttributeValue(i);
final Object val = ObjectConverters.convert(attVal, charType.getValueClass());
final Attribute chara = charType.newInstance();
chara.setValue(val);
att.characteristics().put(attName.toString(), chara);
}
}
}
}
//parse the value
final Object value = readPropertyValue(propertyType,true);
////////////////////////////////////////////////////////////
final Object previousVal = feature.getPropertyValue(propName.toString());
if(propertyType instanceof FeatureAssociationRole){
final FeatureAssociationRole role = (FeatureAssociationRole) propertyType;
if (role.getMaximumOccurs() > 1) {
final List vals = new ArrayList((Collection) previousVal);
vals.add(value);
feature.setPropertyValue(propName.toString(), vals);
} else {
if (previousVal!=null) {
if (previousVal instanceof List) {
((List) previousVal).add(value);
} else if (previousVal instanceof Map) {
if (nameAttribute != null) {
((Map) previousVal).put(nameAttribute, value);
} else {
LOGGER.severe("unable to read a composite attribute : no name has been found");
}
}
} else {
feature.setPropertyValue(propName.toString(), value);
}
}
}else{
if(previousVal!=null){
if(previousVal instanceof Map){
if(nameAttribute!=null){
((Map) previousVal).put(nameAttribute, value);
}else{
LOGGER.severe("unable to read a composite attribute : no name has been found");
}
}else if (previousVal instanceof Collection) {
final List vals = new ArrayList((Collection) previousVal);
vals.add(value);
feature.setPropertyValue(propName.toString(), vals);
}else{
throw new XMLStreamException("Expected a multivalue property");
}
}else{
//new property
if(nameAttribute!=null){
final Map<String, Object> map = new LinkedHashMap<>();
map.put(nameAttribute, value);
feature.setPropertyValue(propName.toString(), map);
}else{
feature.setPropertyValue(propName.toString(), value);
}
}
}
////////////////////////////////////////////////////////////
} else if (event == END_ELEMENT) {
final QName q = reader.getName();
if (q.getLocalPart().equals("featureMember") || nameCache.get(q).equals(tagName)) {
break;
}
}
}
// //apply operations (alias/susbstitutionGroups)
// for(Entry<OperationDescriptor,Object> entry : ops){
// final OperationType type = entry.getKey().getType();
// type.invokeSet(feature, entry.getValue());
// }
return feature;
}
private Object readPropertyValue(PropertyType propertyType, boolean skipCurrent) throws XMLStreamException{
final GenericName propName = nameCache.get(reader.getName());
Object value = null;
if (AttributeConvention.isGeometryAttribute(propertyType)) {
int event;
if(skipCurrent){
event = reader.next();
}else{
event = reader.getEventType();
}
while (event != START_ELEMENT) {
if (event == END_ELEMENT) {
return null;
}
event = reader.next();
}
try {
final boolean longitudeFirst;
if (getProperty(LONGITUDE_FIRST) != null) {
longitudeFirst = (boolean) getProperty(LONGITUDE_FIRST);
} else {
longitudeFirst = true;
}
final Geometry jtsGeom;
final Object geometry = ((JAXBElement) unmarshaller.unmarshal(reader)).getValue();
if (geometry instanceof JTSGeometry) {
final JTSGeometry isoGeom = (JTSGeometry) geometry;
if (isoGeom instanceof JTSMultiCurve) {
((JTSMultiCurve)isoGeom).applyCRSonChild();
}
jtsGeom = isoGeom.getJTSGeometry();
} else if (geometry instanceof PolygonType) {
final PolygonType polygon = ((PolygonType)geometry);
jtsGeom = polygon.getJTSPolygon().getJTSGeometry();
if(polygon.getCoordinateReferenceSystem() != null) {
JTS.setCRS(jtsGeom, polygon.getCoordinateReferenceSystem());
}
} else if (geometry instanceof LineStringPosListType) {
final JTSLineString line = ((LineStringPosListType)geometry).getJTSLineString();
jtsGeom = line.getJTSGeometry();
if(line.getCoordinateReferenceSystem() != null) {
JTS.setCRS(jtsGeom, line.getCoordinateReferenceSystem());
}
} else if (geometry instanceof AbstractGeometry) {
try {
jtsGeom = GeometrytoJTS.toJTS((AbstractGeometry) geometry, longitudeFirst);
} catch (FactoryException ex) {
throw new XMLStreamException("Factory Exception while transforming GML object to JTS", ex);
}
} else {
throw new IllegalArgumentException("unexpected geometry type:" + geometry);
}
value = jtsGeom;
//verify geometry crs
final CoordinateReferenceSystem baseCrs = FeatureExt.getCRS(propertyType);
if (baseCrs!=null && jtsGeom!=null && jtsGeom.getUserData() instanceof CoordinateReferenceSystem) {
if (!Utilities.equalsIgnoreMetadata(baseCrs,jtsGeom.getUserData())) {
throw new XMLStreamException("Unvalid geometry declaration, propertyType CRS is different from geometry CRS.\n"+baseCrs+"\n"+jtsGeom.getUserData());
}
}
} catch (JAXBException ex) {
String msg = ex.getMessage();
if (msg == null && ex.getLinkedException() != null) {
msg = ex.getLinkedException().getMessage();
}
throw new IllegalArgumentException("JAXB exception while reading the feature geometry: " + msg, ex);
}
} else if (propertyType instanceof FeatureAssociationRole) {
final FeatureAssociationRole far = (FeatureAssociationRole) propertyType;
final FeatureType valueType = far.getValueType();
//GML properties have one level of encapsulation (in properties which follow the ogc convention)
final QName currentName = reader.getName();
if (currentName != null && valueType.getName().tip().toString().equals(currentName.getLocalPart())) {
//no encapsulation
value = readFeature(((FeatureAssociationRole) propertyType).getValueType(), nameCache.get(currentName));
} else {
boolean doNext = true;
while (!doNext || reader.hasNext()) {
if(doNext){
reader.next();
}
doNext = true;
int event = reader.getEventType();
if (event == START_ELEMENT) {
final GenericName subName = nameCache.get(reader.getName());
value = readFeature(((FeatureAssociationRole) propertyType).getValueType(), subName);
} else if (event == END_ELEMENT) {
final QName q = reader.getName();
if (nameCache.get(q).equals(propName)) {
break;
}
}
}
}
} else if (propertyType instanceof AttributeType) {
final String content = reader.getElementText();
final Class typeBinding = ((AttributeType)propertyType).getValueClass();
if(List.class.equals(typeBinding) || Map.class.equals(typeBinding)){
value = content;
}else{
value = readValue(content, (AttributeType)propertyType);
}
}
return value;
}
public Object readValue(final String content, final AttributeType type){
Object value = content;
if(type.getValueClass()== byte[].class && content != null){
try {
value = Base64.decode(content);
} catch (IOException ex) {
LOGGER.log(Level.INFO, "Failed to parser binary64 : "+ex.getMessage(),ex);
}
}else{
value = ObjectConverters.convert(value, Numbers.primitiveToWrapper(type.getValueClass()));
}
return value;
}
private Object extractFeatureFromTransaction() throws XMLStreamException {
final List<Feature> features = new ArrayList<Feature>();
boolean insert = false;
while (reader.hasNext()) {
int event = reader.next();
if (event == END_ELEMENT) {
GenericName name = nameCache.get(reader.getName());
if (name.tip().toString().equals("Insert")) {
insert = false;
}
//we are looking for the root mark
} else if (event == START_ELEMENT) {
GenericName name = nameCache.get(reader.getName());
if (name.tip().toString().equals("Insert")) {
insert = true;
continue;
} else if (insert) {
if (name.tip().toString().equals("FeatureCollection")) {
return readFeatureCollection("");
}
boolean find = false;
StringBuilder expectedFeatureType = new StringBuilder();
for (FeatureType ft : featureTypes) {
if (ft.getName().equals(name)) {
features.add((Feature)readFeature(ft));
find = true;
}
expectedFeatureType.append(ft.getName()).append('\n');
}
if (!find) {
throw new IllegalArgumentException("The xml does not describe the same type of feature: \n " +
"Expected: " + expectedFeatureType.toString() + '\n' +
"But was: " + name);
}
}
}
}
return features;
}
@Override
public Map<String, String> extractNamespace(final String xml) {
try {
final XMLInputFactory XMLfactory = XMLInputFactory.newInstance();
XMLfactory.setProperty("http://java.sun.com/xml/stream/properties/report-cdata-event", Boolean.TRUE);
final XMLStreamReader streamReader = XMLfactory.createXMLStreamReader(new StringReader(xml));
final Map<String, String> namespaceMapping = new LinkedHashMap<>();
while (streamReader.hasNext()) {
int event = streamReader.next();
if (event == START_ELEMENT) {
for (int i = 0; i < streamReader.getNamespaceCount(); i++) {
namespaceMapping.put(streamReader.getNamespacePrefix(i), streamReader.getNamespaceURI(i));
}
}
}
return namespaceMapping;
} catch (XMLStreamException ex) {
LOGGER.log(Level.SEVERE, "XMl stream exception while extracting namespace: {0}", ex.getMessage());
}
return null;
}
/**
* Extract An envelope from the BoundedBy XML mark of a feature collection.
*
* @param srsName The extracted CRS identifier.
*
* @return An envelope of the collection bounds.
* @throws XMLStreamException
*/
private JTSEnvelope2D readBounds(final String srsName) throws XMLStreamException {
JTSEnvelope2D bounds = null;
while (reader.hasNext()) {
int event = reader.next();
if (event == END_ELEMENT) {
QName endElement = reader.getName();
if (endElement.getLocalPart().equals("boundedBy")) {
return null;
}
}
}
return bounds;
}
/**
* Return a MarshallerPool depending on the property BINDING_PACKAGE.
*
* accepted values : "JTSWrapper" or null (default). => JTSWrapperMarshallerPool
* "GML" (default). => GMLMarshallerPool
*/
private MarshallerPool getPool() {
final String bindingPackage = (String) properties.get(BINDING_PACKAGE);
if ("JTSWrapper".equals(bindingPackage)) {
return JTSWrapperMarshallerPool.getInstance();
} else if (bindingPackage == null || "GML".equals(bindingPackage)) {
return GMLMarshallerPool.getInstance();
} else {
throw new IllegalArgumentException("Unexpected property value for BINDING_PACKAGE:" + bindingPackage);
}
}
/**
* @deprecated use getProperty(READ_EMBEDDED_FEATURE_TYPE)
*/
@Deprecated
public boolean isReadEmbeddedFeatureType() {
return (Boolean) this.properties.get(READ_EMBEDDED_FEATURE_TYPE);
}
/**
* * @deprecated use getProperties().put(READ_EMBEDDED_FEATURE_TYPE)
*/
@Deprecated
public void setReadEmbeddedFeatureType(boolean readEmbeddedFeatureType) {
this.properties.put(READ_EMBEDDED_FEATURE_TYPE, readEmbeddedFeatureType);
}
private final class JAXPStreamIterator implements FeatureReader{
private boolean singleFeature = false;
private FeatureType type = null;
private Feature next = null;
public JAXPStreamIterator() throws XMLStreamException {
while (reader.hasNext()) {
final int event = reader.getEventType();
//we are looking for the root mark
if (event == START_ELEMENT) {
readFeatureTypes();
final GenericName name = nameCache.get(reader.getName());
String id = "no-gml-id";
for(int i=0,n=reader.getAttributeCount();i<n;i++){
final QName attName = reader.getAttributeName(i);
//search and id property from any namespace
if("id".equals(attName.getLocalPart()) && attName.getNamespaceURI().startsWith(GML)){
id = reader.getAttributeValue(i);
}
}
final StringBuilder expectedFeatureType = new StringBuilder();
if (name.tip().toString().equals("FeatureCollection")) {
singleFeature = false;
return;
} else if (name.tip().toString().equals("Transaction")) {
throw new XMLStreamException("Transaction types are not supported as stream");
} else {
for (FeatureType ft : featureTypes) {
if (ft.getName().equals(name)) {
singleFeature = true;
next = (Feature) readFeature(ft);
type = next.getType();
return;
}
expectedFeatureType.append(ft.getName()).append('\n');
}
}
throw new IllegalArgumentException("The xml does not describe the same type of feature: \n " +
"Expected: " + expectedFeatureType.toString() + '\n' +
"But was: " + name);
}
reader.next();
}
}
@Override
public FeatureType getFeatureType() {
findNext();
if(type==null){
//collection is empty
if(!featureTypes.isEmpty()){
//return the first feature type in the xsd
return featureTypes.get(0);
}
}
return type;
}
@Override
public boolean hasNext() throws FeatureStoreRuntimeException {
findNext();
return next != null;
}
@Override
public Feature next() throws FeatureStoreRuntimeException {
findNext();
Feature t = next;
next = null;
return t;
}
private void findNext() throws FeatureStoreRuntimeException{
if(next!=null || singleFeature) return;
try{
//read a feature in the collection
while (reader.hasNext()) {
int event = reader.next();
//we are looking for the root mark
if (event == START_ELEMENT) {
final GenericName name = nameCache.get(reader.getName());
String fid = null;
if (reader.getAttributeCount() > 0) {
fid = reader.getAttributeValue(0);
}
if (name.tip().toString().equals("featureMember") || name.tip().toString().equals("featureMembers")) {
continue;
} else if (name.tip().toString().equals("boundedBy")) {
while (reader.hasNext()) {
event = reader.next();
if (event == START_ELEMENT) {
break;
}
}
String srsName = null;
if (reader.getAttributeCount() > 0) {
srsName = reader.getAttributeValue(0);
}
final JTSEnvelope2D bounds = readBounds(srsName);
} else {
if (fid == null) {
LOGGER.info("Missing feature id : generating a random one");
fid = UUID.randomUUID().toString();
}
boolean find = false;
StringBuilder expectedFeatureType = new StringBuilder();
for (FeatureType ft : featureTypes) {
if (ft.getName().equals(name)) {
next = (Feature) readFeature(ft);
find = true;
if(type==null) type = next.getType();
return;
}
expectedFeatureType.append(ft.getName()).append('\n');
}
if (!find) {
throw new IllegalArgumentException("The xml does not describe the same type of feature: \n "
+ "Expected: " + expectedFeatureType.toString() + '\n'
+ "But was: " + name);
}
}
}
}
}catch(XMLStreamException ex){
throw new FeatureStoreRuntimeException(ex);
}
}
@Override
public void close() {
dispose();
}
@Override
public void remove() {
throw new UnsupportedOperationException("not supported");
}
}
}