/*- * 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 * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * CityDoctor2 is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with CityDoctor2. If not, see . */ package de.hft.stuttgart.citydoctor2.check; import java.io.BufferedOutputStream; import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; 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.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import javax.xml.transform.Source; import javax.xml.transform.TransformerException; import javax.xml.transform.URIResolver; import javax.xml.transform.dom.DOMSource; import javax.xml.transform.stream.StreamSource; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.w3c.dom.Document; import de.hft.stuttgart.citydoctor2.check.error.AttributeMissingError; import de.hft.stuttgart.citydoctor2.check.error.AttributeValueWrongError; import de.hft.stuttgart.citydoctor2.check.error.SchematronError; import de.hft.stuttgart.citydoctor2.checkresult.utility.CheckReportWriteException; import de.hft.stuttgart.citydoctor2.checks.CheckContainer; import de.hft.stuttgart.citydoctor2.checks.CheckPrototype; import de.hft.stuttgart.citydoctor2.checks.Checks; import de.hft.stuttgart.citydoctor2.checks.SvrlContentHandler; import de.hft.stuttgart.citydoctor2.checks.util.FeatureCheckedListener; import de.hft.stuttgart.citydoctor2.datastructure.CityDoctorModel; import de.hft.stuttgart.citydoctor2.datastructure.CityObject; import de.hft.stuttgart.citydoctor2.datastructure.FeatureType; import de.hft.stuttgart.citydoctor2.parser.CityGmlConsumer; import de.hft.stuttgart.citydoctor2.parser.CityGmlParseException; import de.hft.stuttgart.citydoctor2.parser.CityGmlParser; import de.hft.stuttgart.citydoctor2.parser.ParserConfiguration; import de.hft.stuttgart.citydoctor2.parser.ProgressListener; import de.hft.stuttgart.citydoctor2.reporting.Reporter; import de.hft.stuttgart.citydoctor2.reporting.StreamReporter; import de.hft.stuttgart.citydoctor2.reporting.XmlStreamReporter; 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.quality.model.jaxb.Checking; import de.hft.stuttgart.quality.model.jaxb.Parameter; import de.hft.stuttgart.quality.model.jaxb.Requirement; import de.hft.stuttgart.quality.model.jaxb.RequirementId; import de.hft.stuttgart.quality.model.jaxb.TopLevelFeatureType; import de.hft.stuttgart.quality.model.jaxb.ValidationPlan; import net.sf.saxon.s9api.DOMDestination; import net.sf.saxon.s9api.Destination; import net.sf.saxon.s9api.Processor; import net.sf.saxon.s9api.SAXDestination; import net.sf.saxon.s9api.SaxonApiException; import net.sf.saxon.s9api.XsltCompiler; import net.sf.saxon.s9api.XsltExecutable; import net.sf.saxon.s9api.XsltTransformer; /** * 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 * */ public class Checker { private static final Logger logger = LogManager.getLogger(Checker.class); private ValidationConfiguration config; private List> execLayers; private List includeFilters; private List excludeFilters; private Checks checkConfig; private 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. * @param model the model for which the report is written. */ 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("assets/Logo.png"); 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 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); } }); } static void handleSchematronErrorsForCityObject(List 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(), se.isGeneric()); } else if (AttributeValueWrongError.ID.getIdString().equals(se.getErrorIdString())) { err = new AttributeValueWrongError(co, se.getChildId(), se.getNameOfAttribute(), se.isGeneric()); } else { throw new IllegalStateException( "Unknown error ID was given in schematron file: " + se.getErrorIdString()); } co.addCheckResult(new CheckResult(new CheckId("" + count), ResultStatus.ERROR, err)); count++; } } ValidationPlan createValidationPlan() { ValidationPlan plan = new ValidationPlan(); List filter = createFilter(); for (Entry e : config.getChecks().entrySet()) { RequirementId reqId = mapToRequirement(e.getKey()); if (reqId == null) { continue; } Requirement req = new Requirement(); req.setName(reqId); req.setEnabled(e.getValue().isEnabled()); plan.getRequirements().add(req); CheckPrototype proto = Checks.getCheckPrototypeForId(e.getKey()); Map parameters = e.getValue().getParameters(); if (parameters != null) { for (Entry param : parameters.entrySet()) { Parameter p = new Parameter(); DefaultParameter defaultP = getDefaultParameter(param.getKey(), proto); if (defaultP != null) { p.setUom(defaultP.getUnitType().getGmlRepresentation()); } p.setName(param.getKey()); p.setValue(param.getValue()); req.getParameters().add(p); } } } Requirement missing = new Requirement(); missing.setName(RequirementId.R_SE_ATTRIBUTES_EXISTING); Requirement correct = new Requirement(); correct.setName(RequirementId.R_SE_ATTRIBUTES_CORRECT); missing.setEnabled(config.getSchematronFilePath() != null); correct.setEnabled(config.getSchematronFilePath() != null); plan.getRequirements().add(missing); plan.getRequirements().add(correct); plan.getFilter().addAll(filter); 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()); plan.getGlobalParameters().add(numRounding); plan.getGlobalParameters().add(minVertexDistance); plan.getGlobalParameters().add(schematronFile); return plan; } private DefaultParameter getDefaultParameter(String key, CheckPrototype proto) { for (DefaultParameter param : proto.getDefaultParameter()) { if (param.getName().equals(key)) { return param; } } return null; } private List createFilter() { List filter = new ArrayList<>(); handleInputFilter(filter); if (excludeFilters != null) { for (Filter f : excludeFilters) { if (f instanceof TypeFilter) { TypeFilter tf = (TypeFilter) f; FeatureType type = tf.getType(); TopLevelFeatureType tlft = mapToTopLevelFeatureType(type); if (tlft == null) { continue; } removeFilter(tlft, filter); } } } return filter; } private void handleInputFilter(List filter) { if (includeFilters == null || includeFilters.isEmpty()) { // no filter means, use all addAllFilters(filter); } else { for (Filter f : includeFilters) { if (f instanceof TypeFilter) { TypeFilter tf = (TypeFilter) f; FeatureType type = tf.getType(); TopLevelFeatureType tlft = mapToTopLevelFeatureType(type); if (tlft == null) { continue; } filter.add(new Checking(tlft)); } } if (filter.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(List filter) { filter.add(new Checking(TopLevelFeatureType.BUILDING)); filter.add(new Checking(TopLevelFeatureType.BRIDGE)); filter.add(new Checking(TopLevelFeatureType.LAND)); filter.add(new Checking(TopLevelFeatureType.TRANSPORTATION)); filter.add(new Checking(TopLevelFeatureType.VEGETATION)); filter.add(new Checking(TopLevelFeatureType.WATER)); } private void removeFilter(TopLevelFeatureType tlft, List filter) { for (Checking c : filter) { if (c.getValue().equals(tlft)) { filter.remove(c); return; } } } private TopLevelFeatureType mapToTopLevelFeatureType(FeatureType type) { switch (type) { case BRIDGE: return TopLevelFeatureType.BRIDGE; case BUILDING: return TopLevelFeatureType.BUILDING; case LAND: return TopLevelFeatureType.LAND; case TRANSPORTATION: return TopLevelFeatureType.TRANSPORTATION; case VEGETATION: return TopLevelFeatureType.VEGETATION; case WATER: return TopLevelFeatureType.WATER; default: return null; } } private RequirementId mapToRequirement(CheckId key) { switch (key.getName()) { case "C_GE_R_TOO_FEW_POINTS": return RequirementId.R_GE_R_TOO_FEW_POINTS; case "C_GE_R_NOT_CLOSED": return RequirementId.R_GE_R_NOT_CLOSED; case "C_GE_R_DUPLICATE_POINT": return RequirementId.R_GE_R_CONSECUTIVE_POINTS_SAME; case "C_GE_R_SELF_INTERSECTION": return RequirementId.R_GE_R_SELF_INTERSECTION; case "C_GE_P_INTERIOR_DISCONNECTED": return RequirementId.R_GE_P_INTERIOR_DISCONNECTED; case "C_GE_P_INTERSECTING_RINGS": return RequirementId.R_GE_P_INTERSECTING_RINGS; case "C_GE_P_NON_PLANAR": return RequirementId.R_GE_P_NON_PLANAR; case "C_GE_S_TOO_FEW_POLYGONS": return RequirementId.R_GE_S_TOO_FEW_POLYGONS; case "C_GE_S_NON_MANIFOLD_EDGE": return RequirementId.R_GE_S_NON_MANIFOLD_EDGE; case "C_GE_S_POLYGON_WRONG_ORIENTATION": return RequirementId.R_GE_S_POLYGON_WRONG_ORIENTATION; case "C_GE_S_ALL_POLYGONS_WRONG_ORIENTATION": return RequirementId.R_GE_S_ALL_POLYGONS_WRONG_ORIENTATION; case "C_GE_S_NON_MANIFOLD_VERTEX": return RequirementId.R_GE_S_NON_MANIFOLD_VERTEX; case "C_GE_S_SELF_INTERSECTION": return RequirementId.R_GE_S_SELF_INTERSECTION; case "C_GE_P_HOLE_OUTSIDE": return RequirementId.R_GE_P_HOLE_OUTSIDE; case "C_GE_P_INNER_RINGS_NESTED": return RequirementId.R_GE_P_INNER_RINGS_NESTED; case "C_GE_S_NOT_CLOSED": return RequirementId.R_GE_S_NOT_CLOSED; case "C_GE_S_MULTIPLE_CONNECTED_COMPONENTS": return RequirementId.R_GE_S_MULTIPLE_CONNECTED_COMPONENTS; default: return null; } } public ValidationConfiguration getConfig() { return config; } public static SvrlContentHandler executeSchematronValidationIfAvailable(ValidationConfiguration config, File file) { if (config.getSchematronFilePath() != null && !config.getSchematronFilePath().isEmpty()) { if (logger.isInfoEnabled()) { logger.info(Localization.getText("Checker.schematronValidation")); } Processor processor = new Processor(false); XsltCompiler xsltCompiler = processor.newXsltCompiler(); xsltCompiler.setURIResolver(new URIResolver() { @Override public Source resolve(String href, String base) throws TransformerException { return new StreamSource(Checker.class.getResourceAsStream(href)); } }); try { XsltExecutable includeExecutable = xsltCompiler .compile(new StreamSource(Checker.class.getResourceAsStream("iso_dsdl_include.xsl"))); XsltTransformer includeTransformer = includeExecutable.load(); includeTransformer.setSource(new StreamSource(new File(config.getSchematronFilePath()))); XsltExecutable expandExecutable = xsltCompiler .compile(new StreamSource(Checker.class.getResourceAsStream("iso_abstract_expand.xsl"))); XsltTransformer expandTransformer = expandExecutable.load(); includeTransformer.setDestination(expandTransformer); XsltExecutable xslt2Executable = xsltCompiler .compile(new StreamSource(Checker.class.getResourceAsStream("iso_svrl_for_xslt2.xsl"))); XsltTransformer xslt2Transformer = xslt2Executable.load(); expandTransformer.setDestination(xslt2Transformer); DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); factory.setAttribute(XMLConstants.ACCESS_EXTERNAL_DTD, ""); factory.setAttribute(XMLConstants.ACCESS_EXTERNAL_SCHEMA, ""); factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true); Document doc = factory.newDocumentBuilder().newDocument(); DOMDestination domDestination = new DOMDestination(doc); xslt2Transformer.setDestination(domDestination); includeTransformer.transform(); XsltExecutable schematronExecutable = xsltCompiler.compile(new DOMSource(doc)); XsltTransformer schematronTransformer = schematronExecutable.load(); schematronTransformer.setSource(new StreamSource(file)); SvrlContentHandler handler = new SvrlContentHandler(); Destination dest = new SAXDestination(handler); schematronTransformer.setDestination(dest); schematronTransformer.transform(); if (logger.isInfoEnabled()) { logger.info(Localization.getText("Checker.finishedSchematron")); } return handler; } catch (SaxonApiException | ParserConfigurationException 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 buildExcludeFilters(FilterConfiguration filterConfig) { if (filterConfig == null) { return Collections.emptyList(); } ExcludeFilterConfiguration excludeConfig = filterConfig.getExclude(); if (excludeConfig == null) { return Collections.emptyList(); } else { List 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 buildIncludeFilters(FilterConfiguration filterConfig) { if (filterConfig == null) { return Collections.emptyList(); } IncludeFilterConfiguration includeConfig = filterConfig.getInclude(); if (includeConfig == null) { return Collections.emptyList(); } else { List 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 checks = collectEnabledChecksAndInit(parserConfig, config); execLayers = buildExecutionLayers(checks); } private List collectEnabledChecksAndInit(ParserConfiguration parserConfig, ValidationConfiguration config) { List checks = new ArrayList<>(); for (Entry e : config.getChecks().entrySet()) { if (e.getValue().isEnabled()) { Check c = checkConfig.getCheckForId(e.getKey()); Map parameters = new HashMap<>(); parameters.putAll(e.getValue().getParameters()); parameters.put("numberOfRoundingPlaces", "" + config.getNumberOfRoundingPlaces()); parameters.put("minVertexDistance", "" + config.getMinVertexDistance()); // initialize checks with parameters c.init(parameters, parserConfig); checks.add(c); } } return checks; } private void checkCityModel(CityDoctorModel model, ProgressListener l) { Stream 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 includeFilters, List 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. * * @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 (int i = 0; i < execLayers.size(); i++) { for (Check check : execLayers.get(i)) { if (logger.isTraceEnabled()) { logger.trace(Localization.getText("Checker.executeCheck"), check.getCheckId()); } co.accept(check); } } } public static List> buildExecutionLayers(List checks) { List> result = new ArrayList<>(); Set availableChecks = new HashSet<>(checks); Set usedChecks = new HashSet<>(); while (!availableChecks.isEmpty()) { List layer = new ArrayList<>(); Iterator 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"); } result.add(layer); for (Check c : layer) { usedChecks.add(c.getCheckId()); } } return result; } private static boolean searchForUnusedDependency(Set usedChecks, Check c) { boolean hasUnusedDependency = false; for (CheckId id : c.getDependencies()) { if (!usedChecks.contains(id)) { hasUnusedDependency = true; break; } } return hasUnusedDependency; } public static void streamCheck(File inputFile, String xmlOutput, String pdfOutput, ValidationConfiguration config, String outputFile) throws IOException, CityGmlParseException { streamCheck(inputFile, xmlOutput, pdfOutput, config, "assets/Logo.png", null, outputFile); } public static void streamCheck(File inputFile, String xmlOutput, String pdfOutput, ValidationConfiguration config, String logoLocation, 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, logoLocation, 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, config.getParserConfiguration(), con, outputFile); // write reports if available writeReport(xmlReporter, handler); writeReport(pdfReporter, handler); } 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, String logoLocation, BufferedOutputStream pdfBos, String fileName) { PdfStreamReporter pdfReporter; if (pdfBos != null) { pdfReporter = new PdfStreamReporter(pdfBos, fileName, config, logoLocation); } else { pdfReporter = null; } return pdfReporter; } public static void writeReport(StreamReporter reporter, SvrlContentHandler handler) throws CheckReportWriteException { if (reporter != null) { if (handler != null) { for (SchematronError err : handler.getGeneralErrors()) { reporter.reportGlobalError(err); } for (Entry> e : handler.getFeatureErrors().entrySet()) { for (SchematronError se : e.getValue()) { reporter.addError(e.getKey(), se); } } } 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; } 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); } } }