package nl.ipo.cds.etl.featuretype;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.Reader;
import java.util.HashSet;
import java.util.Set;
import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamReader;
import nl.ipo.cds.domain.AttributeType;
import nl.ipo.cds.domain.FeatureType;
import org.apache.commons.io.IOUtils;
import org.apache.xerces.xs.XSComplexTypeDefinition;
import org.deegree.commons.tom.gml.property.PropertyType;
import org.deegree.feature.types.AppSchema;
import org.deegree.feature.types.property.CodePropertyType;
import org.deegree.feature.types.property.CustomPropertyType;
import org.deegree.feature.types.property.GeometryPropertyType;
import org.deegree.feature.types.property.SimplePropertyType;
import org.deegree.gml.GMLVersion;
import org.deegree.gml.schema.GMLAppSchemaReader;
import org.w3c.dom.ls.LSInput;
public class GMLFeatureTypeParser {
private final GMLVersion gmlVersion;
private final static String XML_SCHEMA_LOCAL_NAME = "schema";
private final static String XML_SCHEMA_NAMESPACE = "http://www.w3.org/2001/XMLSchema";
public GMLFeatureTypeParser () {
this (null);
}
public GMLFeatureTypeParser (final GMLVersion gmlVersion) {
this.gmlVersion = gmlVersion;
}
public FeatureType parseSchema (final AppSchema appSchema, final String featureTypeName) throws FeatureTypeNotFoundException {
// Locate the feature type:
final org.deegree.feature.types.FeatureType featureType = findFeatureType (appSchema, featureTypeName);
if (featureType == null) {
throw new FeatureTypeNotFoundException (featureTypeName, appSchema);
}
final Set<DefaultFeatureTypeAttribute> attributes = new HashSet<DefaultFeatureTypeAttribute> ();
for (final PropertyType propertyType: featureType.getPropertyDeclarations ()) {
final AttributeType attributeType = getAttributeType (propertyType);
if (attributeType == null) {
continue;
}
final DefaultFeatureTypeAttribute attribute = new DefaultFeatureTypeAttribute (new DefaultQName (propertyType.getName ()), attributeType);
if (!attributes.contains (attribute)) {
attributes.add (attribute);
}
}
return new DefaultFeatureType (new DefaultQName (featureType.getName ()), attributes);
}
public FeatureType parseSchema (final InputStream inputStream, final String featureTypeName, final String encoding) throws FeatureTypeNotFoundException, ParseSchemaException {
final AppSchema appSchema = parseApplicationSchema (inputStream, encoding);
return parseSchema (appSchema, featureTypeName);
}
public AppSchema parseApplicationSchema (final InputStream inputStream, final String encoding) throws ParseSchemaException {
final ByteArrayOutputStream byteOutputStream = new ByteArrayOutputStream ();
final byte[] byteArray;
// Load the XML schema into a byte array:
try {
IOUtils.copy (inputStream, byteOutputStream);
byteArray = byteOutputStream.toByteArray ();
} catch (IOException e) {
throw new ParseSchemaException (e);
}
// Probe whether this inputStream contains an XML schema:
try {
final XMLStreamReader xmlStreamReader;
if (encoding == null) {
xmlStreamReader = XMLInputFactory.newInstance ().createXMLStreamReader (new ByteArrayInputStream (byteArray));
} else {
xmlStreamReader = XMLInputFactory.newInstance ().createXMLStreamReader (new ByteArrayInputStream (byteArray), encoding);
}
while (!xmlStreamReader.isStartElement () && xmlStreamReader.hasNext ()) {
xmlStreamReader.next ();
}
if (!xmlStreamReader.isStartElement ()
|| !xmlStreamReader.getLocalName ().equals (XML_SCHEMA_LOCAL_NAME)
|| !xmlStreamReader.getNamespaceURI ().equals (XML_SCHEMA_NAMESPACE)) {
throw new ParseSchemaException ("Not an XML schema");
}
} catch (XMLStreamException e) {
throw new ParseSchemaException (e);
}
// Parse the XML schema:
try {
final GMLAppSchemaReader reader = new GMLAppSchemaReader (gmlVersion, null, createInput (new ByteArrayInputStream (byteArray), encoding));
return reader.extractAppSchema ();
} catch (ClassCastException e) {
throw new ParseSchemaException (e);
} catch (ClassNotFoundException e) {
throw new ParseSchemaException (e);
} catch (InstantiationException e) {
throw new ParseSchemaException (e);
} catch (IllegalAccessException e) {
throw new ParseSchemaException (e);
}
}
private AttributeType getAttributeType (final PropertyType propertyType) {
if (propertyType instanceof GeometryPropertyType) {
return AttributeType.GEOMETRY;
} else if (propertyType instanceof CodePropertyType) {
return AttributeType.CODE;
} else if (propertyType instanceof CustomPropertyType) {
final CustomPropertyType pt = (CustomPropertyType)propertyType;
final XSComplexTypeDefinition typeDef = ((CustomPropertyType)propertyType).getXSDValueType ();
if (typeDef == null || !typeDef.getNamespace ().startsWith ("http://www.opengis.net/gml")) {
return null;
}
final String name = typeDef.getName ();
if ("CodeType".equals (name)) {
return AttributeType.CODE;
} else {
return null;
}
} else if (!(propertyType instanceof SimplePropertyType)) {
return null;
}
final SimplePropertyType pt = (SimplePropertyType)propertyType;
switch (pt.getPrimitiveType ().getBaseType ()) {
case BOOLEAN:
return AttributeType.BOOLEAN;
case DATE:
return AttributeType.DATE;
case DATE_TIME:
return AttributeType.DATE_TIME;
case DECIMAL:
// deegree 3 returns decimal for float and double, correct this:
if ("double".equals (pt.getElementDecl().getTypeDefinition().getName())) {
return AttributeType.DOUBLE;
} else if ("float".equals (pt.getElementDecl().getTypeDefinition().getName())) {
return AttributeType.FLOAT;
} else {
return AttributeType.DECIMAL;
}
case DOUBLE:
return AttributeType.DOUBLE;
case INTEGER:
return AttributeType.INTEGER;
case STRING:
return AttributeType.STRING;
case TIME:
return AttributeType.TIME;
default:
return null;
}
}
private org.deegree.feature.types.FeatureType findFeatureType (final AppSchema appSchema, final String featureTypeName) {
final Set<String> namespaces = appSchema.getAppNamespaces ();
for (final String namespace: namespaces) {
final org.deegree.feature.types.FeatureType ft = appSchema.getFeatureType (new javax.xml.namespace.QName (namespace, featureTypeName));
if (ft != null) {
return ft;
}
}
return null;
}
private LSInput createInput (final InputStream inputStream, final String encoding) {
final Input input = new Input ();
input.setByteStream (inputStream);
input.setEncoding (encoding);
return input;
}
private static class Input implements LSInput {
private Reader characterStream;
private InputStream byteStream;
private String stringData;
private String systemId;
private String publicId;
private String baseURI;
private String encoding;
private boolean certifiedText;
@Override
public Reader getCharacterStream() {
return characterStream;
}
@Override
public void setCharacterStream(Reader characterStream) {
this.characterStream = characterStream;
}
@Override
public InputStream getByteStream() {
return byteStream;
}
@Override
public void setByteStream(InputStream byteStream) {
this.byteStream = byteStream;
}
@Override
public String getStringData() {
return stringData;
}
@Override
public void setStringData(String stringData) {
this.stringData = stringData;
}
@Override
public String getSystemId() {
return systemId;
}
@Override
public void setSystemId(String systemId) {
this.systemId = systemId;
}
@Override
public String getPublicId() {
return publicId;
}
@Override
public void setPublicId(String publicId) {
this.publicId = publicId;
}
@Override
public String getBaseURI() {
return baseURI;
}
@Override
public void setBaseURI(String baseURI) {
this.baseURI = baseURI;
}
@Override
public String getEncoding() {
return encoding;
}
@Override
public void setEncoding(String encoding) {
this.encoding = encoding;
}
@Override
public boolean getCertifiedText() {
return certifiedText;
}
@Override
public void setCertifiedText(boolean certifiedText) {
this.certifiedText = certifiedText;
}
}
}