Test coverage report for ModelElement.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.io.Serializable;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
/**
* Represents a model element of a model. A model element stores the values of
* its attributes (which includes outgoing references to other model elements),
* and keeps track of incoming references from other model elements.
*/
public class ModelElement {
/** Initial size of a hash map that probably will not carry many elements. */
private static final int INITIAL_FEW_ELEMENTS = 0;
/** Separator string used to build fully qualified element names. */
private static final String QUALIFIER_SEPARATOR = ".";
/** The type of this element. */
private MetaModelElement type;
/** The values of the attributes of this model element. */
private Object[] attributeValues;
/**
* Model elements have a running ID to preserve the order in which the
* elements are defined in the XMI source file. Elements with higher IDs
* were defined after elements with lower IDs.
*/
private int runningID;
/**
* Map of elements that have cross-reference attributes pointing to this
* element. Key is the name of the cross-reference attribute, value is the
* set of elements pointing to this element with that cross-reference
* attribute.
*/
private HashMap<String, Collection<ModelElement>> relations;
/**
* Indicates if cross-references to this element should be ignored according
* to the element filter settings.
*/
private boolean ignoreLinks = false;
/**
* Creates a new element.
*
* @param type The element type of the model element.
*/
public ModelElement(MetaModelElement type) {
this.type = type;
Collection<String> attributeList = type.getAttributeNames();
// Initialize the attribute values of the element to empty strings/sets
attributeValues = new Object[attributeList.size()];
for (String attrName : attributeList) {
if (!type.isSetAttribute(attrName)) {
attributeValues[type.getAttributeIndex(attrName)] = "";
}
}
}
/**
* Registers a model element that has a cross-reference attribute pointing
* to this model element.
*
* @param relationName Name of the cross-reference attribute.
* @param source The model element pointing to this element.
*/
void addRelation(String relationName, ModelElement source) {
if (relations == null) {
relations = new HashMap<>(INITIAL_FEW_ELEMENTS);
}
Collection<ModelElement> relset = relations.get(relationName);
if (relset == null) {
// no relations of relationName yet, install set
relset = new HashSet<>(INITIAL_FEW_ELEMENTS);
relations.put(relationName, relset);
}
relset.add(source);
}
/**
* Returns the set of model elements that point to this model element via a
* specified cross-reference attribute.
*
* @param relationName Name of the cross-reference attribute.
* @return The set of model elements that point to this model element with
* the specified cross-reference attribute. May be <code>null</code>
* if there are no such referencing model elements.
*/
public Collection<ModelElement> getRelations(String relationName) {
if (relations != null) {
return relations.get(relationName);
}
return null;
}
/**
* Sets the value of an attribute for this model element. If the attribute
* is multi-valued, the string will be added to the set.
*
* @param attrName Name of the attribute.
* @param value Value of the attribute.
*/
@SuppressWarnings("unchecked")
void setAttribute(String attrName, String value) {
int index = type.getAttributeIndex(attrName);
if (type.isSetAttribute(attrName)) {
@SuppressWarnings("rawtypes")
Collection set = ((Collection) attributeValues[index]);
if (set == null) {
set = new HashSet<>(INITIAL_FEW_ELEMENTS);
attributeValues[index] = set;
}
set.add(value);
} else {
attributeValues[index] = value;
}
}
/**
* Sets the target model element of a single-valued cross-reference
* attribute.
*
* @param attrName Name of the cross-reference attribute.
* @param target The referenced model element
*/
void setRefAttribute(String attrName, ModelElement target) {
attributeValues[type.getAttributeIndex(attrName)] = target;
}
/**
* Sets the value of a multi-valued attribute.
*
* @param attrName Name of the multi-valued attribute.
* @param set The collection of values for the attribute
*/
void setSetAttribute(String attrName, Collection<?> set) {
attributeValues[type.getAttributeIndex(attrName)] = set;
}
/**
* Retrieves the value of a single-valued data attribute for this model
* element.
*
* @param attrName Name of the attribute.
* @return Value of the attribute.
*/
public String getPlainAttribute(String attrName) {
return (String) attributeValues[type.getAttributeIndex(attrName)];
}
/**
* Retrieves the model element referenced by a single-valued cross-reference
* attribute.
*
* @param attrName Name of the cross-reference attribute.
* @return the referenced model element, or <code>null</code> if the
* reference is empty or the reference should be ignored as per
* filter settings.
*/
public ModelElement getRefAttribute(String attrName) {
ModelElement me = (ModelElement) attributeValues[type
.getAttributeIndex(attrName)];
if (me != null && me.ignoreLinks) {
return null;
}
return me;
}
/**
* Retrieves the set of values for a multi-valued attribute. For
* cross-reference attributes, this is a collection of model elements. For
* data attributes, the collection contains strings.
*
* @param attrName Name of the multi-valued attribute.
* @return Collection of model elements or strings stored by the attribute.
*/
public Collection<?> getSetAttribute(String attrName) {
Collection<?> result = (Collection<?>) attributeValues[type
.getAttributeIndex(attrName)];
return result == null ? Collections.EMPTY_SET : result;
}
/**
* Tests whether cross-references to this element should be ignored
* according to the element filter settings.
*
* @return <code>true</code> if the cross-references to this element should
* be ignored
*/
public boolean getLinksIgnored() {
return ignoreLinks;
}
/**
* Marks whether links to this element are to be ignored according to the
* filter settings or not.
*
* @param ignore <code>true</code> if the cross-references to this element
* are to be ignored, else <code>false</code>.
*/
void setLinksIgnored(boolean ignore) {
ignoreLinks = ignore;
}
/**
* Returns the metamodel element type of this model element.
*
* @return The type of this element
* */
public MetaModelElement getType() {
return type;
}
/**
* Sets the running ID of this model element.
*
* @param id The running ID
*/
void setRunningID(int id) {
runningID = id;
}
/**
* Gets the fully qualified name of this model element. This is the path to
* the model in the containment hierarchy, with the names of the owner
* elements separated by dots.
*
* @return Fully qualified name of this model element
*/
public String getFullName() {
StringBuilder sb = new StringBuilder();
ModelElement currentElement = this;
while (currentElement != null) {
String name = currentElement
.getPlainAttribute(MetaModelElement.NAME);
sb.insert(0, name);
currentElement = currentElement.getOwner();
if (currentElement != null) {
sb.insert(0, QUALIFIER_SEPARATOR);
}
}
return sb.toString();
}
/**
* Gets the owner of this model element.
*
* @return Owner of the model element, <code>null</code> for root model
* elements.
*/
public ModelElement getOwner() {
return (ModelElement) attributeValues[type
.getAttributeIndex(MetaModelElement.CONTEXT)];
}
/**
* Gets the model elements owned by this element.
*
* @return A collection of all that this model element owns.
*/
public Collection<ModelElement> getOwnedElements() {
return getRelations(MetaModelElement.CONTEXT);
}
/**
* Gets the XMI ID of this model element.
*
* @return XMI ID of the model element.
*/
public String getXMIID() {
return (String) attributeValues[type
.getAttributeIndex(MetaModelElement.ID)];
}
/**
* Gets the name of this model element.
*
* @return The unqualified name of the model element
*/
public String getName() {
return (String) attributeValues[type
.getAttributeIndex(MetaModelElement.NAME)];
}
/** Returns the XMI ID of the model element as its string representation. */
@Override
public String toString() {
return String.valueOf(getXMIID());
}
/**
* Returns a comparator to sort model elements by the order in which they
* are defined in the XMI file.
*
* @return Model element comparator
*/
public static Comparator<ModelElement> getComparator() {
return new ElementOrderComparator();
}
/**
* Compares running IDs of the model elements.
*/
private static class ElementOrderComparator implements
Comparator<ModelElement>, Serializable {
private static final long serialVersionUID = -7690366551032834958L;
@Override
public int compare(ModelElement e1, ModelElement e2) {
int id1 = e1.runningID;
int id2 = e2.runningID;
return (id1 < id2) ? -1 : ((id1 == id2) ? 0 : 1);
}
}
/**
* Merges the data from another model element with this one. Copies all
* attributes (except "id").
*
* @param other The element with which to merge this one.
*/
void merge(ModelElement other) {
this.ignoreLinks = other.ignoreLinks;
this.runningID = other.runningID;
// copy all attribute values except "id" from other element
MetaModelElement otherType = other.getType();
Collection<String> attributes = otherType.getAttributeNames();
for (String attr : attributes) {
if (!MetaModelElement.ID.equals(attr)) {
int thisIndex = type.getAttributeIndex(attr);
int otherIndex = otherType.getAttributeIndex(attr);
attributeValues[thisIndex] = other.attributeValues[otherIndex];
}
}
}
}