001    package org.hackystat.dailyprojectdata.resource.complexity;
002    
003    import java.io.StringWriter;
004    import java.util.List;
005    import java.util.logging.Logger;
006    
007    import javax.xml.bind.JAXBContext;
008    import javax.xml.bind.Marshaller;
009    import javax.xml.datatype.XMLGregorianCalendar;
010    import javax.xml.parsers.DocumentBuilder;
011    import javax.xml.parsers.DocumentBuilderFactory;
012    import javax.xml.transform.Transformer;
013    import javax.xml.transform.TransformerFactory;
014    import javax.xml.transform.dom.DOMSource;
015    import javax.xml.transform.stream.StreamResult;
016    
017    import org.hackystat.dailyprojectdata.resource.complexity.jaxb.ComplexityDailyProjectData;
018    import org.hackystat.dailyprojectdata.resource.dailyprojectdata.DailyProjectDataResource;
019    import org.hackystat.dailyprojectdata.resource.complexity.jaxb.FileData;
020    import org.hackystat.sensorbase.client.SensorBaseClient;
021    import org.hackystat.sensorbase.resource.sensordata.jaxb.Property;
022    import org.hackystat.sensorbase.resource.sensordata.jaxb.SensorData;
023    import org.hackystat.sensorbase.resource.sensordata.jaxb.SensorDataIndex;
024    import org.hackystat.sensorbase.resource.sensordata.jaxb.SensorDataRef;
025    import org.hackystat.utilities.tstamp.Tstamp;
026    import org.restlet.Context;
027    import org.restlet.data.MediaType;
028    import org.restlet.data.Request;
029    import org.restlet.data.Response;
030    import org.restlet.resource.Representation;
031    import org.restlet.resource.Variant;
032    import org.w3c.dom.Document;
033    
034    /**
035     * Implements the Resource for processing GET
036     * {host}/complexity/{user}/{project}/{starttime}/{type} requests. Requires the
037     * authenticated user to be {user}, the Admin, or a member of {project}.
038     * 
039     * @author Philip Johnson
040     */
041    public class ComplexityResource extends DailyProjectDataResource {
042      
043      private String type;
044      
045      private String tool;
046    
047      /**
048       * The standard constructor.
049       * 
050       * @param context The context.
051       * @param request The request object.
052       * @param response The response object.
053       */
054      public ComplexityResource(Context context, Request request, Response response) {
055        super(context, request, response);
056        this.type = (String) request.getAttributes().get("type");
057        this.tool = (String) request.getAttributes().get("tool");
058      }
059    
060      /**
061       * Returns a ComplexityDailyProjectData instance representing the Complexity
062       * associated with each file for the Project data, or null if not authorized.
063       * Authenticated user must be the uriUser, or Admin, or project member. 
064       * 
065       * @param variant The representational variant requested.
066       * @return The representation.
067       */
068      @Override
069      public Representation represent(Variant variant) {
070        Logger logger = this.server.getLogger();
071        logger.fine("Complexity DPD: Starting");
072        if (variant.getMediaType().equals(MediaType.TEXT_XML)) {
073          try {
074            // [1] get the SensorBaseClient for the user making this request.
075            SensorBaseClient client = super.getSensorBaseClient();
076            // [2] Check the front side cache and return if the DPD is found and is OK to access.
077            String cachedDpd = this.server.getFrontSideCache().get(uriUser, project, uriString);
078            if ((cachedDpd != null) && client.inProject(uriUser, project)) {
079              return super.getStringRepresentation(cachedDpd);
080            }
081            // [2] Get the Snapshot containing the last sent FileMetric data for this Project.
082            XMLGregorianCalendar startTime = Tstamp.makeTimestamp(this.timestamp);
083            XMLGregorianCalendar endTime = Tstamp.incrementDays(startTime, 1);
084            logger.fine("Complexity DPD: Requesting index: " + uriUser + " " + project);
085            SensorDataIndex snapshot = (this.tool == null) ?
086                // Get the latest snapshot from any tool.
087                client.getProjectSensorDataSnapshot(this.uriUser, this.project, startTime, endTime, 
088                "FileMetric") :
089                  // Get the latest snapshot from the specified tool.
090                  client.getProjectSensorDataSnapshot(this.uriUser, this.project, startTime, endTime, 
091                      "FileMetric", this.tool);
092    
093            logger.fine("Complexity DPD: Got index: " + 
094                snapshot.getSensorDataRef().size() + " instances. Now retrieving instances.");
095            // [3] create and return the ComplexityDailyProjectData instance.
096            ComplexityDailyProjectData fileDpd = new ComplexityDailyProjectData();
097            fileDpd.setOwner(uriUser);
098            fileDpd.setProject(project);
099            fileDpd.setStartTime(startTime);
100            fileDpd.setType(this.type);
101            
102            if (!snapshot.getSensorDataRef().isEmpty()) {
103              for (SensorDataRef ref : snapshot.getSensorDataRef()) {
104                SensorData data = client.getSensorData(ref);
105                fileDpd.setOwner(data.getOwner());
106                fileDpd.setTool(data.getTool());
107                String property = this.type + "ComplexityList";
108                String complexities = getProperty(data, property);
109                if (complexities != null) { //NOPMD
110                  FileData fileData = new FileData();
111                  fileData.setFileUri(data.getResource());
112                  fileData.setComplexityValues(complexities);
113                  fileData.setTotalLines(getProperty(data, "TotalLines"));
114                  fileDpd.getFileData().add(fileData);
115                }
116              }
117            }
118            String xmlData = makeComplexityMetric(fileDpd);
119            if (!Tstamp.isTodayOrLater(startTime)) {
120              this.server.getFrontSideCache().put(uriUser, project, uriString, xmlData);
121            }
122            logRequest("Complexity", this.tool, this.type);
123            return super.getStringRepresentation(xmlData);
124          } 
125          catch (Exception e) {
126            setStatusError("Error creating Complexity DPD.", e);
127            return null;
128          }
129        }
130        return null;
131      }
132    
133      /**
134       * Returns the passed SensorData instance as a String encoding of its XML
135       * representation.
136       * 
137       * @param data The SensorData instance.
138       * @return The XML String representation.
139       * @throws Exception If problems occur during translation.
140       */
141      private String makeComplexityMetric(ComplexityDailyProjectData data) throws Exception {
142        JAXBContext complexityJAXB = (JAXBContext) this.server.getContext().getAttributes().get(
143            "ComplexityJAXB");
144        Marshaller marshaller = complexityJAXB.createMarshaller();
145        DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
146        dbf.setNamespaceAware(true);
147        DocumentBuilder documentBuilder = dbf.newDocumentBuilder();
148        Document doc = documentBuilder.newDocument();
149        marshaller.marshal(data, doc);
150        DOMSource domSource = new DOMSource(doc);
151        StringWriter writer = new StringWriter();
152        StreamResult result = new StreamResult(writer);
153        TransformerFactory tf = TransformerFactory.newInstance();
154        Transformer transformer = tf.newTransformer();
155        transformer.transform(domSource, result);
156        return writer.toString();
157      }
158      
159      /**
160       * Returns the string value associated with the specified property key. If no
161       * property with that key exists, null is returned.
162       * @param data The sensor data instance whose property list will be searched.
163       * @param key the property key to search for.
164       * @return The property value with the specified key or null.
165       */
166      private String getProperty(SensorData data, String key) {
167        List<Property> propertyList = data.getProperties().getProperty();
168        for (Property property : propertyList) {
169          if (key.equals(property.getKey())) {
170            return property.getValue();
171          }
172        }
173        return null;
174      }
175    }