/*- * 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.reporting; import java.io.OutputStream; import java.time.ZonedDateTime; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import java.util.concurrent.atomic.AtomicInteger; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import de.hft.stuttgart.citydoctor2.check.CheckConfiguration; import de.hft.stuttgart.citydoctor2.check.CheckError; import de.hft.stuttgart.citydoctor2.check.CheckId; import de.hft.stuttgart.citydoctor2.check.ErrorType; import de.hft.stuttgart.citydoctor2.check.ValidationConfiguration; import de.hft.stuttgart.citydoctor2.checkresult.CheckReport; import de.hft.stuttgart.citydoctor2.checkresult.Environment; import de.hft.stuttgart.citydoctor2.checkresult.ErrorDetails; import de.hft.stuttgart.citydoctor2.checkresult.ErrorReport; import de.hft.stuttgart.citydoctor2.checkresult.ErrorStatistic; import de.hft.stuttgart.citydoctor2.checkresult.ErrorStatus; import de.hft.stuttgart.citydoctor2.checkresult.Feature; import de.hft.stuttgart.citydoctor2.checkresult.FeatureReport; import de.hft.stuttgart.citydoctor2.checkresult.GlobalErrorStatistics; import de.hft.stuttgart.citydoctor2.checkresult.GlobalStatistics; import de.hft.stuttgart.citydoctor2.checkresult.Header; import de.hft.stuttgart.citydoctor2.checkresult.ModelStatistics; import de.hft.stuttgart.citydoctor2.checkresult.PlanCheck; import de.hft.stuttgart.citydoctor2.checkresult.PlanParameter; import de.hft.stuttgart.citydoctor2.checkresult.Statistics; import de.hft.stuttgart.citydoctor2.checkresult.ValidationPlan; import de.hft.stuttgart.citydoctor2.checkresult.ValidationResults; import de.hft.stuttgart.citydoctor2.checkresult.utility.CheckReportWriteException; import de.hft.stuttgart.citydoctor2.datastructure.BridgeObject; import de.hft.stuttgart.citydoctor2.datastructure.Building; import de.hft.stuttgart.citydoctor2.datastructure.BuildingPart; import de.hft.stuttgart.citydoctor2.datastructure.CityObject; import de.hft.stuttgart.citydoctor2.datastructure.LandObject; import de.hft.stuttgart.citydoctor2.datastructure.TransportationObject; import de.hft.stuttgart.citydoctor2.datastructure.Vegetation; import de.hft.stuttgart.citydoctor2.datastructure.WaterObject; import de.hft.stuttgart.citydoctor2.utils.Localization; /** * Report for creating a XML report out of a stream of feature results * * @author Matthias Betz * */ public class XmlStreamReporter implements StreamReporter { private static final Logger logger = LogManager.getLogger(XmlStreamReporter.class); private OutputStream output; private CheckReport report; private Map reportMap; private ValidationConfiguration config; public XmlStreamReporter(OutputStream output, String fileName, ValidationConfiguration config) { this.output = output; this.config = config; reportMap = new HashMap<>(); report = new CheckReport(); report.setHeader(createHeader(fileName)); report.setValidationPlan(createValidationPlan()); report.setValidationResults(new ValidationResults()); } private Header createHeader(String fileName) { Header header = new Header(); header.setDate(ZonedDateTime.now()); header.setFile(fileName); Environment env = new Environment(); header.setEnvironment(env); env.setJavaVersion(System.getProperties().getProperty("java.vm.version")); env.setJavaVmVendor(System.getProperties().getProperty("java.vm.vendor")); env.setJavaVmVersion(System.getProperties().getProperty("java.version")); env.setOsArch(System.getProperties().getProperty("os.arch")); env.setOsName(System.getProperties().getProperty("os.name")); env.setValidationVersion(Localization.getText(Localization.VERSION)); return header; } private ValidationPlan createValidationPlan() { ValidationPlan plan = new ValidationPlan(); plan.setNumberOfRoundingPlaces(config.getNumberOfRoundingPlaces()); List checkConfigs = new ArrayList<>(); plan.setChecks(checkConfigs); for (Entry e : config.getChecks().entrySet()) { PlanCheck checkConfig = new PlanCheck(); checkConfigs.add(checkConfig); checkConfig.setName(e.getKey().toString()); if (e.getValue().isEnabled()) { checkConfig.setActivated(true); Map checkParams = e.getValue().getParameters(); if (checkParams != null && !checkParams.isEmpty()) { List parameters = new ArrayList<>(); checkConfig.setParameters(parameters); for (Entry entry : checkParams.entrySet()) { PlanParameter param = new PlanParameter(); param.setName(entry.getKey()); param.setValue(entry.getValue()); parameters.add(param); } } } else { checkConfig.setActivated(false); } } plan.setSchematronFile(config.getSchematronFilePath()); return plan; } @Override public void report(CityObject co) { if (co instanceof Building) { reportBuilding((Building) co); } else if (co instanceof Vegetation) { reportVegetation((Vegetation) co); } else if (co instanceof TransportationObject) { reportTrans((TransportationObject) co); } else if (co instanceof BridgeObject) { reportBridge((BridgeObject) co); } else if (co instanceof WaterObject) { reportWater((WaterObject) co); } else if (co instanceof LandObject) { reportLand((LandObject) co); } else { throw new IllegalStateException("Not reportable CityObject found: " + co.getClass().getSimpleName()); } } @Override public void reportGlobalError(CheckError err) { report.getValidationResults().getGlobalErrors().add(createErrorReport(err)); } private void reportLand(LandObject lo) { FeatureReport fr = createCityObjectReportNode(lo); report.getValidationResults().getLandReports().add(fr); } private void reportWater(WaterObject wo) { FeatureReport fr = createCityObjectReportNode(wo); report.getValidationResults().getWaterReports().add(fr); } private void reportBridge(BridgeObject bo) { FeatureReport fr = createCityObjectReportNode(bo); report.getValidationResults().getBridgeReports().add(fr); } private void reportTrans(TransportationObject to) { FeatureReport fr = createCityObjectReportNode(to); report.getValidationResults().getTransportationReports().add(fr); } private void reportVegetation(Vegetation v) { FeatureReport fr = createCityObjectReportNode(v); report.getValidationResults().getVegetationReports().add(fr); } private void reportBuilding(Building co) { FeatureReport fr = createCityObjectReportNode(co); for (BuildingPart bp : co.getBuildingParts()) { reportMap.put(bp.getGmlId().getGmlString(), fr); } report.getValidationResults().getBuildingReports().add(fr); } private FeatureReport createCityObjectReportNode(CityObject co) { FeatureReport fr = new FeatureReport(); reportMap.put(co.getGmlId().getGmlString(), fr); fr.setGmlId(co.getGmlId().getGmlString()); List errors = new ArrayList<>(); co.collectContainedErrors(errors); // Filter out duplicate errors (from linked polygons) Set errorSet = new HashSet<>(errors); for (CheckError err : errorSet) { ErrorReport errReport = createErrorReport(err); fr.getErrors().add(errReport); } return fr; } private ErrorReport createErrorReport(CheckError err) { ErrorReport errReport = new ErrorReport(); errReport.setId(err.getErrorId().toString()); if (err.getType() == ErrorType.WARNING) { errReport.setStatus(ErrorStatus.WARNING); } else if (err.getType() == ErrorType.ERROR) { errReport.setStatus(ErrorStatus.ERROR); } else { throw new IllegalStateException("Unknown error type: " + err.getType()); } if (err.getFeature() != null) { Feature feature = new Feature(); feature.setGmlId(err.getFeature().getGmlId().getGmlString()); feature.setType(err.getFeature().getCheckClass().getSimpleName()); errReport.setFeature(feature); } XmlStreamErrorHandler handler = new XmlStreamErrorHandler(config.getParserConfiguration()); err.report(handler); ErrorDetails details = handler.getDetails(); errReport.setErrorDetails(details); return errReport; } @Override public void finishReport() throws CheckReportWriteException { GlobalStatistics stats = new GlobalStatistics(); GlobalErrorStatistics globErrStats = new GlobalErrorStatistics(); globErrStats .setNumErrorBridgeObjects(getNumberOfErrorFeatures(report.getValidationResults().getBridgeReports())); globErrStats.setNumErrorBuildings(getNumberOfErrorFeatures(report.getValidationResults().getBuildingReports())); globErrStats.setNumErrorLandObjects(getNumberOfErrorFeatures(report.getValidationResults().getLandReports())); globErrStats.setNumErrorTransportation( getNumberOfErrorFeatures(report.getValidationResults().getTransportationReports())); globErrStats .setNumErrorVegetation(getNumberOfErrorFeatures(report.getValidationResults().getVegetationReports())); globErrStats.setNumErrorWaterObjects(getNumberOfErrorFeatures(report.getValidationResults().getWaterReports())); stats.setGlobalErrorStats(globErrStats); ModelStatistics modelStats = new ModelStatistics(); modelStats.setNumBridgeObjects(report.getValidationResults().getBridgeReports().size()); modelStats.setNumBuildings(report.getValidationResults().getBuildingReports().size()); modelStats.setNumLandObjects(report.getValidationResults().getLandReports().size()); modelStats.setNumTransportation(report.getValidationResults().getTransportationReports().size()); modelStats.setNumVegetation(report.getValidationResults().getVegetationReports().size()); modelStats.setNumWaterObjects(report.getValidationResults().getWaterReports().size()); stats.setModelStats(modelStats); ErrorStatisticsCollector globalErrorCount = new ErrorStatisticsCollector(); for (ErrorReport errReport : report.getValidationResults().getGlobalErrors()) { globalErrorCount.addError(errReport.getId()); } createStatistics(globalErrorCount, report.getValidationResults().getBridgeReports()); createStatistics(globalErrorCount, report.getValidationResults().getBuildingReports()); createStatistics(globalErrorCount, report.getValidationResults().getLandReports()); createStatistics(globalErrorCount, report.getValidationResults().getTransportationReports()); createStatistics(globalErrorCount, report.getValidationResults().getVegetationReports()); createStatistics(globalErrorCount, report.getValidationResults().getWaterReports()); addStatisticsObjects(stats.getErrorStats(), globalErrorCount); report.setGlobalStatistics(stats); report.saveAs(output); } private void createStatistics(ErrorStatisticsCollector globalErrorCount, List reports) { for (FeatureReport fReport : reports) { if (fReport.getErrors().isEmpty()) { fReport.setErrors(null); continue; } ErrorStatisticsCollector collector = new ErrorStatisticsCollector(); for (ErrorReport errReport : fReport.getErrors()) { collector.addError(errReport.getId()); globalErrorCount.addError(errReport.getId()); } Statistics reportStats = new Statistics(); addStatisticsObjects(reportStats.getErrorStats(), collector); fReport.setStatistics(reportStats); } } private void addStatisticsObjects(List stats, ErrorStatisticsCollector collector) { for (Entry er : collector.getErrorCounts().entrySet()) { if (er.getValue().get() > 0) { ErrorStatistic errStats = new ErrorStatistic(); errStats.setName(er.getKey()); errStats.setCount(er.getValue().get()); stats.add(errStats); } } } private int getNumberOfErrorFeatures(List reports) { int errorFeatures = 0; for (FeatureReport fReport : reports) { boolean hasError = hasError(fReport); if (hasError) { errorFeatures++; } } return errorFeatures; } private boolean hasError(FeatureReport fReport) { return !fReport.getErrors().isEmpty(); } @Override public void addError(String gmlId, CheckError err) { FeatureReport fReport = reportMap.get(gmlId); if (fReport == null) { logger.warn("Error reported for unknown gml id: {}, adding to global errors instead", gmlId); reportGlobalError(err); } else { fReport.getErrors().add(createErrorReport(err)); } } }