/* * Geotoolkit - An Open Source Java GIS Toolkit * http://www.geotoolkit.org * * (C) 2009-2016, 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; import com.vividsolutions.jts.geom.Envelope; import com.vividsolutions.jts.geom.Geometry; import com.vividsolutions.jts.geom.GeometryCollection; import com.vividsolutions.jts.geom.LineString; import com.vividsolutions.jts.geom.LinearRing; import com.vividsolutions.jts.geom.MultiLineString; import com.vividsolutions.jts.geom.MultiPoint; import com.vividsolutions.jts.geom.MultiPolygon; import com.vividsolutions.jts.geom.Point; import com.vividsolutions.jts.geom.Polygon; import java.io.IOException; import java.math.BigDecimal; import java.net.MalformedURLException; import java.net.URI; import java.net.URISyntaxException; import java.net.URL; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.sql.Timestamp; import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.Date; 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 java.util.TimeZone; import java.util.logging.Level; import java.util.logging.Logger; import javax.xml.bind.JAXBException; import javax.xml.bind.Unmarshaller; import javax.xml.namespace.QName; import javax.xml.stream.events.XMLEvent; import net.iharder.Base64; import org.apache.sis.feature.FeatureExt; import org.apache.sis.feature.Features; import org.geotoolkit.util.NamesExt; import org.geotoolkit.xsd.xml.v2001.Import; import org.geotoolkit.xsd.xml.v2001.Include; import org.geotoolkit.xsd.xml.v2001.Schema; import org.geotoolkit.xsd.xml.v2001.XSDMarshallerPool; import org.opengis.util.GenericName; import org.apache.sis.util.logging.Logging; import org.geotoolkit.nio.IOUtilities; 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.PropertyType; /** * * @author Guilhem Legal (Geomatys) */ public class Utils { /** * This named is used for element of type xsd:any. */ public static final String ANY_PROPERTY_NAME = "any"; /** * Convention name used in attribute characteristics to store attribute xsd simple type name. */ public static final GenericName SIMPLETYPE_NAME_CHARACTERISTIC = NamesExt.create("http://sis.apache.org/xsd", "@simpletypename"); private static final Logger LOGGER = Logging.getLogger("org.geotoolkit.feature.xml"); private static final DateFormat timestampFormatter = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"); private static final DateFormat dateFormatter = new SimpleDateFormat("yyyy-MM-dd'Z'"); private static final String GML_311_NAMESPACE = "http://www.opengis.net/gml"; private static final String GML_321_NAMESPACE = "http://www.opengis.net/gml/3.2"; public static final Set<GenericName> GML_FEATURE_TYPES; public static final Set<GenericName> GML_STANDARD_OBJECT_PROPERTIES; public static final Set<GenericName> GML_ABSTRACT_FEATURE_PROPERTIES; static{ timestampFormatter.setTimeZone(TimeZone.getTimeZone("GMT+0")); dateFormatter.setTimeZone(TimeZone.getTimeZone("GMT+0")); GML_FEATURE_TYPES = Collections.unmodifiableSet(new HashSet(Arrays.asList(new GenericName[] { //3.1.1 NamesExt.create(GML_311_NAMESPACE, "AbstractFeature"), NamesExt.create(GML_311_NAMESPACE, "AbstractFeatureType"), NamesExt.create(GML_311_NAMESPACE, "AbstractFeatureCollection"), NamesExt.create(GML_311_NAMESPACE, "AbstractFeatureCollectionType"), NamesExt.create(GML_311_NAMESPACE, "FeatureCollection"), NamesExt.create(GML_311_NAMESPACE, "FeatureCollectionType"), NamesExt.create(GML_311_NAMESPACE, "AbstractCoverage"), NamesExt.create(GML_311_NAMESPACE, "AbstractCoverageType"), NamesExt.create(GML_311_NAMESPACE, "AbstractContinuousCoverage"), NamesExt.create(GML_311_NAMESPACE, "AbstractContinuousCoverageType"), NamesExt.create(GML_311_NAMESPACE, "AbstractDiscreteCoverage"), NamesExt.create(GML_311_NAMESPACE, "AbstractDiscreteCoverageType"), NamesExt.create(GML_311_NAMESPACE, "Observation"), NamesExt.create(GML_311_NAMESPACE, "ObservationType"), NamesExt.create(GML_311_NAMESPACE, "DirectedObservation"), NamesExt.create(GML_311_NAMESPACE, "DirectedObservationType"), NamesExt.create(GML_311_NAMESPACE, "DirectedObservationAtDistance"), NamesExt.create(GML_311_NAMESPACE, "DirectedObservationAtDistanceType"), NamesExt.create(GML_311_NAMESPACE, "MultiPointCoverage"), NamesExt.create(GML_311_NAMESPACE, "MultiPointCoverageType"), NamesExt.create(GML_311_NAMESPACE, "MultiCurveCoverage"), NamesExt.create(GML_311_NAMESPACE, "MultiCurveCoverageType"), NamesExt.create(GML_311_NAMESPACE, "MultiSurfaceCoverage"), NamesExt.create(GML_311_NAMESPACE, "MultiSurfaceCoverageType"), NamesExt.create(GML_311_NAMESPACE, "MultiSolidCoverage"), NamesExt.create(GML_311_NAMESPACE, "MultiSolidCoverageType"), NamesExt.create(GML_311_NAMESPACE, "GridCoverage"), NamesExt.create(GML_311_NAMESPACE, "GridCoverageType"), NamesExt.create(GML_311_NAMESPACE, "_FeatureCollection"), NamesExt.create(GML_311_NAMESPACE, "_FeatureCollectionType"), NamesExt.create(GML_311_NAMESPACE, "_Coverage"), NamesExt.create(GML_311_NAMESPACE, "_CoverageType"), NamesExt.create(GML_311_NAMESPACE, "_ContinuousCoverage"), NamesExt.create(GML_311_NAMESPACE, "_ContinuousCoverageType"), NamesExt.create(GML_311_NAMESPACE, "_DiscreteCoverage"), NamesExt.create(GML_311_NAMESPACE, "_DiscreteCoverageType"), //3.2.1 NamesExt.create(GML_321_NAMESPACE, "AbstractFeature"), NamesExt.create(GML_321_NAMESPACE, "AbstractFeatureType"), NamesExt.create(GML_321_NAMESPACE, "AbstractFeatureCollection"), NamesExt.create(GML_321_NAMESPACE, "AbstractFeatureCollectionType"), NamesExt.create(GML_321_NAMESPACE, "FeatureCollection"), NamesExt.create(GML_321_NAMESPACE, "FeatureCollectionType"), NamesExt.create(GML_321_NAMESPACE, "AbstractCoverage"), NamesExt.create(GML_321_NAMESPACE, "AbstractCoverageType"), NamesExt.create(GML_321_NAMESPACE, "AbstractContinuousCoverage"), NamesExt.create(GML_321_NAMESPACE, "AbstractContinuousCoverageType"), NamesExt.create(GML_321_NAMESPACE, "AbstractDiscreteCoverage"), NamesExt.create(GML_321_NAMESPACE, "AbstractDiscreteCoverageType"), NamesExt.create(GML_321_NAMESPACE, "Observation"), NamesExt.create(GML_321_NAMESPACE, "ObservationType"), NamesExt.create(GML_321_NAMESPACE, "DirectedObservation"), NamesExt.create(GML_321_NAMESPACE, "DirectedObservationType"), NamesExt.create(GML_321_NAMESPACE, "DirectedObservationAtDistance"), NamesExt.create(GML_321_NAMESPACE, "DirectedObservationAtDistanceType"), NamesExt.create(GML_321_NAMESPACE, "DynamicFeatureCollection"), NamesExt.create(GML_321_NAMESPACE, "DynamicFeatureCollectionType"), NamesExt.create(GML_321_NAMESPACE, "DynamicFeature"), NamesExt.create(GML_321_NAMESPACE, "DynamicFeatureType"), NamesExt.create(GML_321_NAMESPACE, "DiscreteCoverage"), NamesExt.create(GML_321_NAMESPACE, "DiscreteCoverageType"), NamesExt.create(GML_321_NAMESPACE, "MultiPointCoverage"), NamesExt.create(GML_321_NAMESPACE, "MultiPointCoverageType"), NamesExt.create(GML_321_NAMESPACE, "MultiCurveCoverage"), NamesExt.create(GML_321_NAMESPACE, "MultiCurveCoverageType"), NamesExt.create(GML_321_NAMESPACE, "MultiSurfaceCoverage"), NamesExt.create(GML_321_NAMESPACE, "MultiSurfaceCoverageType"), NamesExt.create(GML_321_NAMESPACE, "MultiSolidCoverage"), NamesExt.create(GML_321_NAMESPACE, "MultiSolidCoverageType"), NamesExt.create(GML_321_NAMESPACE, "GridCoverage"), NamesExt.create(GML_321_NAMESPACE, "GridCoverageType"), NamesExt.create(GML_321_NAMESPACE, "RectifiedGridCoverage"), NamesExt.create(GML_321_NAMESPACE, "RectifiedGridCoverageType") }))); GML_STANDARD_OBJECT_PROPERTIES = Collections.unmodifiableSet(new HashSet(Arrays.asList(new GenericName[] { //3.1.1 NamesExt.create(GML_311_NAMESPACE, "metaDataProperty"), NamesExt.create(GML_311_NAMESPACE, "description"), NamesExt.create(GML_311_NAMESPACE, "name"), NamesExt.create(GML_311_NAMESPACE, "csName"), //substitution group of name NamesExt.create(GML_311_NAMESPACE, "srsName"), //substitution group of name NamesExt.create(GML_311_NAMESPACE, "datumName"), //substitution group of name NamesExt.create(GML_311_NAMESPACE, "meridianName"), //substitution group of name NamesExt.create(GML_311_NAMESPACE, "ellipsoidName"), //substitution group of name NamesExt.create(GML_311_NAMESPACE, "coordinateOperationName"), //substitution group of name NamesExt.create(GML_311_NAMESPACE, "methodName"), //substitution group of name NamesExt.create(GML_311_NAMESPACE, "parameterName"), //substitution group of name NamesExt.create(GML_311_NAMESPACE, "groupName"), //substitution group of name //3.2.1 NamesExt.create(GML_321_NAMESPACE, "metaDataProperty"), NamesExt.create(GML_321_NAMESPACE, "description"), NamesExt.create(GML_321_NAMESPACE, "descriptionReference"), NamesExt.create(GML_321_NAMESPACE, "name"), NamesExt.create(GML_321_NAMESPACE, "csName"), //substitution group of name NamesExt.create(GML_321_NAMESPACE, "srsName"), //substitution group of name NamesExt.create(GML_321_NAMESPACE, "datumName"), //substitution group of name NamesExt.create(GML_321_NAMESPACE, "meridianName"), //substitution group of name NamesExt.create(GML_321_NAMESPACE, "ellipsoidName"), //substitution group of name NamesExt.create(GML_321_NAMESPACE, "coordinateOperationName"), //substitution group of name NamesExt.create(GML_321_NAMESPACE, "methodName"), //substitution group of name NamesExt.create(GML_321_NAMESPACE, "parameterName"), //substitution group of name NamesExt.create(GML_321_NAMESPACE, "groupName"), //substitution group of name NamesExt.create(GML_321_NAMESPACE, "identifier") }))); GML_ABSTRACT_FEATURE_PROPERTIES = Collections.unmodifiableSet(new HashSet(Arrays.asList(new GenericName[] { //3.1.1 NamesExt.create(GML_311_NAMESPACE, "boundedBy"), NamesExt.create(GML_311_NAMESPACE, "location"), NamesExt.create(GML_311_NAMESPACE, "priorityLocation"), //substitution group of location //3.2.1 NamesExt.create(GML_321_NAMESPACE, "boundedBy"), NamesExt.create(GML_321_NAMESPACE, "location"), NamesExt.create(GML_321_NAMESPACE, "priorityLocation") //substitution group of location }))); } private Utils() {} /** * Return a Name from a QName. * * @param qname a XML QName. * @return a Types Name. */ public static GenericName getNameFromQname(final QName qname) { GenericName name; if (qname.getNamespaceURI() == null || qname.getNamespaceURI().isEmpty()) { name = NamesExt.create(qname.getLocalPart()); } else { name = NamesExt.create(qname); } return name; } /** * Return A QName from a Name. * * @param name a Types name. * @return A XML QName. */ public static QName getQnameFromName(final GenericName name) { QName qname; final String ns = NamesExt.getNamespace(name); if (ns == null || ns.isEmpty()) { qname = new QName(name.tip().toString()); } else { qname = new QName(ns, name.tip().toString()); } return qname; } private static final Map<String, Class> CLASS_BINDING = new HashMap<>(); private static final Set<String> GEOMETRIC_NAME = new HashSet<>(); static { CLASS_BINDING.put("long", Long.class); CLASS_BINDING.put("integer", Integer.class); CLASS_BINDING.put("int", int.class); CLASS_BINDING.put("QName", QName.class); CLASS_BINDING.put("anyURI", URI.class); CLASS_BINDING.put("byte", Byte.class); CLASS_BINDING.put("string", String.class); CLASS_BINDING.put("decimal", BigDecimal.class); CLASS_BINDING.put("short", Short.class); CLASS_BINDING.put("boolean", Boolean.class); CLASS_BINDING.put("dateTime", Timestamp.class); CLASS_BINDING.put("date", Date.class); CLASS_BINDING.put("double", Double.class); CLASS_BINDING.put("float", Float.class); CLASS_BINDING.put("base64Binary",byte[].class); CLASS_BINDING.put("language", String.class); CLASS_BINDING.put("IDREF", String.class); CLASS_BINDING.put("normalizedString",String.class); //TODO String value of the xml should be trimmed CLASS_BINDING.put("NMTOKEN", String.class); CLASS_BINDING.put("nonNegativeInteger", Integer.class); CLASS_BINDING.put("positiveInteger", Integer.class); CLASS_BINDING.put("integerList", Integer.class); CLASS_BINDING.put("time", Date.class); CLASS_BINDING.put("duration", String.class); CLASS_BINDING.put("CalDate", String.class); //TODO should be date CLASS_BINDING.put("anyType", String.class); CLASS_BINDING.put("ID", String.class); CLASS_BINDING.put("StringOrRefType", String.class); CLASS_BINDING.put("token", String.class); CLASS_BINDING.put("NCName", String.class); CLASS_BINDING.put("TimePositionUnion", String.class); CLASS_BINDING.put("Name", String.class); // GML geometry types CLASS_BINDING.put("AbstractGeometry", Geometry.class); CLASS_BINDING.put("AbstractGeometryType", Geometry.class); CLASS_BINDING.put("AbstractGeometryTypeCollection",Geometry.class); CLASS_BINDING.put("GeometryPropertyType", Geometry.class); CLASS_BINDING.put("MultiPoint", MultiPoint.class); CLASS_BINDING.put("MultiPointType", MultiPoint.class); CLASS_BINDING.put("MultiPointPropertyType", MultiPoint.class); CLASS_BINDING.put("Point", Point.class); CLASS_BINDING.put("PointType", Point.class); CLASS_BINDING.put("PointPropertyType", Point.class); CLASS_BINDING.put("Curve", LineString.class); CLASS_BINDING.put("CurveType", LineString.class); CLASS_BINDING.put("CurvePropertyType", LineString.class); CLASS_BINDING.put("MultiGeometry", GeometryCollection.class); CLASS_BINDING.put("MultiGeometryType", GeometryCollection.class); CLASS_BINDING.put("MultiGeometryPropertyType", GeometryCollection.class); CLASS_BINDING.put("CompositeCurve", MultiLineString.class); CLASS_BINDING.put("CompositeCurveType", MultiLineString.class); CLASS_BINDING.put("CompositeCurvePropertyType", MultiLineString.class); CLASS_BINDING.put("MultiLineString", MultiLineString.class); CLASS_BINDING.put("MultiLineStringType", MultiLineString.class); CLASS_BINDING.put("MultiLineStringPropertyType", MultiLineString.class); CLASS_BINDING.put("MultiCurve", MultiLineString.class); CLASS_BINDING.put("MultiCurveType", MultiLineString.class); CLASS_BINDING.put("MultiCurvePropertyType", MultiLineString.class); CLASS_BINDING.put("Envelope", Envelope.class); CLASS_BINDING.put("EnvelopeType", Envelope.class); CLASS_BINDING.put("EnvelopePropertyType", Envelope.class); CLASS_BINDING.put("PolyHedralSurface", MultiPolygon.class); CLASS_BINDING.put("PolyHedralSurfaceType", MultiPolygon.class); CLASS_BINDING.put("PolyHedralSurfacePropertyType", MultiPolygon.class); CLASS_BINDING.put("MultiSurfacePropertyType", MultiPolygon.class); CLASS_BINDING.put("MultiPolygon", MultiPolygon.class); CLASS_BINDING.put("MultiPolygonType", MultiPolygon.class); CLASS_BINDING.put("MultiPolygonPropertyType", MultiPolygon.class); CLASS_BINDING.put("SurfaceType", Polygon.class); CLASS_BINDING.put("SurfacePropertyType", Polygon.class); CLASS_BINDING.put("Polygon", Polygon.class); CLASS_BINDING.put("PolygonType", Polygon.class); CLASS_BINDING.put("PolygonPropertyType", Polygon.class); CLASS_BINDING.put("Ring", LinearRing.class); CLASS_BINDING.put("RingType", LinearRing.class); CLASS_BINDING.put("RingPropertyType", LinearRing.class); CLASS_BINDING.put("LinearRing", LinearRing.class); CLASS_BINDING.put("LinearRingType", LinearRing.class); CLASS_BINDING.put("LinearRingPropertyType", LinearRing.class); for(Entry<String,Class> entry : CLASS_BINDING.entrySet()){ if(Geometry.class.isAssignableFrom(entry.getValue())){ GEOMETRIC_NAME.add(entry.getKey()); } } } public static boolean isGeometricType(final QName elementType) { if (elementType != null && elementType.getNamespaceURI().contains(GML_311_NAMESPACE)) { return GEOMETRIC_NAME.contains(elementType.getLocalPart()); } return false; } public static boolean isGeometricType(final GenericName elementType) { if (elementType != null && NamesExt.getNamespace(elementType)!=null && NamesExt.getNamespace(elementType).contains(GML_311_NAMESPACE)) { return GEOMETRIC_NAME.contains(elementType.tip().toString()); } return false; } public static boolean isPrimitiveType(final QName elementType) { if (elementType != null) { return CLASS_BINDING.containsKey(elementType.getLocalPart()); } return false; } public static boolean isNillable(final PropertyType type) { if(type instanceof AttributeType){ return FeatureExt.getCharacteristicValue(type, GMLConvention.NILLABLE_PROPERTY.toString(), false); } return false; } /** * Return a primitive Class from the specified XML QName (extracted from an xsd file). * * @param name A XML QName. * @return A Class. */ public static Class getTypeFromQName(final QName name) { if (name != null) { final Class result = CLASS_BINDING.get(name.getLocalPart()); if (result == null) { throw new IllegalArgumentException("unexpected type:" + name); } return result; } return null; } /** * Return a primitive Class from the specified XML QName (extracted from an xsd file). * * @param name A XML QName. * @return A Class. */ public static boolean existPrimitiveType(final String name) { if (name != null) { return CLASS_BINDING.get(name) != null; } return false; } private static final Map<Class, QName> NAME_BINDING = new HashMap<>(); static { // Special case when we get a Collection or Map we return String => TODO NAME_BINDING.put(List.class, new QName("http://www.w3.org/2001/XMLSchema", "string")); NAME_BINDING.put(Map.class, new QName("http://www.w3.org/2001/XMLSchema", "string")); NAME_BINDING.put(Collection.class, new QName("http://www.w3.org/2001/XMLSchema", "string")); NAME_BINDING.put(String.class, new QName("http://www.w3.org/2001/XMLSchema", "string")); NAME_BINDING.put(String[].class, new QName("http://www.w3.org/2001/XMLSchema", "string")); NAME_BINDING.put(Float.class, new QName("http://www.w3.org/2001/XMLSchema", "float")); NAME_BINDING.put(Float[].class, new QName("http://www.w3.org/2001/XMLSchema", "float")); NAME_BINDING.put(Long.class, new QName("http://www.w3.org/2001/XMLSchema", "long")); NAME_BINDING.put(Long[].class, new QName("http://www.w3.org/2001/XMLSchema", "long")); NAME_BINDING.put(Integer.class, new QName("http://www.w3.org/2001/XMLSchema", "integer")); NAME_BINDING.put(Integer[].class, new QName("http://www.w3.org/2001/XMLSchema", "integer")); NAME_BINDING.put(Double.class, new QName("http://www.w3.org/2001/XMLSchema", "double")); NAME_BINDING.put(Double[].class, new QName("http://www.w3.org/2001/XMLSchema", "double")); NAME_BINDING.put(Date.class, new QName("http://www.w3.org/2001/XMLSchema", "date")); NAME_BINDING.put(Date[].class, new QName("http://www.w3.org/2001/XMLSchema", "date")); NAME_BINDING.put(java.sql.Date.class, new QName("http://www.w3.org/2001/XMLSchema", "date")); NAME_BINDING.put(java.sql.Date[].class, new QName("http://www.w3.org/2001/XMLSchema", "date")); NAME_BINDING.put(Timestamp.class, new QName("http://www.w3.org/2001/XMLSchema", "dateTime")); NAME_BINDING.put(Timestamp[].class, new QName("http://www.w3.org/2001/XMLSchema", "dateTime")); NAME_BINDING.put(Boolean.class, new QName("http://www.w3.org/2001/XMLSchema", "boolean")); NAME_BINDING.put(Boolean[].class, new QName("http://www.w3.org/2001/XMLSchema", "boolean")); NAME_BINDING.put(BigDecimal.class, new QName("http://www.w3.org/2001/XMLSchema", "decimal")); NAME_BINDING.put(BigDecimal[].class, new QName("http://www.w3.org/2001/XMLSchema", "decimal")); NAME_BINDING.put(Short.class, new QName("http://www.w3.org/2001/XMLSchema", "short")); NAME_BINDING.put(Short[].class, new QName("http://www.w3.org/2001/XMLSchema", "short")); NAME_BINDING.put(int.class, new QName("http://www.w3.org/2001/XMLSchema", "int")); NAME_BINDING.put(int[].class, new QName("http://www.w3.org/2001/XMLSchema", "int")); NAME_BINDING.put(QName.class, new QName("http://www.w3.org/2001/XMLSchema", "QName")); NAME_BINDING.put(QName[].class, new QName("http://www.w3.org/2001/XMLSchema", "QName")); NAME_BINDING.put(URI.class, new QName("http://www.w3.org/2001/XMLSchema", "anyURI")); NAME_BINDING.put(URI[].class, new QName("http://www.w3.org/2001/XMLSchema", "anyURI")); NAME_BINDING.put(URL.class, new QName("http://www.w3.org/2001/XMLSchema", "anyURI")); NAME_BINDING.put(Byte.class, new QName("http://www.w3.org/2001/XMLSchema", "byte")); NAME_BINDING.put(byte[].class, new QName("http://www.w3.org/2001/XMLSchema", "base64Binary")); } private static final Map<Class, QName> GEOMETRY_NAME_BINDING_311 = new HashMap<Class, QName>(); static { GEOMETRY_NAME_BINDING_311.put(MultiPoint.class, new QName(GML_311_NAMESPACE, "MultiPoint")); GEOMETRY_NAME_BINDING_311.put(Point.class, new QName(GML_311_NAMESPACE, "Point")); GEOMETRY_NAME_BINDING_311.put(LineString.class, new QName(GML_311_NAMESPACE, "Curve")); GEOMETRY_NAME_BINDING_311.put(GeometryCollection.class, new QName(GML_311_NAMESPACE, "MultiGeometry")); GEOMETRY_NAME_BINDING_311.put(MultiLineString.class, new QName(GML_311_NAMESPACE, "CompositeCurve")); GEOMETRY_NAME_BINDING_311.put(Envelope.class, new QName(GML_311_NAMESPACE, "Envelope")); GEOMETRY_NAME_BINDING_311.put(MultiPolygon.class, new QName(GML_311_NAMESPACE, "MultiPolygon")); GEOMETRY_NAME_BINDING_311.put(Polygon.class, new QName(GML_311_NAMESPACE, "Polygon")); GEOMETRY_NAME_BINDING_311.put(LinearRing.class, new QName(GML_311_NAMESPACE, "Ring")); } private static final Map<Class, QName> GEOMETRY_NAME_BINDING_321 = new HashMap<Class, QName>(); static { GEOMETRY_NAME_BINDING_321.put(MultiPoint.class, new QName(GML_321_NAMESPACE, "MultiPointType")); GEOMETRY_NAME_BINDING_321.put(Point.class, new QName(GML_321_NAMESPACE, "PointType")); GEOMETRY_NAME_BINDING_321.put(LineString.class, new QName(GML_321_NAMESPACE, "CurveType")); GEOMETRY_NAME_BINDING_321.put(GeometryCollection.class, new QName(GML_321_NAMESPACE, "MultiGeometryType")); GEOMETRY_NAME_BINDING_321.put(MultiLineString.class, new QName(GML_321_NAMESPACE, "CompositeCurveType")); GEOMETRY_NAME_BINDING_321.put(Envelope.class, new QName(GML_321_NAMESPACE, "EnvelopeTypr")); GEOMETRY_NAME_BINDING_321.put(MultiPolygon.class, new QName(GML_321_NAMESPACE, "MultiPolygonType")); GEOMETRY_NAME_BINDING_321.put(Polygon.class, new QName(GML_321_NAMESPACE, "PolygonType")); GEOMETRY_NAME_BINDING_321.put(LinearRing.class, new QName(GML_321_NAMESPACE, "RingType")); } /** * Return a QName intended to be used in a xsd XML file fro mthe specified class. * * @param type A prmitive type Class. * @param gmlVersion * @return A QName describing the class. */ public static QName getQNameFromType(final PropertyType type, final String gmlVersion) { if (type instanceof AttributeType) { final Class binding = ((AttributeType)type).getValueClass(); final QName result; if (Geometry.class.isAssignableFrom(binding)) { if ("3.2.1".equals(gmlVersion)) { result = GEOMETRY_NAME_BINDING_321.get(binding); } else { result = GEOMETRY_NAME_BINDING_311.get(binding); } if (result == null) { if ("3.2.1".equals(gmlVersion)) { return new QName(GML_321_NAMESPACE, "GeometryPropertyType"); } else { return new QName(GML_311_NAMESPACE, "GeometryPropertyType"); } } // maybe we can find a better way to handle Enum. for now we set a String value } else if (binding.isEnum()){ result = new QName("http://www.w3.org/2001/XMLSchema", "string"); } else if (binding.equals(Object.class)) { if ("3.2.1".equals(gmlVersion)) { result = new QName(GML_321_NAMESPACE, "AbstractObject"); } else { result = new QName(GML_311_NAMESPACE, "_Object"); } } else { result = NAME_BINDING.get(binding); } if (result == null) { throw new IllegalArgumentException("unexpected type:" + binding); } return result; } return null; } /** * Return a QName intended to be used in a xsd XML file fro mthe specified class. * * @param type A prmitive type Class. * @param gmlVersion * @return A QName describing the class. */ public static QName getQNameFromType(final FeatureType type, final String gmlVersion) { return new QName(NamesExt.getNamespace(type.getName()), getNameWithTypeSuffix(type.getName().tip().toString())); } /** * Return an String representation of an Event Type. * * @param eventType An XMLEvent type. * @return A string representation or "UNKNOWN_EVENT_TYPE" if the integer does not correspound to an XMLEvent type. */ public static String getEventTypeString(final int eventType) { switch (eventType) { case XMLEvent.START_DOCUMENT: return "START_DOCUMENT"; case XMLEvent.END_DOCUMENT: return "END_DOCUMENT"; case XMLEvent.START_ELEMENT: return "START_ELEMENT"; case XMLEvent.END_ELEMENT: return "END_ELEMENT"; case XMLEvent.PROCESSING_INSTRUCTION: return "PROCESSING_INSTRUCTION"; case XMLEvent.CHARACTERS: return "CHARACTERS"; case XMLEvent.COMMENT: return "COMMENT"; } return "UNKNOWN_EVENT_TYPE"; } /** * Return a String representation of an Object. * Accepted types are : - Integer, Long, String * Else it return null. * * @param obj A primitive object * @return A String representation of the Object. */ public static String getStringValue(final Object obj) { if (obj instanceof String) { return (String) obj; } else if (obj instanceof Timestamp) { return timestampFormatter.format(new Date(((Timestamp)obj).getTime())); } else if (obj instanceof java.sql.Date) { // sql date does not have return obj.toString() + 'Z'; } else if (obj instanceof java.util.Date) { return dateFormatter.format((java.util.Date) obj); } else if (obj instanceof Number || obj instanceof Boolean || obj instanceof URI) { return obj.toString(); } else if (obj instanceof byte[]) { return Base64.encodeBytes((byte[])obj); } else if (obj != null) { LOGGER.log(Level.WARNING, "Unhandled type :" + obj.getClass(),new IllegalArgumentException()); } return null; } /** * Return a List of QName from a Set Of Name. * @param typeNames * @return */ public static List<QName> getQNameListFromNameSet(final Collection<GenericName> typeNames) { final List<QName> result = new ArrayList<QName>(typeNames.size()); for (GenericName typeName : typeNames) { result.add(Utils.getQnameFromName(typeName)); } return result; } /** * Return a List of Name from a Set Of QName. * @param typeNames * @return */ public static List<GenericName> getNameListFromQNameSet(final Collection<QName> typeNames) { final List<GenericName> result = new ArrayList<GenericName>(typeNames.size()); for (QName typeName : typeNames) { result.add(Utils.getNameFromQname(typeName)); } return result; } public static URI resolveURI(URI base, String location) throws MalformedURLException, URISyntaxException{ //try an url if (location.startsWith("http://") || location.startsWith("https://")) { return new URI(location); } //try to file Path p = Paths.get(location); if (Files.exists(p)) { return p.toUri(); } //try a jar resource final URL jarUrl = Utils.class.getResource(location); if (jarUrl != null) { return jarUrl.toURI(); } //try to resolve a relative path if (base!=null) { p = Paths.get(base); if (Files.exists(p)) { if (Files.isRegularFile(p)){ p = p.getParent(); } p = p.resolve(location); if (Files.exists(p)){ return p.toUri(); } } } throw new MalformedURLException("Could not resolve xsd location : "+location); } public static Collection<?> propertyValueAsList(Feature feature, String propertyName){ final Object val = feature.getPropertyValue(propertyName); final PropertyType type = feature.getType().getProperty(propertyName); if(type instanceof AttributeType){ if(((AttributeType)type).getMinimumOccurs()>1){ return (Collection<?>) val; }else{ return Collections.singleton(val); } }else if(type instanceof FeatureAssociationRole){ if(((FeatureAssociationRole)type).getMinimumOccurs()>1){ return (Collection<?>) val; }else{ return Collections.singleton(val); } }else if(type instanceof Operation){ return Collections.singleton(val); }else{ throw new IllegalArgumentException("Unknown propert type : "+type); } } /** * Retrieve an XSD schema from a http location * @param location * @return */ public static Schema getDistantSchema(final String location) { URI schemaUri = null; try { //search in the jar files if we have it if (location.startsWith("http://schemas.opengis.net/")) { String localUrl = location.replace("http://schemas.opengis.net/", "/org/geotoolkit/xsd/"); URL url = Utils.class.getResource(localUrl); if (url != null) { schemaUri = Utils.class.getResource(localUrl).toURI(); } } if (schemaUri==null) { schemaUri = resolveURI(null, location); } } catch (MalformedURLException | URISyntaxException ex) { LOGGER.log(Level.WARNING, ex.getMessage(), ex); return null; } try { LOGGER.log(Level.FINE, "retrieving:{0}", location); final Unmarshaller u = XSDMarshallerPool.getInstance().acquireUnmarshaller(); final Object obj = u.unmarshal(IOUtilities.open(schemaUri)); XSDMarshallerPool.getInstance().recycle(u); if (obj instanceof Schema) { return (Schema) obj; } else { LOGGER.log(Level.WARNING, "Bad content for imported schema:{0}", location); } } catch (IOException ex) { LOGGER.log(Level.WARNING, "IO exception trying to retrieve imported schema:" + location, ex); } catch (JAXBException ex) { LOGGER.log(Level.WARNING, "JAXB exception while reading imported schema:" + location, ex); } LOGGER.log(Level.WARNING, "Schema ressource not found:" + location); return null; } /** * Return the location a the Import/include element. * * if the location is local, the base location will be added to the result. * * @param baseLocation * @param attr * @return */ public static String getIncludedLocation(final String baseLocation, final Object attr) { final String schemaLocation; if (attr instanceof Import) { schemaLocation = ((Import)attr).getSchemaLocation(); } else if (attr instanceof Include) { schemaLocation = ((Include)attr).getSchemaLocation(); } else { return null; } if (schemaLocation != null && baseLocation != null && baseLocation.startsWith("http://") && !schemaLocation.startsWith("http://")) { if(schemaLocation.startsWith("./")){ return baseLocation + schemaLocation.substring(2); }else{ return baseLocation + schemaLocation; } } else { return schemaLocation; } } public static String getNameWithTypeSuffix(String name){ if(name.endsWith("Type")){ return name; }else{ return name+="Type"; } } public static String getNameWithoutTypeSuffix(String name){ if(name.endsWith("Type")){ return name.substring(0,name.length()-4); }else{ return name; } } public static String getNameWithPropertyTypeSuffix(String name){ name = getNameWithoutTypeSuffix(name); if (name.endsWith("PropertyType")) { return name; } else { return name += "PropertyType"; } } public static Set<String> listAllSubNamespaces(FeatureType type, String namespace){ final Set<String> ns = new HashSet<>(); final Set<GenericName> visited = new HashSet<>(); listAllSubNamespaces(type, ns, visited, namespace); return ns; } public static Set<String> listAllSubNamespaces(PropertyType type, String namespace){ final Set<String> ns = new HashSet<>(); final Set<GenericName> visited = new HashSet<>(); listAllSubNamespaces(type, ns, visited, namespace); return ns; } private static void listAllSubNamespaces(FeatureType type, Set<String> ns, Set<GenericName> visited, final String namespace){ final GenericName name = type.getName(); if(visited.contains(name)){ //avoid cyclic loops return; } visited.add(name); String typeUri = NamesExt.getNamespace(type.getName()); for(PropertyType pd : type.getProperties(true)){ if (typeUri.equals(namespace)) { String nsuri = NamesExt.getNamespace(pd.getName()); if(nsuri!=null) ns.add(nsuri); } listAllSubNamespaces(pd, ns, visited, namespace); } } private static void listAllSubNamespaces(PropertyType type, Set<String> ns, Set<GenericName> visited, final String namespace){ final GenericName name = type.getName(); if(visited.contains(name)){ //avoid cyclic loops return; } visited.add(name); String typeUri = NamesExt.getNamespace(type.getName()); if(type instanceof FeatureAssociationRole){ final FeatureType valueType = ((FeatureAssociationRole)type).getValueType(); listAllSubNamespaces(valueType, ns, visited, namespace); } } public static Set<String> listAllNamespaces(PropertyType type){ final Set<String> ns = new HashSet<>(); final Set<GenericName> visited = new HashSet<>(); listAllNamespaces(type, ns, visited); return ns; } public static Set<String> listAllNamespaces(FeatureType type){ final Set<String> ns = new HashSet<>(); final Set<GenericName> visited = new HashSet<>(); listAllNamespaces(type, ns, visited); return ns; } private static void listAllNamespaces(PropertyType type, Set<String> ns, Set<GenericName> visited){ final GenericName name = type.getName(); if(visited.contains(name)){ //avoid cyclic loops return; } visited.add(name); String nsuri = NamesExt.getNamespace(type.getName()); if(nsuri!=null) ns.add(nsuri); if(type instanceof FeatureAssociationRole){ final FeatureAssociationRole far = (FeatureAssociationRole)type; try { final FeatureType valueType = far.getValueType(); listAllNamespaces(valueType, ns, visited); } catch(IllegalStateException ex) { String n = NamesExt.getNamespace(Features.getValueTypeName(far)); if(n!=null) ns.add(n); } } } private static void listAllNamespaces(FeatureType type, Set<String> ns, Set<GenericName> visited){ final GenericName name = type.getName(); if(visited.contains(name)){ //avoid cyclic loops return; } visited.add(name); String nsuri = NamesExt.getNamespace(type.getName()); if(nsuri!=null) ns.add(nsuri); final FeatureType ct = (FeatureType) type; for(PropertyType pd : ct.getProperties(true)){ nsuri = NamesExt.getNamespace(pd.getName()); if(nsuri!=null) ns.add(nsuri); listAllNamespaces(pd, ns, visited); } } }