/*
* CCVisu is a tool for visual graph clustering
* and general force-directed graph layout.
* This file is part of CCVisu.
*
* Copyright (C) 2005-2012 Dirk Beyer
*
* CCVisu 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 2.1 of the License, or (at your option) any later version.
*
* CCVisu 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 CCVisu; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
* Please find the GNU Lesser General Public License in file
* license_lgpl.txt or http://www.gnu.org/licenses/lgpl.txt
*
* Dirk Beyer (firstname.lastname@uni-passau.de)
* University of Passau, Bavaria, Germany
*/
package org.sosy_lab.ccvisu.measuring;
import java.util.Collection;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.Map;
import org.sosy_lab.ccvisu.graph.GraphData;
import org.sosy_lab.ccvisu.graph.GraphEdge;
import org.sosy_lab.ccvisu.graph.GraphVertex;
import org.sosy_lab.ccvisu.graph.Group;
import org.sosy_lab.ccvisu.graph.Position;
import org.sosy_lab.ccvisu.graph.RadiusOfGroup;
public class FeatureDependency {
private static Map<GraphVertex, LinkedList<GraphVertex>> connectedVertices = new HashMap<GraphVertex, LinkedList<GraphVertex>>();
/**
* Calculate the Internal-ratio Feature Dependency (IFD)
* as described by Apel and Beyer
* in "Feature Cohesion in Software Product Lines: An Exploratory Study"
*/
public static int calculateIFD(Group featureGroup, GraphData graph) {
int refCount = 0, iec = 100;
float sum = 0.0f;
for(GraphVertex elementVertex : featureGroup.getNodes()) {
for(GraphEdge e : graph.getAdjacent(elementVertex, ".*")) {
//if((e.getU() != e.getV()) && featureGroup.getNodes().contains(e.getU()) && featureGroup.getNodes().contains(e.getV())) {
if (featureGroup.getNodes().contains(e.getSource()) && featureGroup.getNodes().contains(e.getTarget())) {
refCount++;
}
}
//sum += ((float)refCount) / ((float)(featureGroup.getNodes().size() - 1));
sum += ((float) refCount) / (float) featureGroup.getNodes().size();
refCount = 0;
}
if(featureGroup.getNodes().size() > 1) {
iec = Math.round(100.0f * (sum / (featureGroup.getNodes().size())));
}
return iec;
}
/**
* Calculate the Distance-based Internal-ratio Feature Dependency (IFD_W)
* as described by Apel and Beyer
* in "Feature Cohesion in Software Product Lines: An Exploratory Study"
*/
public static int calculateIFD_W(Group featureGroup, GraphData graph) {
int iec = 100;
float refValue = 0.0f, sum = 0.0f;
for(GraphVertex elementVertex : featureGroup.getNodes()) {
LinkedList<GraphVertex> connectedVertices = getConnectedVertices(elementVertex, ".*", graph);
Position minPos = Position.min(connectedVertices, false);
Position maxPos = Position.max(connectedVertices, false);
float diagonal = minPos.distanceTo(maxPos);
for(GraphEdge e : graph.getAdjacent(elementVertex, ".*")) {
//if((e.getU() != e.getV()) && featureGroup.getNodes().contains(e.getU()) && featureGroup.getNodes().contains(e.getV())) {
if(featureGroup.getNodes().contains(e.getSource()) && featureGroup.getNodes().contains(e.getTarget())) {
refValue = refValue + (1.0f - (e.getLength() / diagonal));
}
}
//sum += refValue / ((float)(featureGroup.getNodes().size() - 1));
sum += refValue / featureGroup.getNodes().size();
refValue = 0.0f;
}
if(featureGroup.getNodes().size() > 1) {
iec = Math.round(100.0f * (sum / (featureGroup.getNodes().size())));
}
return iec;
}
/**
* Calculate the External-ratio Feature Dependency (EFD)
* as described by Apel and Beyer
* in "Feature Cohesion in Software Product Lines: An Exploratory Study"
*/
public static int calculateEFD(Group featureGroup, GraphData graph) {
int countInner = 0, countOuter = 0, ifc = 100;
for(GraphVertex elementVertex : featureGroup) {
for (GraphEdge e : graph.getAdjacent(elementVertex, ".*")) {
if(!featureGroup.getNodes().contains(e.getSource()) || !featureGroup.getNodes().contains(e.getTarget())) {
countOuter++;
} else {
countInner++;
}
}
}
if((countInner + countOuter) > 0) {
ifc = Math.round(100.0f * ((float)countInner / (float)(countInner + countOuter)));
}
return ifc;
}
/**
* Calculate the Distance-based External-ratio Feature Dependency (EFD_W)
* as described by Apel and Beyer
* in "Feature Cohesion in Software Product Lines: An Exploratory Study"
*/
public static int calculateEFD_W(Group featureGroup, GraphData graph) {
int innerCount = 0, outerCount = 0, ifc = 100;
float innerValue = 0.0f, outerValue = 0.0f;
for(GraphVertex elementVertex : featureGroup) {
LinkedList<GraphVertex> connectedVertices = getConnectedVertices(elementVertex, ".*", graph);
Position minPos = Position.min(connectedVertices, false);
Position maxPos = Position.max(connectedVertices, false);
float diagonal = minPos.distanceTo(maxPos);
for (GraphEdge e : graph.getAdjacent(elementVertex, ".*")) {
if(!featureGroup.getNodes().contains(e.getSource()) || !featureGroup.getNodes().contains(e.getTarget())) {
outerValue = outerValue + (1.0f - (e.getLength() / diagonal));
outerCount++;
} else if(e.getSource() == e.getTarget()) {
innerValue = innerValue + 1.0f;
innerCount++;
} else {
innerValue = innerValue + (1.0f - (e.getLength() / diagonal));
innerCount++;
}
}
}
if((innerCount + outerCount) > 0) {
ifc = Math.round(100.0f * (innerValue / (innerValue + outerValue)));
}
return ifc;
}
/**
* Calculate the average radius.
*
* @param featureGroup
* @param graph
* @return
*/
public static int calculateAR(Group featureGroup, GraphData graph) {
if(featureGroup.getNodes().size() == 0) {
return 100;
} else {
return averageRadius(featureGroup, graph);
}
}
public static int calculateMR(Group featureGroup, GraphData graph) {
if(featureGroup.getNodes().size() == 0) {
return 100;
} else {
return maximumRadius(featureGroup, graph);
}
}
private static int averageRadius(Group featureGroup, GraphData graph) {
int numAR = 0;
int sumAR = 0;
LinkedList<GraphVertex> visitedNodes = new LinkedList<GraphVertex>();
for(GraphVertex cur : featureGroup.getNodes()) {
LinkedList<GraphVertex> connectedNodes = getConnectedVertices(cur, ".*", graph);
LinkedList<GraphVertex> connectedFeatureNodes = new LinkedList<GraphVertex>();
for(GraphVertex aux : connectedNodes) {
if(featureGroup.getNodes().contains(aux)) {
connectedFeatureNodes.add(aux);
}
}
if(!visitedNodes.contains(cur)) {
if(connectedFeatureNodes.size() > 1) {
int nbr = connectedFeatureNodes.size();
Position baryCenter = new Position();
for (GraphVertex lCurrVertex : connectedFeatureNodes) {
baryCenter.add(lCurrVertex.getPosition());
}
baryCenter.div(nbr);
float averageRadius = 0;
for (GraphVertex v : connectedFeatureNodes) {
float radius = v.distanceTo(baryCenter);
averageRadius += radius;
}
averageRadius /= nbr;
Position minPos = Position.min(connectedNodes, false);
Position maxPos = Position.max(connectedNodes, false);
float diagonal = minPos.distanceTo(maxPos);
sumAR += Math.round(100.0f * (1.0f - (averageRadius / diagonal)));
numAR++;
visitedNodes.addAll(connectedFeatureNodes);
} else {
sumAR += 100;
numAR++;
}
}
}
return Math.round(sumAR / numAR);
}
private static int maximumRadius(Group featureGroup, GraphData graph) {
int numMR = 0;
int sumMR = 0;
LinkedList<GraphVertex> visitedNodes = new LinkedList<GraphVertex>();
for(GraphVertex cur : featureGroup.getNodes()) {
LinkedList<GraphVertex> connectedNodes = getConnectedVertices(cur, ".*", graph);
LinkedList<GraphVertex> connectedFeatureNodes = new LinkedList<GraphVertex>();
for(GraphVertex aux : connectedNodes) {
if(featureGroup.getNodes().contains(aux)) {
connectedFeatureNodes.add(aux);
}
}
if(!visitedNodes.contains(cur)) {
if(connectedFeatureNodes.size() > 1) {
int nbr = connectedFeatureNodes.size();
Position baryCenter = new Position();
for (GraphVertex lCurrVertex : connectedFeatureNodes) {
baryCenter.add(lCurrVertex.getPosition());
}
baryCenter.div(nbr);
float maxRadius = 0;
for (GraphVertex v : connectedFeatureNodes) {
float radius = v.distanceTo(baryCenter);
maxRadius = Math.max(maxRadius, radius);
}
Position minPos = Position.min(connectedNodes, false);
Position maxPos = Position.max(connectedNodes, false);
float diagonal = minPos.distanceTo(maxPos);
sumMR += Math.round(100.0f * (1.0f - (maxRadius / diagonal)));
numMR++;
visitedNodes.addAll(connectedFeatureNodes);
} else {
sumMR += 100;
numMR++;
}
}
}
return Math.round(sumMR / numMR);
}
private static LinkedList<GraphVertex> getConnectedVertices(GraphVertex current, String relName, GraphData graph) {
if(connectedVertices.containsKey(current)) {
return connectedVertices.get(current);
} else {
LinkedList<GraphVertex> res = new LinkedList<GraphVertex>();
getConnectedVerticesInner(current, relName, res, graph);
return res;
}
}
private static void getConnectedVerticesInner(GraphVertex current, String relName, LinkedList<GraphVertex> res, GraphData graph) {
res.add(current);
connectedVertices.put(current, res);
for(GraphEdge edge : graph.getAdjacentUndirected(current, relName)) {
if(edge.getSource() == current && !res.contains(edge.getTarget())) {
getConnectedVerticesInner(edge.getTarget(), relName, res, graph);
} else if(edge.getTarget() == current && !res.contains(edge.getSource())) {
getConnectedVerticesInner(edge.getSource(), relName, res, graph);
}
}
}
private static boolean belongsToFeature(GraphVertex element, GraphVertex feature, GraphData graph) {
boolean res = false;
Collection<GraphEdge> adjacentEdgeList = graph.getAdjacent(feature, "Introduces");
for (GraphEdge adjEdge : adjacentEdgeList) {
if(adjEdge.getTarget() == element || adjEdge.getSource() == element) {
res = true;
}
}
return res;
}
/////////////////////////////////////////////////////////////////////////////////////
// old measures (not included in the paper)
/////////////////////////////////////////////////////////////////////////////////////
public static int pdPlainWeighted(Group featureGroup, GraphData graph) {
int count = 0;
float value = 0.0f;
for(GraphVertex elementVertex : featureGroup) {
LinkedList<GraphVertex> connectedVertices = getConnectedVertices(elementVertex, ".*", graph);
Position minPos = Position.min(connectedVertices, false);
Position maxPos = Position.max(connectedVertices, false);
float diagonal = minPos.distanceTo(maxPos);
for (GraphEdge e : graph.getAdjacent(elementVertex, ".*")) {
if(featureGroup.getNodes().contains(e.getSource()) && featureGroup.getNodes().contains(e.getTarget())) {
if(e.getSource() == e.getTarget()) {
value = value + 1.0f;
} else {
value = value + (1.0f - (e.getLength() / diagonal));
}
count++;
}
}
}
if(count > 0) {
return Math.round(100.0f * (value / count));
} else {
return 100;
}
}
public static int iecExtended(GraphVertex featureVertex, GraphData graph) {
int refCount = 0, iec = 100;
float sum = 0.0f;
int num = graph.getAdjacent(featureVertex, "Introduces").size();
for(GraphEdge intrEdge : graph.getAdjacent(featureVertex, "Introduces")) {
GraphVertex elementVertex;
if(intrEdge.getSource() == featureVertex) {
elementVertex = intrEdge.getTarget();
} else {
elementVertex = intrEdge.getSource();
}
for(GraphEdge e : graph.getAdjacent(elementVertex, ".*")) {
if((e.getSource() != e.getTarget())
&& belongsToFeature(e.getSource(), featureVertex, graph)
&& belongsToFeature(e.getTarget(), featureVertex, graph)) {
refCount++;
}
}
sum += ((float)refCount) / ((float)(num - 1));
refCount = 0;
}
if(num > 1) {
iec = Math.round(100.0f * (sum / num));
}
return iec;
}
public static int iecExtendedWeighted(GraphVertex featureVertex, GraphData graph) {
int iec = 100;
float refValue = 0.0f, sum = 0.0f;
int num = graph.getAdjacent(featureVertex, "Introduces").size();
for(GraphEdge intrEdge : graph.getAdjacent(featureVertex, "Introduces")) {
GraphVertex elementVertex;
if(intrEdge.getSource() == featureVertex) {
elementVertex = intrEdge.getTarget();
} else {
elementVertex = intrEdge.getSource();
}
LinkedList<GraphVertex> connectedVertices = getConnectedVertices(elementVertex, ".*", graph);
Position minPos = Position.min(connectedVertices, false);
Position maxPos = Position.max(connectedVertices, false);
float diagonal = minPos.distanceTo(maxPos);
for(GraphEdge e : graph.getAdjacent(elementVertex, ".*")) {
if((e.getSource() != e.getTarget())
&& belongsToFeature(e.getSource(), featureVertex, graph)
&& belongsToFeature(e.getTarget(), featureVertex, graph)) {
refValue = refValue + (1.0f - (e.getLength() / diagonal));
}
}
sum += (refValue) / ((num - 1));
refValue = 0;
}
if(num > 1) {
iec = Math.round(100.0f * (sum / num));
}
return iec;
}
public static int ifcExtended(GraphVertex featureVertex, GraphData graph) {
int countInner = 0, countOuter = 0, ifc = 100;
for(GraphEdge intrEdge : graph.getAdjacent(featureVertex, "Introduces")) {
GraphVertex elementVertex;
if(intrEdge.getSource() == featureVertex) {
elementVertex = intrEdge.getTarget();
} else {
elementVertex = intrEdge.getSource();
}
for (GraphEdge e : graph.getAdjacent(elementVertex, ".*")) {
if(!e.getRelName().equals("Introduces")) {
if(!belongsToFeature(e.getSource(), featureVertex, graph)
|| !belongsToFeature(e.getTarget(), featureVertex, graph)) {
countOuter++;
} else {
countInner++;
}
}
}
}
if((countInner + countOuter) > 0) {
ifc = Math.round(100.0f * ((float)countInner / (float)(countInner + countOuter)));
}
return ifc;
}
public static int ifcExtendedWeighted(GraphVertex featureVertex, GraphData graph) {
int innerCount = 0, outerCount = 0, ifc = 100;
float innerValue = 0.0f, outerValue = 0.0f;
for(GraphEdge intrEdge : graph.getAdjacent(featureVertex, "Introduces")) {
GraphVertex elementVertex;
if(intrEdge.getSource() == featureVertex) {
elementVertex = intrEdge.getTarget();
} else {
elementVertex = intrEdge.getSource();
}
LinkedList<GraphVertex> connectedVertices = getConnectedVertices(elementVertex, ".*", graph);
Position minPos = Position.min(connectedVertices, false);
Position maxPos = Position.max(connectedVertices, false);
float diagonal = minPos.distanceTo(maxPos);
for (GraphEdge e : graph.getAdjacent(elementVertex, ".*")) {
if(!e.getRelName().equals("Introduces")) {
if(!belongsToFeature(e.getSource(), featureVertex, graph)
|| !belongsToFeature(e.getTarget(), featureVertex, graph)) {
outerValue = outerValue + (1.0f - (e.getLength() / diagonal));
outerCount++;
} else if(e.getSource() == e.getTarget()) {
innerValue = innerValue + 1.0f;
innerCount++;
} else {
innerValue = innerValue + (1.0f - (e.getLength() / diagonal));
innerCount++;
}
}
}
}
if((innerCount + outerCount) > 0) {
ifc = Math.round(100.0f * (innerValue / (innerValue + outerValue)));
}
return ifc;
}
public static int pdExtendedWeighted(GraphVertex featureVertex, GraphData graph) {
int count = 0;
float value = 0.0f;
for(GraphEdge intrEdge : graph.getAdjacent(featureVertex, "Introduces")) {
GraphVertex elementVertex;
if(intrEdge.getSource() == featureVertex) {
elementVertex = intrEdge.getTarget();
} else {
elementVertex = intrEdge.getSource();
}
LinkedList<GraphVertex> connectedVertices = getConnectedVertices(elementVertex, ".*", graph);
Position minPos = Position.min(connectedVertices, false);
Position maxPos = Position.max(connectedVertices, false);
float diagonal = minPos.distanceTo(maxPos);
for (GraphEdge e : graph.getAdjacent(elementVertex, ".*")) {
if(!e.getRelName().equals("Introduces")) {
if(belongsToFeature(e.getSource(), featureVertex, graph)
&& belongsToFeature(e.getTarget(), featureVertex, graph)) {
if(e.getSource() == e.getTarget()) {
value = value + 1.0f;
} else {
value = value + (1.0f - (e.getLength() / diagonal));
}
count++;
}
}
}
}
if(count > 0) {
return Math.round(100.0f * (value / count));
} else {
return 100;
}
}
public static int arExtended(GraphVertex featureVertex, GraphData graph) {
LinkedList<GraphVertex> elements = new LinkedList<GraphVertex>();
for(GraphEdge intrEdge : graph.getAdjacent(featureVertex, "Introduces")) {
GraphVertex elementVertex;
if(intrEdge.getSource() == featureVertex) {
elementVertex = intrEdge.getTarget();
} else {
elementVertex = intrEdge.getSource();
}
elements.add(elementVertex);
}
if(elements.size() == 0) {
return 100;
} else {
Position minPos = Position.min(graph.getVertices(), false);
Position maxPos = Position.max(graph.getVertices(), false);
float diagonal = minPos.distanceTo(maxPos);
return Math.round(100.0f * (1.0f - (new RadiusOfGroup(elements).getAverageRadius() / diagonal)));
}
}
public static int mrExtended(GraphVertex featureVertex, GraphData graph) {
LinkedList<GraphVertex> elements = new LinkedList<GraphVertex>();
for(GraphEdge intrEdge : graph.getAdjacent(featureVertex, "Introduces")) {
GraphVertex elementVertex;
if(intrEdge.getSource() == featureVertex) {
elementVertex = intrEdge.getTarget();
} else {
elementVertex = intrEdge.getSource();
}
elements.add(elementVertex);
}
if(elements.size() == 0) {
return 100;
} else {
Position minPos = Position.min(graph.getVertices(), false);
Position maxPos = Position.max(graph.getVertices(), false);
float diagonal = minPos.distanceTo(maxPos);
return Math.round(100.0f * (1.0f - (new RadiusOfGroup(elements).getMaxRadius() / diagonal)));
}
}
}