/* * 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.xml.schema; import java.io.File; import java.io.IOException; import java.net.MalformedURLException; import java.net.URL; import java.util.HashMap; import java.util.HashSet; import java.util.Map.Entry; import java.util.Set; import javax.xml.XMLConstants; import javax.xml.parsers.SAXParserFactory; import javax.xml.transform.Source; import javax.xml.transform.stream.StreamSource; import org.citygml4j.model.module.Modules; import org.citygml4j.model.module.citygml.CityGMLModule; import org.citygml4j.xml.io.reader.MissingADESchemaException; import org.w3c.dom.Element; import org.xml.sax.EntityResolver; import org.xml.sax.ErrorHandler; import org.xml.sax.InputSource; import org.xml.sax.Locator; import org.xml.sax.SAXException; import com.sun.xml.xsom.XSSchema; import com.sun.xml.xsom.XSSchemaSet; import com.sun.xml.xsom.parser.AnnotationParserFactory; import com.sun.xml.xsom.parser.XSOMParser; public class SchemaHandler { private static SchemaHandler instance = null; private final HashSet<XSSchemaSet> schemaSets; private final HashMap<String, String> schemaLocations; private final HashMap<String, String> visited; private final HashMap<String, Schema> schemas; private EntityResolver schemaEntityResolver; private ErrorHandler schemaErrorHandler; private AnnotationParserFactory annotationParserFactory; public static synchronized SchemaHandler newInstance() throws SAXException { if (instance == null) { instance = new SchemaHandler(); URL schemaURL = SchemaHandler.class.getResource("/schemas/CityGML/citygml4j_profile.xsd"); if (schemaURL == null) throw new SAXException("Failed to parse CityGML schemas. Could not find '/schemas/CityGML/citygml4j_profile.xsd' on classpath."); instance.parse(schemaURL.toString()); } SchemaHandler schemaHandler = new SchemaHandler(); schemaHandler.schemaSets.addAll(instance.schemaSets); schemaHandler.visited.putAll(instance.visited); // CityGML 0.4.0 schemaHandler.schemaLocations.put("http://www.citygml.org/citygml/1/0/0", SchemaHandler.class.getResource("/schemas/CityGML/0.4.0/CityGML.xsd").toString()); return schemaHandler; } private SchemaHandler() { // just to thwart instantiation schemaSets = new HashSet<XSSchemaSet>(); schemaLocations = new HashMap<String, String>(); visited = new HashMap<String, String>(); schemas = new HashMap<String, Schema>(); } public void reset() { schemaSets.clear(); schemaLocations.clear(); visited.clear(); schemas.clear(); schemaSets.addAll(instance.schemaSets); visited.putAll(instance.visited); } public Schema getSchema(String namespaceURI) { Schema schema = schemas.get(namespaceURI); if (schema != null) return schema; // CityGML 0.4.0 if ("http://www.citygml.org/citygml/1/0/0".equals(namespaceURI)) { try { parse(schemaLocations.get(namespaceURI)); } catch (SAXException e) { // } } XSSchemaSet schemaSet = getXSSchemaSet(namespaceURI); if (schemaSet != null) { schema = new Schema(schemaSet, namespaceURI, this); schemas.put(namespaceURI, schema); } return schema; } private XSSchemaSet getXSSchemaSet(String namespaceURI) { for (XSSchemaSet schemaSet : schemaSets) for (XSSchema schema : schemaSet.getSchemas()) if (schema.getTargetNamespace().equals(namespaceURI)) return schemaSet; return null; } public EntityResolver getSchemaEntityResolver() { return schemaEntityResolver; } public void setSchemaEntityResolver(EntityResolver schemaEntityResolver) { this.schemaEntityResolver = schemaEntityResolver; } public ErrorHandler getErrorHandler() { return schemaErrorHandler; } public void setErrorHandler(ErrorHandler schemaErrorHandler) { this.schemaErrorHandler = schemaErrorHandler; } public AnnotationParserFactory getAnnotationParser() { return annotationParserFactory; } public void setAnnotationParser(AnnotationParserFactory annotationParserFactory) { this.annotationParserFactory = annotationParserFactory; } public boolean registerSchemaLocation(String namespaceURI, File schemaLocation) { if (Modules.getModule(namespaceURI) != null) return false; // CityGML 0.4.0 if ("http://www.citygml.org/citygml/1/0/0".equals(namespaceURI)) return false; schemaLocations.put(namespaceURI, schemaLocation.toURI().toString()); return true; } public void parseSchema(Element element) throws SAXException { String schemaLocation = element.getAttributeNS(XMLConstants.W3C_XML_SCHEMA_INSTANCE_NS_URI, "schemaLocation"); if (schemaLocation.length() == 0) schemaLocation = element.getAttribute("schemaLocation"); if (schemaLocation.length() > 0) { String[] split = schemaLocation.trim().split("\\s+"); if (split.length % 2 == 0) for (int i = 0; i < split.length; i += 2) parseSchema(split[i], split[i+1]); } } public void parseSchema(File schemaLocation) throws SAXException { parse(schemaLocation.toURI().toString()); } public void parseSchema(String namespaceURI, String schemaLocation) throws SAXException { if (visited.containsKey(namespaceURI)) return; if (schemaLocations.containsKey(namespaceURI)) schemaLocation = schemaLocations.get(namespaceURI); try { parse(schemaLocation); } catch (SAXException e) { if (schemaEntityResolver != null) { InputSource inputSource = null; try { inputSource = schemaEntityResolver.resolveEntity(namespaceURI, schemaLocation); if (inputSource != null) parse(inputSource.getSystemId()); } catch (IOException io) { throw new SAXException("Caused by: ", io); } } else throw e; } } public void resolveAndParseSchema(String namespaceURI) throws SAXException, MissingADESchemaException { if (visited.containsKey(namespaceURI)) return; InputSource is = null; if (schemaEntityResolver != null) { try { is = schemaEntityResolver.resolveEntity(namespaceURI, null); } catch (IOException e) { throw new SAXException(e); } } if (is == null) throw new MissingADESchemaException("Failed to resolve ADE Schema document for target namespace " + namespaceURI); parse(is); } private void parse(String schemaLocation) throws SAXException { if (schemaLocation == null) return; parse(new InputSource(schemaLocation)); } private void parse(InputSource is) throws SAXException { if (is == null) return; XSOMParser parser = new XSOMParser(SAXParserFactory.newInstance()); parser.setEntityResolver(new EntityResolver() { public InputSource resolveEntity(String publicId, String systemId) throws SAXException, IOException { InputSource inputSource = null; if (publicId != null) { for (Entry<String, String> entry : visited.entrySet()) { if (entry.getKey().equals(publicId)) { inputSource = new InputSource(entry.getValue()); inputSource.setPublicId(publicId); inputSource.setSystemId(entry.getValue()); break; } } } if (inputSource == null && publicId != null && schemaEntityResolver != null) inputSource = schemaEntityResolver.resolveEntity(publicId, systemId); return inputSource; } }); if (schemaErrorHandler != null) parser.setErrorHandler(schemaErrorHandler); if (annotationParserFactory != null) parser.setAnnotationParser(annotationParserFactory); parser.parse(is); XSSchemaSet schemaSet = parser.getResult(); if (schemaSet != null) { for (XSSchema schema : schemaSet.getSchemas()) { Locator locator = schema.getLocator(); if (locator != null) { String systemId = locator.getSystemId(); String visitedId = visited.get(schema.getTargetNamespace()); if (visitedId == null) visited.put(schema.getTargetNamespace(), systemId); else { try { URL cachedURL = new URL(visitedId); URL offeredURL = new URL(systemId); if (!(cachedURL.getProtocol().equals("file") || cachedURL.getProtocol().equals("jar")) && (offeredURL.getProtocol().equals("file") || offeredURL.getProtocol().equals("jar"))) visited.put(schema.getTargetNamespace(), systemId); } catch (MalformedURLException e) { // } } } } schemaSets.add(schemaSet); } } public Set<String> getTargetNamespaces() { return visited.keySet(); } public int size() { return visited.size(); } public boolean isEmpty() { return visited.isEmpty(); } public Source[] getSchemaSources() { Source[] sources = new Source[visited.size()]; int i = 0; for (String systemId : visited.values()) sources[i++] = new StreamSource(systemId); return sources; } public Source getSchemaSource(Schema schema) { String systemId = visited.get(schema.namespaceURI); if (systemId != null) return new StreamSource(systemId); return null; } public Source getSchemaSource(CityGMLModule module) { String systemId = visited.get(module.getNamespaceURI()); if (systemId != null) return new StreamSource(systemId); return null; } }