de.ii.xtraplatform.ogc.api.gml.parser.GMLSchemaParser.java Source code

Java tutorial

Introduction

Here is the source code for de.ii.xtraplatform.ogc.api.gml.parser.GMLSchemaParser.java

Source

/**
 * Copyright 2017 European Union, interactive instruments GmbH
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 */
/**
 * bla
 */
package de.ii.xtraplatform.ogc.api.gml.parser;

import com.sun.xml.xsom.XSAttributeUse;
import com.sun.xml.xsom.XSComplexType;
import com.sun.xml.xsom.XSElementDecl;
import com.sun.xml.xsom.XSModelGroup;
import com.sun.xml.xsom.XSParticle;
import com.sun.xml.xsom.XSSchema;
import com.sun.xml.xsom.XSSchemaSet;
import com.sun.xml.xsom.XSTerm;
import com.sun.xml.xsom.XSType;
import com.sun.xml.xsom.parser.XSOMParser;
import de.ii.xtraplatform.ogc.api.GML;
import de.ii.xtraplatform.ogc.api.exceptions.SchemaParseException;
import de.ii.xtraplatform.util.xml.XMLPathTracker;
import org.apache.http.HttpEntity;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.xml.sax.EntityResolver;
import org.xml.sax.ErrorHandler;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;

import java.io.IOException;
import java.net.URI;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 *
 * @author zahnen
 */
public class GMLSchemaParser {

    private static final Logger LOGGER = LoggerFactory.getLogger(GMLSchemaParser.class);
    private final List<GMLSchemaAnalyzer> analyzers;
    private final XMLPathTracker currentPath;
    private final List<XSElementDecl> abstractObjectDecl;
    private XSType gcoObjectType;
    private Set<String> complexTypes;
    private final URI baseURI;
    private EntityResolver entityResolver;

    public GMLSchemaParser(List<GMLSchemaAnalyzer> analyzers, URI baseURI) {
        this(analyzers, baseURI, new OGCEntityResolver());
    }

    public GMLSchemaParser(List<GMLSchemaAnalyzer> analyzers, URI baseURI, EntityResolver entityResolver) {
        this.analyzers = analyzers;
        this.currentPath = new XMLPathTracker();
        this.abstractObjectDecl = new ArrayList<XSElementDecl>();
        this.baseURI = baseURI;
        this.entityResolver = entityResolver;
    }

    public void parse(HttpEntity entity, Map<String, List<String>> elements) throws SchemaParseException {
        try {
            InputSource is = new InputSource(entity.getContent());
            parse(is, elements);
        } catch (IOException ex) {
            LOGGER.error("Error parsing application schema. {}", ex);
            throw new SchemaParseException("Error parsing application schema. {}", ex.getMessage());
        } finally {
            EntityUtils.consumeQuietly(entity);
        }
    }

    public void parse(InputSource is, Map<String, List<String>> elements) {
        parse(is, elements, true);
    }

