From 671c277b85d32bf040be34048e6167d6675d747e Mon Sep 17 00:00:00 2001 From: Riegel <alexander.riegel@hft-stuttgart.de> Date: Thu, 12 Dec 2024 12:45:29 +0100 Subject: [PATCH] Style: Reformat code --- .../citydoctor2/parser/CityGmlParser.java | 1310 +++++++------- .../citydoctor2/zip/CityGmlZipArchive.java | 25 +- .../citydoctor2/zip/CityGmlZipEntry.java | 37 +- .../stuttgart/citydoctor2/zip/ZipTest.java | 17 +- .../stuttgart/citydoctor2/check/Checker.java | 1559 ++++++++--------- .../citydoctor2/check/CheckerTest.java | 177 +- 6 files changed, 1564 insertions(+), 1561 deletions(-) diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/parser/CityGmlParser.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/parser/CityGmlParser.java index 73501da..765bb50 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/parser/CityGmlParser.java +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/parser/CityGmlParser.java @@ -1,6 +1,6 @@ /*- * Copyright 2022 Beuth Hochschule für Technik Berlin, Hochschule für Technik Stuttgart - * + * * This file is part of CityDoctor2. * * CityDoctor2 is free software: you can redistribute it and/or modify @@ -18,28 +18,16 @@ */ package de.hft.stuttgart.citydoctor2.parser; -import java.io.*; -import java.nio.charset.StandardCharsets; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.util.ArrayList; -import java.util.List; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import javax.xml.XMLConstants; -import javax.xml.namespace.QName; -import javax.xml.parsers.ParserConfigurationException; -import javax.xml.parsers.SAXParser; -import javax.xml.parsers.SAXParserFactory; -import javax.xml.transform.Source; -import javax.xml.transform.stream.StreamSource; -import javax.xml.validation.Schema; -import javax.xml.validation.SchemaFactory; -import javax.xml.validation.Validator; - +import de.hft.stuttgart.citydoctor2.datastructure.CityDoctorModel; +import de.hft.stuttgart.citydoctor2.datastructure.CityObject; +import de.hft.stuttgart.citydoctor2.mapper.citygml3.Citygml3FeatureMapper; +import de.hft.stuttgart.citydoctor2.mapper.citygml3.GMLValidationHandler; +import de.hft.stuttgart.citydoctor2.math.Vector3d; +import de.hft.stuttgart.citydoctor2.utils.Localization; import de.hft.stuttgart.citydoctor2.zip.CityGmlZipEntry; import de.hft.stuttgart.citydoctor2.zip.CityGmlZipEntryFile; +import de.hft.stuttgart.quality.QualityADEContext; +import de.hft.stuttgart.quality.QualityADEModule; import org.apache.logging.log4j.Level; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -69,605 +57,623 @@ import org.locationtech.proj4j.CoordinateReferenceSystem; import org.locationtech.proj4j.ProjCoordinate; import org.locationtech.proj4j.proj.Projection; import org.locationtech.proj4j.units.Units; -import org.xml.sax.*; +import org.xml.sax.InputSource; +import org.xml.sax.SAXException; +import org.xml.sax.SAXNotRecognizedException; +import org.xml.sax.SAXNotSupportedException; +import org.xml.sax.SAXParseException; import org.xmlobjects.schema.SchemaHandler; import org.xmlobjects.schema.SchemaHandlerException; import org.xmlobjects.stream.XMLReader; import org.xmlobjects.stream.XMLReaderFactory; -import de.hft.stuttgart.citydoctor2.datastructure.CityDoctorModel; -import de.hft.stuttgart.citydoctor2.datastructure.CityObject; -import de.hft.stuttgart.citydoctor2.mapper.citygml3.Citygml3FeatureMapper; -import de.hft.stuttgart.citydoctor2.mapper.citygml3.GMLValidationHandler; -import de.hft.stuttgart.citydoctor2.math.Vector3d; -import de.hft.stuttgart.citydoctor2.utils.Localization; -import de.hft.stuttgart.quality.QualityADEContext; -import de.hft.stuttgart.quality.QualityADEModule; +import javax.xml.XMLConstants; +import javax.xml.namespace.QName; +import javax.xml.parsers.ParserConfigurationException; +import javax.xml.parsers.SAXParser; +import javax.xml.parsers.SAXParserFactory; +import javax.xml.transform.Source; +import javax.xml.transform.stream.StreamSource; +import javax.xml.validation.Schema; +import javax.xml.validation.SchemaFactory; +import javax.xml.validation.Validator; +import java.io.BufferedInputStream; +import java.io.ByteArrayInputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.nio.charset.StandardCharsets; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; /** * Utility class to parse CityGML files. - * - * @author Matthias Betz * + * @author Matthias Betz */ public class CityGmlParser { - private static final String CITY_OBJECT_MEMBER = "cityObjectMember"; - - private static final String WGS_84 = "EPSG:4326"; - - private static final Logger logger = LogManager.getLogger(CityGmlParser.class); - - private static final CRSFactory CRS_FACTORY = new CRSFactory(); - // EPSG:31467 - private static final Pattern P_EPSG = Pattern.compile("^(EPSG:\\d+)$"); - // urn:ogc:def:crs,crs:EPSG:6.12:31467,crs:EPSG:6.12:5783 - // or - // urn:ogc:def:crs,crs:EPSG::28992 - private static final Pattern P_OGC = Pattern.compile("urn:ogc:def:crs,crs:EPSG:[\\d\\.]*:([\\d]+)\\D*"); - - private static final Pattern P_OGC2 = Pattern.compile("urn:ogc:def:crs:EPSG:[\\d\\.]*:([\\d]+)\\D*"); - - // urn:adv:crs:DE_DHDN_3GK3*DE_DHHN92_NH - // urn:adv:crs:ETRS89_UTM32*DE_DHHN92_NH - private static final Pattern P_URN = Pattern.compile("urn:adv:crs:([^\\*]+)"); - - private static final SAXParserFactory FACTORY; - - private static CityGMLContext context; - private static List<QName> chunkProperties = new ArrayList<>(); - - - static { - System.setProperty("javax.xml.transform.TransformerFactory", "com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl"); - FACTORY = SAXParserFactory.newInstance(); - try { - FACTORY.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, false); - } catch (SAXNotRecognizedException | SAXNotSupportedException | ParserConfigurationException e) { - logger.catching(e); - } - - chunkProperties.add(new QName(CityGMLConstants.CITYGML_1_0_CORE_NAMESPACE, CITY_OBJECT_MEMBER)); - chunkProperties.add(new QName(CityGMLConstants.CITYGML_2_0_CORE_NAMESPACE, CITY_OBJECT_MEMBER)); - chunkProperties.add(new QName(CityGMLConstants.CITYGML_3_0_CORE_NAMESPACE, CITY_OBJECT_MEMBER)); - } - - private CityGmlParser() { - } - - public static synchronized CityGMLContext getContext() { - if (context == null) { - try { - context = CityGMLContext.newInstance(CityGmlParser.class.getClassLoader()); - // also setup ades - ADERegistry adeRegistry = ADERegistry.getInstance(); - adeRegistry.loadADE(new QualityADEContext()); - } catch (CityGMLContextException e) { - logger.fatal("Unable to create citygml4j context", e); - throw new IllegalStateException("Unable to create citygml4j context"); - } catch (ADEException e) { - logger.fatal("Unable to add ADE plugins to citygml4j", e); - throw new IllegalStateException("Unable to add ADE plugins to citygml4j"); - } - } - return context; - } - - public static CityDoctorModel parseCityGmlFileSilently(String file, ParserConfiguration config) - throws CityGmlParseException, InvalidGmlFileException { - return parseCityGmlFile(file, config, null, null, false); - } - - public static CityDoctorModel parseCityGmlFile(String file, ParserConfiguration config) - throws CityGmlParseException, InvalidGmlFileException { - return parseCityGmlFile(file, config, null, null, true); - } - - public static CityDoctorModel parseCityGmlFile(String file, ParserConfiguration config, ProgressListener l) - throws CityGmlParseException, InvalidGmlFileException { - return parseCityGmlFile(file, config, l, null, true); - } - - public static CityDoctorModel parseCityGmlFile(String filePath, ParserConfiguration config, ProgressListener l, - GMLValidationHandler handler, boolean verbose) throws CityGmlParseException, InvalidGmlFileException { - CityGMLContext context = getContext(); - Path file = Paths.get(filePath); - if (config.getValidate()) { - List<String> messages = validateFile(context, handler, file); - if (!messages.isEmpty()) { - throw new InvalidGmlFileException("Invalid GML File. First error: \n" + messages.get(0)); - } - } - - try { - parseEpsgCodeFromFile(file, config); - CityGMLInputFactory in = context.createCityGMLInputFactory() - .withChunking(ChunkOptions.chunkByProperties(chunkProperties).skipCityModel(false)); - try (ObservedInputStream ois = new ObservedInputStream(file.toFile())) { - if (l != null) { - ois.addListener(l::updateProgress); - } - return readAndKeepFeatures(config, file, in, ois, verbose); - } - } catch (CityGMLReadException | IOException e) { - throw new CityGmlParseException("Failed to read CityGML file", e); - } - } - - public static CityDoctorModel parseCityGmlZipEntry(CityGmlZipEntry entry, ParserConfiguration config) + private static final String CITY_OBJECT_MEMBER = "cityObjectMember"; + + private static final String WGS_84 = "EPSG:4326"; + + private static final Logger logger = LogManager.getLogger(CityGmlParser.class); + + private static final CRSFactory CRS_FACTORY = new CRSFactory(); + // EPSG:31467 + private static final Pattern P_EPSG = Pattern.compile("^(EPSG:\\d+)$"); + // urn:ogc:def:crs,crs:EPSG:6.12:31467,crs:EPSG:6.12:5783 + // or + // urn:ogc:def:crs,crs:EPSG::28992 + private static final Pattern P_OGC = Pattern.compile("urn:ogc:def:crs,crs:EPSG:[\\d\\.]*:([\\d]+)\\D*"); + + private static final Pattern P_OGC2 = Pattern.compile("urn:ogc:def:crs:EPSG:[\\d\\.]*:([\\d]+)\\D*"); + + // urn:adv:crs:DE_DHDN_3GK3*DE_DHHN92_NH + // urn:adv:crs:ETRS89_UTM32*DE_DHHN92_NH + private static final Pattern P_URN = Pattern.compile("urn:adv:crs:([^\\*]+)"); + + private static final SAXParserFactory FACTORY; + + private static CityGMLContext context; + private static List<QName> chunkProperties = new ArrayList<>(); + + + static { + System.setProperty("javax.xml.transform.TransformerFactory", "com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl"); + FACTORY = SAXParserFactory.newInstance(); + try { + FACTORY.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, false); + } catch (SAXNotRecognizedException | SAXNotSupportedException | ParserConfigurationException e) { + logger.catching(e); + } + + chunkProperties.add(new QName(CityGMLConstants.CITYGML_1_0_CORE_NAMESPACE, CITY_OBJECT_MEMBER)); + chunkProperties.add(new QName(CityGMLConstants.CITYGML_2_0_CORE_NAMESPACE, CITY_OBJECT_MEMBER)); + chunkProperties.add(new QName(CityGMLConstants.CITYGML_3_0_CORE_NAMESPACE, CITY_OBJECT_MEMBER)); + } + + private CityGmlParser() { + } + + public static synchronized CityGMLContext getContext() { + if (context == null) { + try { + context = CityGMLContext.newInstance(CityGmlParser.class.getClassLoader()); + // also setup ades + ADERegistry adeRegistry = ADERegistry.getInstance(); + adeRegistry.loadADE(new QualityADEContext()); + } catch (CityGMLContextException e) { + logger.fatal("Unable to create citygml4j context", e); + throw new IllegalStateException("Unable to create citygml4j context"); + } catch (ADEException e) { + logger.fatal("Unable to add ADE plugins to citygml4j", e); + throw new IllegalStateException("Unable to add ADE plugins to citygml4j"); + } + } + return context; + } + + public static CityDoctorModel parseCityGmlFileSilently(String file, ParserConfiguration config) + throws CityGmlParseException, InvalidGmlFileException { + return parseCityGmlFile(file, config, null, null, false); + } + + public static CityDoctorModel parseCityGmlFile(String file, ParserConfiguration config) + throws CityGmlParseException, InvalidGmlFileException { + return parseCityGmlFile(file, config, null, null, true); + } + + public static CityDoctorModel parseCityGmlFile(String file, ParserConfiguration config, ProgressListener l) + throws CityGmlParseException, InvalidGmlFileException { + return parseCityGmlFile(file, config, l, null, true); + } + + public static CityDoctorModel parseCityGmlFile(String filePath, ParserConfiguration config, ProgressListener l, + GMLValidationHandler handler, boolean verbose) throws CityGmlParseException, InvalidGmlFileException { + CityGMLContext context = getContext(); + Path file = Paths.get(filePath); + if (config.getValidate()) { + List<String> messages = validateFile(context, handler, file); + if (!messages.isEmpty()) { + throw new InvalidGmlFileException("Invalid GML File. First error: \n" + messages.get(0)); + } + } + + try { + parseEpsgCodeFromFile(file, config); + CityGMLInputFactory in = context.createCityGMLInputFactory() + .withChunking(ChunkOptions.chunkByProperties(chunkProperties).skipCityModel(false)); + try (ObservedInputStream ois = new ObservedInputStream(file.toFile())) { + if (l != null) { + ois.addListener(l::updateProgress); + } + return readAndKeepFeatures(config, file, in, ois, verbose); + } + } catch (CityGMLReadException | IOException e) { + throw new CityGmlParseException("Failed to read CityGML file", e); + } + } + + public static CityDoctorModel parseCityGmlZipEntry(CityGmlZipEntry entry, ParserConfiguration config) throws CityGmlParseException, InvalidGmlFileException, IOException { - CityGMLContext context = getContext(); - - if (config.getValidate()) { - try (CityGmlZipEntryFile entryFile = new CityGmlZipEntryFile(entry)){ - List<String> messages = validateStream(entryFile.getInputStream(),context); - if (!messages.isEmpty()) { - throw new InvalidGmlFileException("Invalid GML File. First error: \n" + messages.get(0)); - } - } catch (Exception e) { + CityGMLContext context = getContext(); + + if (config.getValidate()) { + try (CityGmlZipEntryFile entryFile = new CityGmlZipEntryFile(entry)) { + List<String> messages = validateStream(entryFile.getInputStream(), context); + if (!messages.isEmpty()) { + throw new InvalidGmlFileException("Invalid GML File. First error: \n" + messages.get(0)); + } + } catch (Exception e) { throw new CityGmlParseException(e); } } - return decompressAndParseCityGmlEntry(entry, config, context); - } - - public static CityDoctorModel decompressAndParseCityGmlEntry(CityGmlZipEntry entry, ParserConfiguration config, CityGMLContext context) - throws CityGmlParseException { - return decompressAndParseCityGmlEntry(entry, config, null, context); - } - - public static CityDoctorModel decompressAndParseCityGmlEntry(CityGmlZipEntry entry, ParserConfiguration config, ProgressListener l, CityGMLContext context) - throws CityGmlParseException { - - try (CityGmlZipEntryFile entryFile = new CityGmlZipEntryFile(entry)){ - BufferedInputStream bis = new BufferedInputStream(entryFile.getInputStream()); - readEpsgCodeFromInputStream(bis,config); - CityGMLInputFactory in = context.createCityGMLInputFactory() - .withChunking(ChunkOptions.chunkByProperties(chunkProperties).skipCityModel(false)); - try(ObservedInputStream ois = new ObservedInputStream(bis, bis.available())){ - if (l != null){ - ois.addListener(l::updateProgress); - } - return readAndKeepFeatures(config, entry, in, ois, false); - } - } catch (CityGMLReadException | IOException e) { - throw new CityGmlParseException("Failed to read CityGML file", e); - } catch (Exception e) { + return decompressAndParseCityGmlEntry(entry, config, context); + } + + public static CityDoctorModel decompressAndParseCityGmlEntry(CityGmlZipEntry entry, ParserConfiguration config, CityGMLContext context) + throws CityGmlParseException { + return decompressAndParseCityGmlEntry(entry, config, null, context); + } + + public static CityDoctorModel decompressAndParseCityGmlEntry(CityGmlZipEntry entry, ParserConfiguration config, ProgressListener l, CityGMLContext context) + throws CityGmlParseException { + + try (CityGmlZipEntryFile entryFile = new CityGmlZipEntryFile(entry)) { + BufferedInputStream bis = new BufferedInputStream(entryFile.getInputStream()); + readEpsgCodeFromInputStream(bis, config); + CityGMLInputFactory in = context.createCityGMLInputFactory() + .withChunking(ChunkOptions.chunkByProperties(chunkProperties).skipCityModel(false)); + try (ObservedInputStream ois = new ObservedInputStream(bis, bis.available())) { + if (l != null) { + ois.addListener(l::updateProgress); + } + return readAndKeepFeatures(config, entry, in, ois, false); + } + } catch (CityGMLReadException | IOException e) { + throw new CityGmlParseException("Failed to read CityGML file", e); + } catch (Exception e) { + throw new CityGmlParseException(e); + } + } + + private static CityDoctorModel readAndKeepFeatures(ParserConfiguration config, CityGmlZipEntry entry, + CityGMLInputFactory inputFactory, ObservedInputStream ois, boolean verbose) throws CityGMLReadException { + return readAndKeepModel(new Citygml3FeatureMapper(config, entry), inputFactory, ois, verbose); + } + + private static List<String> validateStream(InputStream vis, CityGMLContext context) throws CityGmlParseException { + GMLValidationHandler handler = new GMLValidationHandler(); + + try { + BufferedInputStream bis = new BufferedInputStream(vis); + SchemaHandler schemaHandler = new ValidationSchemaHandler(context.getDefaultSchemaHandler()); + readAdditionalSchemaDefinitions(context, bis, schemaHandler); + Source[] schemas = schemaHandler.getSchemas(); + SchemaFactory schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI); + schemaFactory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true); + Schema schema = schemaFactory.newSchema(schemas); + Validator validator = schema.newValidator(); + validator.setErrorHandler(handler); + validator.validate(new StreamSource(bis)); + return handler.getMessages(); + } catch (SchemaHandlerException | SAXException | IOException e) { + throw new CityGmlParseException("Failed to validate CityGML file", e); + } + } + + private static void readAdditionalSchemaDefinitions(CityGMLContext context, BufferedInputStream bis, SchemaHandler schemaHandler) + throws CityGmlParseException { + bis.mark(Integer.MAX_VALUE); + try (XMLReader reader = XMLReaderFactory.newInstance(context.getXMLObjects()) + .withSchemaHandler(schemaHandler) + .createReader(bis)) { + reader.nextTag(); + bis.reset(); + } catch (Exception e) { + throw new CityGmlParseException("Failed to read Schema from stream.", e); + } + } + + private static void readEpsgCodeFromInputStream(BufferedInputStream bis, ParserConfiguration config) throws CityGmlParseException { + try { + // Mark start position of GML-"file" + bis.mark(Integer.MAX_VALUE); + // Buffer the first 10000 chars of the Stream, EPSG/envelope info should be found within that range + int peekingWidth = Math.min(10000, bis.available()); + byte[] buf = new byte[peekingWidth]; + bis.read(buf, 0, peekingWidth); + bis.reset(); + parseEpsgCodeFromBuffer(buf, config); + } catch (ParserConfigurationException | SAXException | IOException e) { + throw new CityGmlParseException("Failed to read CityGML file", e); + } + + } + + public static void streamCityGml(CityGmlZipEntry entry, ParserConfiguration config, CityGmlConsumer cityObjectConsumer, + String outputFile) throws CityGmlParseException, IOException { + try (CityGmlZipEntryFile entryFile = new CityGmlZipEntryFile(entry)) { + parseEpsgCodeFromStream(entryFile.getInputStream(), config); + startReadingCityGmlZipEntry(entry, config, null, cityObjectConsumer, outputFile); + } catch (ParserConfigurationException | SAXException | IOException e) { + throw new CityGmlParseException("Failed to read CityGML stream", e); + } + } + + public static void streamCityGml(String file, ParserConfiguration config, CityGmlConsumer cityObjectConsumer, + String outputFile) throws CityGmlParseException { + Path f = Paths.get(file); + streamCityGml(f, config, null, cityObjectConsumer, outputFile); + } + + public static void streamCityGml(File file, ParserConfiguration parserConfig, CityGmlConsumer cityObjectConsumer, + String outputFile) throws CityGmlParseException { + streamCityGml(file.toPath(), parserConfig, null, cityObjectConsumer, outputFile); + } + + public static void streamCityGml(Path file, ParserConfiguration config, ProgressListener l, + CityGmlConsumer cityObjectConsumer, String outputFile) throws CityGmlParseException { + parseEpsgCodeFromFile(file, config); + startReadingCityGmlFile(file, config, l, cityObjectConsumer, outputFile); + } + + public static CityModel parseOnlyCityModel(File inputFile) throws CityGmlParseException { + try { + CityGMLInputFactory inputFactory = context.createCityGMLInputFactory() + .withChunking(ChunkOptions.chunkByProperties(chunkProperties).skipCityModel(false)); + try (CityGMLReader reader = inputFactory.createCityGMLReader(inputFile)) { + while (reader.hasNext()) { + AbstractFeature chunk = reader.next(); + if (chunk instanceof CityModel cModel) { + cModel.setCityObjectMembers(null); + return cModel; + } + } + } + } catch (CityGMLReadException e) { throw new CityGmlParseException(e); } + throw new CityGmlParseException("Did not find any CityModel in CityGML file"); } - private static CityDoctorModel readAndKeepFeatures(ParserConfiguration config, CityGmlZipEntry entry, - CityGMLInputFactory inputFactory, ObservedInputStream ois, boolean verbose) throws CityGMLReadException { - return readAndKeepModel(new Citygml3FeatureMapper(config, entry), inputFactory, ois, verbose); - } - - private static List<String> validateStream(InputStream vis, CityGMLContext context) throws CityGmlParseException { - GMLValidationHandler handler = new GMLValidationHandler(); - - try { - BufferedInputStream bis = new BufferedInputStream(vis); - SchemaHandler schemaHandler = new ValidationSchemaHandler(context.getDefaultSchemaHandler()); - readAdditionalSchemaDefinitions(context, bis, schemaHandler); - Source[] schemas = schemaHandler.getSchemas(); - SchemaFactory schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI); - schemaFactory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true); - Schema schema = schemaFactory.newSchema(schemas); - Validator validator = schema.newValidator(); - validator.setErrorHandler(handler); - validator.validate(new StreamSource(bis)); - return handler.getMessages(); - } catch (SchemaHandlerException | SAXException | IOException e) { - throw new CityGmlParseException("Failed to validate CityGML file", e); - } - } - - private static void readAdditionalSchemaDefinitions(CityGMLContext context, BufferedInputStream bis, SchemaHandler schemaHandler) - throws CityGmlParseException { - bis.mark(Integer.MAX_VALUE); - try (XMLReader reader = XMLReaderFactory.newInstance(context.getXMLObjects()) - .withSchemaHandler(schemaHandler) - .createReader(bis)) { - reader.nextTag(); - bis.reset(); - } catch (Exception e) { - throw new CityGmlParseException("Failed to read Schema from stream.", e); - } - } - - private static void readEpsgCodeFromInputStream(BufferedInputStream bis, ParserConfiguration config) throws CityGmlParseException { - try{ - // Mark start position of GML-"file" - bis.mark(Integer.MAX_VALUE); - // Buffer the first 10000 chars of the Stream, EPSG/envelope info should be found within that range - int peekingWidth = Math.min(10000, bis.available()); - byte[] buf = new byte[peekingWidth]; - bis.read(buf, 0, peekingWidth); - bis.reset(); - parseEpsgCodeFromBuffer(buf, config); - } catch (ParserConfigurationException | SAXException | IOException e) { - throw new CityGmlParseException("Failed to read CityGML file", e); - } - - } - - public static void streamCityGml(CityGmlZipEntry entry, ParserConfiguration config, CityGmlConsumer cityObjectConsumer, - String outputFile) throws CityGmlParseException, IOException { - try (CityGmlZipEntryFile entryFile = new CityGmlZipEntryFile(entry)) { - parseEpsgCodeFromStream(entryFile.getInputStream(), config); - startReadingCityGmlZipEntry(entry, config, null,cityObjectConsumer, outputFile); - } catch (ParserConfigurationException | SAXException | IOException e) { - throw new CityGmlParseException("Failed to read CityGML stream", e); - } - } - - public static void streamCityGml(String file, ParserConfiguration config, CityGmlConsumer cityObjectConsumer, - String outputFile) throws CityGmlParseException { - Path f = Paths.get(file); - streamCityGml(f, config, null, cityObjectConsumer, outputFile); - } - - public static void streamCityGml(File file, ParserConfiguration parserConfig, CityGmlConsumer cityObjectConsumer, - String outputFile) throws CityGmlParseException { - streamCityGml(file.toPath(), parserConfig, null, cityObjectConsumer, outputFile); - } - - public static void streamCityGml(Path file, ParserConfiguration config, ProgressListener l, - CityGmlConsumer cityObjectConsumer, String outputFile) throws CityGmlParseException { - parseEpsgCodeFromFile(file, config); - startReadingCityGmlFile(file, config, l, cityObjectConsumer, outputFile); - } - - public static CityModel parseOnlyCityModel(File inputFile) throws CityGmlParseException { - try { - CityGMLInputFactory inputFactory = context.createCityGMLInputFactory() - .withChunking(ChunkOptions.chunkByProperties(chunkProperties).skipCityModel(false)); - try (CityGMLReader reader = inputFactory.createCityGMLReader(inputFile)) { - while (reader.hasNext()) { - AbstractFeature chunk = reader.next(); - if (chunk instanceof CityModel cModel) { - cModel.setCityObjectMembers(null); - return cModel; - } - } - } - } catch (CityGMLReadException e) { - throw new CityGmlParseException(e); - } - throw new CityGmlParseException("Did not find any CityModel in CityGML file"); - } - - private static void startReadingCityGmlFile(Path file, ParserConfiguration config, ProgressListener l, - CityGmlConsumer cityObjectConsumer, String outputFile) { - try (ObservedInputStream ois = new ObservedInputStream(file.toFile())) { - if (l != null) { - ois.addListener(l::updateProgress); - } - readAndDiscardFeatures(file, config, ois, cityObjectConsumer, outputFile); - } catch (IOException | CityGMLReadException e) { - logger.error(Localization.getText("CityGmlParser.errorReadingGmlFile"), e.getMessage()); - logger.catching(Level.ERROR, e); - } - } - - private static void readAndDiscardFeatures(Path file, ParserConfiguration config, ObservedInputStream ois, - CityGmlConsumer cityObjectConsumer, String outputFile) throws CityGMLReadException { - Citygml3FeatureMapper mapper = new Citygml3FeatureMapper(config, file); - readAndDiscardModel(mapper, ois, cityObjectConsumer, outputFile); - - } - - private static void startReadingCityGmlZipEntry(CityGmlZipEntry entry, ParserConfiguration config, ProgressListener l, - CityGmlConsumer cityObjectConsumer, String outputFile) { - try (CityGmlZipEntryFile entryFile = new CityGmlZipEntryFile(entry); - ObservedInputStream ois = new ObservedInputStream(entryFile.getInputStream(), entry.getFileSize())){ - if (l != null) { - ois.addListener(l::updateProgress); - } - streamAndDiscardFeatures(entry, config, ois, cityObjectConsumer, outputFile); - } catch (IOException | CityGMLReadException e) { - logger.error(Localization.getText("CityGmlParser.errorReadingGmlFile"), e.getMessage()); - logger.catching(Level.ERROR, e); - } - } - - private static void streamAndDiscardFeatures(CityGmlZipEntry entry, ParserConfiguration config, ObservedInputStream ois, - CityGmlConsumer cityObjectConsumer, String outputFile) throws CityGMLReadException { - Citygml3FeatureMapper mapper = new Citygml3FeatureMapper(config, entry); - readAndDiscardModel(mapper, ois, cityObjectConsumer, outputFile); - } - - private static void readAndDiscardModel(Citygml3FeatureMapper mapper, ObservedInputStream ois, - CityGmlConsumer cityObjectConsumer, String outputFile) throws CityGMLReadException { - getContext(); - CityGMLInputFactory inputFactory = context.createCityGMLInputFactory() - .withChunking(ChunkOptions.chunkByProperties(chunkProperties).skipCityModel(false)); - CityGMLChunkWriter writer = null; - try (CityGMLReader reader = inputFactory.createCityGMLReader(ois)) { - - CityDoctorModel model = mapper.getModel(); - boolean isInitialized = false; - while (reader.hasNext()) { - AbstractFeature chunk = reader.next(); - if (writer == null) { - writer = createCityModelWriter(outputFile, reader); - } - if (!isInitialized && writer != null && reader.getParentInfo() != null - && reader.getParentInfo().getTypeName().getLocalPart().equals("CityModel")) { - FeatureInfo parentInfo = reader.getParentInfo(); - writer.withCityModelInfo(parentInfo); - isInitialized = true; - } - if (chunk instanceof AbstractCityObject ag) { - ag.accept(mapper); - drainCityModel(model, cityObjectConsumer); - writeAbstractCityObject(writer, ag); - } else if (chunk instanceof CityModel cModel) { - cModel.setCityObjectMembers(null); - mapper.setCityModel(cModel); - cityObjectConsumer.accept(cModel); - writeCityModel(writer, cModel); - } else if (writer != null) { - writer.writeMember(chunk); - } - } - // end of stream - logger.debug("End of gml file stream"); - } catch (CityGMLReadException e) { - logger.error(Localization.getText("CityGmlParser.errorReadingGmlFile"), e.getMessage(), e); - } catch (CityGMLWriteException e) { - logger.error(Localization.getText("CityGmlParser.errorWritingGmlFile"), e.getMessage(), e); - } finally { - if (writer != null) { - try { - writer.close(); - } catch (CityGMLWriteException e) { - // ignore - } - } - } - } - - private static void writeCityModel(CityGMLChunkWriter writer, CityModel cModel) { - if (writer != null) { - for (ADEProperty genEle : cModel.getADEProperties()) { - writer.getCityModelInfo().addADEProperty(genEle); - } - } - } - - private static void writeAbstractCityObject(CityGMLChunkWriter writer, AbstractCityObject ag) - throws CityGMLWriteException { - if (writer != null) { - writer.writeMember(ag); - } - } - - private static CityGMLChunkWriter createCityModelWriter(String outputFile, CityGMLReader reader) - throws CityGMLWriteException { - if (outputFile == null) { - return null; - } - CityGMLContext gmlContext = CityGmlParser.getContext(); - CityGMLVersion version = CityGMLModules.getCityGMLVersion(reader.getName().getNamespaceURI()); - CityGMLOutputFactory factory = gmlContext.createCityGMLOutputFactory(version); - CityGMLChunkWriter writer = factory.createCityGMLChunkWriter(new File(outputFile), - StandardCharsets.UTF_8.name()); - writer.withPrefix("qual", QualityADEModule.NAMESPACE_URI); - writer.withSchemaLocation(QualityADEModule.NAMESPACE_URI, QualityADEModule.NAMESPACE_URI + "/qualityAde.xsd"); - writer.withIndent(" "); - writer.withDefaultPrefixes(); - writer.withDefaultSchemaLocations(); - return writer; - } - - private static CityDoctorModel readAndKeepFeatures(ParserConfiguration config, Path file, - CityGMLInputFactory inputFactory, ObservedInputStream ois, boolean verbose) throws CityGMLReadException { - return readAndKeepModel(new Citygml3FeatureMapper(config, file), inputFactory, ois, verbose); - } - - private static CityDoctorModel readAndKeepModel(Citygml3FeatureMapper mapper, CityGMLInputFactory inputFactory, - ObservedInputStream ois, boolean verbose) throws CityGMLReadException{ - try (CityGMLReader reader = inputFactory.createCityGMLReader(ois)) { - CityGMLVersion version = null; - // model is read in chunked mode - // object members are replaced by href in model - // need to remove the refs and re-add unparsed objects - List<AbstractCityObject> acos = new ArrayList<>(); - while (reader.hasNext()) { - AbstractFeature chunk = reader.next(); - version = CityGMLModules.getCityGMLVersion(reader.getName().getNamespaceURI()); - if (chunk instanceof CityModel cModel) { - cModel.setCityObjectMembers(null); - mapper.setCityModel(cModel); - mapper.setCityGMLVersion(version); - } else if (chunk instanceof AbstractCityObject aco) { - acos.add(aco); - aco.accept(mapper); - } - } - - if (mapper.getModel().getCityModel() == null) { - // file does not contain a city model? - // create it for now - mapper.setCityModel(new CityModel()); - } - CityModel cModel = mapper.getModel().getCityModel(); - - // remove those that should have been parsed - List<AbstractCityObject> parsedCityObjects = mapper.getModel().createFeatureStream() - .map(CityObject::getGmlObject).toList(); - acos.removeAll(parsedCityObjects); - // re-add all not parsed objects - for (AbstractCityObject aco : acos) { - cModel.getCityObjectMembers().add(new AbstractCityObjectProperty(aco)); - } - if (logger.isInfoEnabled() && verbose) { - logger.info(Localization.getText("CityGmlParser.parsedObjects"), - mapper.getModel().getNumberOfFeatures()); - } - mapper.setCityGMLVersion(version); - return mapper.getModel(); - } - } - - private static void parseEpsgCodeFromFile(Path file, ParserConfiguration config) throws CityGmlParseException { - try (BufferedInputStream bis = new BufferedInputStream(new FileInputStream(file.toFile()))) { - parseEpsgCodeFromStream(bis, config); - } catch (ParserConfigurationException | SAXException | IOException e) { - throw new CityGmlParseException("Failed to read CityGML file", e); - } - } - - private static void parseEpsgCodeFromStream(InputStream is, ParserConfiguration config) - throws ParserConfigurationException, SAXException { - SAXParser parser = FACTORY.newSAXParser(); - CityGmlHandler handler = new CityGmlHandler(); - try { - parser.parse(new InputSource(is), handler); - } catch (EnvelopeFoundException e) { - try { - parseCoordinateSystem(config, handler); - } catch (Exception e2) { - logEpsgParseError(e2); - } - } catch (Exception e) { - logEpsgParseError(e); - } - } - - private static void parseEpsgCodeFromBuffer(byte[] buffer, ParserConfiguration config) - throws ParserConfigurationException, SAXException { - InputStream is = new ByteArrayInputStream(buffer); - SAXParser parser = FACTORY.newSAXParser(); - CityGmlHandler handler = new CityGmlHandler(); - try { - parser.parse(new InputSource(is), handler); - } catch (EnvelopeFoundException e) { - try { - parseCoordinateSystem(config, handler); - - } catch (Exception e2) { - logEpsgParseError(e2); - } - }catch (SAXParseException spe){ - // suppress XML document structure warning - if (!spe.getMessage().matches("XML document structures must start and end within the same entity.")){ - logEpsgParseError(spe); - } - }catch (Exception e) { - logEpsgParseError(e); - } - } - - private static void logEpsgParseError(Exception e){ - logger.debug("Exception while parsing for EPSG code", e); - if (logger.isWarnEnabled()) { - logger.warn(Localization.getText("CityGmlParser.noEPSG")); - } - } - - private static void parseCoordinateSystem(ParserConfiguration config, CityGmlHandler handler) { - if (handler.getEpsg() == null) { - return; - } - CoordinateReferenceSystem crs = crsFromSrsName(handler.getEpsg()); - if (crs == null) { - // could not find a coordinate system for srsName - // assuming metric system - return; - } - if (crs.getProjection().getUnits() == Units.METRES) { - // coordinate system is in meters, do not convert - if (logger.isInfoEnabled()) { - logger.info(Localization.getText("CityGmlParser.noConversionNeeded")); - } - return; - } - parseMeterConversion(config, crs); - Vector3d low = handler.getLowerCorner(); - Vector3d up = handler.getUpperCorner(); - double centerLong = low.getX() + ((up.getX() - low.getX()) / 2); - double centerLat = low.getY() + ((up.getY() - low.getY()) / 2); - if (!crs.getName().equals(WGS_84)) { - // need to convert coordinates first to WGS84, then find UTM Zone - CoordinateReferenceSystem wgs84 = crsFromSrsName(WGS_84); - ProjCoordinate p1 = new ProjCoordinate(); - p1.setValue(centerLong, centerLat); - ProjCoordinate p2 = new ProjCoordinate(); - BasicCoordinateTransform bct = new BasicCoordinateTransform(crs, wgs84); - bct.transform(p1, p2); - centerLong = p2.x; - centerLat = p2.y; - } - int zone = (int) (31 + Math.round(centerLong / 6)); - CoordinateReferenceSystem utm; - if (centerLat < 0) { - // south - logger.info("Converting coordiante system to UTM zone {}S", zone); - utm = CRS_FACTORY.createFromParameters("UTM", "+proj=utm +ellps=WGS84 +units=m +zone=" + zone + " +south"); - } else { - // north - logger.info("Converting coordiante system to UTM zone {}N", zone); - utm = CRS_FACTORY.createFromParameters("UTM", "+proj=utm +ellps=WGS84 +units=m +zone=" + zone); - } - config.setCoordinateSystem(crs, utm); - } - - private static void parseMeterConversion(ParserConfiguration config, CoordinateReferenceSystem crs) { - Projection projection = crs.getProjection(); - double fromMetres = projection.getFromMetres(); - if (fromMetres > 0) { - // also transform height information - config.setFromMeters(fromMetres); - } else { - config.setFromMeters(1.0); - } - } - - /** - * The srsName (The name by which this reference system is identified) inside - * the CityGML file can have multiple formats. This method tries to parse the - * string and detect the corresponding reference system. If it is found, it - * returns a proj4j.CoordinateReferenceSystem. It throws an - * IllegalArgumentException otherwise. - * - * This method should be able to parse any EPSG id : e.g. "EPSG:1234". German - * Citygmls might also have "DE_DHDN_3GK3" or "ETRS89_UTM32" as srsName, so - * those are also included. It isn't guaranteed that those formats are correctly - * parsed, though. - * - * The EPSG ids and parameters are defined in resources ('nad/epsg') inside - * proj4j-0.1.0.jar. Some EPSG ids are missing though, e.g. 7415 - * - * @param srsName - * @return CoordinateReferenceSystem - */ - private static CoordinateReferenceSystem crsFromSrsName(String srsName) { - srsName = srsName.trim(); - Matcher mEPSG = P_EPSG.matcher(srsName); - if (mEPSG.find()) { - if ("EPSG:4979".contentEquals(srsName)) { - srsName = "EPSG:4236"; - } else if ("EPSG:7415".contentEquals(srsName)) { - return CRS_FACTORY.createFromParameters("EPSG:7415", - "+proj=sterea +lat_0=52.15616055555555 +lon_0=5.38763888888889 +k=0.9999079 +x_0=155000 +y_0=463000 +ellps=bessel +towgs84=565.417,50.3319,465.552,-0.398957,0.343988,-1.8774,4.0725 +units=m +no_defs"); - } - return CRS_FACTORY.createFromName(srsName); - } - - Matcher mOGC = P_OGC.matcher(srsName); - if (mOGC.find()) { - return CRS_FACTORY.createFromName("EPSG:" + mOGC.group(1)); - } - Matcher mOGC2 = P_OGC2.matcher(srsName); - if (mOGC2.find()) { - return CRS_FACTORY.createFromName("EPSG:" + mOGC2.group(1)); - } - Matcher mURN = P_URN.matcher(srsName); - // NOTE: Could use a HashMap if the switch/case becomes too long. - if (mURN.find()) { + private static void startReadingCityGmlFile(Path file, ParserConfiguration config, ProgressListener l, + CityGmlConsumer cityObjectConsumer, String outputFile) { + try (ObservedInputStream ois = new ObservedInputStream(file.toFile())) { + if (l != null) { + ois.addListener(l::updateProgress); + } + readAndDiscardFeatures(file, config, ois, cityObjectConsumer, outputFile); + } catch (IOException | CityGMLReadException e) { + logger.error(Localization.getText("CityGmlParser.errorReadingGmlFile"), e.getMessage()); + logger.catching(Level.ERROR, e); + } + } + + private static void readAndDiscardFeatures(Path file, ParserConfiguration config, ObservedInputStream ois, + CityGmlConsumer cityObjectConsumer, String outputFile) throws CityGMLReadException { + Citygml3FeatureMapper mapper = new Citygml3FeatureMapper(config, file); + readAndDiscardModel(mapper, ois, cityObjectConsumer, outputFile); + + } + + private static void startReadingCityGmlZipEntry(CityGmlZipEntry entry, ParserConfiguration config, ProgressListener l, + CityGmlConsumer cityObjectConsumer, String outputFile) { + try (CityGmlZipEntryFile entryFile = new CityGmlZipEntryFile(entry); + ObservedInputStream ois = new ObservedInputStream(entryFile.getInputStream(), entry.getFileSize())) { + if (l != null) { + ois.addListener(l::updateProgress); + } + streamAndDiscardFeatures(entry, config, ois, cityObjectConsumer, outputFile); + } catch (IOException | CityGMLReadException e) { + logger.error(Localization.getText("CityGmlParser.errorReadingGmlFile"), e.getMessage()); + logger.catching(Level.ERROR, e); + } + } + + private static void streamAndDiscardFeatures(CityGmlZipEntry entry, ParserConfiguration config, ObservedInputStream ois, + CityGmlConsumer cityObjectConsumer, String outputFile) throws CityGMLReadException { + Citygml3FeatureMapper mapper = new Citygml3FeatureMapper(config, entry); + readAndDiscardModel(mapper, ois, cityObjectConsumer, outputFile); + } + + private static void readAndDiscardModel(Citygml3FeatureMapper mapper, ObservedInputStream ois, + CityGmlConsumer cityObjectConsumer, String outputFile) throws CityGMLReadException { + getContext(); + CityGMLInputFactory inputFactory = context.createCityGMLInputFactory() + .withChunking(ChunkOptions.chunkByProperties(chunkProperties).skipCityModel(false)); + CityGMLChunkWriter writer = null; + try (CityGMLReader reader = inputFactory.createCityGMLReader(ois)) { + + CityDoctorModel model = mapper.getModel(); + boolean isInitialized = false; + while (reader.hasNext()) { + AbstractFeature chunk = reader.next(); + if (writer == null) { + writer = createCityModelWriter(outputFile, reader); + } + if (!isInitialized && writer != null && reader.getParentInfo() != null + && reader.getParentInfo().getTypeName().getLocalPart().equals("CityModel")) { + FeatureInfo parentInfo = reader.getParentInfo(); + writer.withCityModelInfo(parentInfo); + isInitialized = true; + } + if (chunk instanceof AbstractCityObject ag) { + ag.accept(mapper); + drainCityModel(model, cityObjectConsumer); + writeAbstractCityObject(writer, ag); + } else if (chunk instanceof CityModel cModel) { + cModel.setCityObjectMembers(null); + mapper.setCityModel(cModel); + cityObjectConsumer.accept(cModel); + writeCityModel(writer, cModel); + } else if (writer != null) { + writer.writeMember(chunk); + } + } + // end of stream + logger.debug("End of gml file stream"); + } catch (CityGMLReadException e) { + logger.error(Localization.getText("CityGmlParser.errorReadingGmlFile"), e.getMessage(), e); + } catch (CityGMLWriteException e) { + logger.error(Localization.getText("CityGmlParser.errorWritingGmlFile"), e.getMessage(), e); + } finally { + if (writer != null) { + try { + writer.close(); + } catch (CityGMLWriteException e) { + // ignore + } + } + } + } + + private static void writeCityModel(CityGMLChunkWriter writer, CityModel cModel) { + if (writer != null) { + for (ADEProperty genEle : cModel.getADEProperties()) { + writer.getCityModelInfo().addADEProperty(genEle); + } + } + } + + private static void writeAbstractCityObject(CityGMLChunkWriter writer, AbstractCityObject ag) + throws CityGMLWriteException { + if (writer != null) { + writer.writeMember(ag); + } + } + + private static CityGMLChunkWriter createCityModelWriter(String outputFile, CityGMLReader reader) + throws CityGMLWriteException { + if (outputFile == null) { + return null; + } + CityGMLContext gmlContext = CityGmlParser.getContext(); + CityGMLVersion version = CityGMLModules.getCityGMLVersion(reader.getName().getNamespaceURI()); + CityGMLOutputFactory factory = gmlContext.createCityGMLOutputFactory(version); + CityGMLChunkWriter writer = factory.createCityGMLChunkWriter(new File(outputFile), + StandardCharsets.UTF_8.name()); + writer.withPrefix("qual", QualityADEModule.NAMESPACE_URI); + writer.withSchemaLocation(QualityADEModule.NAMESPACE_URI, QualityADEModule.NAMESPACE_URI + "/qualityAde.xsd"); + writer.withIndent(" "); + writer.withDefaultPrefixes(); + writer.withDefaultSchemaLocations(); + return writer; + } + + private static CityDoctorModel readAndKeepFeatures(ParserConfiguration config, Path file, + CityGMLInputFactory inputFactory, ObservedInputStream ois, boolean verbose) throws CityGMLReadException { + return readAndKeepModel(new Citygml3FeatureMapper(config, file), inputFactory, ois, verbose); + } + + private static CityDoctorModel readAndKeepModel(Citygml3FeatureMapper mapper, CityGMLInputFactory inputFactory, + ObservedInputStream ois, boolean verbose) throws CityGMLReadException { + try (CityGMLReader reader = inputFactory.createCityGMLReader(ois)) { + CityGMLVersion version = null; + // model is read in chunked mode + // object members are replaced by href in model + // need to remove the refs and re-add unparsed objects + List<AbstractCityObject> acos = new ArrayList<>(); + while (reader.hasNext()) { + AbstractFeature chunk = reader.next(); + version = CityGMLModules.getCityGMLVersion(reader.getName().getNamespaceURI()); + if (chunk instanceof CityModel cModel) { + cModel.setCityObjectMembers(null); + mapper.setCityModel(cModel); + mapper.setCityGMLVersion(version); + } else if (chunk instanceof AbstractCityObject aco) { + acos.add(aco); + aco.accept(mapper); + } + } + + if (mapper.getModel().getCityModel() == null) { + // file does not contain a city model? + // create it for now + mapper.setCityModel(new CityModel()); + } + CityModel cModel = mapper.getModel().getCityModel(); + + // remove those that should have been parsed + List<AbstractCityObject> parsedCityObjects = mapper.getModel().createFeatureStream() + .map(CityObject::getGmlObject).toList(); + acos.removeAll(parsedCityObjects); + // re-add all not parsed objects + for (AbstractCityObject aco : acos) { + cModel.getCityObjectMembers().add(new AbstractCityObjectProperty(aco)); + } + if (logger.isInfoEnabled() && verbose) { + logger.info(Localization.getText("CityGmlParser.parsedObjects"), + mapper.getModel().getNumberOfFeatures()); + } + mapper.setCityGMLVersion(version); + return mapper.getModel(); + } + } + + private static void parseEpsgCodeFromFile(Path file, ParserConfiguration config) throws CityGmlParseException { + try (BufferedInputStream bis = new BufferedInputStream(new FileInputStream(file.toFile()))) { + parseEpsgCodeFromStream(bis, config); + } catch (ParserConfigurationException | SAXException | IOException e) { + throw new CityGmlParseException("Failed to read CityGML file", e); + } + } + + private static void parseEpsgCodeFromStream(InputStream is, ParserConfiguration config) + throws ParserConfigurationException, SAXException { + SAXParser parser = FACTORY.newSAXParser(); + CityGmlHandler handler = new CityGmlHandler(); + try { + parser.parse(new InputSource(is), handler); + } catch (EnvelopeFoundException e) { + try { + parseCoordinateSystem(config, handler); + } catch (Exception e2) { + logEpsgParseError(e2); + } + } catch (Exception e) { + logEpsgParseError(e); + } + } + + private static void parseEpsgCodeFromBuffer(byte[] buffer, ParserConfiguration config) + throws ParserConfigurationException, SAXException { + InputStream is = new ByteArrayInputStream(buffer); + SAXParser parser = FACTORY.newSAXParser(); + CityGmlHandler handler = new CityGmlHandler(); + try { + parser.parse(new InputSource(is), handler); + } catch (EnvelopeFoundException e) { + try { + parseCoordinateSystem(config, handler); + + } catch (Exception e2) { + logEpsgParseError(e2); + } + } catch (SAXParseException spe) { + // suppress XML document structure warning + if (!spe.getMessage().matches("XML document structures must start and end within the same entity.")) { + logEpsgParseError(spe); + } + } catch (Exception e) { + logEpsgParseError(e); + } + } + + private static void logEpsgParseError(Exception e) { + logger.debug("Exception while parsing for EPSG code", e); + if (logger.isWarnEnabled()) { + logger.warn(Localization.getText("CityGmlParser.noEPSG")); + } + } + + private static void parseCoordinateSystem(ParserConfiguration config, CityGmlHandler handler) { + if (handler.getEpsg() == null) { + return; + } + CoordinateReferenceSystem crs = crsFromSrsName(handler.getEpsg()); + if (crs == null) { + // could not find a coordinate system for srsName + // assuming metric system + return; + } + if (crs.getProjection().getUnits() == Units.METRES) { + // coordinate system is in meters, do not convert + if (logger.isInfoEnabled()) { + logger.info(Localization.getText("CityGmlParser.noConversionNeeded")); + } + return; + } + parseMeterConversion(config, crs); + Vector3d low = handler.getLowerCorner(); + Vector3d up = handler.getUpperCorner(); + double centerLong = low.getX() + ((up.getX() - low.getX()) / 2); + double centerLat = low.getY() + ((up.getY() - low.getY()) / 2); + if (!crs.getName().equals(WGS_84)) { + // need to convert coordinates first to WGS84, then find UTM Zone + CoordinateReferenceSystem wgs84 = crsFromSrsName(WGS_84); + ProjCoordinate p1 = new ProjCoordinate(); + p1.setValue(centerLong, centerLat); + ProjCoordinate p2 = new ProjCoordinate(); + BasicCoordinateTransform bct = new BasicCoordinateTransform(crs, wgs84); + bct.transform(p1, p2); + centerLong = p2.x; + centerLat = p2.y; + } + int zone = (int) (31 + Math.round(centerLong / 6)); + CoordinateReferenceSystem utm; + if (centerLat < 0) { + // south + logger.info("Converting coordiante system to UTM zone {}S", zone); + utm = CRS_FACTORY.createFromParameters("UTM", "+proj=utm +ellps=WGS84 +units=m +zone=" + zone + " +south"); + } else { + // north + logger.info("Converting coordiante system to UTM zone {}N", zone); + utm = CRS_FACTORY.createFromParameters("UTM", "+proj=utm +ellps=WGS84 +units=m +zone=" + zone); + } + config.setCoordinateSystem(crs, utm); + } + + private static void parseMeterConversion(ParserConfiguration config, CoordinateReferenceSystem crs) { + Projection projection = crs.getProjection(); + double fromMetres = projection.getFromMetres(); + if (fromMetres > 0) { + // also transform height information + config.setFromMeters(fromMetres); + } else { + config.setFromMeters(1.0); + } + } + + /** + * The srsName (The name by which this reference system is identified) inside + * the CityGML file can have multiple formats. This method tries to parse the + * string and detect the corresponding reference system. If it is found, it + * returns a proj4j.CoordinateReferenceSystem. It throws an + * IllegalArgumentException otherwise. + * <p> + * This method should be able to parse any EPSG id : e.g. "EPSG:1234". German + * Citygmls might also have "DE_DHDN_3GK3" or "ETRS89_UTM32" as srsName, so + * those are also included. It isn't guaranteed that those formats are correctly + * parsed, though. + * <p> + * The EPSG ids and parameters are defined in resources ('nad/epsg') inside + * proj4j-0.1.0.jar. Some EPSG ids are missing though, e.g. 7415 + * + * @param srsName + * @return CoordinateReferenceSystem + */ + private static CoordinateReferenceSystem crsFromSrsName(String srsName) { + srsName = srsName.trim(); + Matcher mEPSG = P_EPSG.matcher(srsName); + if (mEPSG.find()) { + if ("EPSG:4979".contentEquals(srsName)) { + srsName = "EPSG:4236"; + } else if ("EPSG:7415".contentEquals(srsName)) { + return CRS_FACTORY.createFromParameters("EPSG:7415", + "+proj=sterea +lat_0=52.15616055555555 +lon_0=5.38763888888889 +k=0.9999079 +x_0=155000 +y_0=463000 +ellps=bessel +towgs84=565.417,50.3319,465.552,-0.398957,0.343988,-1.8774,4.0725 +units=m +no_defs"); + } + return CRS_FACTORY.createFromName(srsName); + } + + Matcher mOGC = P_OGC.matcher(srsName); + if (mOGC.find()) { + return CRS_FACTORY.createFromName("EPSG:" + mOGC.group(1)); + } + Matcher mOGC2 = P_OGC2.matcher(srsName); + if (mOGC2.find()) { + return CRS_FACTORY.createFromName("EPSG:" + mOGC2.group(1)); + } + Matcher mURN = P_URN.matcher(srsName); + // NOTE: Could use a HashMap if the switch/case becomes too long. + if (mURN.find()) { return switch (mURN.group(1)) { case "DE_DHDN_3GK2" -> CRS_FACTORY.createFromName("EPSG:31466"); case "DE_DHDN_3GK3" -> CRS_FACTORY.createFromName("EPSG:31467"); @@ -676,59 +682,59 @@ public class CityGmlParser { case "ETRS89_UTM32" -> CRS_FACTORY.createFromName("EPSG:25832"); default -> null; }; - } - if (srsName.equals("http://www.opengis.net/def/crs/EPSG/0/6697")) { - return CRS_FACTORY.createFromParameters("EPSG:6697", "+proj=longlat +ellps=GRS80 +no_defs +axis=neu"); - } - return null; - } - - private static List<String> validateFile(CityGMLContext context, GMLValidationHandler handler, Path file) - throws CityGmlParseException { - if (handler == null) { - handler = new GMLValidationHandler(); - } - try { - SchemaHandler schemaHandler = new ValidationSchemaHandler(context.getDefaultSchemaHandler()); + } + if (srsName.equals("http://www.opengis.net/def/crs/EPSG/0/6697")) { + return CRS_FACTORY.createFromParameters("EPSG:6697", "+proj=longlat +ellps=GRS80 +no_defs +axis=neu"); + } + return null; + } + + private static List<String> validateFile(CityGMLContext context, GMLValidationHandler handler, Path file) + throws CityGmlParseException { + if (handler == null) { + handler = new GMLValidationHandler(); + } + try { + SchemaHandler schemaHandler = new ValidationSchemaHandler(context.getDefaultSchemaHandler()); readAdditionalSchemaDefinitions(context, file, schemaHandler); - Source[] schemas = schemaHandler.getSchemas(); - SchemaFactory schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI); - schemaFactory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true); - Schema schema = schemaFactory.newSchema(schemas); - Validator validator = schema.newValidator(); - validator.setErrorHandler(handler); - validator.validate(new StreamSource(file.toFile())); - return handler.getMessages(); - } catch (SchemaHandlerException | SAXException | IOException e) { - throw new CityGmlParseException("Failed to validate CityGML file", e); - } - } - - private static void readAdditionalSchemaDefinitions(CityGMLContext context, Path file, SchemaHandler schemaHandler) - throws CityGmlParseException { - try (XMLReader reader = XMLReaderFactory.newInstance(context.getXMLObjects()) - .withSchemaHandler(schemaHandler) - .createReader(file)) { - reader.nextTag(); - } catch (Exception e) { - throw new CityGmlParseException("Failed to read file " + file.toAbsolutePath() + ".", e); - } - } - - private static void drainCityModel(CityDoctorModel model, CityGmlConsumer cityObjectConsumer) { - drainCityObjectList(model.getBuildings(), cityObjectConsumer); - drainCityObjectList(model.getBridges(), cityObjectConsumer); - drainCityObjectList(model.getVegetation(), cityObjectConsumer); - drainCityObjectList(model.getLand(), cityObjectConsumer); - drainCityObjectList(model.getTransportation(), cityObjectConsumer); - drainCityObjectList(model.getWater(), cityObjectConsumer); - } - - private static void drainCityObjectList(List<? extends CityObject> objects, CityGmlConsumer cityObjectConsumer) { - for (CityObject co : objects) { - cityObjectConsumer.accept(co); - } - objects.clear(); - } + Source[] schemas = schemaHandler.getSchemas(); + SchemaFactory schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI); + schemaFactory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true); + Schema schema = schemaFactory.newSchema(schemas); + Validator validator = schema.newValidator(); + validator.setErrorHandler(handler); + validator.validate(new StreamSource(file.toFile())); + return handler.getMessages(); + } catch (SchemaHandlerException | SAXException | IOException e) { + throw new CityGmlParseException("Failed to validate CityGML file", e); + } + } + + private static void readAdditionalSchemaDefinitions(CityGMLContext context, Path file, SchemaHandler schemaHandler) + throws CityGmlParseException { + try (XMLReader reader = XMLReaderFactory.newInstance(context.getXMLObjects()) + .withSchemaHandler(schemaHandler) + .createReader(file)) { + reader.nextTag(); + } catch (Exception e) { + throw new CityGmlParseException("Failed to read file " + file.toAbsolutePath() + ".", e); + } + } + + private static void drainCityModel(CityDoctorModel model, CityGmlConsumer cityObjectConsumer) { + drainCityObjectList(model.getBuildings(), cityObjectConsumer); + drainCityObjectList(model.getBridges(), cityObjectConsumer); + drainCityObjectList(model.getVegetation(), cityObjectConsumer); + drainCityObjectList(model.getLand(), cityObjectConsumer); + drainCityObjectList(model.getTransportation(), cityObjectConsumer); + drainCityObjectList(model.getWater(), cityObjectConsumer); + } + + private static void drainCityObjectList(List<? extends CityObject> objects, CityGmlConsumer cityObjectConsumer) { + for (CityObject co : objects) { + cityObjectConsumer.accept(co); + } + objects.clear(); + } } diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/zip/CityGmlZipArchive.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/zip/CityGmlZipArchive.java index 5578165..9413aa3 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/zip/CityGmlZipArchive.java +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/zip/CityGmlZipArchive.java @@ -5,12 +5,14 @@ import de.hft.stuttgart.citydoctor2.utils.ArchivePacker; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import java.io.*; - +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.Serial; +import java.io.Serializable; import java.nio.file.Path; import java.util.ArrayList; import java.util.List; - import java.util.zip.ZipEntry; import java.util.zip.ZipFile; import java.util.zip.ZipInputStream; @@ -32,8 +34,7 @@ public class CityGmlZipArchive implements Serializable { CityGmlZipArchive cgmlArchive = new CityGmlZipArchive(Path.of(zipFile)); try (ZipInputStream zis = new ZipInputStream(new FileInputStream(zipFile))) { ZipEntry ze; - while ((ze = zis.getNextEntry()) != null ) - { + while ((ze = zis.getNextEntry()) != null) { if (ze.isDirectory()) { continue; } @@ -49,9 +50,9 @@ public class CityGmlZipArchive implements Serializable { } } - public void mountArchive(ParserConfiguration config){ + public void mountArchive(ParserConfiguration config) { try (ZipFile zip = new ZipFile(archivePath.toFile())) { - for (CityGmlZipEntry entry : entries){ + for (CityGmlZipEntry entry : entries) { entry.loadEntry(config); } } catch (IOException e) { @@ -59,9 +60,9 @@ public class CityGmlZipArchive implements Serializable { } } - private CityGmlZipArchive(Path archivePath){ + private CityGmlZipArchive(Path archivePath) { this.archivePath = archivePath; - this.archiveNameRE = archivePath.getFileName().toString().replace(".zip","") + File.separator; + this.archiveNameRE = archivePath.getFileName().toString().replace(".zip", "") + File.separator; } private void setEntries(List<CityGmlZipEntry> entries) { @@ -69,16 +70,16 @@ public class CityGmlZipArchive implements Serializable { entries.forEach(e -> e.setArchive(this)); } - public void exportToZipFile(String path) { + public void exportToZipFile(String path) { ArchivePacker.packArchive(path, this); } public CityGmlZipEntry getEntry(String fileName) { fileName = stripArchivePath(fileName); - for(CityGmlZipEntry entry : entries){ + for (CityGmlZipEntry entry : entries) { String entryName = stripArchivePath(entry.getFileName()); - if(entryName.equals(fileName)){ + if (entryName.equals(fileName)) { return entry; } } diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/zip/CityGmlZipEntry.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/zip/CityGmlZipEntry.java index c3383c0..7d6474b 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/zip/CityGmlZipEntry.java +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/zip/CityGmlZipEntry.java @@ -27,39 +27,39 @@ public class CityGmlZipEntry implements Serializable { private static final long MB = 1024 * 1024L; private ZipEntryErrorType errorType = null; - public static CityGmlZipEntry of(ZipEntry entry,CityGmlZipArchive parentArchive, ParserConfiguration config){ + public static CityGmlZipEntry of(ZipEntry entry, CityGmlZipArchive parentArchive, ParserConfiguration config) { CityGmlZipEntry ze = CityGmlZipEntry.register(entry, parentArchive); ze.loadEntry(config); return ze; } - public void loadEntry(ParserConfiguration config){ - if (decompressed){ + public void loadEntry(ParserConfiguration config) { + if (decompressed) { return; } - if (errorType != null){ + if (errorType != null) { logger.warn("Tried loading erroneous CityGmlZipEntry"); return; } - try{ + try { this.model = CityGmlParser.parseCityGmlZipEntry(this, config); this.decompressed = true; } catch (CityGmlParseException | InvalidGmlFileException e) { logger.error(e); this.errorType = ZipEntryErrorType.INVALID_CITY_GML_FILE; - } catch (IOException e){ + } catch (IOException e) { logger.error(e); this.errorType = ZipEntryErrorType.IO_ERROR; } } - public static CityGmlZipEntry register(ZipEntry entry, CityGmlZipArchive parentArchive){ - CityGmlZipEntry cgzEntry = new CityGmlZipEntry(entry, parentArchive,false); - try{ + public static CityGmlZipEntry register(ZipEntry entry, CityGmlZipArchive parentArchive) { + CityGmlZipEntry cgzEntry = new CityGmlZipEntry(entry, parentArchive, false); + try { if (!cgzEntry.entrySizeWithinMemoryLimits()) { cgzEntry.errorType = ZipEntryErrorType.EXCESSIVE_FILESIZE; } - } catch (IOException e){ + } catch (IOException e) { logger.error(e); cgzEntry.errorType = ZipEntryErrorType.IO_ERROR; } @@ -67,11 +67,11 @@ public class CityGmlZipEntry implements Serializable { } private boolean entrySizeWithinMemoryLimits() throws IOException { - long memoryLimit = (long) Math.ceil(((double) Runtime.getRuntime().maxMemory() / MB)*0.9); + long memoryLimit = (long) Math.ceil(((double) Runtime.getRuntime().maxMemory() / MB) * 0.9); if (fileSize == -1L) { - try (CityGmlZipEntryFile entryFile = new CityGmlZipEntryFile(this)){ + try (CityGmlZipEntryFile entryFile = new CityGmlZipEntryFile(this)) { long filesize = entryFile.getFileSize(); - if (filesize != -1){ + if (filesize != -1) { this.fileSize = filesize; } else { return false; @@ -83,9 +83,9 @@ public class CityGmlZipEntry implements Serializable { return memoryLimit > fileSize; } - protected CityGmlZipEntry(ZipEntry entry,CityGmlZipArchive parentArchive, boolean decompressed) { + protected CityGmlZipEntry(ZipEntry entry, CityGmlZipArchive parentArchive, boolean decompressed) { this.fileName = entry.getName(); - if (entry.getSize() != -1){ + if (entry.getSize() != -1) { this.fileSize = entry.getSize(); } this.model = null; @@ -93,15 +93,14 @@ public class CityGmlZipEntry implements Serializable { this.parentArchive = parentArchive; } - public void setArchive(CityGmlZipArchive archive){ + public void setArchive(CityGmlZipArchive archive) { parentArchive = archive; } - public CityGmlZipArchive getArchive(){ + public CityGmlZipArchive getArchive() { return parentArchive; } - public String getFileName() { return fileName; } @@ -118,7 +117,7 @@ public class CityGmlZipEntry implements Serializable { fileSize = size; } - public long getFileSize(){ + public long getFileSize() { return fileSize; } } diff --git a/CityDoctorParent/CityDoctorModel/src/test/java/de/hft/stuttgart/citydoctor2/zip/ZipTest.java b/CityDoctorParent/CityDoctorModel/src/test/java/de/hft/stuttgart/citydoctor2/zip/ZipTest.java index 8c0b41c..71a8d8e 100644 --- a/CityDoctorParent/CityDoctorModel/src/test/java/de/hft/stuttgart/citydoctor2/zip/ZipTest.java +++ b/CityDoctorParent/CityDoctorModel/src/test/java/de/hft/stuttgart/citydoctor2/zip/ZipTest.java @@ -9,11 +9,14 @@ import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; -import static org.junit.Assert.*; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; public class ZipTest { - ParserConfiguration config = new ParserConfiguration(8,false); + ParserConfiguration config = new ParserConfiguration(8, false); @Test @@ -25,8 +28,8 @@ public class ZipTest { checkMockArchive(cgmlArch); } - private void checkMockArchive(CityGmlZipArchive cgmlArch){ - assertEquals(5,cgmlArch.getEntries().size()); + private void checkMockArchive(CityGmlZipArchive cgmlArch) { + assertEquals(5, cgmlArch.getEntries().size()); for (CityGmlZipEntry entry : cgmlArch.getEntries()) { assertNotNull(entry); assertTrue(entry.getFileName().matches("^mock[1-5].gml$")); @@ -79,8 +82,8 @@ public class ZipTest { } @Test - public void testXMLValidation(){ - ParserConfiguration valConfig = new ParserConfiguration(8,true); + public void testXMLValidation() { + ParserConfiguration valConfig = new ParserConfiguration(8, true); CityGmlZipArchive cgmlArch = CityGmlZipArchive.register("src/test/resources/zip/validate.zip"); assertNotNull(cgmlArch); cgmlArch.mountArchive(valConfig); @@ -91,7 +94,7 @@ public class ZipTest { @Test - public void testImplicitParsing(){ + public void testImplicitParsing() { CityGmlZipArchive cgmlArch = CityGmlZipArchive.register("src/test/resources/zip/implicit.zip"); assertNotNull(cgmlArch); cgmlArch.mountArchive(config); diff --git a/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/check/Checker.java b/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/check/Checker.java index f498e1c..e3cb538 100644 --- a/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/check/Checker.java +++ b/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/check/Checker.java @@ -1,6 +1,6 @@ /*- * Copyright 2020 Beuth Hochschule für Technik Berlin, Hochschule für Technik Stuttgart - * + * * This file is part of CityDoctor2. * * CityDoctor2 is free software: you can redistribute it and/or modify @@ -18,41 +18,6 @@ */ package de.hft.stuttgart.citydoctor2.check; -import java.io.BufferedOutputStream; -import java.io.File; -import java.io.FileInputStream; -import java.io.FileNotFoundException; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.UncheckedIOException; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Set; -import java.util.stream.Stream; - -import javax.xml.XMLConstants; -import javax.xml.transform.Result; -import javax.xml.transform.Source; -import javax.xml.transform.Transformer; -import javax.xml.transform.TransformerException; -import javax.xml.transform.TransformerFactory; -import javax.xml.transform.dom.DOMResult; -import javax.xml.transform.dom.DOMSource; -import javax.xml.transform.sax.SAXResult; -import javax.xml.transform.stream.StreamSource; - -import de.hft.stuttgart.citydoctor2.zip.CityGmlZipEntry; -import de.hft.stuttgart.citydoctor2.zip.CityGmlZipEntryFile; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; - import de.hft.stuttgart.citydoctor2.check.error.AttributeInvalidError; import de.hft.stuttgart.citydoctor2.check.error.AttributeMissingError; import de.hft.stuttgart.citydoctor2.check.error.AttributeValueWrongError; @@ -78,6 +43,8 @@ import de.hft.stuttgart.citydoctor2.reporting.XmlValidationReporter; import de.hft.stuttgart.citydoctor2.reporting.pdf.PdfReporter; import de.hft.stuttgart.citydoctor2.reporting.pdf.PdfStreamReporter; import de.hft.stuttgart.citydoctor2.utils.Localization; +import de.hft.stuttgart.citydoctor2.zip.CityGmlZipEntry; +import de.hft.stuttgart.citydoctor2.zip.CityGmlZipEntryFile; import de.hft.stuttgart.quality.model.enums.RequirementId; import de.hft.stuttgart.quality.model.enums.TopLevelFeatureType; import de.hft.stuttgart.quality.model.properties.CheckingProperty; @@ -88,309 +55,339 @@ import de.hft.stuttgart.quality.model.properties.RequirementProperty; import de.hft.stuttgart.quality.model.types.Checking; import de.hft.stuttgart.quality.model.types.Parameter; import de.hft.stuttgart.quality.model.types.ValidationPlan; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import javax.xml.XMLConstants; +import javax.xml.transform.Result; +import javax.xml.transform.Source; +import javax.xml.transform.Transformer; +import javax.xml.transform.TransformerException; +import javax.xml.transform.TransformerFactory; +import javax.xml.transform.dom.DOMResult; +import javax.xml.transform.dom.DOMSource; +import javax.xml.transform.sax.SAXResult; +import javax.xml.transform.stream.StreamSource; +import java.io.BufferedOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.UncheckedIOException; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; +import java.util.stream.Stream; /** * The main container class for checking. It contains the logic for validation, * as well as contains the state of the checks performed. - * - * @author Matthias Betz * + * @author Matthias Betz */ public class Checker { - private static final Logger logger = LogManager.getLogger(Checker.class); - - private ValidationConfiguration config; - private List<List<Check>> execLayers; - - private List<Filter> includeFilters; - private List<Filter> excludeFilters; - - private final Checks checkConfig; - private final CityDoctorModel model; - - public Checker(CityDoctorModel model) { - this(ValidationConfiguration.loadStandardValidationConfig(), model); - } - - public Checker(ValidationConfiguration config, CityDoctorModel model) { - this.model = model; - checkConfig = new Checks(); - setValidationConfig(config); - } - - public Checks getChecks() { - return checkConfig; - } - - public CityDoctorModel getModel() { - return model; - } - - /** - * Write the xml report for the given CityDoctorModel. If no report location is - * given or this checker has not validated anything, nothing is done. - * - * @param xmlOutput the output file location for the XML report. Can be null. - */ - public void writeXmlReport(String xmlOutput) { - if (!model.isValidated() || xmlOutput == null) { - return; - } - File xmlFile = new File(xmlOutput); - if (xmlFile.getParentFile() != null) { - xmlFile.getParentFile().mkdirs(); - } - Reporter reporter = new XmlValidationReporter(); - try (BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(xmlFile.getAbsolutePath()))) { - reporter.writeReport(checkConfig, bos, model, config); - } catch (CheckReportWriteException | IOException e) { - logger.error(Localization.getText("Checker.failXml"), e); - } - } - - public void writePdfReport(String pdfOutput) { - if (!model.isValidated() || pdfOutput == null) { - return; - } - File pdfFile = new File(pdfOutput); - if (pdfFile.getParentFile() != null) { - pdfFile.getParentFile().mkdirs(); - } - Reporter reporter = new PdfReporter(); - try (BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(pdfFile.getAbsolutePath()))) { - reporter.writeReport(checkConfig, bos, model, config); - } catch (IOException | CheckReportWriteException e) { - logger.error(Localization.getText("Checker.failPdf"), e); - } - } - - public void runChecks() { - runChecks((ProgressListener) null); - } - - public void runChecks(String xmlOutput) { - runChecks(); - writeXmlReport(xmlOutput); - } - - public void runChecks(String xmlOutput, String pdfOutput) { - runChecks(); - writeXmlReport(xmlOutput); - writePdfReport(pdfOutput); - } - - public void runChecks(String xmlOutput, String pdfOutput, ProgressListener l) { - runChecks(l); - writeXmlReport(xmlOutput); - writePdfReport(pdfOutput); - } - - public void runChecks(ProgressListener l) { - if (config == null) { - config = ValidationConfiguration.loadStandardValidationConfig(); - } - checkCityModel(model, l); - if (logger.isInfoEnabled()) { - logger.info(Localization.getText("Checker.checksFinished")); - } - SvrlContentHandler handler = executeSchematronValidationIfAvailable(config, model.getFile()); - if (handler != null) { - handleSchematronResults(handler); - } - model.setValidated(createValidationPlan()); - } - - private void handleSchematronResults(SvrlContentHandler handler) { - model.addGlobalErrors(handler.getGeneralErrors()); - Map<String, CityObject> featureMap = new HashMap<>(); - model.createFeatureStream().forEach(f -> featureMap.put(f.getGmlId().getGmlString(), f)); - handler.getFeatureErrors().forEach((k, v) -> { - if (k.trim().isEmpty()) { - // missing gml id, ignore? - return; - } - CityObject co = featureMap.get(k); - if (co == null) { - // gml id reported by schematron was not found, add to general errors - for (SchematronError se : v) { - model.addGlobalError(se); - } - } else { - handleSchematronErrorsForCityObject(v, co); - } - }); - } - - public static void handleSchematronErrorsForCityObject(List<SchematronError> v, CityObject co) { - int count = 0; - for (SchematronError se : v) { - CheckError err; - if (AttributeMissingError.ID.getIdString().equals(se.getErrorIdString())) { - err = new AttributeMissingError(co, se.getChildId(), se.getNameOfAttribute()); - } else if (AttributeValueWrongError.ID.getIdString().equals(se.getErrorIdString())) { - err = new AttributeValueWrongError(co, se.getChildId(), se.getNameOfAttribute()); - } else if (AttributeInvalidError.ID.getIdString().equals(se.getErrorIdString())) { - err = new AttributeInvalidError(co, se.getChildId(), se.getNameOfAttribute()); - } else { - throw new IllegalStateException( - "Unknown error ID was given in schematron file: " + se.getErrorIdString()); - } - co.addCheckResult(new CheckResult(new CheckId("SchematronCheck " + count), ResultStatus.ERROR, err)); - count++; - } - } - - ValidationPlan createValidationPlan() { - ValidationPlan plan = new ValidationPlan(); - de.hft.stuttgart.quality.model.types.Filter filter = createFilter(); - plan.setFilter(new FilterProperty(filter)); - - Map<String, de.hft.stuttgart.citydoctor2.check.Requirement> reqs = Checks.getAvailableRequirements(); - for (Entry<String, RequirementConfiguration> e : config.getRequirements().entrySet()) { - RequirementId reqId = mapToRequirement(e.getKey()); - if (reqId == null) { - continue; - } - de.hft.stuttgart.quality.model.types.Requirement req = new de.hft.stuttgart.quality.model.types.Requirement(); - req.setRequirementType(reqId); - req.setEnabled(e.getValue().isEnabled()); - plan.getRequirements().add(new RequirementProperty(req)); - Map<String, String> parameters = e.getValue().getParameters(); - if (parameters != null) { - for (Entry<String, String> param : parameters.entrySet()) { - Parameter p = new Parameter(); - DefaultParameter defaultP = getDefaultParameter(e.getKey(), reqs, param.getKey()); - if (defaultP != null) { - p.setUom(defaultP.getUnitType().getGmlRepresentation()); - } - p.setName(param.getKey()); - p.setValue(param.getValue()); - req.getParameters().add(new ParameterProperty(p)); - } - } - } - - de.hft.stuttgart.quality.model.types.Requirement missing = new de.hft.stuttgart.quality.model.types.Requirement(); - missing.setRequirementType(RequirementId.R_SE_ATTRIBUTES_EXISTING); - de.hft.stuttgart.quality.model.types.Requirement correct = new de.hft.stuttgart.quality.model.types.Requirement(); - correct.setRequirementType(RequirementId.R_SE_ATTRIBUTES_CORRECT); - missing.setEnabled(config.getSchematronFilePath() != null); - correct.setEnabled(config.getSchematronFilePath() != null); - plan.getRequirements().add(new RequirementProperty(missing)); - plan.getRequirements().add(new RequirementProperty(correct)); - - Parameter numRounding = new Parameter(); - numRounding.setName("numberOfRoundingPlaces"); - numRounding.setValue("" + config.getNumberOfRoundingPlaces()); - Parameter minVertexDistance = new Parameter(); - minVertexDistance.setName("minVertexDistance"); - minVertexDistance.setUom("m"); - minVertexDistance.setValue("" + config.getMinVertexDistance()); - Parameter schematronFile = new Parameter(); - schematronFile.setName("schematronFile"); - schematronFile.setValue(config.getSchematronFilePath()); - de.hft.stuttgart.quality.model.types.GlobalParameters globParams = new de.hft.stuttgart.quality.model.types.GlobalParameters(); - plan.setGlobalParameters(new GlobalParametersProperty(globParams)); - globParams.getParameters().add(new ParameterProperty(numRounding)); - globParams.getParameters().add(new ParameterProperty(minVertexDistance)); - globParams.getParameters().add(new ParameterProperty(schematronFile)); - return plan; - } - - private DefaultParameter getDefaultParameter(String reqKey, - Map<String, de.hft.stuttgart.citydoctor2.check.Requirement> reqs, String paramName) { - de.hft.stuttgart.citydoctor2.check.Requirement requirement = reqs.get(reqKey); - if (requirement != null) { - for (DefaultParameter param : requirement.getDefaultParameter()) { - if (param.getName().equals(paramName)) { - return param; - } - } - } - return null; - } - - private de.hft.stuttgart.quality.model.types.Filter createFilter() { - var filter = new de.hft.stuttgart.quality.model.types.Filter(); - handleInputFilter(filter); - if (excludeFilters != null) { - for (Filter f : excludeFilters) { - if (f instanceof TypeFilter tf) { - FeatureType type = tf.getType(); - TopLevelFeatureType tlft = mapToTopLevelFeatureType(type); - if (tlft == null) { - continue; - } - removeFilter(tlft, filter); - } - } - } - return filter; - } - - private void handleInputFilter(de.hft.stuttgart.quality.model.types.Filter filter) { - if (includeFilters == null || includeFilters.isEmpty()) { - // no filter means, use all - addAllFilters(filter); - } else { - for (Filter f : includeFilters) { - if (f instanceof TypeFilter tf) { - FeatureType type = tf.getType(); - TopLevelFeatureType tlft = mapToTopLevelFeatureType(type); - if (tlft == null) { - continue; - } - Checking c = new Checking(); - c.setFeatureType(tlft); - filter.getChecking().add(new CheckingProperty(c)); - } - } - if (filter.getChecking().isEmpty()) { - // this happens if no type include filter was used - // it is possible only single objects were tested then - // so include everything - addAllFilters(filter); - } - } - } - - private void addAllFilters(de.hft.stuttgart.quality.model.types.Filter filter) { - Checking buildingChecking = new Checking(); - buildingChecking.setFeatureType(TopLevelFeatureType.BUILDING); - filter.getChecking().add(new CheckingProperty(buildingChecking)); - - Checking bridgeChecking = new Checking(); - bridgeChecking.setFeatureType(TopLevelFeatureType.BRIDGE); - filter.getChecking().add(new CheckingProperty(bridgeChecking)); - - Checking landChecking = new Checking(); - landChecking.setFeatureType(TopLevelFeatureType.LAND); - filter.getChecking().add(new CheckingProperty(landChecking)); - - Checking transportationChecking = new Checking(); - transportationChecking.setFeatureType(TopLevelFeatureType.TRANSPORTATION); - filter.getChecking().add(new CheckingProperty(transportationChecking)); - - Checking vegetationChecking = new Checking(); - vegetationChecking.setFeatureType(TopLevelFeatureType.VEGETATION); - filter.getChecking().add(new CheckingProperty(vegetationChecking)); - - Checking waterChecking = new Checking(); - waterChecking.setFeatureType(TopLevelFeatureType.WATER); - filter.getChecking().add(new CheckingProperty(waterChecking)); - } - - private void removeFilter(TopLevelFeatureType tlft, de.hft.stuttgart.quality.model.types.Filter filter) { - for (CheckingProperty c : filter.getChecking()) { - if (c.getObject().getFeatureType().equals(tlft)) { - filter.getChecking().remove(c); - return; - } - } - } - - private TopLevelFeatureType mapToTopLevelFeatureType(FeatureType type) { + private static final Logger logger = LogManager.getLogger(Checker.class); + + private ValidationConfiguration config; + private List<List<Check>> execLayers; + + private List<Filter> includeFilters; + private List<Filter> excludeFilters; + + private final Checks checkConfig; + private final CityDoctorModel model; + + public Checker(CityDoctorModel model) { + this(ValidationConfiguration.loadStandardValidationConfig(), model); + } + + public Checker(ValidationConfiguration config, CityDoctorModel model) { + this.model = model; + checkConfig = new Checks(); + setValidationConfig(config); + } + + public Checks getChecks() { + return checkConfig; + } + + public CityDoctorModel getModel() { + return model; + } + + /** + * Write the xml report for the given CityDoctorModel. If no report location is + * given or this checker has not validated anything, nothing is done. + * + * @param xmlOutput the output file location for the XML report. Can be null. + */ + public void writeXmlReport(String xmlOutput) { + if (!model.isValidated() || xmlOutput == null) { + return; + } + File xmlFile = new File(xmlOutput); + if (xmlFile.getParentFile() != null) { + xmlFile.getParentFile().mkdirs(); + } + Reporter reporter = new XmlValidationReporter(); + try (BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(xmlFile.getAbsolutePath()))) { + reporter.writeReport(checkConfig, bos, model, config); + } catch (CheckReportWriteException | IOException e) { + logger.error(Localization.getText("Checker.failXml"), e); + } + } + + public void writePdfReport(String pdfOutput) { + if (!model.isValidated() || pdfOutput == null) { + return; + } + File pdfFile = new File(pdfOutput); + if (pdfFile.getParentFile() != null) { + pdfFile.getParentFile().mkdirs(); + } + Reporter reporter = new PdfReporter(); + try (BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(pdfFile.getAbsolutePath()))) { + reporter.writeReport(checkConfig, bos, model, config); + } catch (IOException | CheckReportWriteException e) { + logger.error(Localization.getText("Checker.failPdf"), e); + } + } + + public void runChecks() { + runChecks((ProgressListener) null); + } + + public void runChecks(String xmlOutput) { + runChecks(); + writeXmlReport(xmlOutput); + } + + public void runChecks(String xmlOutput, String pdfOutput) { + runChecks(); + writeXmlReport(xmlOutput); + writePdfReport(pdfOutput); + } + + public void runChecks(String xmlOutput, String pdfOutput, ProgressListener l) { + runChecks(l); + writeXmlReport(xmlOutput); + writePdfReport(pdfOutput); + } + + public void runChecks(ProgressListener l) { + if (config == null) { + config = ValidationConfiguration.loadStandardValidationConfig(); + } + checkCityModel(model, l); + if (logger.isInfoEnabled()) { + logger.info(Localization.getText("Checker.checksFinished")); + } + SvrlContentHandler handler = executeSchematronValidationIfAvailable(config, model.getFile()); + if (handler != null) { + handleSchematronResults(handler); + } + model.setValidated(createValidationPlan()); + } + + private void handleSchematronResults(SvrlContentHandler handler) { + model.addGlobalErrors(handler.getGeneralErrors()); + Map<String, CityObject> featureMap = new HashMap<>(); + model.createFeatureStream().forEach(f -> featureMap.put(f.getGmlId().getGmlString(), f)); + handler.getFeatureErrors().forEach((k, v) -> { + if (k.trim().isEmpty()) { + // missing gml id, ignore? + return; + } + CityObject co = featureMap.get(k); + if (co == null) { + // gml id reported by schematron was not found, add to general errors + for (SchematronError se : v) { + model.addGlobalError(se); + } + } else { + handleSchematronErrorsForCityObject(v, co); + } + }); + } + + public static void handleSchematronErrorsForCityObject(List<SchematronError> v, CityObject co) { + int count = 0; + for (SchematronError se : v) { + CheckError err; + if (AttributeMissingError.ID.getIdString().equals(se.getErrorIdString())) { + err = new AttributeMissingError(co, se.getChildId(), se.getNameOfAttribute()); + } else if (AttributeValueWrongError.ID.getIdString().equals(se.getErrorIdString())) { + err = new AttributeValueWrongError(co, se.getChildId(), se.getNameOfAttribute()); + } else if (AttributeInvalidError.ID.getIdString().equals(se.getErrorIdString())) { + err = new AttributeInvalidError(co, se.getChildId(), se.getNameOfAttribute()); + } else { + throw new IllegalStateException( + "Unknown error ID was given in schematron file: " + se.getErrorIdString()); + } + co.addCheckResult(new CheckResult(new CheckId("SchematronCheck " + count), ResultStatus.ERROR, err)); + count++; + } + } + + ValidationPlan createValidationPlan() { + ValidationPlan plan = new ValidationPlan(); + de.hft.stuttgart.quality.model.types.Filter filter = createFilter(); + plan.setFilter(new FilterProperty(filter)); + + Map<String, de.hft.stuttgart.citydoctor2.check.Requirement> reqs = Checks.getAvailableRequirements(); + for (Entry<String, RequirementConfiguration> e : config.getRequirements().entrySet()) { + RequirementId reqId = mapToRequirement(e.getKey()); + if (reqId == null) { + continue; + } + de.hft.stuttgart.quality.model.types.Requirement req = new de.hft.stuttgart.quality.model.types.Requirement(); + req.setRequirementType(reqId); + req.setEnabled(e.getValue().isEnabled()); + plan.getRequirements().add(new RequirementProperty(req)); + Map<String, String> parameters = e.getValue().getParameters(); + if (parameters != null) { + for (Entry<String, String> param : parameters.entrySet()) { + Parameter p = new Parameter(); + DefaultParameter defaultP = getDefaultParameter(e.getKey(), reqs, param.getKey()); + if (defaultP != null) { + p.setUom(defaultP.getUnitType().getGmlRepresentation()); + } + p.setName(param.getKey()); + p.setValue(param.getValue()); + req.getParameters().add(new ParameterProperty(p)); + } + } + } + + de.hft.stuttgart.quality.model.types.Requirement missing = new de.hft.stuttgart.quality.model.types.Requirement(); + missing.setRequirementType(RequirementId.R_SE_ATTRIBUTES_EXISTING); + de.hft.stuttgart.quality.model.types.Requirement correct = new de.hft.stuttgart.quality.model.types.Requirement(); + correct.setRequirementType(RequirementId.R_SE_ATTRIBUTES_CORRECT); + missing.setEnabled(config.getSchematronFilePath() != null); + correct.setEnabled(config.getSchematronFilePath() != null); + plan.getRequirements().add(new RequirementProperty(missing)); + plan.getRequirements().add(new RequirementProperty(correct)); + + Parameter numRounding = new Parameter(); + numRounding.setName("numberOfRoundingPlaces"); + numRounding.setValue("" + config.getNumberOfRoundingPlaces()); + Parameter minVertexDistance = new Parameter(); + minVertexDistance.setName("minVertexDistance"); + minVertexDistance.setUom("m"); + minVertexDistance.setValue("" + config.getMinVertexDistance()); + Parameter schematronFile = new Parameter(); + schematronFile.setName("schematronFile"); + schematronFile.setValue(config.getSchematronFilePath()); + de.hft.stuttgart.quality.model.types.GlobalParameters globParams = new de.hft.stuttgart.quality.model.types.GlobalParameters(); + plan.setGlobalParameters(new GlobalParametersProperty(globParams)); + globParams.getParameters().add(new ParameterProperty(numRounding)); + globParams.getParameters().add(new ParameterProperty(minVertexDistance)); + globParams.getParameters().add(new ParameterProperty(schematronFile)); + return plan; + } + + private DefaultParameter getDefaultParameter(String reqKey, + Map<String, de.hft.stuttgart.citydoctor2.check.Requirement> reqs, String paramName) { + de.hft.stuttgart.citydoctor2.check.Requirement requirement = reqs.get(reqKey); + if (requirement != null) { + for (DefaultParameter param : requirement.getDefaultParameter()) { + if (param.getName().equals(paramName)) { + return param; + } + } + } + return null; + } + + private de.hft.stuttgart.quality.model.types.Filter createFilter() { + var filter = new de.hft.stuttgart.quality.model.types.Filter(); + handleInputFilter(filter); + if (excludeFilters != null) { + for (Filter f : excludeFilters) { + if (f instanceof TypeFilter tf) { + FeatureType type = tf.getType(); + TopLevelFeatureType tlft = mapToTopLevelFeatureType(type); + if (tlft == null) { + continue; + } + removeFilter(tlft, filter); + } + } + } + return filter; + } + + private void handleInputFilter(de.hft.stuttgart.quality.model.types.Filter filter) { + if (includeFilters == null || includeFilters.isEmpty()) { + // no filter means, use all + addAllFilters(filter); + } else { + for (Filter f : includeFilters) { + if (f instanceof TypeFilter tf) { + FeatureType type = tf.getType(); + TopLevelFeatureType tlft = mapToTopLevelFeatureType(type); + if (tlft == null) { + continue; + } + Checking c = new Checking(); + c.setFeatureType(tlft); + filter.getChecking().add(new CheckingProperty(c)); + } + } + if (filter.getChecking().isEmpty()) { + // this happens if no type include filter was used + // it is possible only single objects were tested then + // so include everything + addAllFilters(filter); + } + } + } + + private void addAllFilters(de.hft.stuttgart.quality.model.types.Filter filter) { + Checking buildingChecking = new Checking(); + buildingChecking.setFeatureType(TopLevelFeatureType.BUILDING); + filter.getChecking().add(new CheckingProperty(buildingChecking)); + + Checking bridgeChecking = new Checking(); + bridgeChecking.setFeatureType(TopLevelFeatureType.BRIDGE); + filter.getChecking().add(new CheckingProperty(bridgeChecking)); + + Checking landChecking = new Checking(); + landChecking.setFeatureType(TopLevelFeatureType.LAND); + filter.getChecking().add(new CheckingProperty(landChecking)); + + Checking transportationChecking = new Checking(); + transportationChecking.setFeatureType(TopLevelFeatureType.TRANSPORTATION); + filter.getChecking().add(new CheckingProperty(transportationChecking)); + + Checking vegetationChecking = new Checking(); + vegetationChecking.setFeatureType(TopLevelFeatureType.VEGETATION); + filter.getChecking().add(new CheckingProperty(vegetationChecking)); + + Checking waterChecking = new Checking(); + waterChecking.setFeatureType(TopLevelFeatureType.WATER); + filter.getChecking().add(new CheckingProperty(waterChecking)); + } + + private void removeFilter(TopLevelFeatureType tlft, de.hft.stuttgart.quality.model.types.Filter filter) { + for (CheckingProperty c : filter.getChecking()) { + if (c.getObject().getFeatureType().equals(tlft)) { + filter.getChecking().remove(c); + return; + } + } + } + + private TopLevelFeatureType mapToTopLevelFeatureType(FeatureType type) { return switch (type) { case BRIDGE -> TopLevelFeatureType.BRIDGE; case BUILDING -> TopLevelFeatureType.BUILDING; @@ -400,298 +397,298 @@ public class Checker { case WATER -> TopLevelFeatureType.WATER; default -> null; }; - } - - private RequirementId mapToRequirement(String requirementName) { - try { - return RequirementId.valueOf(requirementName); - } catch (IllegalArgumentException e) { - return null; - } - } - - public ValidationConfiguration getConfig() { - return config; - } - - public static SvrlContentHandler executeSchematronValidationIfAvailable(ValidationConfiguration config, File file) { - if (file == null || !file.exists()) { - return null; - } - try { - return executeSchematronValidationIfAvailable(config, new FileInputStream(file)); - } catch (FileNotFoundException e) { - throw new UncheckedIOException(e); - } - } - - public static SvrlContentHandler executeSchematronValidationIfAvailable(ValidationConfiguration config, - InputStream in) { - if (config.getSchematronFilePath() != null && !config.getSchematronFilePath().isEmpty()) { - if (logger.isInfoEnabled()) { - logger.info(Localization.getText("Checker.schematronValidation")); - } - - try { - TransformerFactory transformerFactory = TransformerFactory.newInstance("net.sf.saxon.TransformerFactoryImpl", Checker.class.getClassLoader()); - transformerFactory.setAttribute(XMLConstants.ACCESS_EXTERNAL_DTD, ""); - transformerFactory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true); - - transformerFactory.setURIResolver((href, base) -> new StreamSource(Checker.class.getResourceAsStream(href))); - - Source dsdlXslSource = new StreamSource(Checker.class.getResourceAsStream("iso_dsdl_include.xsl")); - Transformer dsdlXslTransformer = transformerFactory.newTransformer(dsdlXslSource); - - DOMResult dsdlXslResult = new DOMResult(); - dsdlXslTransformer.transform(new StreamSource(new File(config.getSchematronFilePath())), dsdlXslResult); - - Source abstractExpandXsl = new StreamSource(Checker.class.getResourceAsStream("iso_abstract_expand.xsl")); - Transformer abstractExpandTransformer = transformerFactory.newTransformer(abstractExpandXsl); - - DOMResult abstractExpandResult = new DOMResult(); - abstractExpandTransformer.transform(new DOMSource(dsdlXslResult.getNode()), abstractExpandResult); - - Source svrlXslSource = new StreamSource(Checker.class.getResourceAsStream("iso_svrl_for_xslt2.xsl")); - Transformer svrlTransformer = transformerFactory.newTransformer(svrlXslSource); - - DOMResult schematronXsltResult = new DOMResult(); - svrlTransformer.transform(new DOMSource(abstractExpandResult.getNode()), schematronXsltResult); - - Transformer schematronTransformer = transformerFactory.newTransformer(new DOMSource(schematronXsltResult.getNode())); - Source cityGmlSource = new StreamSource(in); - - SvrlContentHandler handler = new SvrlContentHandler(); - Result finalResult = new SAXResult(handler); - - schematronTransformer.transform(cityGmlSource, finalResult); - return handler; - } catch (TransformerException e) { - logger.catching(e); - } - } - return null; - } - - private void buildFilters() { - FilterConfiguration filterConfig = config.getFilter(); - if (filterConfig == null) { - includeFilters = Collections.emptyList(); - excludeFilters = Collections.emptyList(); - return; - } - excludeFilters = buildExcludeFilters(filterConfig); - includeFilters = buildIncludeFilters(filterConfig); - } - - private List<Filter> buildExcludeFilters(FilterConfiguration filterConfig) { - if (filterConfig == null) { - return Collections.emptyList(); - } - ExcludeFilterConfiguration excludeConfig = filterConfig.getExclude(); - if (excludeConfig == null) { - return Collections.emptyList(); - } else { - List<Filter> filters = new ArrayList<>(); - if (excludeConfig.getTypes() != null) { - for (FeatureType excludeType : excludeConfig.getTypes()) { - filters.add(new TypeFilter(excludeType)); - } - } - if (excludeConfig.getIds() != null) { - for (String excludePattern : excludeConfig.getIds()) { - Filter f = new EqualsIgnoreCaseFilter(excludePattern); - filters.add(f); - } - } - return filters; - } - } - - private List<Filter> buildIncludeFilters(FilterConfiguration filterConfig) { - if (filterConfig == null) { - return Collections.emptyList(); - } - IncludeFilterConfiguration includeConfig = filterConfig.getInclude(); - if (includeConfig == null) { - return Collections.emptyList(); - } else { - List<Filter> filters = new ArrayList<>(); - if (includeConfig.getTypes() != null) { - for (FeatureType includeType : includeConfig.getTypes()) { - filters.add(new TypeFilter(includeType)); - } - } - if (includeConfig.getIds() != null) { - for (String includePattern : includeConfig.getIds()) { - Filter f = new EqualsIgnoreCaseFilter(includePattern); - filters.add(f); - } - } - return filters; - } - } - - private void setValidationConfig(ValidationConfiguration config) { - if (config == null) { - throw new IllegalArgumentException("Validation configuration may not be null"); - } - this.config = config; - buildFilters(); - ParserConfiguration parserConfig = config.getParserConfiguration(); - List<Check> checks = collectEnabledChecksAndInit(parserConfig, config); - execLayers = buildExecutionLayers(checks); - } - - private List<Check> collectEnabledChecksAndInit(ParserConfiguration parserConfig, ValidationConfiguration config) { - Set<CheckId> enabledCheck = new HashSet<>(); - Map<CheckId, Map<String, String>> parameterMap = new HashMap<>(); - for (Entry<String, RequirementConfiguration> e : config.getRequirements().entrySet()) { - de.hft.stuttgart.citydoctor2.check.Requirement req = Checks.getAvailableRequirements().get(e.getKey()); - if (req == null) { - logger.warn("Could not find any check that satisfies requirement {}, it will not be checked", - e.getKey()); - } else { - if (e.getValue().isEnabled()) { - // this requirement is enabled - insertGlobalParameters(config, enabledCheck, parameterMap, e, req); - } - } - } - fillParameterMapsWithDefaultParameter(enabledCheck, parameterMap); - ArrayList<Check> checkList = new ArrayList<>(); - for (CheckId id : enabledCheck) { - Check c = checkConfig.getCheckForId(id); - c.init(parameterMap.get(id), parserConfig); - checkList.add(c); - } - return checkList; - } - - private void insertGlobalParameters(ValidationConfiguration config, Set<CheckId> enabledCheck, - Map<CheckId, Map<String, String>> parameterMap, Entry<String, RequirementConfiguration> e, - de.hft.stuttgart.citydoctor2.check.Requirement req) { - for (CheckPrototype proto : Checks.getCheckPrototypes()) { - if (proto.checksRequirements().contains(req)) { - // this requirement is checked by this check - // put all requirement parameter in the map - parameterMap.compute(proto.getCheckId(), (k, v) -> { - if (v == null) { - v = new HashMap<>(); - v.put(GlobalParameters.NUMBER_OF_ROUNDING_PLACES, config.getNumberOfRoundingPlacesAsString()); - v.put(GlobalParameters.MIN_VERTEX_DISTANCE, config.getMinVertexDistanceAsString()); - } - v.putAll(e.getValue().getParameters()); - return v; - }); - enabledCheck.add(proto.getCheckId()); - collectDependencyChecks(proto, enabledCheck); - } - } - } - - private void fillParameterMapsWithDefaultParameter(Set<CheckId> enabledCheck, - Map<CheckId, Map<String, String>> parameterMap) { - for (CheckId id : enabledCheck) { - CheckPrototype proto = Checks.getCheckPrototypeForId(id); - Map<String, String> map = parameterMap.computeIfAbsent(id, k -> new HashMap<>()); - for (de.hft.stuttgart.citydoctor2.check.Requirement req : proto.checksRequirements()) { - if (proto.checksRequirements().contains(req)) { - for (DefaultParameter param : req.getDefaultParameter()) { - map.computeIfAbsent(param.getName(), k -> param.getValue()); - } - } - } - } - } - - private void collectDependencyChecks(CheckPrototype proto, Set<CheckId> enabledChecks) { - enabledChecks.addAll(proto.getDependencies()); - for (CheckId id : proto.getDependencies()) { - if (enabledChecks.contains(id)) { - continue; - } - CheckPrototype depProto = Checks.getCheckPrototypeForId(id); - collectDependencyChecks(depProto, enabledChecks); - } - } - - private void checkCityModel(CityDoctorModel model, ProgressListener l) { - Stream<CityObject> features = model.createFeatureStream(); - float featureSum = model.getNumberOfFeatures(); - // stupid lamda with final variable restrictions - int[] currentFeature = new int[1]; - features.forEach(co -> { - if (config.getParserConfiguration().useLowMemoryConsumption()) { - // no edges have been created yet, create them - co.prepareForChecking(); - } - // check every feature - executeChecksForCityObject(co); - - if (config.getParserConfiguration().useLowMemoryConsumption()) { - // low memory consumption, remove edges again - co.clearMetaInformation(); - } - if (l != null) { - currentFeature[0]++; - l.updateProgress(currentFeature[0] / featureSum); - } - }); - } - - private boolean filterObject(CityObject co) { - return isObjectIncluded(co, includeFilters, excludeFilters); - } - - private boolean isObjectIncluded(CityObject co, List<Filter> includeFilters, List<Filter> excludeFilters) { - if (!includeFilters.isEmpty()) { - boolean include = false; - for (Filter f : includeFilters) { - if (f.matches(co)) { - include = true; - break; - } - } - if (!include) { - // not included, ignore - return false; - } - } - // check if object is excluded - for (Filter f : excludeFilters) { - if (f.matches(co)) { - // exclude object - return false; - } - } - return true; - } - - /** - * Checks the city object if it has not been removed by the filters. The check - * result are stored into the city object itself. - * - * @param co the city object that is going to be checked - */ - private void executeChecksForCityObject(CityObject co) { - if (!filterObject(co)) { - return; - } - executeChecksForCheckable(co); - } - - /** - * Executes all checks for the checkable. This will bypass the filters. This - * will clear the old check results - * - * @param co the checkable. - */ - public void executeChecksForCheckable(Checkable co) { - // throw away old results - co.clearAllContainedCheckResults(); - if (logger.isDebugEnabled()) { - logger.debug(Localization.getText("Checker.checkFeature"), co); - } + } + + private RequirementId mapToRequirement(String requirementName) { + try { + return RequirementId.valueOf(requirementName); + } catch (IllegalArgumentException e) { + return null; + } + } + + public ValidationConfiguration getConfig() { + return config; + } + + public static SvrlContentHandler executeSchematronValidationIfAvailable(ValidationConfiguration config, File file) { + if (file == null || !file.exists()) { + return null; + } + try { + return executeSchematronValidationIfAvailable(config, new FileInputStream(file)); + } catch (FileNotFoundException e) { + throw new UncheckedIOException(e); + } + } + + public static SvrlContentHandler executeSchematronValidationIfAvailable(ValidationConfiguration config, + InputStream in) { + if (config.getSchematronFilePath() != null && !config.getSchematronFilePath().isEmpty()) { + if (logger.isInfoEnabled()) { + logger.info(Localization.getText("Checker.schematronValidation")); + } + + try { + TransformerFactory transformerFactory = TransformerFactory.newInstance("net.sf.saxon.TransformerFactoryImpl", Checker.class.getClassLoader()); + transformerFactory.setAttribute(XMLConstants.ACCESS_EXTERNAL_DTD, ""); + transformerFactory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true); + + transformerFactory.setURIResolver((href, base) -> new StreamSource(Checker.class.getResourceAsStream(href))); + + Source dsdlXslSource = new StreamSource(Checker.class.getResourceAsStream("iso_dsdl_include.xsl")); + Transformer dsdlXslTransformer = transformerFactory.newTransformer(dsdlXslSource); + + DOMResult dsdlXslResult = new DOMResult(); + dsdlXslTransformer.transform(new StreamSource(new File(config.getSchematronFilePath())), dsdlXslResult); + + Source abstractExpandXsl = new StreamSource(Checker.class.getResourceAsStream("iso_abstract_expand.xsl")); + Transformer abstractExpandTransformer = transformerFactory.newTransformer(abstractExpandXsl); + + DOMResult abstractExpandResult = new DOMResult(); + abstractExpandTransformer.transform(new DOMSource(dsdlXslResult.getNode()), abstractExpandResult); + + Source svrlXslSource = new StreamSource(Checker.class.getResourceAsStream("iso_svrl_for_xslt2.xsl")); + Transformer svrlTransformer = transformerFactory.newTransformer(svrlXslSource); + + DOMResult schematronXsltResult = new DOMResult(); + svrlTransformer.transform(new DOMSource(abstractExpandResult.getNode()), schematronXsltResult); + + Transformer schematronTransformer = transformerFactory.newTransformer(new DOMSource(schematronXsltResult.getNode())); + Source cityGmlSource = new StreamSource(in); + + SvrlContentHandler handler = new SvrlContentHandler(); + Result finalResult = new SAXResult(handler); + + schematronTransformer.transform(cityGmlSource, finalResult); + return handler; + } catch (TransformerException e) { + logger.catching(e); + } + } + return null; + } + + private void buildFilters() { + FilterConfiguration filterConfig = config.getFilter(); + if (filterConfig == null) { + includeFilters = Collections.emptyList(); + excludeFilters = Collections.emptyList(); + return; + } + excludeFilters = buildExcludeFilters(filterConfig); + includeFilters = buildIncludeFilters(filterConfig); + } + + private List<Filter> buildExcludeFilters(FilterConfiguration filterConfig) { + if (filterConfig == null) { + return Collections.emptyList(); + } + ExcludeFilterConfiguration excludeConfig = filterConfig.getExclude(); + if (excludeConfig == null) { + return Collections.emptyList(); + } else { + List<Filter> filters = new ArrayList<>(); + if (excludeConfig.getTypes() != null) { + for (FeatureType excludeType : excludeConfig.getTypes()) { + filters.add(new TypeFilter(excludeType)); + } + } + if (excludeConfig.getIds() != null) { + for (String excludePattern : excludeConfig.getIds()) { + Filter f = new EqualsIgnoreCaseFilter(excludePattern); + filters.add(f); + } + } + return filters; + } + } + + private List<Filter> buildIncludeFilters(FilterConfiguration filterConfig) { + if (filterConfig == null) { + return Collections.emptyList(); + } + IncludeFilterConfiguration includeConfig = filterConfig.getInclude(); + if (includeConfig == null) { + return Collections.emptyList(); + } else { + List<Filter> filters = new ArrayList<>(); + if (includeConfig.getTypes() != null) { + for (FeatureType includeType : includeConfig.getTypes()) { + filters.add(new TypeFilter(includeType)); + } + } + if (includeConfig.getIds() != null) { + for (String includePattern : includeConfig.getIds()) { + Filter f = new EqualsIgnoreCaseFilter(includePattern); + filters.add(f); + } + } + return filters; + } + } + + private void setValidationConfig(ValidationConfiguration config) { + if (config == null) { + throw new IllegalArgumentException("Validation configuration may not be null"); + } + this.config = config; + buildFilters(); + ParserConfiguration parserConfig = config.getParserConfiguration(); + List<Check> checks = collectEnabledChecksAndInit(parserConfig, config); + execLayers = buildExecutionLayers(checks); + } + + private List<Check> collectEnabledChecksAndInit(ParserConfiguration parserConfig, ValidationConfiguration config) { + Set<CheckId> enabledCheck = new HashSet<>(); + Map<CheckId, Map<String, String>> parameterMap = new HashMap<>(); + for (Entry<String, RequirementConfiguration> e : config.getRequirements().entrySet()) { + de.hft.stuttgart.citydoctor2.check.Requirement req = Checks.getAvailableRequirements().get(e.getKey()); + if (req == null) { + logger.warn("Could not find any check that satisfies requirement {}, it will not be checked", + e.getKey()); + } else { + if (e.getValue().isEnabled()) { + // this requirement is enabled + insertGlobalParameters(config, enabledCheck, parameterMap, e, req); + } + } + } + fillParameterMapsWithDefaultParameter(enabledCheck, parameterMap); + ArrayList<Check> checkList = new ArrayList<>(); + for (CheckId id : enabledCheck) { + Check c = checkConfig.getCheckForId(id); + c.init(parameterMap.get(id), parserConfig); + checkList.add(c); + } + return checkList; + } + + private void insertGlobalParameters(ValidationConfiguration config, Set<CheckId> enabledCheck, + Map<CheckId, Map<String, String>> parameterMap, Entry<String, RequirementConfiguration> e, + de.hft.stuttgart.citydoctor2.check.Requirement req) { + for (CheckPrototype proto : Checks.getCheckPrototypes()) { + if (proto.checksRequirements().contains(req)) { + // this requirement is checked by this check + // put all requirement parameter in the map + parameterMap.compute(proto.getCheckId(), (k, v) -> { + if (v == null) { + v = new HashMap<>(); + v.put(GlobalParameters.NUMBER_OF_ROUNDING_PLACES, config.getNumberOfRoundingPlacesAsString()); + v.put(GlobalParameters.MIN_VERTEX_DISTANCE, config.getMinVertexDistanceAsString()); + } + v.putAll(e.getValue().getParameters()); + return v; + }); + enabledCheck.add(proto.getCheckId()); + collectDependencyChecks(proto, enabledCheck); + } + } + } + + private void fillParameterMapsWithDefaultParameter(Set<CheckId> enabledCheck, + Map<CheckId, Map<String, String>> parameterMap) { + for (CheckId id : enabledCheck) { + CheckPrototype proto = Checks.getCheckPrototypeForId(id); + Map<String, String> map = parameterMap.computeIfAbsent(id, k -> new HashMap<>()); + for (de.hft.stuttgart.citydoctor2.check.Requirement req : proto.checksRequirements()) { + if (proto.checksRequirements().contains(req)) { + for (DefaultParameter param : req.getDefaultParameter()) { + map.computeIfAbsent(param.getName(), k -> param.getValue()); + } + } + } + } + } + + private void collectDependencyChecks(CheckPrototype proto, Set<CheckId> enabledChecks) { + enabledChecks.addAll(proto.getDependencies()); + for (CheckId id : proto.getDependencies()) { + if (enabledChecks.contains(id)) { + continue; + } + CheckPrototype depProto = Checks.getCheckPrototypeForId(id); + collectDependencyChecks(depProto, enabledChecks); + } + } + + private void checkCityModel(CityDoctorModel model, ProgressListener l) { + Stream<CityObject> features = model.createFeatureStream(); + float featureSum = model.getNumberOfFeatures(); + // stupid lamda with final variable restrictions + int[] currentFeature = new int[1]; + features.forEach(co -> { + if (config.getParserConfiguration().useLowMemoryConsumption()) { + // no edges have been created yet, create them + co.prepareForChecking(); + } + // check every feature + executeChecksForCityObject(co); + + if (config.getParserConfiguration().useLowMemoryConsumption()) { + // low memory consumption, remove edges again + co.clearMetaInformation(); + } + if (l != null) { + currentFeature[0]++; + l.updateProgress(currentFeature[0] / featureSum); + } + }); + } + + private boolean filterObject(CityObject co) { + return isObjectIncluded(co, includeFilters, excludeFilters); + } + + private boolean isObjectIncluded(CityObject co, List<Filter> includeFilters, List<Filter> excludeFilters) { + if (!includeFilters.isEmpty()) { + boolean include = false; + for (Filter f : includeFilters) { + if (f.matches(co)) { + include = true; + break; + } + } + if (!include) { + // not included, ignore + return false; + } + } + // check if object is excluded + for (Filter f : excludeFilters) { + if (f.matches(co)) { + // exclude object + return false; + } + } + return true; + } + + /** + * Checks the city object if it has not been removed by the filters. The check + * result are stored into the city object itself. + * + * @param co the city object that is going to be checked + */ + private void executeChecksForCityObject(CityObject co) { + if (!filterObject(co)) { + return; + } + executeChecksForCheckable(co); + } + + /** + * Executes all checks for the checkable. This will bypass the filters. This + * will clear the old check results + * + * @param co the checkable. + */ + public void executeChecksForCheckable(Checkable co) { + // throw away old results + co.clearAllContainedCheckResults(); + if (logger.isDebugEnabled()) { + logger.debug(Localization.getText("Checker.checkFeature"), co); + } for (List<Check> execLayer : execLayers) { for (Check check : execLayer) { if (logger.isTraceEnabled()) { @@ -700,162 +697,162 @@ public class Checker { co.accept(check); } } - } - - public static List<List<Check>> buildExecutionLayers(List<Check> checks) { - List<List<Check>> result = new ArrayList<>(); - - Set<Check> availableChecks = new HashSet<>(checks); - Set<CheckId> usedChecks = new HashSet<>(); - - while (!availableChecks.isEmpty()) { - List<Check> layer = new ArrayList<>(); - Iterator<Check> iterator = availableChecks.iterator(); - while (iterator.hasNext()) { - Check c = iterator.next(); - boolean hasUnusedDependency = searchForUnusedDependency(usedChecks, c); - if (!hasUnusedDependency) { - iterator.remove(); - layer.add(new CheckContainer(c)); - } - } - if (layer.isEmpty()) { - throw new IllegalStateException( - "There are checks that have dependencies that are not executed or are unknown, aborting"); - } - result.add(layer); - for (Check c : layer) { - usedChecks.add(c.getCheckId()); - } - } - return result; - } - - private static boolean searchForUnusedDependency(Set<CheckId> usedChecks, Check c) { - boolean hasUnusedDependency = false; - for (CheckId id : c.getDependencies()) { - if (!usedChecks.contains(id)) { - hasUnusedDependency = true; - break; - } - } - return hasUnusedDependency; - } - - public static void streamCheck (CityGmlZipEntry entry, String xmlOutput, String pdfOutput, ValidationConfiguration config, - String outputFile) throws IOException, CityGmlParseException{ - try (BufferedOutputStream xmlBos = getXmlOutputMaybe(xmlOutput); - BufferedOutputStream pdfBos = getPdfOutputMaybe(pdfOutput)) { - Checker c = new Checker(config, null); - String fileName = entry.getFileName(); - - // create reporter if available - XmlStreamReporter xmlReporter = getXmlReporter(config, xmlBos, fileName); - PdfStreamReporter pdfReporter = getPdfReporter(config, pdfBos, fileName); - - // execute schematron first - try (CityGmlZipEntryFile entryFile = new CityGmlZipEntryFile(entry)) { - SvrlContentHandler handler = executeSchematronValidationIfAvailable(config, entryFile.getInputStream()); - - CityGmlConsumer con = new StreamCityGmlConsumer(c, xmlReporter, pdfReporter, handler, config, null); - // parse and validate - CityGmlParser.streamCityGml(entry, config.getParserConfiguration(), con, outputFile); - - // write reports if available - writeReport(xmlReporter); - writeReport(pdfReporter); - } - } catch (CheckReportWriteException e) { - logger.error(Localization.getText("Checker.failReports"), e); - } - } - - - public static void streamCheck(File inputFile, String xmlOutput, String pdfOutput, ValidationConfiguration config, - String outputFile) throws IOException, CityGmlParseException { - streamCheck(inputFile, xmlOutput, pdfOutput, config, null, outputFile); - } - - public static void streamCheck(File inputFile, String xmlOutput, String pdfOutput, ValidationConfiguration config, - FeatureCheckedListener l, String outputFile) throws IOException, CityGmlParseException { - try (BufferedOutputStream xmlBos = getXmlOutputMaybe(xmlOutput); - BufferedOutputStream pdfBos = getPdfOutputMaybe(pdfOutput)) { - Checker c = new Checker(config, null); - String fileName = inputFile.getName(); - - // create reporter if available - XmlStreamReporter xmlReporter = getXmlReporter(config, xmlBos, fileName); - PdfStreamReporter pdfReporter = getPdfReporter(config, pdfBos, fileName); - - // execute schematron first - SvrlContentHandler handler = executeSchematronValidationIfAvailable(config, inputFile); - CityGmlConsumer con = new StreamCityGmlConsumer(c, xmlReporter, pdfReporter, handler, config, l); - // parse and validate - CityGmlParser.streamCityGml(inputFile.getAbsolutePath(), config.getParserConfiguration(), con, outputFile); - - // write reports if available - writeReport(xmlReporter); - writeReport(pdfReporter); - } catch (CheckReportWriteException e) { - logger.error(Localization.getText("Checker.failReports"), e); - } - } - - private static XmlStreamReporter getXmlReporter(ValidationConfiguration config, BufferedOutputStream xmlBos, - String fileName) { - XmlStreamReporter xmlReporter; - if (xmlBos != null) { - xmlReporter = new XmlStreamReporter(xmlBos, fileName, config); - } else { - xmlReporter = null; - } - return xmlReporter; - } - - private static PdfStreamReporter getPdfReporter(ValidationConfiguration config, BufferedOutputStream pdfBos, - String fileName) { - PdfStreamReporter pdfReporter; - if (pdfBos != null) { - pdfReporter = new PdfStreamReporter(pdfBos, fileName, config); - } else { - pdfReporter = null; - } - return pdfReporter; - } - - public static void writeReport(StreamReporter reporter) throws CheckReportWriteException { - if (reporter != null) { - reporter.finishReport(); - } - } - - public static BufferedOutputStream getPdfOutputMaybe(String pdfOutput) throws FileNotFoundException { - return pdfOutput != null ? new BufferedOutputStream(new FileOutputStream(pdfOutput)) : null; - } - - public static BufferedOutputStream getXmlOutputMaybe(String xmlOutput) throws FileNotFoundException { - return xmlOutput != null ? new BufferedOutputStream(new FileOutputStream(xmlOutput)) : null; - } - - /** - * Checks the city object and writes report information into reporters. Clears - * old check results. If the city object would be filtered by the configured - * filters it is ignored and old check results are not cleared. - * - * @param xmlReporter a xml reporter - * @param pdfReporter a pdf reporter - * @param co the city object to be checked - */ - public void checkFeature(XmlStreamReporter xmlReporter, PdfStreamReporter pdfReporter, CityObject co) { - if (logger.isDebugEnabled()) { - logger.debug(Localization.getText("Checker.checkFeature"), co); - } - executeChecksForCityObject(co); - if (xmlReporter != null) { - xmlReporter.report(co); - } - if (pdfReporter != null) { - pdfReporter.report(co); - } - } + } + + public static List<List<Check>> buildExecutionLayers(List<Check> checks) { + List<List<Check>> result = new ArrayList<>(); + + Set<Check> availableChecks = new HashSet<>(checks); + Set<CheckId> usedChecks = new HashSet<>(); + + while (!availableChecks.isEmpty()) { + List<Check> layer = new ArrayList<>(); + Iterator<Check> iterator = availableChecks.iterator(); + while (iterator.hasNext()) { + Check c = iterator.next(); + boolean hasUnusedDependency = searchForUnusedDependency(usedChecks, c); + if (!hasUnusedDependency) { + iterator.remove(); + layer.add(new CheckContainer(c)); + } + } + if (layer.isEmpty()) { + throw new IllegalStateException( + "There are checks that have dependencies that are not executed or are unknown, aborting"); + } + result.add(layer); + for (Check c : layer) { + usedChecks.add(c.getCheckId()); + } + } + return result; + } + + private static boolean searchForUnusedDependency(Set<CheckId> usedChecks, Check c) { + boolean hasUnusedDependency = false; + for (CheckId id : c.getDependencies()) { + if (!usedChecks.contains(id)) { + hasUnusedDependency = true; + break; + } + } + return hasUnusedDependency; + } + + public static void streamCheck(CityGmlZipEntry entry, String xmlOutput, String pdfOutput, ValidationConfiguration config, + String outputFile) throws IOException, CityGmlParseException { + try (BufferedOutputStream xmlBos = getXmlOutputMaybe(xmlOutput); + BufferedOutputStream pdfBos = getPdfOutputMaybe(pdfOutput)) { + Checker c = new Checker(config, null); + String fileName = entry.getFileName(); + + // create reporter if available + XmlStreamReporter xmlReporter = getXmlReporter(config, xmlBos, fileName); + PdfStreamReporter pdfReporter = getPdfReporter(config, pdfBos, fileName); + + // execute schematron first + try (CityGmlZipEntryFile entryFile = new CityGmlZipEntryFile(entry)) { + SvrlContentHandler handler = executeSchematronValidationIfAvailable(config, entryFile.getInputStream()); + + CityGmlConsumer con = new StreamCityGmlConsumer(c, xmlReporter, pdfReporter, handler, config, null); + // parse and validate + CityGmlParser.streamCityGml(entry, config.getParserConfiguration(), con, outputFile); + + // write reports if available + writeReport(xmlReporter); + writeReport(pdfReporter); + } + } catch (CheckReportWriteException e) { + logger.error(Localization.getText("Checker.failReports"), e); + } + } + + + public static void streamCheck(File inputFile, String xmlOutput, String pdfOutput, ValidationConfiguration config, + String outputFile) throws IOException, CityGmlParseException { + streamCheck(inputFile, xmlOutput, pdfOutput, config, null, outputFile); + } + + public static void streamCheck(File inputFile, String xmlOutput, String pdfOutput, ValidationConfiguration config, + FeatureCheckedListener l, String outputFile) throws IOException, CityGmlParseException { + try (BufferedOutputStream xmlBos = getXmlOutputMaybe(xmlOutput); + BufferedOutputStream pdfBos = getPdfOutputMaybe(pdfOutput)) { + Checker c = new Checker(config, null); + String fileName = inputFile.getName(); + + // create reporter if available + XmlStreamReporter xmlReporter = getXmlReporter(config, xmlBos, fileName); + PdfStreamReporter pdfReporter = getPdfReporter(config, pdfBos, fileName); + + // execute schematron first + SvrlContentHandler handler = executeSchematronValidationIfAvailable(config, inputFile); + CityGmlConsumer con = new StreamCityGmlConsumer(c, xmlReporter, pdfReporter, handler, config, l); + // parse and validate + CityGmlParser.streamCityGml(inputFile.getAbsolutePath(), config.getParserConfiguration(), con, outputFile); + + // write reports if available + writeReport(xmlReporter); + writeReport(pdfReporter); + } catch (CheckReportWriteException e) { + logger.error(Localization.getText("Checker.failReports"), e); + } + } + + private static XmlStreamReporter getXmlReporter(ValidationConfiguration config, BufferedOutputStream xmlBos, + String fileName) { + XmlStreamReporter xmlReporter; + if (xmlBos != null) { + xmlReporter = new XmlStreamReporter(xmlBos, fileName, config); + } else { + xmlReporter = null; + } + return xmlReporter; + } + + private static PdfStreamReporter getPdfReporter(ValidationConfiguration config, BufferedOutputStream pdfBos, + String fileName) { + PdfStreamReporter pdfReporter; + if (pdfBos != null) { + pdfReporter = new PdfStreamReporter(pdfBos, fileName, config); + } else { + pdfReporter = null; + } + return pdfReporter; + } + + public static void writeReport(StreamReporter reporter) throws CheckReportWriteException { + if (reporter != null) { + reporter.finishReport(); + } + } + + public static BufferedOutputStream getPdfOutputMaybe(String pdfOutput) throws FileNotFoundException { + return pdfOutput != null ? new BufferedOutputStream(new FileOutputStream(pdfOutput)) : null; + } + + public static BufferedOutputStream getXmlOutputMaybe(String xmlOutput) throws FileNotFoundException { + return xmlOutput != null ? new BufferedOutputStream(new FileOutputStream(xmlOutput)) : null; + } + + /** + * Checks the city object and writes report information into reporters. Clears + * old check results. If the city object would be filtered by the configured + * filters it is ignored and old check results are not cleared. + * + * @param xmlReporter a xml reporter + * @param pdfReporter a pdf reporter + * @param co the city object to be checked + */ + public void checkFeature(XmlStreamReporter xmlReporter, PdfStreamReporter pdfReporter, CityObject co) { + if (logger.isDebugEnabled()) { + logger.debug(Localization.getText("Checker.checkFeature"), co); + } + executeChecksForCityObject(co); + if (xmlReporter != null) { + xmlReporter.report(co); + } + if (pdfReporter != null) { + pdfReporter.report(co); + } + } } diff --git a/CityDoctorParent/CityDoctorValidation/src/test/java/de/hft/stuttgart/citydoctor2/check/CheckerTest.java b/CityDoctorParent/CityDoctorValidation/src/test/java/de/hft/stuttgart/citydoctor2/check/CheckerTest.java index 8e2df6a..7b1a04d 100644 --- a/CityDoctorParent/CityDoctorValidation/src/test/java/de/hft/stuttgart/citydoctor2/check/CheckerTest.java +++ b/CityDoctorParent/CityDoctorValidation/src/test/java/de/hft/stuttgart/citydoctor2/check/CheckerTest.java @@ -1,6 +1,6 @@ /*- * Copyright 2020 Beuth Hochschule für Technik Berlin, Hochschule für Technik Stuttgart - * + * * This file is part of CityDoctor2. * * CityDoctor2 is free software: you can redistribute it and/or modify @@ -18,16 +18,6 @@ */ package de.hft.stuttgart.citydoctor2.check; -import java.io.File; -import java.io.IOException; - -import de.hft.stuttgart.citydoctor2.parser.ParserConfiguration; -import de.hft.stuttgart.citydoctor2.zip.CityGmlZipArchive; -import de.hft.stuttgart.citydoctor2.zip.CityGmlZipEntry; -import org.junit.Rule; -import org.junit.Test; -import org.junit.rules.TemporaryFolder; - import de.hft.stuttgart.citydoctor2.CityDoctorValidation; import de.hft.stuttgart.citydoctor2.datastructure.Building; import de.hft.stuttgart.citydoctor2.datastructure.CityDoctorModel; @@ -35,99 +25,106 @@ import de.hft.stuttgart.citydoctor2.exceptions.CityDoctorWriteException; import de.hft.stuttgart.citydoctor2.parser.CityGmlParseException; import de.hft.stuttgart.citydoctor2.parser.CityGmlParser; import de.hft.stuttgart.citydoctor2.parser.InvalidGmlFileException; +import de.hft.stuttgart.citydoctor2.parser.ParserConfiguration; +import de.hft.stuttgart.citydoctor2.zip.CityGmlZipArchive; +import de.hft.stuttgart.citydoctor2.zip.CityGmlZipEntry; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; + +import java.io.File; +import java.io.IOException; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; /** - * * @author Matthias Betz - * */ public class CheckerTest { - @Rule - public TemporaryFolder folder = new TemporaryFolder(); + @Rule + public TemporaryFolder folder = new TemporaryFolder(); - @Test - public void testSchematron() throws CityGmlParseException, InvalidGmlFileException { - ValidationConfiguration config = ValidationConfiguration.loadStandardValidationConfig(); - config.getRequirements().get(Requirement.R_SE_BS_ROOF_UNFRAGMENTED.toString()).setEnabled(false); - config.setSchematronFilePathInGlobalParameters("src/test/resources/schematronTest.xml"); - CityDoctorModel model = CityGmlParser.parseCityGmlFile( - "src/test/resources/SimpleSolid_SrefBS_SchematronTest.gml", config.getParserConfiguration()); - Checker checker = new Checker(config, model); - checker.runChecks(); - for (Building b : model.getBuildings()) { - if (b.getGmlId().getGmlString().equals("_Simple_BD.1")) { - assertTrue(b.containsAnyError()); - } else { - assertFalse(b.containsAnyError()); - } - } - assertFalse(model.getGlobalErrors().isEmpty()); - } + @Test + public void testSchematron() throws CityGmlParseException, InvalidGmlFileException { + ValidationConfiguration config = ValidationConfiguration.loadStandardValidationConfig(); + config.getRequirements().get(Requirement.R_SE_BS_ROOF_UNFRAGMENTED.toString()).setEnabled(false); + config.setSchematronFilePathInGlobalParameters("src/test/resources/schematronTest.xml"); + CityDoctorModel model = CityGmlParser.parseCityGmlFile( + "src/test/resources/SimpleSolid_SrefBS_SchematronTest.gml", config.getParserConfiguration()); + Checker checker = new Checker(config, model); + checker.runChecks(); + for (Building b : model.getBuildings()) { + if (b.getGmlId().getGmlString().equals("_Simple_BD.1")) { + assertTrue(b.containsAnyError()); + } else { + assertFalse(b.containsAnyError()); + } + } + assertFalse(model.getGlobalErrors().isEmpty()); + } - @Test - public void testChecker() throws CityGmlParseException, IOException, InvalidGmlFileException, CityDoctorWriteException { + @Test + public void testChecker() throws CityGmlParseException, IOException, InvalidGmlFileException, CityDoctorWriteException { - File f = folder.newFile(); - File f2 = folder.newFile(); - try { - String[] args = new String[6]; - args[0] = "-in"; - args[1] = "src/test/resources/QA-CS-CONCOMP.gml"; - args[2] = "-xmlReport"; - args[3] = f.getAbsolutePath(); - args[4] = "-pdfReport"; - args[5] = f2.getAbsolutePath(); - CityDoctorValidation.main(args); - assertTrue(f.exists()); - assertTrue(f2.exists()); - } finally { - f.delete(); - f2.delete(); - } - } + File f = folder.newFile(); + File f2 = folder.newFile(); + try { + String[] args = new String[6]; + args[0] = "-in"; + args[1] = "src/test/resources/QA-CS-CONCOMP.gml"; + args[2] = "-xmlReport"; + args[3] = f.getAbsolutePath(); + args[4] = "-pdfReport"; + args[5] = f2.getAbsolutePath(); + CityDoctorValidation.main(args); + assertTrue(f.exists()); + assertTrue(f2.exists()); + } finally { + f.delete(); + f2.delete(); + } + } - @Test - public void testStreaming() throws CityGmlParseException, IOException, InvalidGmlFileException, CityDoctorWriteException { - File f = folder.newFile(); - File f2 = folder.newFile(); - File f3 = folder.newFile(); - try { - String[] args = new String[10]; - args[0] = "-in"; - args[1] = "src/test/resources/testarea.gml"; - args[2] = "-config"; - args[3] = "src/test/resources/testConfigWithStreaming.yml"; - args[4] = "-pdfReport"; - args[5] = f.getAbsolutePath(); - args[6] = "-xmlReport"; - args[7] = f2.getAbsolutePath(); - args[8] = "-out"; - args[9] = f3.getAbsolutePath(); - CityDoctorValidation.main(args); - assertTrue(f.exists()); - assertTrue(f2.exists()); - assertTrue(f3.exists()); - } finally { - f.delete(); - f2.delete(); - f3.delete(); - } - } + @Test + public void testStreaming() throws CityGmlParseException, IOException, InvalidGmlFileException, CityDoctorWriteException { + File f = folder.newFile(); + File f2 = folder.newFile(); + File f3 = folder.newFile(); + try { + String[] args = new String[10]; + args[0] = "-in"; + args[1] = "src/test/resources/testarea.gml"; + args[2] = "-config"; + args[3] = "src/test/resources/testConfigWithStreaming.yml"; + args[4] = "-pdfReport"; + args[5] = f.getAbsolutePath(); + args[6] = "-xmlReport"; + args[7] = f2.getAbsolutePath(); + args[8] = "-out"; + args[9] = f3.getAbsolutePath(); + CityDoctorValidation.main(args); + assertTrue(f.exists()); + assertTrue(f2.exists()); + assertTrue(f3.exists()); + } finally { + f.delete(); + f2.delete(); + f3.delete(); + } + } - @Test - public void testZipEntryChecking() throws CityGmlParseException, IOException, InvalidGmlFileException, CityDoctorWriteException { - CityGmlZipArchive cgmlArch = CityGmlZipArchive.register("src/test/resources/zipArchive.zip"); - assertNotNull(cgmlArch); - cgmlArch.mountArchive(new ParserConfiguration(8,false)); - ValidationConfiguration config = ValidationConfiguration.loadStandardValidationConfig(); - for (CityGmlZipEntry entry : cgmlArch.getEntries()){ - Checker.streamCheck(entry, null, null, config, null ); - } - } + @Test + public void testZipEntryChecking() throws CityGmlParseException, IOException, InvalidGmlFileException, CityDoctorWriteException { + CityGmlZipArchive cgmlArch = CityGmlZipArchive.register("src/test/resources/zipArchive.zip"); + assertNotNull(cgmlArch); + cgmlArch.mountArchive(new ParserConfiguration(8, false)); + ValidationConfiguration config = ValidationConfiguration.loadStandardValidationConfig(); + for (CityGmlZipEntry entry : cgmlArch.getEntries()) { + Checker.streamCheck(entry, null, null, config, null); + } + } } -- GitLab