package br.ufmg.dcc.labsoft.aserg.modularitycheck.bugparser.model.bug.linker; import java.io.IOException; import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.StringTokenizer; import org.jdom.JDOMException; import br.ufmg.dcc.labsoft.aserg.modularitycheck.bugparser.model.bug.Bug; import br.ufmg.dcc.labsoft.aserg.modularitycheck.bugparser.model.bug.parser.BugTrackerParser; import br.ufmg.dcc.labsoft.aserg.modularitycheck.bugparser.model.source.Commit; import br.ufmg.dcc.labsoft.aserg.modularitycheck.bugparser.model.source.VersionControlManager; public class BugLinker { private VersionControlManager version_control_manager; private BugTrackerParser bug_tracker_parser; public BugLinker(VersionControlManager vcparser, BugTrackerParser btparser) { version_control_manager = vcparser; bug_tracker_parser = btparser; } public Set<Bug> getLinkedBugs() throws JDOMException, IOException { Set<Bug> linkedBugs = null; List<Commit> commits = version_control_manager.getCommits(); if(commits.size() > 0) { linkedBugs = new HashSet<Bug>(); System.out.println("Number of Commits = " + commits.size()); System.out.println("Linking bugs..."); List<Bug> bugs = bug_tracker_parser.getBugs(); int nbugs = bugs.size(); System.out.println("Number of Bugs = " + nbugs); removeNotAssociatedIssues(commits, bugs); linkedBugs = removeTangledCommits(commits, bugs); } return linkedBugs; } /*** * Removing commits associated to multiple maintenance issues. Phase #4 * * @param commits * @param bugs * @return */ private Set<Bug> removeTangledCommits(List<Commit> commits, List<Bug> bugs) { Set<Bug> linkedBugs = new HashSet<Bug>(); Set<Bug> tempLinkedBugs = null; for (Commit commit : commits) { tempLinkedBugs = new HashSet<Bug>(); String msg = commit.getMsg(); if (msg == null) msg = ""; for (Bug bug : bugs) { String bugId = bug.getId(); if (hasID(msg, bugId)) { for (String file : commit.getFiles()) { if (br.ufmg.dcc.labsoft.aserg.modularitycheck.bugparser.util.Utils .isValid(file)) bug.addFile(file); } if (bug.getModifiedFiles().size() > 0) tempLinkedBugs.add(bug); } } if (tempLinkedBugs.size() == 1) linkedBugs.addAll(tempLinkedBugs); } System.out .println("Phase #4 - Number of Commits: " + linkedBugs.size()); return linkedBugs; } /*** * When there are multiple commits referring to the same Issue-ID, we merge * all of them�including the changed classes�in a single commit. Phase * #3 * * @param commits * @param bugs */ public void mergeCommits(List<Commit> commits, List<Bug> bugs) { int counter = 0; for (Commit commit : commits) { String msg = commit.getMsg(); if (msg == null) msg = ""; for (Bug bug : bugs) { String bugId = bug.getId(); if (hasID(msg, bugId)) { for (String file : commit.getFiles()) { if (br.ufmg.dcc.labsoft.aserg.modularitycheck.bugparser.util.Utils .isValid(file)) bug.addFile(file); } } } } for (Bug bug : bugs) if (bug.getModifiedFiles().size() > 0) counter++; System.out.println("Phase #3 - Number of Commits: " + counter); } /*** * Remove commits that change only test classes or do not change class files * - Phase #2 * * @param commits * @param bugs */ public void removeTestAndNotChangingClasses(List<Commit> commits, List<Bug> bugs) { int counter = 0; int unitary = 0; for (Commit commit : commits) { String msg = commit.getMsg(); boolean committed = false; if (msg == null) msg = ""; for (Bug bug : bugs) { String bugId = bug.getId(); if (hasID(msg, bugId)) { for (String file : commit.getFiles()) { if (br.ufmg.dcc.labsoft.aserg.modularitycheck.bugparser.util.Utils .isValid(file)) bug.addFile(file); } if (bug.getModifiedFiles().size() > 0) { counter++; committed = true; } } if (committed) break; } } for (Bug bug : bugs) { if (bug.getModifiedFiles().size() == 1) unitary++; } System.out.println("Phase #2 - Number of Commits: " + counter); System.out.println("Phase #2 - Number of Unitary Commits: " + unitary); } /*** * Count the number of mapped commits - Phase #1 * * @param commits * @param bugs */ public void removeNotAssociatedIssues(List<Commit> commits, List<Bug> bugs) { int counter = 0; int unitary = 0; for (Commit commit : commits) { String msg = commit.getMsg(); boolean commited = false; if (msg == null) msg = ""; for (Bug bug : bugs) { String bugId = bug.getId(); if (hasID(msg, bugId)) { if (!commited) { commited = true; counter++; } } if (commited) break; } if (!commited) { if (commit.getFiles().size() == 1) unitary++; } } System.out.println("Phase #1 - Number of Commits: " + counter); System.out.println("Phase #1 - Number of Unitary Commits: " + unitary); } private boolean hasID(String phrase, String word) { return hasID(phrase, word, null); } private boolean hasID(String phrase, String word, String extra_delims) { String default_delim = " .;,_[]{:}='()#\"%*|?~!"; if (extra_delims != null) default_delim += extra_delims; StringTokenizer st = new StringTokenizer(phrase, default_delim); while (st.hasMoreTokens()) { String token = st.nextToken(); if (token.equals(word)) { return true; } } return false; } public void original(List<Commit> commits, List<Bug> bugs) { int counter = 0; Set<Bug> linkedBugs = new HashSet<Bug>(); Set<Bug> tempLinkedBugs = null; for (Commit commit : commits) { tempLinkedBugs = new HashSet<Bug>(); String msg = commit.getMsg(); if (msg == null) msg = ""; for (Bug bug : bugs) { String bugId = bug.getId(); if (hasID(msg, bugId)) { for (String file : commit.getFiles()) { if (br.ufmg.dcc.labsoft.aserg.modularitycheck.bugparser.util.Utils .isValid(file)) bug.addFile(file); } if (bug.getModifiedFiles().size() > 0) { tempLinkedBugs.add(bug);// pre4 counter++; } } } if (tempLinkedBugs.size() == 1) linkedBugs.addAll(tempLinkedBugs); } System.out.println("Number of commits: " + counter); System.out.println("Linked bugs: " + linkedBugs.size()); } }