    public void parse(InputSource is, Map<String, List<String>> elements, boolean lax) {
        //LOGGER.debug("Parsing GML application schema");
        XSOMParser parser = new XSOMParser();

        try {
            parser.setErrorHandler(new GMLSchemaParserErrorHandler());

            parser.setEntityResolver(entityResolver);

            is.setSystemId(baseURI.toString());

            parser.parse(is);

            XSSchemaSet schemas = parser.getResult();

            for (GML.VERSION version : GML.VERSION.values()) {
                XSSchema schema0 = schemas.getSchema(GML.getWord(version, GML.NAMESPACE.URI));
                if (schema0 != null) {
                    XSElementDecl a = schema0.getElementDecl(GML.getWord(version, GML.VOCABULARY.ABSTRACT_OBJECT));
                    if (a != null) {
                        abstractObjectDecl.add(a);
                    }
                }
            }

            XSSchema schema1 = schemas.getSchema("http://www.isotc211.org/2005/gco");
            if (schema1 != null) {
                gcoObjectType = schema1.getElementDecl("AbstractObject").getType();
            }

            for (Map.Entry<String, List<String>> ns : elements.entrySet()) {
                String nsuri = ns.getKey();
                String oldNsUri = null;
                //LOGGER.debug("namespace {}", nsuri);

                XSSchema schema = schemas.getSchema(nsuri);

                // workaround for broken WFSs where FeatureTypes are in different namespaces in Capabilities and Schema
                // in this case we search in the targetNamespace of the Schema
                if (schema == null && lax) {
                    LOGGER.info(
                            "Schema for Namespace '{}' not found, searching in targetNamespace schema instead. ",
                            ns.getKey());

                    // looks as if the schema for the targetNamespace of the document is always second in the list
                    schema = schemas.getSchema(1);
                    oldNsUri = nsuri;
                    nsuri = schema.getTargetNamespace();
                }

                for (String e : ns.getValue()) {
                    XSElementDecl elem = schema.getElementDecl(e);
                    if (elem != null && elem.getType().isComplexType()) {
                        //LOGGER.debug(" - element {}, type: {}", elem.getName(), elem.getType().getName());

                        for (GMLSchemaAnalyzer analyzer : analyzers) {
                            if (oldNsUri != null) {
                                analyzer.analyzeNamespaceRewrite(oldNsUri, nsuri, elem.getName());
                            }
                            analyzer.analyzeFeatureType(nsuri, elem.getName());
                            for (XSAttributeUse att : elem.getType().asComplexType().getAttributeUses()) {
                                //LOGGER.debug("   - attribute {}, required: {}, type: {}, ns: {}", att.getDecl().getName(), att.isRequired(), att.getDecl().getType().getName(), att.getDecl().getTargetNamespace());

                                analyzer.analyzeAttribute(att.getDecl().getTargetNamespace(),
                                        att.getDecl().getName(), att.getDecl().getType().getName(),
                                        att.isRequired());
                            }
                        }
                        XSParticle particle = elem.getType().asComplexType().getContentType().asParticle();
                        if (particle != null) {
                            XSTerm term = particle.getTerm();
                            if (term.isModelGroup()) {
                                complexTypes = new HashSet<String>();
                                parseGroup(term.asModelGroup(), 1, false);
                            }
                        }
                    }
                }
            }
        } catch (SAXException ex) {

            // File included in schema not found
            if (ex.getCause() != null && ex.getCause().getClass().getName().contains("FileNotFoundException")) {
                LOGGER.error(
                        "The GML application schema provided by the WFS imports schema '{}', but that schema cannot be accessed. A valid GML application schema is required to determine the layers of the proxy service and its characteristics.. Please contact the WFS provider to correct the schema error.",
                        ex.getCause().getMessage());
                throw new SchemaParseException(
                        "The GML application schema provided by the WFS imports schema '{}', but that schema cannot be accessed. A valid GML application schema is required to determine the layers of the proxy service and its characteristics.. Please contact the WFS provider to correct the schema error.",
                        ex.getCause().getMessage());
            }

            String msg = ex.getMessage();
            String msgex = "";
            if (msg != null && !msg.isEmpty()) {
                msg = "Parser details: " + msg;

                msgex = msg.replaceAll("<", "&lt;").replaceAll(">", "&gt;");
            }

            LOGGER.error(
                    "The GML application schema provided by the WFS is invalid. A valid GML application schema is required to determine the layers of the proxy service and its characteristics.. Please contact the WFS provider to correct the schema error. {}",
                    msg);
            SchemaParseException spe = new SchemaParseException(
                    "The GML application schema provided by the WFS is invalid. A valid GML application schema is required to determine the layers of the proxy service and its characteristics.. Please contact the WFS provider to correct the schema error. {}",
                    "");

            spe.addDetail(msgex);

            throw spe;
        }
    }

    private void parseGroup(XSModelGroup xsModelGroup, int depth, boolean isParentMultible) {
        //LOGGER.debug(new String(new char[depth]).replace("\0", "  ") + " - {}", xsModelGroup.getCompositor());

        // reset already visited complex types for every first level property of the featuretype,
        // meaning for every new path that should not contain recursion
        if (depth == 2) {
            complexTypes = new HashSet<String>();
        }

        XSParticle[] particles = xsModelGroup.getChildren();

        if (particles.length > 0) {
            for (XSParticle p : particles) {
                XSTerm pterm = p.getTerm();

                if (pterm.isElementDecl()) {
                    parseElementDecl(pterm.asElementDecl(), p.getMinOccurs().longValue(),
                            p.getMaxOccurs().longValue(), depth, isParentMultible);
                } else if (pterm.isModelGroup()) {
                    parseGroup(pterm.asModelGroup(), depth, isParentMultible);
                } else if (pterm.isModelGroupDecl()) {
                    parseGroup(pterm.asModelGroupDecl().getModelGroup(), depth, isParentMultible);
                }
            }
        }
    }

