diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/check/Checkable.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/check/Checkable.java index 1349baef3be02f1de4dec87158626446a36c0b48..e281a64db05f146777c57c606705a5f84a4b918f 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/check/Checkable.java +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/check/Checkable.java @@ -65,7 +65,12 @@ public abstract class Checkable implements Serializable { * @param c the check from which the check method is called with the Checkable * instance as parameter. */ - public abstract void accept(Check c); + public void accept(Check c) { + if (c.canExecute(this)) { + c.check(this); + } + setValidated(true); + } /** * The GML-ID of the checkable. This is necessary so specific features can be diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/check/ErrorId.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/check/ErrorId.java index f0238847c7602a3831becff424244ecb3c150c23..b3a179f8a939d70d2622c9a081a8a22778394290 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/check/ErrorId.java +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/check/ErrorId.java @@ -101,4 +101,8 @@ public class ErrorId implements Serializable { return name; } + public String getIdString() { + return name; + } + } diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/check/ErrorVisitor.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/check/ErrorVisitor.java index 840258e333f8bd09c1d72bd1e25eff657ba346fa..1e3a7258d1069fb1673587f3ead266f2e9a41859 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/check/ErrorVisitor.java +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/check/ErrorVisitor.java @@ -19,6 +19,8 @@ package de.hft.stuttgart.citydoctor2.check; import de.hft.stuttgart.citydoctor2.check.error.AllPolygonsWrongOrientationError; +import de.hft.stuttgart.citydoctor2.check.error.AttributeMissingError; +import de.hft.stuttgart.citydoctor2.check.error.AttributeValueWrongError; import de.hft.stuttgart.citydoctor2.check.error.ConsecutivePointSameError; import de.hft.stuttgart.citydoctor2.check.error.DependenciesNotMetError; import de.hft.stuttgart.citydoctor2.check.error.MultipleConnectedComponentsError; @@ -122,5 +124,9 @@ public interface ErrorVisitor { public void visit(SurfaceUnfragmentedError err); public void visit(TinyEdgeError err); + + public void visit(AttributeMissingError err); + + public void visit(AttributeValueWrongError err); } diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/check/error/AttributeMissingError.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/check/error/AttributeMissingError.java new file mode 100644 index 0000000000000000000000000000000000000000..91a8d893a8032d81dffbcee1db1867bd579f37ff --- /dev/null +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/check/error/AttributeMissingError.java @@ -0,0 +1,93 @@ +/*- + * 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 <https://www.gnu.org/licenses/>. + */ +package de.hft.stuttgart.citydoctor2.check.error; + +import de.hft.stuttgart.citydoctor2.check.CheckError; +import de.hft.stuttgart.citydoctor2.check.ErrorId; +import de.hft.stuttgart.citydoctor2.check.ErrorReport; +import de.hft.stuttgart.citydoctor2.check.ErrorType; +import de.hft.stuttgart.citydoctor2.check.ErrorVisitor; +import de.hft.stuttgart.citydoctor2.check.HealingMethod; +import de.hft.stuttgart.citydoctor2.check.ModificationListener; +import de.hft.stuttgart.citydoctor2.datastructure.CityObject; +import de.hft.stuttgart.citydoctor2.datastructure.GmlElement; + +public class AttributeMissingError implements CheckError { + + private static final long serialVersionUID = 185026674309965067L; + + public static final ErrorId ID = new ErrorId("SEM_ATTRIBUTE_MISSING"); + + private CityObject co; + private boolean generic; + private String childId; + private String nameOfAttribute; + + public AttributeMissingError(CityObject co, String childId, String nameOfAttribute, boolean generic) { + this.co = co; + this.childId = childId; + this.nameOfAttribute = nameOfAttribute; + this.generic = generic; + } + + @Override + public ErrorType getType() { + return ErrorType.ERROR; + } + + @Override + public ErrorId getErrorId() { + return ID; + } + + public String getChildId() { + return childId; + } + + public boolean isGeneric() { + return generic; + } + + public String getNameOfAttribute() { + return nameOfAttribute; + } + + @Override + public GmlElement getFeature() { + return co; + } + + @Override + public void accept(ErrorVisitor errorVisitor) { + errorVisitor.visit(this); + } + + @Override + public boolean accept(HealingMethod method, ModificationListener l) { + return method.visit(this, l); + } + + @Override + public void report(ErrorReport report) { + report.add("childId", childId); + report.add("name", nameOfAttribute); + report.add("generic", "" + generic); + } + +} diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/check/error/AttributeValueWrongError.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/check/error/AttributeValueWrongError.java new file mode 100644 index 0000000000000000000000000000000000000000..21d7f2a1a55231874f5ec63b704d381e31064629 --- /dev/null +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/check/error/AttributeValueWrongError.java @@ -0,0 +1,93 @@ +/*- + * 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 <https://www.gnu.org/licenses/>. + */ +package de.hft.stuttgart.citydoctor2.check.error; + +import de.hft.stuttgart.citydoctor2.check.CheckError; +import de.hft.stuttgart.citydoctor2.check.ErrorId; +import de.hft.stuttgart.citydoctor2.check.ErrorReport; +import de.hft.stuttgart.citydoctor2.check.ErrorType; +import de.hft.stuttgart.citydoctor2.check.ErrorVisitor; +import de.hft.stuttgart.citydoctor2.check.HealingMethod; +import de.hft.stuttgart.citydoctor2.check.ModificationListener; +import de.hft.stuttgart.citydoctor2.datastructure.CityObject; +import de.hft.stuttgart.citydoctor2.datastructure.GmlElement; + +public class AttributeValueWrongError implements CheckError { + + private static final long serialVersionUID = 6106964709204961560L; + + public static final ErrorId ID = new ErrorId("SEM_ATTRIBUTE_WRONG_VALUE"); + + private CityObject co; + private boolean generic; + private String childId; + private String nameOfAttribute; + + public AttributeValueWrongError(CityObject co, String childId, String nameOfAttribute, boolean generic) { + this.co = co; + this.childId = childId; + this.nameOfAttribute = nameOfAttribute; + this.generic = generic; + } + + @Override + public ErrorType getType() { + return ErrorType.ERROR; + } + + @Override + public ErrorId getErrorId() { + return ID; + } + + @Override + public GmlElement getFeature() { + return co; + } + + public String getChildId() { + return childId; + } + + public boolean isGeneric() { + return generic; + } + + public String getNameOfAttribute() { + return nameOfAttribute; + } + + @Override + public void accept(ErrorVisitor errorVisitor) { + errorVisitor.visit(this); + } + + @Override + public boolean accept(HealingMethod method, ModificationListener l) { + return method.visit(this, l); + } + + @Override + public void report(ErrorReport report) { + report.add("childId", childId); + report.add("name", nameOfAttribute); + report.add("generic", "" + generic); + } + +} diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/check/error/SchematronError.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/check/error/SchematronError.java index e682d3fe87d77c16fb7728ba6c21e8a261091ce7..0910d8645460ffa1e17eb1408eb1a5b5fc721a80 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/check/error/SchematronError.java +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/check/error/SchematronError.java @@ -31,10 +31,19 @@ public class SchematronError implements CheckError { private static final long serialVersionUID = -2964084500589868928L; - private String text; + private String errorId; + private String gmlId; + private String childId; + private String nameOfAttribute; + private boolean generic; - public SchematronError(String text) { - this.text = text; + + public SchematronError(String errorId, String gmlId, String childId, String nameOfAttribute, boolean generic) { + this.errorId = errorId; + this.gmlId = gmlId; + this.childId = childId; + this.nameOfAttribute = nameOfAttribute; + this.generic = generic; } @Override @@ -49,18 +58,34 @@ public class SchematronError implements CheckError { @Override public void report(ErrorReport report) { - report.add("Description", text); + report.add("errorId", errorId); } @Override public String toString() { - return "SchematronError [text=" + text + "]"; + return "SchematronError [text=" + errorId + "]"; } - - public String getText() { - return text; + + public String getChildId() { + return childId; } - + + public String getGmlId() { + return gmlId; + } + + public String getNameOfAttribute() { + return nameOfAttribute; + } + + public String getErrorIdString() { + return errorId; + } + + public boolean isGeneric() { + return generic; + } + @Override public ErrorType getType() { return ErrorType.ERROR; diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/Geometry.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/Geometry.java index c9847ba897362d823a6129188f67bb3a0913a747..eb53d7b3f6322cd3476dd9b87ea465330a31acea 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/Geometry.java +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/Geometry.java @@ -413,11 +413,13 @@ public class Geometry extends GmlElement { @Override public void clearMetaInformation() { - for (Vertex v : vertices) { - v.clearAdjacentRings(); + if (vertices != null) { + for (Vertex v : vertices) { + v.clearAdjacentRings(); + } + vertices = null; } edges = null; - vertices = null; edgeMap = null; } } diff --git a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/GmlElement.java b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/GmlElement.java index ea50cf2296b17c0348fbdba700bf8a7fee907f74..80137f6f2508a24ae76e0e604d4fe76bc71920ec 100644 --- a/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/GmlElement.java +++ b/CityDoctorParent/CityDoctorModel/src/main/java/de/hft/stuttgart/citydoctor2/datastructure/GmlElement.java @@ -25,7 +25,6 @@ import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.UncheckedIOException; -import de.hft.stuttgart.citydoctor2.check.Check; import de.hft.stuttgart.citydoctor2.check.Checkable; /** @@ -54,14 +53,6 @@ public abstract class GmlElement extends Checkable { } } - @Override - public void accept(Check c) { - if (c.canExecute(this)) { - c.check(this); - } - setValidated(true); - } - public void setGmlId(GmlId id) { gmlId = id; } 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 7086dc53fe6d90808087b992bc249c2b72f5f0d4..1eb48ed9057367befcb61085c543324078ff6364 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 @@ -47,13 +47,13 @@ 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.Checks; import de.hft.stuttgart.citydoctor2.checks.SvrlContentHandler; import de.hft.stuttgart.citydoctor2.checks.util.FeatureCheckedListener; -import de.hft.stuttgart.citydoctor2.datastructure.Building; -import de.hft.stuttgart.citydoctor2.datastructure.BuildingPart; import de.hft.stuttgart.citydoctor2.datastructure.CityDoctorModel; import de.hft.stuttgart.citydoctor2.datastructure.CityObject; import de.hft.stuttgart.citydoctor2.datastructure.FeatureType; @@ -111,7 +111,7 @@ public class Checker { public Checks getChecks() { return checkConfig; } - + public CityDoctorModel getModel() { return model; } @@ -187,36 +187,47 @@ public class Checker { SvrlContentHandler handler = executeSchematronValidationIfAvailable(config, model.getFile()); if (handler != null) { model.addGlobalErrors(handler.getGeneralErrors()); - Map<String, Checkable> featureMap = new HashMap<>(); + Map<String, CityObject> featureMap = new HashMap<>(); model.createFeatureStream().forEach(f -> featureMap.put(f.getGmlId().getGmlString(), f)); - for (Building b : model.getBuildings()) { - for (BuildingPart bp : b.getBuildingParts()) { - featureMap.put(bp.getGmlId().getGmlString(), bp); - } - } handler.getFeatureErrors().forEach((k, v) -> { if (k.trim().isEmpty()) { // missing gml id, ignore? return; } - Checkable checkable = featureMap.get(k); - if (checkable == null) { + CityObject co = featureMap.get(k); + if (co == null) { // gml id reported by schematron was not found, add to general errors - model.addGlobalError(v); + for (SchematronError se : v) { + model.addGlobalError(se); + } } else { - checkable.addCheckResult(new CheckResult(CheckId.C_SEM_SCHEMATRON, ResultStatus.ERROR, v)); + 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++; + } } }); } isValidated = true; } - + public ValidationConfiguration getConfig() { return config; } - public static SvrlContentHandler executeSchematronValidationIfAvailable(ValidationConfiguration config, - File file) { + public static SvrlContentHandler executeSchematronValidationIfAvailable(ValidationConfiguration config, File file) { if (config.getSchematronFilePath() != null && !config.getSchematronFilePath().isEmpty()) { if (logger.isInfoEnabled()) { logger.info(Localization.getText("Checker.schematronValidation")); @@ -372,7 +383,7 @@ public class Checker { } // check every feature executeChecksForCityObject(co); - + if (config.getParserConfiguration().useLowMemoryConsumption()) { // low memory consumption, remove edges again co.clearMetaInformation(); @@ -448,10 +459,10 @@ public class Checker { 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(); @@ -464,7 +475,8 @@ public class Checker { } } if (layer.isEmpty()) { - throw new IllegalStateException("There are checks that have dependencies that are not executed or are unknown"); + throw new IllegalStateException( + "There are checks that have dependencies that are not executed or are unknown"); } result.add(layer); for (Check c : layer) { @@ -526,8 +538,10 @@ public class Checker { for (SchematronError err : handler.getGeneralErrors()) { reporter.reportGlobalError(err); } - for (Entry<String, SchematronError> e : handler.getFeatureErrors().entrySet()) { - reporter.addError(e.getKey(), e.getValue()); + for (Entry<String, List<SchematronError>> e : handler.getFeatureErrors().entrySet()) { + for (SchematronError se : e.getValue()) { + reporter.addError(e.getKey(), se); + } } } reporter.finishReport(); diff --git a/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/checks/SvrlContentHandler.java b/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/checks/SvrlContentHandler.java index 1dd4fb232d4d975e8f7dffd0403f8b490b75620e..9e50484db22aa37405ab6eaf3bcea605a4d7c7a8 100644 --- a/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/checks/SvrlContentHandler.java +++ b/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/checks/SvrlContentHandler.java @@ -45,7 +45,7 @@ public class SvrlContentHandler implements ContentHandler { private StringBuilder buffer; - private Map<String, SchematronError> featureErrors; + private Map<String, List<SchematronError>> featureErrors; private List<SchematronError> generalErrors; public SvrlContentHandler() { @@ -53,7 +53,7 @@ public class SvrlContentHandler implements ContentHandler { generalErrors = new ArrayList<>(); } - public Map<String, SchematronError> getFeatureErrors() { + public Map<String, List<SchematronError>> getFeatureErrors() { return featureErrors; } @@ -104,13 +104,23 @@ public class SvrlContentHandler implements ContentHandler { // not needed if (nextIsTextContent && "text".equals(localName)) { String text = buffer.toString(); - int index = text.indexOf(": "); - if (index == -1) { + String[] split = text.split("\\|\\|"); + if (split.length != 5) { + throw new IllegalStateException( + "Schematron File is not formed according to specification for CityDoctor."); + } + String gmlId = split[0]; + String childId = split[1]; + String errorId = split[2]; + String nameOfAttribute = split[3]; + boolean generic = Boolean.parseBoolean(split[4]); + SchematronError err = new SchematronError(errorId, gmlId, childId, nameOfAttribute, generic); + if (gmlId == null || gmlId.isEmpty()) { // general error - generalErrors.add(new SchematronError(text)); + generalErrors.add(err); } else { - String gmlId = text.substring(0, index); - featureErrors.put(gmlId, new SchematronError(text)); + List<SchematronError> errors = featureErrors.computeIfAbsent(gmlId, k -> new ArrayList<>()); + errors.add(err); } buffer = null; nextIsTextContent = false; diff --git a/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/reporting/pdf/PdfStreamReporter.java b/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/reporting/pdf/PdfStreamReporter.java index 0b72d2d165675eb6621dc3dc7eb1fdb1c7a47533..bfd739e009101bfb21c380e8055b33750a8dcd25 100644 --- a/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/reporting/pdf/PdfStreamReporter.java +++ b/CityDoctorParent/CityDoctorValidation/src/main/java/de/hft/stuttgart/citydoctor2/reporting/pdf/PdfStreamReporter.java @@ -39,7 +39,6 @@ import de.hft.stuttgart.citydoctor2.check.CheckResult; import de.hft.stuttgart.citydoctor2.check.ErrorId; import de.hft.stuttgart.citydoctor2.check.ResultStatus; import de.hft.stuttgart.citydoctor2.check.ValidationConfiguration; -import de.hft.stuttgart.citydoctor2.check.error.SchematronError; import de.hft.stuttgart.citydoctor2.checkresult.utility.CheckReportWriteException; import de.hft.stuttgart.citydoctor2.datastructure.BoundarySurface; import de.hft.stuttgart.citydoctor2.datastructure.BridgeObject; @@ -445,10 +444,8 @@ public class PdfStreamReporter implements StreamReporter { String text = "Global error " + err.getErrorId(); globalErrors.add10PtTextElement(text, 10); - if (err instanceof SchematronError) { - text = ((SchematronError) err).getText(); - globalErrors.add10PtTextElement(text, 20); - } + PdfErrorHandler handler = new PdfErrorHandler(globalErrors, config.getParserConfiguration()); + err.report(handler); } @Override