/*-
* 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 extends Checkable> getCheckClass() {
return getClass();
}
}