    private void parseElementDecl(XSElementDecl decl, long minOccurs, long maxOccurs, int depth,
            boolean isParentMultible) {

        String propertyName = null;
        XSType propertyType = null;
        boolean isObject = false;

        propertyName = decl.getName();

        String typeName = decl.getType().getBaseType().getName();
        String typeName0 = decl.getType().getName();

        propertyType = decl.getType();

        if (decl.getType().getName() != null) {
            XSElementDecl decl0 = decl.getSubstAffiliation();

            while (decl0 != null) {
                for (XSElementDecl e : abstractObjectDecl) {
                    if (decl0.equals(e)) {
                        isObject = true;
                        break;
                    }
                }
                if (isObject) {
                    break;
                }

                decl0 = decl0.getSubstAffiliation();
            }

            if (gcoObjectType != null) {
                if (propertyType.isDerivedFrom(gcoObjectType)) {
                    isObject = true;
                }
            }

        } else if (!decl.getType().getBaseType().getName().equals("anyType")) {
            propertyType = decl.getType().getBaseType();
        }

        String propertyTypeName = propertyType.getName();

        if (propertyType.isSimpleType()) {
            if (propertyType.asSimpleType().getPrimitiveType() != null) {
                propertyType = propertyType.asSimpleType().getPrimitiveType();
                propertyTypeName = propertyType.getName();
            } else if (propertyType.asSimpleType().asUnion() != null) {
                propertyTypeName = "string";
            }
        }

        boolean isComplex = propertyType.isComplexType();
        if (propertyType.isComplexType() && propertyType.asComplexType().getContentType().asSimpleType() != null) {
            if (propertyType.asComplexType().getContentType().asSimpleType().getPrimitiveType() != null) {
                propertyType = propertyType.asComplexType().getContentType().asSimpleType().getPrimitiveType();
                propertyTypeName = propertyType.getName();
                isComplex = propertyType.isComplexType();
            } else if (propertyType.asComplexType().getContentType().asSimpleType().asUnion() != null) {
                propertyTypeName = "string";
                isComplex = false;
            }
        }

        if (propertyType.isComplexType() && (maxOccurs > 1 || maxOccurs == -1)) {
            isParentMultible = true;
        }

        /*
         LOGGER.debug(new String(new char[depth]).replace("\0", "  ") + " - property, name: {}, type: {}, min: {}, max: {}, ns: {}",
         propertyName, propertyType.getName(), p.getMinOccurs(), p.getMaxOccurs(),
         decl.getTargetNamespace());
         * */
        propertyTypeName = propertyTypeName == null ? "" : propertyTypeName;

        for (GMLSchemaAnalyzer analyzer : analyzers) {

            analyzer.analyzeProperty(decl.getTargetNamespace(), propertyName, propertyTypeName, minOccurs,
                    maxOccurs, depth, isParentMultible, isComplex, isObject);
        }

        if (propertyType.isComplexType()) {
            parseComplexType(propertyType.asComplexType(), depth + 1, isParentMultible);
        }
    }

    private void parseComplexType(XSComplexType type, int depth, boolean isParenMultible) {
        if (type != null) {
            /*for (XSAttributeUse att : elem.getAttributeUses()) {
             LOGGER.debug("   - attribute {}, required: {}, type: {}, ns: {}", att.getDecl().getName(), att.isRequired(), att.getDecl().getType().getName(), att.getDecl().getTargetNamespace());
             analyzer.analyzeAttribute(att.getDecl().getTargetNamespace(), att.getDecl().getName(), att.getDecl().getType().getName(), att.isRequired());
             }*/

            // stop analyzing if the complex type already occurred in this path to avoid recursion
            if (complexTypes.add(type.getTargetNamespace() + ":" + type.getName())) {
                XSParticle particle = type.getContentType().asParticle();
                if (particle != null) {
                    XSTerm term = particle.getTerm();
                    if (term.isModelGroup()) {
                        parseGroup(term.asModelGroup(), depth, isParenMultible);
                    }
                }
            }
        }
    }

    private class GMLSchemaParserErrorHandler implements ErrorHandler {

        @Override
        public void warning(SAXParseException exception) throws SAXException {
            throw exception;
        }

        @Override
        public void error(SAXParseException exception) throws SAXException {
            throw exception;
        }

        @Override
        public void fatalError(SAXParseException exception) throws SAXException {
            throw exception;
        }
    }
}