/* * To change this template, choose Tools | Templates * and open the template in the editor. */ package br.uff.ic.oceano.ostra.service.traceability; import br.uff.ic.oceano.core.exception.ServiceException; import br.uff.ic.oceano.core.exception.VCSException; import br.uff.ic.oceano.core.factory.ObjectFactory; import br.uff.ic.oceano.core.model.ProjectUser; import br.uff.ic.oceano.core.model.Revision; import br.uff.ic.oceano.core.model.SoftwareProject; import br.uff.ic.oceano.core.service.PersistenceService; import br.uff.ic.oceano.core.service.vcs.VCSService; import br.uff.ic.oceano.ostra.model.VersionedItem; import com.google.common.collect.SortedArraySet; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; /** * * @author DanCastellani */ public class TraceabilityService implements PersistenceService { //CONSTANTS private static final String[] REFERENCE_TAGS = {"REFS"}; private static final String[] CLOSE_TAGS = {"CLOSES"}; private static final List<String> ALL_TAGS = new ArrayList<String>(); private static final String TICKET_NUMBER_REFERENCE = "#"; private static final String HAS_SYMBOL = "x"; //Control private VCSService vcsService; private Set<String> tickets; private Set<String> files; private Map<String, List<String>> ticketFilesMap; public void setup() { vcsService = ObjectFactory.getObjectWithDataBaseDependencies(VCSService.class); } public TraceabilityService() { } private void init() { tickets = new SortedArraySet<String>(); files = new SortedArraySet<String>(); ticketFilesMap = new HashMap<String, List<String>>(); ALL_TAGS.addAll(Arrays.asList(CLOSE_TAGS)); ALL_TAGS.addAll(Arrays.asList(REFERENCE_TAGS)); } public String[][] buildTraceabilityMatrix(SoftwareProject project, ProjectUser pu) throws ServiceException { init(); Set<Revision> revisions; try { System.out.println("Recovering revisions..."); revisions = vcsService.getRevisions(project, pu); System.out.println("ok"); } catch (VCSException ex) { System.out.println("error"); throw new ServiceException(ex); } System.out.println("Processing revisions"); for (Revision revision : revisions) { System.out.println("revision = " + revision); final Collection<String> referedTickets = getReferedTickets(revision.getMessage()); if (referedTickets.size() > 0) { List<String> changedFiles = new LinkedList<String>(); for (VersionedItem versionedItem : revision.getChangedFilesAsList()) { changedFiles.add(versionedItem.getItem().getPath()); } addTicketsAndFilesToMaps(referedTickets, changedFiles); } } System.out.println("Building matrix"); String[][] traceabilityMatrix = new String[files.size() + 1][tickets.size() + 1]; Map<String, Integer> ticketIndices = new HashMap<String, Integer>(); Map<String, Integer> fileIndices = new HashMap<String, Integer>(); initMatrixHeaders(traceabilityMatrix, ticketIndices, fileIndices); if (ticketFilesMap != null && !ticketFilesMap.isEmpty()) { for (String ticket : ticketFilesMap.keySet()) { int ticketIndice = ticketIndices.get(ticket); if (ticketFilesMap.get(ticket) != null && !ticketFilesMap.get(ticket).isEmpty()) { for (String file : ticketFilesMap.get(ticket)) { int fileIndex = fileIndices.get(file); if (traceabilityMatrix[fileIndex][ticketIndice] == null) { traceabilityMatrix[fileIndex][ticketIndice] = HAS_SYMBOL; } } } } } System.out.println("done"); return traceabilityMatrix; } private boolean containsIgnoringCase(String word, List<String> words) { for (String string : words) { if (string.equalsIgnoreCase(word)) { return true; } } return false; } private Collection<String> getReferedTickets(final String message) throws ServiceException { try { Set<String> referedTickets = new HashSet<String>(); String[] messageWords = message.split(" "); for (int i = 0; i < messageWords.length;) { String word = messageWords[i++]; if (containsIgnoringCase(word, ALL_TAGS)) { word = messageWords[i++]; if (word.contains(TICKET_NUMBER_REFERENCE)) { if (word.length() == 1) { word = messageWords[i++]; } else { word = word.substring(1); } String number = ""; do { number += word.charAt(0); word = word.substring(1); } while (number.matches("\\d*") && !word.isEmpty()); // if it is a number, we dont have to take out the last char if (!number.matches("\\d*")) { number = number.substring(0, number.length() - 1); } String ticket = TICKET_NUMBER_REFERENCE + number; verifyTicket(ticket); System.out.println(" found ticket: " + ticket); referedTickets.add(ticket); } } } return referedTickets; } catch (ServiceException ex) { throw new ServiceException("Malformed reference message: <" + message + ">"); } } private void verifyTicket(String word) throws ServiceException { try { Integer.parseInt(word.substring(1)); } catch (NumberFormatException ex) { throw new ServiceException(); } } private void addTicketsAndFilesToMaps(Collection<String> referedTickets, List<String> referedFiles) { //add tickets and related files for (String ticket : referedTickets) { if (tickets.contains(ticket)) { final List<String> listOfFiles = ticketFilesMap.get(ticket); listOfFiles.addAll(referedFiles); ticketFilesMap.put(ticket, listOfFiles); } else { tickets.add(ticket); ticketFilesMap.put(ticket, referedFiles); } } //add files for (String file : referedFiles) { if (!files.contains(file)) { files.add(file); } } } private void initMatrixHeaders(String[][] traceabilityMatrix, Map<String, Integer> ticketIndices, Map<String, Integer> fileIndices) { int i = 1; for (String file : files) { traceabilityMatrix[i][0] = file; fileIndices.put(file, i++); } int j = 1; for (String ticket : tickets) { traceabilityMatrix[0][j] = ticket; ticketIndices.put(ticket, j++); } } }