/*-
* 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.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import de.hft.stuttgart.citydoctor2.check.error.DependenciesNotMetError;
import de.hft.stuttgart.citydoctor2.datastructure.AbstractBuilding;
import de.hft.stuttgart.citydoctor2.datastructure.BoundarySurface;
import de.hft.stuttgart.citydoctor2.datastructure.BridgeObject;
import de.hft.stuttgart.citydoctor2.datastructure.Building;
import de.hft.stuttgart.citydoctor2.datastructure.BuildingInstallation;
import de.hft.stuttgart.citydoctor2.datastructure.BuildingPart;
import de.hft.stuttgart.citydoctor2.datastructure.CityObject;
import de.hft.stuttgart.citydoctor2.datastructure.Geometry;
import de.hft.stuttgart.citydoctor2.datastructure.LandObject;
import de.hft.stuttgart.citydoctor2.datastructure.LinearRing;
import de.hft.stuttgart.citydoctor2.datastructure.Opening;
import de.hft.stuttgart.citydoctor2.datastructure.Polygon;
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.parser.ParserConfiguration;
/**
* The general check class containing the methods which will be called by check
* engine. To create a new check override one or more check(...) methods and
* implement the {@link Check#getApplicableToClasses()} method by returning a
* list of classes you wish to check. CheckResult objects can be attached to
* every Checkable. If the check has parameters override the
* {@link Check#init(Map, ParserConfiguration)} method to get the value of a
* parameter if a user has specified one in the validation plan. It will be
* contained in the Map as a string. If you have parameters you will need to
* override the {@link Check#getDefaultParameter()} method as well to declare
* which parameters you have, which name and default value they have. The name
* will be the key for the Map in the init method previously mentioned. If your
* check has dependencies you can declare them in the
* {@link Check#getDependencies()} method. Be sure not to create cyclic
* dependencies as that would result in undefined behavior.
*
* @author Matthias Betz
*
*/
public abstract class Check {
private List> applicableToClasses = new ArrayList<>(2);
@SuppressWarnings("unchecked")
public Check() {
Method[] declaredMethods = getClass().getDeclaredMethods();
for (Method m : declaredMethods) {
if ("check".equals(m.getName())) {
Class>[] parameterTypes = m.getParameterTypes();
if (parameterTypes.length == 1 && Checkable.class.isAssignableFrom(parameterTypes[0])) {
applicableToClasses.add((Class) parameterTypes[0]);
}
}
}
}
/**
* Returns all classes for which the check needs to be executed
*
* @return a list of classes which the check applies to
*/
public List> getApplicableToClasses() {
return applicableToClasses;
}
/**
* A list of dependencies which the check depends on. Each of those dependencies
* needs to be executed and must have reported no error before this check can be
* executed.
*
* @return a list of CheckIds which the check depends on.
*/
public List getDependencies() {
return Collections.emptyList();
}
/**
* Getter for the check id.
*
* @return the check id.
*/
public abstract CheckId getCheckId();
/**
* Getter for the check type.
*
* @return the check type
*/
public abstract CheckType getType();
/**
* Checks whether the check can be executed on this checkable, meaning the
* checkable or its content can not have any error of any check dependent on
* this check.
* If the check cannot be executed a CheckResult will be created with the
* ResultStatus = DEPENDENCIES_NOT_MET.
*
* @param c the checkable
* @param crc container for all check results
* @return true if the check can be executed, false if the checkable itself or
* one of its containing checkables have an error.
*/
public boolean canExecute(Checkable c) {
// ignore objects where this check doesn't apply to
if (!canBeApplied(c)) {
return false;
}
// check that object doesn't have errors for dependencies of this check
for (CheckId dependencyCheck : getDependencies()) {
boolean hasError = c.containsError(dependencyCheck);
if (hasError) {
// check whether a result was already inserted
if (!c.hasDependencyNotMetError(getCheckId())) {
// insert dependency error
CheckError err = new DependenciesNotMetError(dependencyCheck);
CheckResult cr = new CheckResult(getCheckId(), ResultStatus.DEPENDENCIES_NOT_MET, err);
c.addCheckResult(cr);
}
return false;
}
}
return true;
}
private boolean canBeApplied(Checkable c) {
for (Class checkableClass : getApplicableToClasses()) {
if (checkableClass.isAssignableFrom(c.getCheckClass())) {
return true;
}
}
return false;
}
/**
* check anything
*
* @param checkable a checkable
*/
public void check(Checkable checkable) {
}
/**
* check buildings and building parts
*
* @param ab building or building part
*/
public void check(AbstractBuilding ab) {
}
/**
* check boundary surfaces
*
* @param bs a boundary surface
*/
public void check(BoundarySurface bs) {
}
/**
* check bridges
*
* @param bo a bridge
*/
public void check(BridgeObject bo) {
}
/**
* check only buildings
*
* @param b a building
*/
public void check(Building b) {
}
/**
* check building installations
*
* @param bi a building installation
*/
public void check(BuildingInstallation bi) {
}
/**
* check only building parts
*
* @param bp a building part
*/
public void check(BuildingPart bp) {
}
/**
* check all city objects
*
* @param co a city object
*/
public void check(CityObject co) {
}
/**
* check land use objects
*
* @param lo a land use object
*/
public void check(LandObject lo) {
}
/**
* check openings
*
* @param o an opening
*/
public void check(Opening o) {
}
/**
* check transportation objects
*
* @param to a transportation object
*/
public void check(TransportationObject to) {
}
/**
* check vegetation objects
*
* @param veg a vegetation object
*/
public void check(Vegetation veg) {
}
/**
* check water objects
*
* @param wo a water object
*/
public void check(WaterObject wo) {
}
/**
* check geometries
*
* @param geom a geometry
*/
public void check(Geometry geom) {
}
/**
* check polygons
*
* @param poly a polygon
*/
public void check(Polygon poly) {
}
/**
* check linear rings
*
* @param ring a linear ring
*/
public void check(LinearRing ring) {
}
/**
* The initialization method of this check. It will be called before any check
* method will be executed. Override this if you want to have configurable
* parameters.
*
* @param params the parameter map containing the parameters for the check in
* String form. The key should be the same String provided by the
* {@link Check#getDefaultParameter()} method
* @param config sometimes there are global parameters which can be used by
* checks. Those are be stored in this container
*/
public void init(Map params, ParserConfiguration config) {
}
/**
* This methods gives checks the possibility to declare default parameters, used
* primarily in the GUI.
*
* @return a list of default parameters, not null
*/
public List getDefaultParameter() {
return Collections.emptyList();
}
@Override
public String toString() {
return "Check [id=" + getCheckId() + "]";
}
/**
* Create a new instance of this check. This is needed to keep the checks
* dynamic
*
* @return a new instance of this check
*/
public abstract Check createNewInstance();
}