001: /*
002: * Hammurapi
003: * Automated Java code review system.
004: * Copyright (C) 2004 Johannes Bellert
005: *
006: * This program is free software; you can redistribute it and/or modify
007: * it under the terms of the GNU General Public License as published by
008: * the Free Software Foundation; either version 2 of the License, or
009: * (at your option) any later version.
010: *
011: * This program is distributed in the hope that it will be useful,
012: * but WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
014: * GNU General Public License for more details.
015: *
016: * You should have received a copy of the GNU General Public License
017: * along with this program; if not, write to the Free Software
018: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
019: *
020: * URL: http://www.pavelvlasov.com/pv/content/menu.show?id=products.jtaste
021: * e-Mail: Johannes.Bellert@ercgroup.com
022: *
023: * * Created on Mar 23, 2004
024: *
025: */
026: package org.hammurapi.inspectors.metrics;
027:
028: import java.io.FileInputStream;
029: import java.io.FileOutputStream;
030: import java.util.Enumeration;
031: import java.util.HashMap;
032: import java.util.Iterator;
033: import java.util.Map;
034: import java.util.Properties;
035: import java.util.Vector;
036:
037: import org.hammurapi.InspectorBase;
038: import org.hammurapi.HammurapiException;
039: import org.hammurapi.inspectors.metrics.reporting.LocReporter;
040: import org.hammurapi.inspectors.metrics.statistics.DescriptiveStatistic;
041: import org.hammurapi.inspectors.metrics.statistics.IntVector;
042: import org.hammurapi.results.AnnotationContext;
043: import org.hammurapi.results.LinkedAnnotation;
044: import org.w3c.dom.Document;
045: import org.w3c.dom.Element;
046:
047: import com.pavelvlasov.config.ConfigurationException;
048: import com.pavelvlasov.config.Parameterizable;
049: import com.pavelvlasov.jsel.Method;
050: import com.pavelvlasov.jsel.Repository;
051: import com.pavelvlasov.jsel.TypeDefinition;
052: import com.pavelvlasov.render.RenderRequest;
053: import com.pavelvlasov.render.RenderingException;
054: import com.pavelvlasov.render.dom.AbstractRenderer;
055: import com.pavelvlasov.review.SourceMarker;
056:
057: public class NcssInspector extends InspectorBase implements
058: Parameterizable {
059:
060: /**
061: * A sorted distribution list of class NCSS
062: *
063: */
064: private IntVector ncssClassList = new IntVector();
065:
066: /**
067: * A sorted distribution list of function NCSS
068: *
069: */
070: private IntVector ncssFunctionList = new IntVector();
071:
072: /**
073: * Stores the setting form the configuration for the maximum allowed
074: * NCSS for an implemened method.
075: */
076: private Integer functionMaxLoc;
077:
078: /**
079: * Stores the setting form the configuration for the maximum allowed
080: * NCSS for a class.
081: */
082: private Integer classMaxLoc;
083:
084: /**
085: * Stores the setting form the configuration for the maximum allowed
086: * NCSS for a class.
087: */
088: private Integer classMaxFunction;
089:
090: /**
091: * if ncssReport == 1, then generate the NCSS metric report
092: */
093: private Integer ncssReport;
094:
095: /**
096: * You may want to see the Swing Chart for Debug purpose
097: */
098: private Integer chartDebugWindow;
099:
100: double functionAvg = 0.0;
101: double classAvg = 0.0;
102:
103: private Vector packageCodeMetricList = new Vector();
104: private Vector functionCodeMetricList = new Vector();
105: private Map types = new HashMap();
106: private Map packages = new HashMap();
107: private CodeMetric projectMetric = new CodeMetric();
108: String projectBaseDir = "";
109: String xmlResourceName = this .getClass().getName()
110: .replace('.', '/')
111: + "XmlPretty.xsl";
112: LocReporter locReport;
113:
114: public void visit(com.pavelvlasov.jsel.Package pkg) {
115: CodeMetric packageCodeMetrics = new CodeMetric();
116: packageCodeMetrics.setDescriptonEntity("Package");
117: packageCodeMetrics.setName(pkg.getName());
118: packages.put(pkg.getName(), packageCodeMetrics);
119:
120: int sum = projectMetric.getNcss()
121: + packageCodeMetrics.getNcss();
122: projectMetric.setNcss(sum);
123: sum = projectMetric.getNumber() + 1;
124: projectMetric.setNumber(sum);
125:
126: projectMetric.getChildren().add(packageCodeMetrics);
127: }
128:
129: public void visit(TypeDefinition typeDefinition) {
130: int start = typeDefinition.getAst().getFirstToken().getLine();
131: int end = typeDefinition.getAst().getLastToken().getLine();
132: CodeMetric typeCodeMetric = new CodeMetric();
133: typeCodeMetric.setDescriptonEntity("Class");
134: typeCodeMetric.setName(typeDefinition.getFcn());
135:
136: // packageName.replace('.','/')+compilationUnit.getName()
137: // System.out.println( "++ " +typeDefinition.getCompilationUnit().getPackage().getName().toString() );
138:
139: typeCodeMetric.source_url = ((SourceMarker) typeDefinition)
140: .getSourceURL();
141: typeCodeMetric.source_line = ((SourceMarker) typeDefinition)
142: .getLine();
143: typeCodeMetric.source_col = ((SourceMarker) typeDefinition)
144: .getColumn();
145:
146: typeCodeMetric.setNcss(end - start);
147:
148: types.put((String) typeDefinition.getFcn(), typeCodeMetric);
149:
150: // lookup for package
151: CodeMetric packageCodeMetric = (CodeMetric) packages
152: .get(typeDefinition.getCompilationUnit().getPackage()
153: .getName());
154: int sum = packageCodeMetric.getNcss()
155: + typeCodeMetric.getNcss();
156: packageCodeMetric.setNcss(sum);
157:
158: packageCodeMetric.setNumber(packageCodeMetric.getNumber() + 1);
159: sum = packageCodeMetric.getFunction()
160: + typeCodeMetric.getFunction();
161: packageCodeMetric.setFunction(sum);
162:
163: sum = projectMetric.getFunction()
164: + packageCodeMetric.getFunction();
165: projectMetric.setFunction(sum);
166:
167: packageCodeMetric.getChildren().add(typeCodeMetric);
168:
169: context.addMetric(typeDefinition, "NCSS Class Inspector",
170: typeCodeMetric.getNcss());
171: if (this .classMaxLoc != null
172: && typeCodeMetric.getNcss() > classMaxLoc.intValue()) {
173: context.reportViolation(typeDefinition,
174: new Object[] { classMaxLoc,
175: new Integer(typeCodeMetric.getNcss()) });
176: }
177:
178: }
179:
180: public void visit(Method code) {
181: int start = code.getAst().getFirstToken().getLine();
182: int end = code.getAst().getLastToken().getLine();
183: CodeMetric methodCodeMetrics = new CodeMetric();
184: methodCodeMetrics.setDescriptonEntity("MethodImplemented");
185: methodCodeMetrics.setName(code.getEnclosingType().getFcn()
186: + " >> " + code.getName());
187:
188: methodCodeMetrics.source_url = ((SourceMarker) code)
189: .getSourceURL();
190: methodCodeMetrics.source_line = ((SourceMarker) code).getLine();
191: methodCodeMetrics.source_col = ((SourceMarker) code)
192: .getColumn();
193:
194: methodCodeMetrics.setNcss(end - start);
195:
196: // lookup for class
197:
198: CodeMetric classCodeMetric = (CodeMetric) types
199: .get((String) code.getEnclosingType().getFcn());
200: if (classCodeMetric == null) {
201: new Exception("Can't get CodeMetric Object for "
202: + (String) code.getEnclosingType().getFcn());
203: } else {
204: classCodeMetric
205: .setFunction(classCodeMetric.getFunction() + 1);
206: classCodeMetric.getChildren().add(methodCodeMetrics);
207: context.addMetric(code, "NCSS Method p Class Inspector",
208: classCodeMetric.getFunction());
209: }
210: context.addMetric(code, "NCSS Method Inspector",
211: methodCodeMetrics.getNcss());
212: if (this .functionMaxLoc != null
213: && methodCodeMetrics.getNcss() > functionMaxLoc
214: .intValue()) {
215: context.reportViolation(code, new Object[] {
216: functionMaxLoc,
217: new Integer(methodCodeMetrics.getNcss()) });
218: }
219:
220: if (this .classMaxFunction != null
221: && classCodeMetric != null
222: && classCodeMetric.getFunction() > classMaxFunction
223: .intValue()) {
224: context.reportViolation(code, new Object[] {
225: classMaxFunction,
226: new Integer(classCodeMetric.getFunction()) });
227: }
228:
229: }
230:
231: private void aggregate() {
232:
233: Iterator it = types.values().iterator();
234: while (it.hasNext()) {
235: CodeMetric classCodeMetric = (CodeMetric) it.next();
236: int last = classCodeMetric.getName().lastIndexOf('.');
237: String packg = last == -1 ? "" : classCodeMetric.getName()
238: .substring(0, last);
239: CodeMetric packageCodeMetric = (CodeMetric) packages
240: .get(packg);
241: int sum = packageCodeMetric.getFunction()
242: + classCodeMetric.getFunction();
243: packageCodeMetric.setFunction(sum);
244:
245: // this.projectMetric.function += packCm.function;
246: }
247: this .copyDeepCodeMetric();
248: functionAvg = DescriptiveStatistic.mean(ncssFunctionList);
249: classAvg = DescriptiveStatistic.mean(ncssClassList);
250: return;
251: }
252:
253: private void copyDeepCodeMetric() {
254:
255: Enumeration enumP = projectMetric.getChildren().elements();
256: while (enumP.hasMoreElements()) {
257: CodeMetric cmP = (CodeMetric) enumP.nextElement();
258: // Classes
259: Enumeration enumC = cmP.getChildren().elements();
260: while (enumC.hasMoreElements()) {
261: CodeMetric cmC = (CodeMetric) enumC.nextElement();
262: this .packageCodeMetricList.add(cmC);
263: this .ncssClassList.addElement(cmC.getNcss());
264: // Functions
265: Enumeration enumF = cmC.getChildren().elements();
266: while (enumF.hasMoreElements()) {
267: CodeMetric cmF = (CodeMetric) enumF.nextElement();
268: this .functionCodeMetricList.add(cmF);
269: this .ncssFunctionList.addElement(cmF.getNcss());
270: }
271: }
272: }
273: }
274:
275: public void leave(Repository repository) {
276: aggregate();
277:
278: context.annotate(new LinkedAnnotation() {
279: private String path;
280:
281: public String getName() {
282: return getContext().getDescriptor().getName();
283: }
284:
285: public String getPath() {
286: return path;
287: }
288:
289: public void render(AnnotationContext context)
290: throws HammurapiException {
291: String errorCausingFile = "";
292: projectMetric.setName("Project");
293: projectMetric.setDescriptonEntity(projectMetric
294: .getName());
295:
296: // Output images here. See AnnotationTest for details.
297:
298: class NcssInspectorRenderer extends AbstractRenderer {
299: NcssInspectorRenderer() {
300: super (new RenderRequest(NcssInspector.this ));
301: }
302:
303: public Element render(Document document)
304: throws RenderingException {
305: NcssInspector ncssInspector = (NcssInspector) request
306: .getRenderee();
307: Element entities = document
308: .createElement("Entities");
309: Element ncssInspectorElement = document
310: .createElement("SourceCodeMetric");
311:
312: Element classMaxLocE = document
313: .createElement("ClassMaxLoc");
314: classMaxLocE.setAttribute("number", String
315: .valueOf(classMaxLoc));
316: ncssInspectorElement.appendChild(classMaxLocE);
317:
318: Element classMaxFuncE = document
319: .createElement("ClassMaxFunction");
320: classMaxFuncE.setAttribute("number", String
321: .valueOf(classMaxFunction));
322: ncssInspectorElement.appendChild(classMaxFuncE);
323:
324: Element funcMaxLocE = document
325: .createElement("FunctionMaxLoc");
326: funcMaxLocE.setAttribute("number", String
327: .valueOf(functionMaxLoc));
328: ncssInspectorElement.appendChild(funcMaxLocE);
329:
330: Element functionAverage = document
331: .createElement("FunctionNcssAverage");
332: ncssInspectorElement
333: .appendChild(functionAverage);
334: functionAverage.setAttribute("number", String
335: .valueOf(functionAvg));
336:
337: Element classAverage = document
338: .createElement("ClassNcssAverage");
339: ncssInspectorElement.appendChild(classAverage);
340: classAverage.setAttribute("number", String
341: .valueOf(classAvg));
342:
343: Element jpgClassFileEntry = document
344: .createElement("JpgClassFileEntry");
345: jpgClassFileEntry.setAttribute("chartFile",
346: String.valueOf(locReport
347: .getJpgClassFileEntry()
348: .getPath().toString()));
349: ncssInspectorElement
350: .appendChild(jpgClassFileEntry);
351:
352: Element jpgFunctionFileEntry = document
353: .createElement("JpgFunctionFileEntry");
354:
355: jpgFunctionFileEntry.setAttribute("chartFile",
356: String.valueOf(locReport
357: .getJpgFunctionFileEntry()
358: .getPath().toString()));
359: ncssInspectorElement
360: .appendChild(jpgFunctionFileEntry);
361:
362: Element pmd = ncssInspector.projectMetric
363: .toDom(document);
364: ncssInspectorElement.appendChild(pmd);
365: return ncssInspectorElement;
366: }
367: } //-- end class NcssInspectorRenderer
368:
369: locReport = new LocReporter(context, classMaxLoc,
370: functionMaxLoc, ncssReport, chartDebugWindow);
371: locReport.setNcssClassList(ncssClassList);
372: locReport.setNcssFunctionList(ncssFunctionList);
373:
374: locReport.doIt(projectMetric);
375:
376: AnnotationContext.FileEntry fileEntry = context
377: .getNextFile(context.getExtension());
378: path = fileEntry.getPath();
379: // System.out.println( ".> " +this.getPath().toString() );
380:
381: AnnotationContext.FileEntry fileEntryXml = context
382: .getNextFile(".xml");
383: try {
384: NcssInspectorRenderer renderer = new NcssInspectorRenderer();
385: FileOutputStream out = new FileOutputStream(
386: fileEntry.getFile());
387:
388: renderer
389: .setEmbeddedStyle(context.isEmbeddedStyle());
390: try {
391: errorCausingFile = fileEntry.getFile()
392: .getAbsolutePath();
393: renderer
394: .render(
395: context.getParameter("style") == null ? null
396: : new FileInputStream(
397: context
398: .getParameter(
399: "style")
400: .toString()),
401: out);
402: } finally {
403: out.close();
404: }
405: //-- write a XML file for other XSL usage
406: FileOutputStream outXml = new FileOutputStream(
407: fileEntryXml.getFile());
408:
409: try {
410: errorCausingFile = fileEntryXml.getFile()
411: .getAbsolutePath();
412: //-- write a XML file for other XSL usage
413: renderer.setEmbeddedStyle(false);
414: renderer.render(outXml);
415: // renderer.setPrettyPrint( true );
416: // InputStream inStream=getClass().getClassLoader().getResourceAsStream(xmlResourceName);
417: // renderer.render(inStream, outXml);
418:
419: } finally {
420: outXml.close();
421: }
422: } catch (Exception e) {
423: throw new HammurapiException("Can't save "
424: + errorCausingFile + ". " + e.getMessage());
425: }
426: }
427:
428: public Properties getProperties() {
429: Properties ret = new Properties();
430: ret.setProperty("left-panel", "yes");
431: ret.setProperty("target", "NCSS");
432: return ret;
433: }
434: });
435: }
436:
437: /**
438: * Configures the rule. Reads in the values of the parameters operation-max-complexity and
439: * class-max-complexity.
440: *
441: * @param name the name of the parameter being loaded from Hammurapi configuration
442: * @param value the value of the parameter being loaded from Hammurapi configuration
443: * @exception ConfigurationException in case of a not supported parameter
444: */
445: public boolean setParameter(String name, Object value)
446: throws ConfigurationException {
447: if ("function-max-loc".equals(name)) {
448: functionMaxLoc = new Integer(Integer.parseInt(value
449: .toString()));
450: } else if ("class-max-loc".equals(name)) {
451: classMaxLoc = new Integer(Integer
452: .parseInt(value.toString()));
453: } else if ("class-max-function".equals(name)) {
454: classMaxFunction = new Integer(Integer.parseInt(value
455: .toString()));
456: } else if ("chart-debug-window".equals(name)) {
457: chartDebugWindow = new Integer(Integer.parseInt(value
458: .toString()));
459: } else if ("ncss-report".equals(name)) {
460: ncssReport = new Integer(Integer.parseInt(value.toString()));
461:
462: } else {
463: throw new ConfigurationException("Parameter '" + name
464: + "' is not supported");
465: }
466: return true;
467: }
468:
469: }
|