/* * citygml4j - The Open Source Java API for CityGML * https://github.com/citygml4j * * Copyright 2013-2017 Claus Nagel <claus.nagel@gmail.com> * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.citygml4j.builder.jaxb.xml.io.reader; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Set; import javax.xml.namespace.QName; import org.citygml4j.model.citygml.CityGML; import org.citygml4j.model.citygml.CityGMLClass; import org.citygml4j.model.citygml.ade.ADEComponent; import org.citygml4j.model.module.Module; import org.citygml4j.model.module.Modules; import org.citygml4j.model.module.citygml.CityGMLModule; import org.citygml4j.model.module.gml.GMLCoreModule; import org.citygml4j.xml.io.reader.FeatureReadMode; import org.citygml4j.xml.io.reader.MissingADESchemaException; import org.citygml4j.xml.schema.ElementDecl; import org.citygml4j.xml.schema.Schema; import org.citygml4j.xml.schema.SchemaHandler; import org.xml.sax.SAXException; public class XMLElementChecker { private final SchemaHandler schemaHandler; private final FeatureReadMode featureReadMode; private final boolean keepInlineAppearance; private final boolean parseSchema; private final boolean failOnMissingADESchema; private final Set<Class<? extends CityGML>> excludes; private HashSet<String> cityGMLFeatureProperties; private HashMap<String, HashSet<String>> adeFeatureProperties; XMLElementChecker(SchemaHandler schemaHandler, FeatureReadMode featureReadMode, boolean keepInlineAppearance, boolean parseSchema, boolean failOnMissingADESchema, Set<Class<? extends CityGML>> exlcudes, List<QName> featureProperties) { this.schemaHandler = schemaHandler; this.featureReadMode = featureReadMode; this.keepInlineAppearance = keepInlineAppearance; this.parseSchema = parseSchema; this.failOnMissingADESchema = failOnMissingADESchema; this.excludes = exlcudes; if (featureReadMode == FeatureReadMode.SPLIT_PER_COLLECTION_MEMBER) initCollectionSplitProperties(featureProperties); } public boolean isCityGMLElement(String namespaceURI) { return namespaceURI.startsWith("http://www.opengis.net/citygml"); } public boolean isCityGMLElement(QName name) { return isCityGMLElement(name.getNamespaceURI()); } public boolean isGMLElement(String namespaceURI) { return GMLCoreModule.v3_1_1.getNamespaceURI().equals(namespaceURI); } public boolean isGMLElement(QName name) { return isGMLElement(name.getNamespaceURI()); } public boolean isParentInfoElement(String namespaceURI, String localPart) { if (isGMLElement(namespaceURI)) { if (localPart.equals("metaDataProperty") || localPart.equals("description") || localPart.equals("name") || localPart.equals("boundedBy") || localPart.equals("location")) return true; } else if (isCityGMLElement(namespaceURI)) { if (localPart.equals("appearance") || localPart.equals("appearanceMember")) return true; } return false; } public boolean isParentInfoElement(QName name) { return isParentInfoElement(name.getNamespaceURI(), name.getLocalPart()); } public ElementInfo getGMLFeatureProperty(String localName) { ElementInfo elementInfo = null; if (localName.equals("featureMember")) { elementInfo = new ElementInfo(); elementInfo.isFeatureProperty = true; elementInfo.hasXLink = true; } else if (localName.equals("featureMembers")) { elementInfo = new ElementInfo(); elementInfo.isFeatureProperty = true; } return elementInfo; } public ElementInfo getCityGMLFeatureProperty(QName name, XMLChunkImpl currentChunk) { ElementInfo elementInfo = null; String localName = name.getLocalPart(); String namespaceURI = name.getNamespaceURI(); boolean isFeatureProperty = false; boolean skipNestedElements = false; if (featureReadMode == FeatureReadMode.SPLIT_PER_COLLECTION_MEMBER) { if (cityGMLFeatureProperties.contains(localName)) isFeatureProperty = true; } else { Module module = Modules.getModule(namespaceURI); if (module instanceof CityGMLModule) { CityGMLModule cityGMLModule = (CityGMLModule)module; isFeatureProperty = cityGMLModule.hasFeaturePropertyElement(localName); if (localName.equals("appearance")) skipNestedElements = keepInlineAppearance; } } if (isFeatureProperty) { elementInfo = new ElementInfo(); elementInfo.isFeatureProperty = true; elementInfo.hasXLink = true; elementInfo.skipNestedElements = skipNestedElements; } return elementInfo; } public ElementInfo getCityGMLFeature(QName name, boolean isSetType) { ElementInfo elementInfo = null; String localName = name.getLocalPart(); String namespaceURI = name.getNamespaceURI(); Module module = Modules.getModule(namespaceURI); if (module instanceof CityGMLModule) { CityGMLModule cityGMLModule = (CityGMLModule)module; Class<? extends CityGML> featureClass = cityGMLModule.getFeatureElementClass(localName); if (featureClass != null) { elementInfo = new ElementInfo(); elementInfo.isFeature = true; if (isSetType) elementInfo.type = CityGMLClass.fromModelClass(featureClass); if (!excludes.isEmpty()) { for (Class<? extends CityGML> exclude : excludes) { if (isSubclass(featureClass, exclude)) { elementInfo.isFeature = false; break; } } } } } return elementInfo; } public ElementInfo getADEElementInfo(QName name, ElementInfo lastElementInfo, boolean checkForFeature, boolean isSetType) throws MissingADESchemaException { ElementInfo elementInfo = null; String localName = name.getLocalPart(); String namespaceURI = name.getNamespaceURI(); Schema schema = schemaHandler.getSchema(namespaceURI); // try and resolve unknown ADE schema if (schema == null && parseSchema) { try { schemaHandler.resolveAndParseSchema(namespaceURI); schema = schemaHandler.getSchema(namespaceURI); } catch (SAXException e) { // } catch (MissingADESchemaException e) { if (failOnMissingADESchema) throw e; } } if (schema != null) { ElementDecl parent = lastElementInfo != null ? lastElementInfo.elementDecl : null; ElementDecl elementDecl = schema.getElementDecl(localName, parent); if (elementDecl != null) { elementInfo = new ElementInfo(elementDecl); if (checkForFeature && elementDecl.isGlobal() && elementDecl.isFeature()) { elementInfo.isFeature = true; if (isSetType) elementInfo.type = CityGMLClass.ADE_COMPONENT; if (!excludes.isEmpty()) { for (Class<? extends CityGML> exclude : excludes) { if (isSubclass(ADEComponent.class, exclude)) { elementInfo.isFeature = false; break; } } } } else if (elementDecl.hasXLinkAttribute()) { elementInfo.isFeatureProperty = true; elementInfo.hasXLink = true; } } } return elementInfo; } public ElementInfo getElementInfo(QName name, XMLChunkImpl currentChunk, ElementInfo lastElementInfo, boolean isSetType) throws MissingADESchemaException { if (lastElementInfo != null && lastElementInfo.skipNestedElements) return lastElementInfo; ElementInfo elementInfo = null; if (lastElementInfo != null && lastElementInfo.isFeatureProperty) { // we do not support GML features if (isCityGMLElement(name)) elementInfo = getCityGMLFeature(name, isSetType); else elementInfo = getADEElementInfo(name, lastElementInfo, true, isSetType); } else { if (isGMLElement(name)) elementInfo = getGMLFeatureProperty(name.getLocalPart()); else if (isCityGMLElement(name)) elementInfo = getCityGMLFeatureProperty(name, currentChunk); else if (checkADEFeatureProperty(name)) elementInfo = getADEElementInfo(name, lastElementInfo, false, false); } return elementInfo; } private void initCollectionSplitProperties(List<QName> featureProperties) { cityGMLFeatureProperties = new HashSet<String>(); cityGMLFeatureProperties.add("cityObjectMember"); cityGMLFeatureProperties.add("appearanceMember"); cityGMLFeatureProperties.add("groupMember"); cityGMLFeatureProperties.add("parent"); for (QName featureProperty : featureProperties) { String localName = featureProperty.getLocalPart(); String namespaceURI = featureProperty.getNamespaceURI(); if (namespaceURI.length() == 0) { for (CityGMLModule module : Modules.getCityGMLModules()) { if (module.hasFeaturePropertyElement(localName)) { cityGMLFeatureProperties.add(localName); break; } } } else if (isCityGMLElement(namespaceURI)) { Module module = Modules.getModule(namespaceURI); if (module instanceof CityGMLModule && ((CityGMLModule)module).hasFeaturePropertyElement(localName)) cityGMLFeatureProperties.add(localName); } else { if (adeFeatureProperties == null) adeFeatureProperties = new HashMap<String, HashSet<String>>(); HashSet<String> propertySet = adeFeatureProperties.get(namespaceURI); if (propertySet == null) { propertySet = new HashSet<String>(); adeFeatureProperties.put(namespaceURI, propertySet); } propertySet.add(localName); } } } private boolean checkADEFeatureProperty(QName name) { if (featureReadMode == FeatureReadMode.SPLIT_PER_FEATURE) return true; if (adeFeatureProperties != null) { HashSet<String> properties = adeFeatureProperties.get(name.getNamespaceURI()); if (properties != null) return properties.contains(name.getLocalPart()); } return false; } private boolean isSubclass(Class<?> a, Class<?> b) { if (a == null || b == null) return false; if (a == b) return true; if (a.getSuperclass() != Object.class) return isSubclass(a.getSuperclass(), b); return false; } static class ElementInfo { private final ElementDecl elementDecl; private boolean isFeature = false; private boolean isFeatureProperty = false; private boolean hasXLink = false; private boolean skipNestedElements = false; private CityGMLClass type = CityGMLClass.UNDEFINED; ElementInfo() { elementDecl = null; } ElementInfo(ElementDecl decl) { this.elementDecl = decl; } boolean isFeature() { return isFeature; } boolean hasXLink() { return hasXLink; } CityGMLClass getType() { return type; } } }