001    package org.hackystat.sensor.ant.javancss;
002    
003    import java.io.File;
004    import java.util.Date;
005    import java.util.HashMap;
006    import java.util.Map;
007    
008    import javax.xml.bind.JAXBContext;
009    import javax.xml.bind.Unmarshaller;
010    import javax.xml.datatype.XMLGregorianCalendar;
011    
012    import org.apache.tools.ant.BuildException;
013    import org.hackystat.sensor.ant.javancss.jaxb.Javancss;
014    import org.hackystat.sensor.ant.task.HackystatSensorTask;
015    import org.hackystat.utilities.tstamp.Tstamp;
016    
017    /**
018     * Implements an Ant task that parses the XML files generated by JavaNCSS. The Ant Task sends the
019     * FileMetric data to a Hackystat server.
020     * 
021     * @author Philip Johnson
022     */
023    public class JavaNcssSensor extends HackystatSensorTask {
024    
025      /** The name of this tool. */
026      private static String tool = "JavaNCSS";
027    
028      /** Initialize a new instance of a JavaNcssSensor. */
029      public JavaNcssSensor() {
030        super(tool);
031      }
032    
033      /**
034       * Initialize a new instance of a JavaNcssSensor for testing purposes.
035       * 
036       * @param host The SensorBase host URL.
037       * @param email The SensorBase email to use.
038       * @param password The SensorBase password to use.
039       */
040      public JavaNcssSensor(String host, String email, String password) {
041        super(host, email, password, tool);
042      }
043    
044      /**
045       * Parses the JavaNCSS XML file and sends the resulting FileMetric data to the SensorBase server.
046       * 
047       * @throws BuildException If there is an error.
048       */
049      @Override
050      public void executeInternal() throws BuildException {
051        this.setupSensorShell();
052        int numberOfEntries = 0;
053        Date startTime = new Date();
054        for (File dataFile : getDataFiles()) {
055          try {
056            verboseInfo("Processing JavaNCSS file: " + dataFile);
057            numberOfEntries += this.processJavaNcssXmlFile(dataFile);
058          }
059          catch (Exception e) {
060            signalError("Failure processing: " + dataFile, e);
061          }
062        }
063        // We've collected the data, now send it. 
064        this.sendAndQuit();
065        summaryInfo(startTime, "FileMetric", numberOfEntries);
066      }
067    
068      /**
069       * Processes the JavaNCSS XML data file, generating sensor data.
070       * 
071       * @param xmlFile The file containing the JavaNCSS data.
072       * @return The number of FileMetrics instances generated.
073       * @throws BuildException If problems occur.
074       */
075      int processJavaNcssXmlFile(File xmlFile) throws BuildException {
076        // The start time for all entries will be approximated by the XML file's last mod time.
077        // Use the TstampSet to make it unique.
078        long startTime = xmlFile.lastModified();
079        int count = 0;
080        try {
081          JAXBContext context = JAXBContext
082              .newInstance(org.hackystat.sensor.ant.javancss.jaxb.ObjectFactory.class);
083          Unmarshaller unmarshaller = context.createUnmarshaller();
084    
085          // JavaNCSS report.
086          Javancss javancss = (Javancss) unmarshaller.unmarshal(xmlFile);
087          // Construct the mapping from Java file paths to their CCN data.
088          CcnData ccnData = new CcnData(getSourceFiles(), javancss.getFunctions());
089    
090          for (File resource : ccnData.getFiles()) {
091            long tstamp = this.tstampSet.getUniqueTstamp(startTime);
092            XMLGregorianCalendar tstampXml = Tstamp.makeTimestamp(tstamp);
093            XMLGregorianCalendar runtimeXml = Tstamp.makeTimestamp(this.runtime);
094            // Create the sensor data instance key/value map.
095            Map<String, String> keyValMap = new HashMap<String, String>();
096            // Required
097            keyValMap.put("Tool", "JavaNCSS");
098            keyValMap.put("SensorDataType", "FileMetric");
099            keyValMap.put("Runtime", runtimeXml.toString());
100            keyValMap.put("Timestamp", tstampXml.toString());
101            keyValMap.put("Resource", resource.getAbsolutePath());
102            // Expected
103            keyValMap.put("TotalLines", String.valueOf(ccnData.getTotalLines(resource)));
104            // Optional, but of course the whole point of this sensor.
105            keyValMap.put("CyclomaticComplexityList", ccnData.getCcnData(resource));
106            // add data to sensorshell
107            this.sensorShell.add(keyValMap);
108            count++;
109          }
110          return count;
111        }
112        catch (Exception e) {
113          throw new BuildException(errMsgPrefix + "Failure: " + e.getMessage(), e);
114        }
115      }
116    }