/*- * 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.Serializable; import java.util.HashMap; import java.util.List; import java.util.Map; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import de.hft.stuttgart.citydoctor2.datastructure.GmlId; /** * Interface to indicate that this object can be checked by Checks. * * @author Matthias Betz * */ public abstract class Checkable implements Serializable { private static final long serialVersionUID = -1707871839265057882L; private static final Logger logger = LogManager.getLogger(Checkable.class); private Map checkResults = new HashMap<>(); private boolean isValidated = false; protected void setValidated(boolean validated) { isValidated = validated; } public boolean isValidated() { return isValidated; } /** * This object will be visited by the given check. Indirect implementers have to * call super.visit() to ensure that every layer of classes is * correctly checked. This method should call the visit method of each Checkable * object contained in the implementing class.
* Note: every object should only be visited once, therefore a clear * structure has to be constructed in order to know which object is contained * where. For example a Polygon has vertices as references but the actual * storage location is in Geometry. Therefore only in Geometry the call to visit * vertices is made. * * @param c the check from which the check method is called with the Checkable * instance as parameter. */ 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 * excluded via the ID. * * @return the GML-ID */ public abstract GmlId getGmlId(); public boolean hasGmlId() { return false; } /** * This should be called before executing a check if low memory consumption * method has been enabled. This should create edges and additional meta * information necessary to perform checks. */ public abstract void prepareForChecking(); /** * This should be called after checking has been done. This should remove any * created meta information like edges to free up additional memory space */ public abstract void clearMetaInformation(); /** * This method checks if the object or any object contained within this * checkable has an error. It counts as an error if the result status if the * given check is DEPENDENCIES_NOT_MET. * * @param checkIdentifier the name of the check for which an error is searched * @return true if an error has been found with the given check */ public boolean containsError(CheckId checkIdentifier) { CheckResult cs = getCheckResult(checkIdentifier); if (cs == null) { return false; } ResultStatus rs = cs.getResultStatus(); return rs == ResultStatus.ERROR || rs == ResultStatus.DEPENDENCIES_NOT_MET; } /** * Returns the check result for the given CheckId * * @param id the id * @return the check result or null if none was found */ public CheckResult getCheckResult(CheckId id) { return checkResults.get(id); } /** * Returns the check result for the given Check. * * @param c the check * @return the check result or null if none was found */ public CheckResult getCheckResult(Check c) { return getCheckResult(c.getCheckId()); } /** * * @return all check results for this checkable. */ public Map getAllCheckResults() { return checkResults; } /** * Checks whether this checkable has a DependencyNotMetError for the given * CheckId. * * @param id the CheckId for which the error is searched. * @return true if it contains such an error, false if not or no CheckResult is * available for the given id. */ public boolean hasDependencyNotMetError(CheckId id) { CheckResult cr = checkResults.get(id); if (cr == null) { return false; } return cr.getResultStatus() == ResultStatus.DEPENDENCIES_NOT_MET; } /** * Checks whether this checkable has an error. Dependency errors are not * considered for this function. This will only check this checkable and not * traverse any checkables contained in this instance. * * @return true if it has an error, otherwise false */ public boolean hasAnyError() { for (CheckResult cr : checkResults.values()) { if (cr.getResultStatus() == ResultStatus.ERROR) { return true; } } return false; } /** * Checks whether any check results have been reported for this checkable * * @return true if any checks have reported results, false otherwise */ public boolean hasCheckResults() { return !checkResults.isEmpty(); } /** * Adds the result of a check to this checkable. * * @param cr the check result */ public void addCheckResult(CheckResult cr) { checkResults.put(cr.getCheckIdentifier(), cr); if (cr.getResultStatus() == ResultStatus.ERROR && logger.isDebugEnabled()) { logger.debug("{} has found an error of type {}", cr.getCheckIdentifier(), cr.getError().getErrorId()); } else if (cr.getResultStatus() == ResultStatus.WARNING && logger.isDebugEnabled()) { logger.debug("{} has found a warning of type {}", cr.getCheckIdentifier(), cr.getError().getErrorId()); } } /** * Clears all errors from this checkable */ public void clearCheckResults() { setValidated(false); checkResults.clear(); } /** * Removes all errors from this instance and all contained checkables. */ public abstract void clearAllContainedCheckResults(); /** * * @return false if the checkable or all checkables contained in this one don't * have any error. */ public boolean containsAnyError() { for (CheckResult cr : checkResults.values()) { if (cr.getResultStatus() == ResultStatus.ERROR || cr.getResultStatus() == ResultStatus.DEPENDENCIES_NOT_MET) { return true; } } return false; } /** * Collects all errors from this checkable and all contained checkables and adds * them to the given list. DEPENDENCY_NOT_MET errors are excluded from this. * * @param errors the collection in which the errors are added. */ public void collectContainedErrors(List errors) { for (CheckResult cr : checkResults.values()) { if (cr.getResultStatus() == ResultStatus.ERROR) { errors.add(cr.getError()); } } } /** * As which class is this object being tested. This is used when there are * different implementations for the same object type. For example a polygon can * be a link to another polygon as well as the polygon which contains the actual * data. Checks declare which objects they check, which would be Polygon in this * example. Now there are two kinds of polygons but both are treated as * polygons. * * @return the class for which the object should be treated as. */ public Class getCheckClass() { return getClass(); } }