Test coverage report for Model.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.model;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;

import com.sdmetrics.math.MappedCollectionsIterator;

/**
 * Container for the model elements of the UML design to be analyzed. Provides
 * access to the model elements and element filtering based on qualified names.
 */
public class Model implements Iterable<ModelElement> {
	/** Metamodel on which the elements of this model are based. */
	private final MetaModel metaModel;

	/**
	 * Data structure to hold all model elements. Elements of each type are
	 * stored in a random access list.
	 */
	private final HashMap<MetaModelElement, ArrayList<ModelElement>> elementsByType;

	/**
	 * Data structure to hold the model elements that have been accepted for
	 * output as per the element filter settings.
	 */
	private HashMap<MetaModelElement, ArrayList<ModelElement>> acceptedElementsByType;

	/**
	 * Creates a new, empty model.
	 * 
	 * @param metaModel Metamodel that defines the element types and attributes.
	 */
	public Model(MetaModel metaModel) {
		this.metaModel = metaModel;
		elementsByType = new HashMap<>(metaModel.getNumberOfTypes());
		for (MetaModelElement type : metaModel) {
			elementsByType.put(type, new ArrayList<ModelElement>());
		}
	}

	/**
	 * Applies filter settings to the elements of this model.
	 * 
	 * @param filterStrings The list of element filters to apply. Can be
	 *        <code>null</code> or empty to disable filtering.
	 * @param acceptMatchingElements Set to <code>true</code> to accept elements
	 *        matching at least on of the element filters. Set to
	 *        <code>false</code> to accept only elements that matching none of
	 *        the element filters.
	 * @param ignoreRelationsToRejectedElements Set to <code>true</code> to
	 *        ignore links to rejected elements for metrics calculation, set to
	 *        <code>false</code> to include links to rejected elements for
	 *        metrics calculation.
	 */
	public void setFilter(String[] filterStrings,
			boolean acceptMatchingElements,
			boolean ignoreRelationsToRejectedElements) {

		if (filterStrings == null || filterStrings.length == 0) {
			// disable filtering; mark
			for (ModelElement elem : this) {
				elem.setLinksIgnored(false);
			}
			acceptedElementsByType = null; // means no filter is set
			return;
		}

		ElementFilters elementFilters = new ElementFilters(filterStrings);
		acceptedElementsByType = new HashMap<>(metaModel.getNumberOfTypes());

		// iterate over all elements and determine filter status
		for (MetaModelElement type : metaModel) {
			ArrayList<ModelElement> acceptedElementList = new ArrayList<>();
			acceptedElementsByType.put(type, acceptedElementList);

			for (ModelElement elem : elementsByType.get(type)) {

				boolean elementAccepted;
				if (elementFilters.matches(elem)) {
					elementAccepted = acceptMatchingElements;
				} else {
					elementAccepted = !acceptMatchingElements;
				}

				if (elementAccepted) {
					acceptedElementList.add(elem);
				}

				elem.setLinksIgnored(ignoreRelationsToRejectedElements
						&& !elementAccepted);
			}
		}
	}

	/**
	 * Returns the list of all elements of a given type. For example, a list of
	 * all classes, all packages, etc. This method ignores filter settings, and
	 * always returns all model elements.
	 * 
	 * @param type The type ID of the elements to return.
	 * @return A random access list of all elements of the specified type.
	 */
	public List<ModelElement> getElements(MetaModelElement type) {
		return elementsByType.get(type);
	}

	/**
	 * Returns the list of accepted elements of a given type. If the element
	 * filter is active, this method only returns the elements that should
	 * appear in the output data tables, as per the filter settings.
	 * 
	 * @param type The type of the elements to return.
	 * @return A random access list of all accepted elements of the specified
	 *         type.
	 */
	public List<ModelElement> getAcceptedElements(MetaModelElement type) {
		if (acceptedElementsByType == null) {
			// filters not set or disabled => return all elements.
			return elementsByType.get(type);
		}
		return acceptedElementsByType.get(type);
	}

	/**
	 * Returns an iterator over all model elements of the model, ignoring any
	 * filters settings.
	 * 
	 * @return Iterator over all model elements of all types.
	 */
	@Override
	public Iterator<ModelElement> iterator() {
		return new MappedCollectionsIterator<>(elementsByType);
	}

	/**
	 * Retrieves the metamodel on which this model is based.
	 * 
	 * @return This model's metamodel.
	 */
	public MetaModel getMetaModel() {
		return metaModel;
	}

	/**
	 * Adds a model element to this model.
	 * 
	 * @param el The model element to add.
	 */
	void addElement(ModelElement el) {
		elementsByType.get(el.getType()).add(el);
	}

	/**
	 * Removes a model element from this model.
	 * @param el The model element to remove.
	 */
	void removeElement(ModelElement el) {
		elementsByType.get(el.getType()).remove(el);
	}
}