Test coverage report for RuleProcedureCycle.java - www.sdmetrics.com

/*
 * SDMetrics Open Core for UML design measurement
 * Copyright (c) Juergen Wuest
 * To contact the author, see <http://www.sdmetrics.com/Contact.html>.
 * 
 * This file is part of the SDMetrics Open Core.
 * 
 * SDMetrics Open Core is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
    
 * SDMetrics Open Core 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 Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with SDMetrics Open Core.  If not, see <http://www.gnu.org/licenses/>.
 *
 */
package com.sdmetrics.metrics;

import java.util.HashMap;

import com.sdmetrics.model.ModelElement;

/**
 * Checks a "cycle" rule.
 * <p>
 * Implementation note on cache usage: this procedure uses the cache of the rule
 * engine to store the strongly connected components of the graph considered by
 * the rule. The optimal time to clear the cache is when you are done checking
 * this rule for all model elements.
 */
public class RuleProcedureCycle extends RuleProcedure {

	private static final String CC_CACHE_KEY = "RuleProcedureCycle.connectedComponents";

	@Override
	public void checkRule(ModelElement element, Rule rule)
			throws SDMetricsException {
		// check if connected components have already been calculated for this
		// rule
		HashMap<Rule, StronglyConnectedComponents<ModelElement>> ccCache = getConnectedComponentCache();
		StronglyConnectedComponents<ModelElement> scc = ccCache.get(rule);
		if (scc == null) {
			// No, so calculate and cache them
			scc = new StronglyConnectedComponents<>();
			ProcedureAttributes attrs = rule.getAttributes();
			int minCount = getMinExpressionValue(element, attrs, "minnodes",
					null);

			ElementGraph graph = new ElementGraph(getMetricsEngine(),
					getModel().getAcceptedElements(element.getType()),
					attrs.getRequiredExpression("nodes"), null);

			scc.calculateConnectedComponents(graph, minCount, true);
			ccCache.put(rule, scc);
		}

		// report if the model element is part of a cycle
		int ccIndex = scc.getIndexFor(element);
		if (ccIndex >= 0) {
			int ccSize = scc.getConnectedComponent(ccIndex).size();
			StringBuilder label = new StringBuilder("cyc# ");
			label.append(ccIndex + 1);
			if (ccSize > 1) {
				label.append(" (");
				label.append(ccSize);
				label.append(" nodes)");
			} else {
				label.append(" (1 node)");
			}
			reportViolation(element, rule, label.toString());
		}
	}

	/*
	 * Each cycle rule in the metric definition file calculates the connected
	 * components for one graph, and uses the same graph for all model elements
	 * the rule checks. Therefore, the SCC of the graph are calculated once per
	 * rule, and then stored in the rule engine's cache for the checking of
	 * subsequent elements.
	 */
	private HashMap<Rule, StronglyConnectedComponents<ModelElement>> getConnectedComponentCache() {
		@SuppressWarnings("unchecked")
		HashMap<Rule, StronglyConnectedComponents<ModelElement>> result = 
			(HashMap<Rule, StronglyConnectedComponents<ModelElement>>) getValuesCache().get(CC_CACHE_KEY);
		if (result == null) {
			result = new HashMap<>();
			getValuesCache().put(CC_CACHE_KEY, result);
		}
		return result;
	}
}