//----------------------------------------------------------------------------//
// //
// C h e c k S u i t e //
// //
//----------------------------------------------------------------------------//
// <editor-fold defaultstate="collapsed" desc="hdr"> //
// Copyright © Hervé Bitteur and others 2000-2013. All rights reserved. //
// This software is released under the GNU General Public License. //
// Goto http://kenai.com/projects/audiveris to report bugs or suggestions. //
//----------------------------------------------------------------------------//
// </editor-fold>
package omr.check;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
/**
* Class {@code CheckSuite} represents a suite of homogeneous checks,
* that is checks working on the same type.
*
* Every check in the suite is assigned a weight, to represent its relative
* importance in the suite.
*
* @param <C> the subtype of Checkable-compatible objects used in the
* homogeneous collection of checks in this suite
*
* @author Hervé Bitteur
*/
public class CheckSuite<C extends Checkable>
{
//~ Static fields/initializers ---------------------------------------------
/** Usual logger utility */
private static final Logger logger = LoggerFactory.getLogger(CheckSuite.class);
//~ Instance fields --------------------------------------------------------
/** Name of this suite */
protected String name;
/** Minimum threshold for final grade */
protected double threshold;
/** List of checks in the suite */
private final List<Check<C>> checks = new ArrayList<>();
/** List of related weights in the suite */
private final List<Double> weights = new ArrayList<>();
/** Total checks weight */
private double totalWeight = 0.0d;
//~ Constructors -----------------------------------------------------------
//------------//
// CheckSuite //
//------------//
/**
* Create a suite of checks
*
* @param name the name for the suite (for debug)
* @param threshold the threshold to test results
*/
public CheckSuite (String name,
double threshold)
{
this.name = name;
this.threshold = threshold;
}
//~ Methods ----------------------------------------------------------------
//-----//
// add //
//-----//
/**
* Add a check to the suite, with its assigned weight
*
* @param weight the weight of this check in the suite
* @param check the check to add to the suite
*/
public void add (double weight,
Check<C> check)
{
checks.add(check);
weights.add(weight);
totalWeight += weight;
}
//--------//
// addAll //
//--------//
/**
* Add all checks of another suite
*
* @param suite the suite of checks to be appended
* @return the suite with checks appended, for easy chaining
*/
public CheckSuite<C> addAll (CheckSuite<C> suite)
{
int index = 0;
for (Check<C> check : suite.checks) {
double weight = suite.weights.get(index++);
add(weight, check);
}
// Allow chaining
return this;
}
//------//
// dump //
//------//
/**
* Dump a readable description of all checks of this suite.
*/
public void dump ()
{
StringBuilder sb = new StringBuilder(String.format("%n"));
if (name != null) {
sb.append(name);
}
sb.append(String.format(" Check Suite: threshold=%f%n", threshold));
dumpSpecific(sb);
sb.append(String.format(
"Weight Name Covariant Low High%n"));
sb.append(String.format(
"------ ---- ------ --- ----%n"));
int index = 0;
for (Check<C> check : checks) {
sb.append(String.format(
"%4.1f %-19s %5b % 6.2f % 6.2f %n",
weights.get(index++),
check.getName(),
check.isCovariant(),
check.getLow(),
check.getHigh()));
}
logger.info(sb.toString());
}
//-----------//
// getChecks //
//-----------//
/**
* Report the collection of checks that compose this suite.
*
* @return the collection of checks
*/
public List<Check<C>> getChecks ()
{
return checks;
}
//---------//
// getName //
//---------//
/**
* Report the name of this suite.
*
* @return suite name
*/
public String getName ()
{
return name;
}
//--------------//
// getThreshold //
//--------------//
/**
* Report the assigned threshold.
*
* @return the assigned minimum result
*/
public double getThreshold ()
{
return threshold;
}
//----------------//
// getTotalWeight //
//----------------//
/**
* Report the sum of all individual checks.
*
* @return the total weight of the checks in the suite
*/
public double getTotalWeight ()
{
return totalWeight;
}
//------------//
// getWeights //
//------------//
/**
* Report the weights of the checks.
* (collection parallel to the suite checks)
*
* @return the collection of checks weights
*/
public List<Double> getWeights ()
{
return weights;
}
//------//
// pass //
//------//
/**
* Pass sequentially the checks in the suite, stopping at the first
* test with red result.
*
* @param object the object to be checked
* @return the computed grade.
*/
public double pass (C object)
{
boolean debug = logger.isDebugEnabled() || object.isVip();
double grade = 0.0d;
CheckResult result = new CheckResult();
StringBuilder sb = null;
if (debug) {
sb = new StringBuilder(512);
sb.append(name).append(" ").append(object).append(" ");
}
int index = 0;
for (Check<C> check : checks) {
check.pass(object, result, true);
if (debug) {
sb.append(
String.format("%15s:%5.2f", check.getName(),
result.value));
}
if (result.flag == Check.RED) {
// The check totally failed, we give up immediately!
if (debug) {
logger.info(sb.toString());
}
return result.flag;
} else {
// Aggregate results
double weight = weights.get(index);
grade += (result.flag * weight);
}
index++;
}
// Final grade
grade /= totalWeight;
if (debug) {
sb.append(String.format(" => %5.2f ", grade));
logger.info(sb.toString());
}
return grade;
}
//----------------//
// passCollection //
//----------------//
/**
* Pass the whole collection of suites in a row and return
* the global result.
*
* @param <T> The specific type of checked object
* @param object the object to be checked
* @param suites the collection of check suites to pass
*
* @return the global result
*/
public static <T extends Checkable> double passCollection (T object,
Collection<CheckSuite<T>> suites)
{
double totalWeight = 0.0d;
double grade = 0.0d;
for (CheckSuite<T> suite : suites) {
double res = suite.pass(object);
// If one totally failed, give up immediately
if (res == Check.RED) {
return res;
} else {
// Aggregate results
double weight = suite.getTotalWeight();
totalWeight += weight;
grade += (res * weight);
}
}
// Final grade
return grade / totalWeight;
}
//---------//
// setName //
//---------//
/**
* Assings a new name to the check suite.
*
* @param name the new name
*/
public void setName (String name)
{
this.name = name;
}
//--------------//
// setThreshold //
//--------------//
/**
* Allows to assign a new threshold for the suite.
*
* @param threshold the new minimum result
*/
public void setThreshold (double threshold)
{
this.threshold = threshold;
}
//--------------//
// dumpSpecific //
//--------------//
/**
* Just an empty placeholder, meant to be overridden.
*
* @param sb StringBuilder to populate
*/
protected void dumpSpecific (StringBuilder sb)
{
}
}