package br.ufmg.dcc.labsoft.aserg.modularitycheck.enhancements.processing.data.clustering.chameleon; import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import data.handler.CarryFileMemory; import br.ufmg.dcc.labsoft.aserg.modularitycheck.bugparser.parser.Parser; import br.ufmg.dcc.labsoft.aserg.modularitycheck.enhancements.metrics.Focus; import br.ufmg.dcc.labsoft.aserg.modularitycheck.enhancements.properties.util.Properties; import br.ufmg.dcc.labsoft.aserg.modularitycheck.enhancements.properties.util.Utils; public class CoChangeGraph { private static List<int[]> edges; private static Map<Integer, List<int[]>> packageGraph; private static int edgeNumber; private static int countVertexes; protected static List<String> keys; public static List<String> list; private static List<String> allPackages; private static ClusterMetrics[] clusterMetrics; public static final double DENSITY = 0.5; private static final double SUPPORT = 2; public static final int MIN_SIZE = 4; private static final String R_CLASS_FILE = "packages.rlabel"; public static final String CLUSTER_FILE = "sparse.graph.clustering."; // chameleon's output /**** class interface * @throws IOException ************/ public static void retrieveGraph(String path, int type, boolean preProcessed) throws IOException { Commits commit = null; switch (type) { case Parser.MANAGER_GIT: commit = new GITCommit(); case Parser.MANAGER_SVN: commit = new SVNCommit(); default: break; } try { commit.readCommits(path); } catch (IOException e) { e.printStackTrace(); } List<String> packages = commit.getPackages(); System.out.println("Creating co-change Graph"); upDateAttributes(); createAdjacencyList(commit.getMapping(), packages); System.out.println("Pruning edges with small weight"); packages = preProcessGraph(packages); System.out.println("Detecting connected components"); packages = detectGraphComponents(packages); System.out.println("Updating indexes in hash table"); clearPackages(packages); updateAdjacencyList(packages); if (!preProcessed) { preProcessPhase(packages); System.out.println("Co-change graph created"); allPackages = new ArrayList<String>(); allPackages.addAll(packages); }else{ detectPatterns(); } } private static void updateAdjacencyList(List<String> packages) { List<String> packageTemp = new ArrayList<String>(); Map<Integer, List<int[]>> packageMapTemp = new HashMap<Integer, List<int[]>>(); Set<Integer> keys = packageGraph.keySet(); String className = ""; String keyName = ""; List<int[]> adjacents = null; List<int[]> tempAdjacents = null; for(Integer key : keys){ keyName = packages.get(key); if(!packageTemp.contains(keyName)) packageTemp.add(keyName); adjacents = packageGraph.get(key); tempAdjacents = new ArrayList<int[]>(); for(int[] adjacent : adjacents){ className = packages.get(adjacent[0]); if(!packageTemp.contains(className)) packageTemp.add(className); tempAdjacents.add(new int[]{packageTemp.indexOf(className), adjacent[1]}); } packageMapTemp.put(packageTemp.indexOf(keyName), tempAdjacents); } packageGraph.clear(); packageGraph.putAll(packageMapTemp); packageMapTemp = null; packages.clear(); packages.addAll(packageTemp); packageTemp = null; } private static void clearPackages(List<String> packages) { int index = -1; for(String p : packages){ index = packages.indexOf(p); if(!p.isEmpty() && !packageGraph.containsKey(index)) packages.set(index, ""); } } /*** * After performing chameleon the co-change patterns are detected from clusters * @throws IOException * @throws FileNotFoundException */ public static void detectPatterns() throws FileNotFoundException, IOException { Map<String, List<String>> hash = readClusterFile(); clusterMetrics = new ClusterMetrics[keys.size()]; Focus.measureFocus(hash, keys, clusterMetrics); calculateClusterDensity(allPackages, hash); } public static int getCountVertexes() { return countVertexes; } public static boolean statusCoChangeGraph(){ return (packageGraph!=null && packageGraph.size() > 9); } /********* private methods *****************/ /****** second phase **********/ public static Map<String, List<String>> readClusterFile() throws FileNotFoundException, IOException { Map<String, List<String>> hash = new HashMap<String, List<String>>(); File[] files = new File(Properties.getResultPath()).listFiles(); int k; for (k = 0; k < files.length; k++) { if (files[k].getName().startsWith(CLUSTER_FILE)) break; } String[] openedFile = new CarryFileMemory(Properties.getResultPath() + files[k].getName()).carryCompleteFile(); List<String> temp = null; for (int i = 0; i < allPackages.size(); i++) { String line = openedFile[i].trim(); if (!line.isEmpty() && !line.equals("-1")) { if (!hash.containsKey(line)) temp = new ArrayList<String>(); else temp = hash.get(line); temp.add(allPackages.get(i)); hash.put(line, temp); } } saveClusters(hash, Properties.CLUSTER_GLUTO); return hash; } public static void saveClusters(Map<String, List<String>> hash, String clusterFile) throws IOException { Iterator<String> keySet = hash.keySet().iterator(); list = new ArrayList<String>(); while (keySet.hasNext()) { list.add(keySet.next()); } Collections.sort(list); keySet = list.iterator(); ArrayList<String> clusters = new ArrayList<String>(); keys = new ArrayList<String>(); while (keySet.hasNext()) { StringBuilder cache = new StringBuilder(); String key = keySet.next(); List<String> packages = hash.get(key); if (packages.size() >= MIN_SIZE) { keys.add(key); for (String pkg : packages) cache.append(pkg).append(Properties.COMMA); cache.deleteCharAt(cache.lastIndexOf(Properties.COMMA)); clusters.add(cache.toString()); } } StringBuilder cache = new StringBuilder(); int index = 0; for (String value : clusters) { if (value != null) { cache.append(value).append(Properties.NEW_LINE); clusters.set(index, null); } index++; } clusters = null; Utils.writeFile(cache.toString(), Properties.getResultPath() + clusterFile); } public static void calculateClusterDensity(List<String> packages, Map<String, List<String>> hash) { StringBuilder densityCache = new StringBuilder(); StringBuilder weightCache = new StringBuilder(); System.out.println("Cluster Density"); int clusterId = 0; int numberOfEdges = 0; double sumOfEdges; double weight = 0; double density = 0; int completeGraph = 0; Iterator<String> iterator = keys.iterator(); dropDuplicates(); while (iterator.hasNext()) {//for each cluster String key = iterator.next(); List<String> classes = hash.get(key); int[] indexes = new int[classes.size()]; for (int i = 0; i < classes.size(); i++) { indexes[i] = packages.indexOf(classes.get(i)); } numberOfEdges = 0; sumOfEdges = 0; for(int[] edge : edges){ if(contains(indexes, edge[0]) && contains(indexes, edge[1])) { numberOfEdges++; int value = calculateClusterWeight(clusterId, edge); if(value != -1 ) sumOfEdges += value; else System.out.println("Error: " + edge.toString()); } } completeGraph = (classes.size() * (classes.size() - 1)) / 2; density = (double) numberOfEdges / completeGraph; weight = sumOfEdges / (double) numberOfEdges; clusterMetrics[clusterId].setDensity(density); clusterMetrics[clusterId].setSize(classes.size()); clusterMetrics[clusterId].setWeight(weight); densityCache.append(String.valueOf(density)).append( Properties.NEW_LINE); weightCache.append(String.valueOf(weight)).append( Properties.NEW_LINE); clusterId++; }//end try { Utils.writeFile(densityCache.toString(), Properties.getResultPath() + Properties.DENSITY_DATA); Utils.writeFile(densityCache.toString(), Properties.getResultPath() + Properties.WEIGHT_DATA); } catch (IOException e) { e.printStackTrace(); } } private static void dropDuplicates() { List<int[]> edge = new ArrayList<int[]>(); Set<Integer> keys = packageGraph.keySet(); List<int[]> adjacents = null; List<Integer> analyzedKeys = new ArrayList<Integer>(); for(int key : keys) { adjacents = packageGraph.get(key); analyzedKeys.add(key); for(int[] vj : adjacents) if(!analyzedKeys.contains(vj[0])) edge.add(new int[]{key, vj[0]}); } edges.clear(); edges.addAll(edge); } /*** * * @param indexes * @param v * @return */ private static boolean contains(int[] indexes, int v) { for(int vertice : indexes){ if(vertice == v) { return true; } } return false; } /*** * * @param clusterId * @param edge */ public static int calculateClusterWeight(int clusterId, int[] edge) { List<int[]> adjacents = packageGraph.get(edge[0]); for(int[] v : adjacents) if(v[0] == edge[1]) return v[1]; return -1; } /*** First phase *******/ /*** * Prepare the base to run chameleon * @param packages * @throws IOException */ private static void preProcessPhase(List<String> packages) throws IOException { createFormatClutoAdjacencyList(packages); saveRClassFile(packages); countVertexes = packageGraph.size(); } private static void saveRClassFile(List<String> packages) throws IOException { StringBuilder cache = new StringBuilder(); for (String pkg : packages) cache.append(pkg).append(Properties.NEW_LINE); Utils.writeFile(cache.toString(), Properties.getResultPath() + R_CLASS_FILE); } public static void createFormatClutoAdjacencyList(List<String> packages) throws IOException { StringBuilder cache = new StringBuilder(); StringBuilder temp = new StringBuilder(); List<int[]> edges = null; edgeNumber = 0; countNumberEdges(); for(int key = 0; key < packages.size(); key++){ edges = packageGraph.get(key); for(int[] adjacents : edges) temp.append(String.valueOf(adjacents[0]+1) + Properties.BLANK + String.valueOf((double)adjacents[1])+ Properties.BLANK); temp.deleteCharAt(temp.lastIndexOf(Properties.BLANK)); temp.append(Properties.NEW_LINE); } cache.append(String.valueOf(packages.size()) + Properties.BLANK + String.valueOf(edgeNumber) + Properties.NEW_LINE); cache.append(temp.toString()); temp = null; Utils.writeFile(cache.toString(), Properties.getResultPath() + Properties.CLUTO_GRAPH); } private static void countNumberEdges() { edges = getEdges(packageGraph.size()); Collections.sort(edges, new Comparator<int[]>() { public int compare(int[] vi, int[] vj) { return new Integer(vi[1]).compareTo(new Integer(vj[1])); } }); for(int[] edge : edges) Arrays.sort(edge); Set<int[]> graphEdges = new HashSet<int[]>(edges); edges.clear(); edges.addAll(graphEdges); edgeNumber = edges.size(); graphEdges = null; } private static void createAdjacencyList(Map<Integer, Integer[]> packageMapping, List<String> packages) { Iterator<Integer> keySet = packageMapping.keySet().iterator(); while (keySet.hasNext()) { int key = keySet.next(); Integer[] indexes = packageMapping.get(key); if (indexes.length > 1) { for (int i = 0; i < indexes.length; i++) { String temp = packages.get(indexes[i]); if (temp.startsWith(Properties.BAR)) temp = temp.substring(1); for (int j = i + 1; j < indexes.length; j++) { String temp2 = packages.get(indexes[j]); if (temp2.startsWith(Properties.BAR)) temp2 = temp2.substring(1); if(packageGraph.containsKey(indexes[i])) insertAdjacentVertice(indexes[i], indexes[j]); else insertNewKey(indexes[i], indexes[j]); if(packageGraph.containsKey(indexes[j])) insertAdjacentVertice(indexes[j], indexes[i]); else insertNewKey(indexes[j], indexes[i]); } } } } System.out.println("Tamanho do co-change graph:"+packageGraph.size()); } private static void insertNewKey(Integer vi, Integer vj) { List<int[]> adjacents = new ArrayList<int[]>(); adjacents.add(new int[]{vj, 1}); packageGraph.put(vi, adjacents); } private static void insertAdjacentVertice(Integer key, int vj) { List<int[]> adjacents = null; int[] adjacentVertex = null; int index = -1; adjacents = packageGraph.get(key); index = searchAdjacentVertex(vj, adjacents); if(index == -1) { adjacentVertex = new int[]{vj, 1}; adjacents.add(adjacentVertex); } else { adjacentVertex = adjacents.get(index); adjacentVertex[1] += 1.0; adjacents.set(index, adjacentVertex); } packageGraph.put(key, adjacents); } private static int searchAdjacentVertex(Integer vj, List<int[]> adjacents) { for(int i = 0; i < adjacents.size(); i++) { if(adjacents.get(i)[0] == vj) return i; } return -1; } /*** * Prune edges with small weights and update the co-change graph * @param packages * @return */ private static List<String> preProcessGraph( List<String> packages) { System.out.println("Number of vertices: " + packageGraph.size()); List<Integer> removedIndexes = pruneGraph(); System.out.println("Number of removed vertices"); packages = updateClasses(removedIndexes, packages); return packages; } /*** * update the matrix packageGraph and the list packages * * @param removedIndexes * @param packages * @return */ private static List<String> updateClasses(List<Integer> removedIndexes, List<String> packages) { for(int remove : removedIndexes) { packageGraph.remove(remove); packages.set(remove, ""); } List<Integer> newRemoving = new ArrayList<Integer>(); List<int[]> adjacent = null; List<int[]> updatedEdges = null; Set<Integer> keySet = packageGraph.keySet(); for(int key : keySet){ adjacent = packageGraph.get(key); updatedEdges = new ArrayList<int[]>(); for(int[] vj : adjacent) if(!removedIndexes.contains(vj[0])) updatedEdges.add(vj); if(updatedEdges.size() > 0) packageGraph.put(key, updatedEdges); else newRemoving.add(key); } for(int key : newRemoving) packageGraph.remove(key); return packages; } private static List<Integer> pruneGraph() { List<Integer> removedVertices = new ArrayList<Integer>(); Set<Integer> keySet = packageGraph.keySet(); List<int[]> adjacents = null; List<int[]> newAdjacents = null; for(Integer key : keySet){ adjacents = packageGraph.get(key); newAdjacents = new ArrayList<int[]>(); if(adjacents.size() == 0) removedVertices.add(key); else{ for(int[] temp : adjacents){ if(temp[1] >= SUPPORT) newAdjacents.add(temp); } if(newAdjacents.size() > 0) packageGraph.put(key, newAdjacents); else removedVertices.add(key); } } return removedVertices; } private static List<String> detectGraphComponents(List<String> packages) throws IOException { List<Integer> vertexes = new ArrayList<Integer>(packageGraph.keySet()); List<Integer> adjacentVertex = new ArrayList<Integer>(); List<Integer> removeKeys = new ArrayList<Integer>(); int nAdjacency = 0; Collections.sort(vertexes); while(!vertexes.isEmpty()){ nAdjacency = packageGraph.get(vertexes.get(0)).size(); if((nAdjacency + 1) < MIN_SIZE){ adjacentVertex.add(vertexes.get(0)); getVerticesAdjacents(vertexes.get(0), adjacentVertex, 1); if(adjacentVertex.size() < MIN_SIZE) { removeKeys.addAll(adjacentVertex); } vertexes.removeAll(adjacentVertex); }else vertexes.remove(0); nAdjacency = 0; adjacentVertex = new ArrayList<Integer>(); } packages = posProcessGraph(packages, removeKeys); return packages; } private static void getVerticesAdjacents(Integer vertex, List<Integer> adjacentVertex, int index) { List<int[]> adjacents = packageGraph.get(vertex); for(int i = 0; i < adjacents.size(); i++) if(!adjacentVertex.contains(adjacents.get(i)[0])) adjacentVertex.add(adjacents.get(i)[0]); if(index < adjacentVertex.size() && adjacentVertex.size() < MIN_SIZE) getVerticesAdjacents(adjacentVertex.get(index), adjacentVertex, index+1); } private static List<String> posProcessGraph(List<String> packages, List<Integer> removeKeys) { for(Integer key : removeKeys) { packageGraph.remove(key); packages.set(key, ""); } return packages; } /*** * Retrieve graph's edges * @param size * @return */ private static List<int[]> getEdges(int size){ List<int[]> edges = new ArrayList<int[]>(); Set<Integer> keys = packageGraph.keySet(); List<int[]> adjacents = null; for(int key : keys) { adjacents = packageGraph.get(key); for(int[] vj : adjacents) edges.add(new int[]{key, vj[0]}); } return edges; } public static ClusterMetrics[] getClusterMetrics() { return clusterMetrics; } public static void dispose() { packageGraph = null; clusterMetrics = null; keys = list = allPackages = null; edges = null; countVertexes = edgeNumber = 0; } private static void upDateAttributes() { packageGraph = new HashMap<Integer, List<int[]>>(); } }