001 package org.hackystat.sensor.ant.dependencyfinder; 002 003 import java.io.File; 004 import java.util.ArrayList; 005 import java.util.Date; 006 import java.util.HashMap; 007 import java.util.List; 008 import java.util.Map; 009 010 import javax.xml.bind.JAXBContext; 011 import javax.xml.bind.Unmarshaller; 012 import javax.xml.datatype.XMLGregorianCalendar; 013 014 import org.apache.tools.ant.BuildException; 015 import org.hackystat.sensor.ant.dependencyfinder.jaxb.Dependencies; 016 import org.hackystat.sensor.ant.dependencyfinder.jaxb.Package; 017 import org.hackystat.sensor.ant.dependencyfinder.jaxb.Class; 018 import org.hackystat.sensor.ant.task.HackystatSensorTask; 019 import org.hackystat.sensor.ant.util.JavaClass2FilePathMapper; 020 import org.hackystat.utilities.tstamp.Tstamp; 021 022 /** 023 * Implements an Ant task that parses the XML files generated by DependencyFinder. 024 * The Ant Task sends the Coupling data to a Hackystat server. 025 * 026 * @author Philip Johnson 027 */ 028 public class DependencyFinderSensor extends HackystatSensorTask { 029 030 /** The name of this tool. */ 031 private static String tool = "DependencyFinder"; 032 033 /** Initialize a new instance of this sensor. */ 034 public DependencyFinderSensor() { 035 super(tool); 036 } 037 038 /** 039 * Initialize a new instance of this sensor for testing purposes. 040 * 041 * @param host The SensorBase host URL. 042 * @param email The SensorBase email to use. 043 * @param password The SensorBase password to use. 044 */ 045 public DependencyFinderSensor(String host, String email, String password) { 046 super(host, email, password, tool); 047 } 048 049 /** 050 * Parses the tool's XML file and sends the resulting data to the SensorBase server. 051 * 052 * @throws BuildException If there is an error. 053 */ 054 @Override 055 public void executeInternal() throws BuildException { 056 this.setupSensorShell(); 057 int numberOfEntries = 0; 058 Date startTime = new Date(); 059 for (File dataFile : getDataFiles()) { 060 try { 061 verboseInfo("Processing DependencyFinder file: " + dataFile); 062 numberOfEntries += this.processDependencyFinderXmlFile(dataFile); 063 } 064 catch (Exception e) { 065 signalError("Failure processing: " + dataFile, e); 066 } 067 } 068 // We've collected the data, now send it. 069 this.sendAndQuit(); 070 summaryInfo(startTime, "Coupling", numberOfEntries); 071 } 072 073 /** 074 * Processes a single DependencyFinder XML data file, generating sensor data. 075 * 076 * @param xmlFile The file containing the DependencyFinder data. 077 * @return The number of Coupling instances generated. 078 * @throws BuildException If problems occur. 079 */ 080 int processDependencyFinderXmlFile(File xmlFile) throws BuildException { 081 // The start time for all entries will be approximated by the XML file's last mod time. 082 // Use the TstampSet to make it unique. 083 long startTime = xmlFile.lastModified(); 084 int count = 0; 085 try { 086 JAXBContext context = JAXBContext 087 .newInstance(org.hackystat.sensor.ant.dependencyfinder.jaxb.ObjectFactory.class); 088 Unmarshaller unmarshaller = context.createUnmarshaller(); 089 090 // DependencyFinder XML report. 091 Dependencies dependencies = (Dependencies) unmarshaller.unmarshal(xmlFile); 092 // Construct a mapper from class names to their file path. 093 JavaClass2FilePathMapper mapper = new JavaClass2FilePathMapper(getSourceFiles()); 094 List<Package> packages = new ArrayList<Package>(); 095 if (dependencies.getPackage() != null) { 096 packages = dependencies.getPackage(); 097 } 098 099 for (Package packageElement : packages) { 100 List<Class> classList = new ArrayList<Class>(); 101 if (packageElement.getClazz() != null) { 102 classList = packageElement.getClazz(); 103 } 104 for (Class classElement : classList) { 105 String resource = mapper.getFilePath(classElement.getName()); 106 107 if (resource != null) { 108 long tstamp = this.tstampSet.getUniqueTstamp(startTime); 109 XMLGregorianCalendar tstampXml = Tstamp.makeTimestamp(tstamp); 110 XMLGregorianCalendar runtimeXml = Tstamp.makeTimestamp(this.runtime); 111 // Create the sensor data instance key/value map. 112 Map<String, String> keyValMap = new HashMap<String, String>(); 113 // Required for all sensor data 114 keyValMap.put("Tool", tool); 115 keyValMap.put("SensorDataType", "Coupling"); 116 keyValMap.put("Runtime", runtimeXml.toString()); 117 keyValMap.put("Timestamp", tstampXml.toString()); 118 keyValMap.put("Resource", resource); 119 // Expected for "Coupling" sensor data. 120 keyValMap.put("Type", "class"); 121 keyValMap.put("Afferent", String.valueOf(getAfferent(classElement))); 122 keyValMap.put("Efferent", String.valueOf(getEfferent(classElement))); 123 // add data to sensorshell 124 this.sensorShell.add(keyValMap); 125 count++; 126 } 127 } 128 } 129 } 130 catch (Throwable e) { 131 throw new BuildException(errMsgPrefix + "Failure: " + e.getMessage(), e); 132 } 133 return count; 134 } 135 136 /** 137 * Gets the Afferent (inbound) number of couplings. 138 * @param classElement The class element to count. 139 * @return The integer number of afferent links. 140 */ 141 private int getAfferent(Class classElement) { 142 return (classElement.getInbound() == null) ? 0 : classElement.getInbound().size(); 143 } 144 145 /** 146 * Gets the Efferent (outbound) number of couplings. 147 * @param classElement The class element to count. 148 * @return The integer number of efferent links. 149 */ 150 private int getEfferent(Class classElement) { 151 return (classElement.getOutbound() == null) ? 0 : classElement.getOutbound().size(); 152 } 153 }