- Fix Audit messages to remove fullpath in it. It won't work on references though. - Fix MAudit handler to print stderr as well so that a missing agent or invalid options return a message. - Enhance options to reflect those available also in Webgain QA 2.1.2+ git-svn-id: https://svn.apache.org/repos/asf/ant/core/trunk@271226 13f79535-47bb-0310-9956-ffa450edef68master
| @@ -1,7 +1,7 @@ | |||
| /* | |||
| * The Apache Software License, Version 1.1 | |||
| * | |||
| * Copyright (c) 2001 The Apache Software Foundation. All rights | |||
| * Copyright (c) 2001-2002 The Apache Software Foundation. All rights | |||
| * reserved. | |||
| * | |||
| * Redistribution and use in source and binary forms, with or without | |||
| @@ -53,25 +53,25 @@ | |||
| */ | |||
| package org.apache.tools.ant.taskdefs.optional.metamata; | |||
| import java.io.File; | |||
| import java.io.FileWriter; | |||
| import java.io.IOException; | |||
| import java.io.PrintWriter; | |||
| import java.util.Enumeration; | |||
| import java.util.Hashtable; | |||
| import java.util.Vector; | |||
| import org.apache.tools.ant.BuildException; | |||
| import org.apache.tools.ant.DirectoryScanner; | |||
| import org.apache.tools.ant.Project; | |||
| import org.apache.tools.ant.Task; | |||
| import org.apache.tools.ant.taskdefs.ExecuteStreamHandler; | |||
| import org.apache.tools.ant.taskdefs.Execute; | |||
| import org.apache.tools.ant.types.Path; | |||
| import org.apache.tools.ant.types.CommandlineJava; | |||
| import org.apache.tools.ant.taskdefs.ExecuteStreamHandler; | |||
| import org.apache.tools.ant.types.Commandline; | |||
| import org.apache.tools.ant.types.CommandlineJava; | |||
| import org.apache.tools.ant.types.FileSet; | |||
| import org.apache.tools.ant.DirectoryScanner; | |||
| import java.io.File; | |||
| import java.io.IOException; | |||
| import java.io.FileWriter; | |||
| import java.io.PrintWriter; | |||
| import java.util.Hashtable; | |||
| import java.util.Vector; | |||
| import java.util.Enumeration; | |||
| import java.util.Random; | |||
| import org.apache.tools.ant.types.Path; | |||
| import org.apache.tools.ant.util.FileUtils; | |||
| /** | |||
| * Somewhat abstract framework to be used for other metama 2.0 tasks. | |||
| @@ -80,11 +80,9 @@ import java.util.Random; | |||
| * For more information, visit the website at | |||
| * <a href="http://www.metamata.com">www.metamata.com</a> | |||
| * | |||
| * @author <a href="mailto:sbailliez@imediation.com">Stephane Bailliez</a> | |||
| * @author <a href="mailto:sbailliez@apache.org">Stephane Bailliez</a> | |||
| */ | |||
| public abstract class AbstractMetamataTask extends Task{ | |||
| //--------------------------- ATTRIBUTES ----------------------------------- | |||
| public abstract class AbstractMetamataTask extends Task { | |||
| /** | |||
| * The user classpath to be provided. It matches the -classpath of the | |||
| @@ -116,7 +114,7 @@ public abstract class AbstractMetamataTask extends Task{ | |||
| // be set when calling scanFileSets(); | |||
| protected Hashtable includedFiles = null; | |||
| public AbstractMetamataTask(){ | |||
| public AbstractMetamataTask() { | |||
| } | |||
| /** initialize the task with the classname of the task to run */ | |||
| @@ -126,8 +124,12 @@ public abstract class AbstractMetamataTask extends Task{ | |||
| } | |||
| /** the metamata.home property to run all tasks. */ | |||
| public void setMetamatahome(final File metamataHome){ | |||
| this.metamataHome = metamataHome; | |||
| public void setHome(final File value) { | |||
| this.metamataHome = value; | |||
| } | |||
| public void setMetamatahome(final File value) { | |||
| setHome(value); | |||
| } | |||
| /** user classpath */ | |||
| @@ -139,8 +141,8 @@ public abstract class AbstractMetamataTask extends Task{ | |||
| } | |||
| /** create the source path for this task */ | |||
| public Path createSourcepath(){ | |||
| if (sourcePath == null){ | |||
| public Path createSourcepath() { | |||
| if (sourcePath == null) { | |||
| sourcePath = new Path(project); | |||
| } | |||
| return sourcePath; | |||
| @@ -152,7 +154,7 @@ public abstract class AbstractMetamataTask extends Task{ | |||
| } | |||
| /** -mx or -Xmx depending on VM version */ | |||
| public void setMaxmemory(String max){ | |||
| public void setMaxmemory(String max) { | |||
| if (Project.getJavaVersion().startsWith("1.1")) { | |||
| createJvmarg().setValue("-mx" + max); | |||
| } else { | |||
| @@ -177,8 +179,6 @@ public abstract class AbstractMetamataTask extends Task{ | |||
| } | |||
| } | |||
| //--------------------- PRIVATE/PROTECTED METHODS -------------------------- | |||
| /** check the options and build the command line */ | |||
| protected void setUp() throws BuildException { | |||
| checkOptions(); | |||
| @@ -190,10 +190,12 @@ public abstract class AbstractMetamataTask extends Task{ | |||
| // set the metamata.home property | |||
| final Commandline.Argument vmArgs = cmdl.createVmArgument(); | |||
| vmArgs.setValue("-Dmetamata.home=" + metamataHome.getAbsolutePath() ); | |||
| vmArgs.setValue("-Dmetamata.home=" + metamataHome.getAbsolutePath()); | |||
| // retrieve all the files we want to scan | |||
| includedFiles = scanFileSets(); | |||
| includedFiles = scanSources(new Hashtable()); | |||
| //String[] entries = sourcePath.list(); | |||
| //includedFiles = scanSources(new Hashtable(), entries); | |||
| log(includedFiles.size() + " files added for audit", Project.MSG_VERBOSE); | |||
| // write all the options to a temp file and use it ro run the process | |||
| @@ -220,34 +222,33 @@ public abstract class AbstractMetamataTask extends Task{ | |||
| if (process.execute() != 0) { | |||
| throw new BuildException("Metamata task failed."); | |||
| } | |||
| } catch (IOException e){ | |||
| throw new BuildException("Failed to launch Metamata task: " + e); | |||
| } catch (IOException e) { | |||
| throw new BuildException("Failed to launch Metamata task", e); | |||
| } | |||
| } | |||
| /** clean up all the mess that we did with temporary objects */ | |||
| protected void cleanUp(){ | |||
| if (optionsFile != null){ | |||
| protected void cleanUp() { | |||
| if (optionsFile != null) { | |||
| optionsFile.delete(); | |||
| optionsFile = null; | |||
| } | |||
| } | |||
| /** return the location of the jar file used to run */ | |||
| protected final File getMetamataJar(File home){ | |||
| return new File(new File(home.getAbsolutePath()), "lib/metamata.jar"); | |||
| protected final File getMetamataJar(File home) { | |||
| return new File(home, "lib/metamata.jar"); | |||
| } | |||
| /** validate options set */ | |||
| protected void checkOptions() throws BuildException { | |||
| // do some validation first | |||
| if (metamataHome == null || !metamataHome.exists()){ | |||
| throw new BuildException("'metamatahome' must point to Metamata home directory."); | |||
| if (metamataHome == null || !metamataHome.exists()) { | |||
| throw new BuildException("'home' must point to Metamata home directory."); | |||
| } | |||
| metamataHome = project.resolveFile(metamataHome.getPath()); | |||
| File jar = getMetamataJar(metamataHome); | |||
| if (!jar.exists()){ | |||
| throw new BuildException( jar + " does not exist. Check your metamata installation."); | |||
| if (!jar.exists()) { | |||
| throw new BuildException(jar + " does not exist. Check your metamata installation."); | |||
| } | |||
| } | |||
| @@ -261,65 +262,94 @@ public abstract class AbstractMetamataTask extends Task{ | |||
| fw = new FileWriter(tofile); | |||
| PrintWriter pw = new PrintWriter(fw); | |||
| final int size = options.size(); | |||
| for (int i = 0; i < size; i++){ | |||
| pw.println( options.elementAt(i) ); | |||
| for (int i = 0; i < size; i++) { | |||
| pw.println(options.elementAt(i)); | |||
| } | |||
| pw.flush(); | |||
| } catch (IOException e){ | |||
| } catch (IOException e) { | |||
| throw new BuildException("Error while writing options file " + tofile, e); | |||
| } finally { | |||
| if (fw != null){ | |||
| if (fw != null) { | |||
| try { | |||
| fw.close(); | |||
| } catch (IOException ignored){} | |||
| } catch (IOException ignored) { | |||
| } | |||
| } | |||
| } | |||
| } | |||
| protected Hashtable getFileMapping(){ | |||
| protected Hashtable getFileMapping() { | |||
| return includedFiles; | |||
| } | |||
| /** | |||
| * convenient method for JDK 1.1. Will copy all elements from src to dest | |||
| */ | |||
| protected static final void addAllVector(Vector dest, Enumeration files){ | |||
| protected static final void addAllVector(Vector dest, Enumeration files) { | |||
| while (files.hasMoreElements()) { | |||
| dest.addElement( files.nextElement() ); | |||
| dest.addElement(files.nextElement()); | |||
| } | |||
| } | |||
| protected static final File createTmpFile(){ | |||
| // must be compatible with JDK 1.1 !!!! | |||
| final long rand = (new Random(System.currentTimeMillis())).nextLong(); | |||
| File file = new File("metamata" + rand + ".tmp"); | |||
| return file; | |||
| protected final File createTmpFile() { | |||
| return FileUtils.newFileUtils().createTempFile("metamata", ".tmp", getProject().getBaseDir()); | |||
| } | |||
| /** | |||
| * @return the list of .java files (as their absolute path) that should | |||
| * be audited. | |||
| */ | |||
| protected Hashtable scanFileSets(){ | |||
| protected Hashtable scanSources(Hashtable map) { | |||
| Hashtable files = new Hashtable(); | |||
| for (int i = 0; i < fileSets.size(); i++){ | |||
| for (int i = 0; i < fileSets.size(); i++) { | |||
| FileSet fs = (FileSet) fileSets.elementAt(i); | |||
| DirectoryScanner ds = fs.getDirectoryScanner(project); | |||
| ds.scan(); | |||
| String[] f = ds.getIncludedFiles(); | |||
| log(i + ") Adding " + f.length + " files from directory " + ds.getBasedir(), Project.MSG_VERBOSE); | |||
| for (int j = 0; j < f.length; j++){ | |||
| for (int j = 0; j < f.length; j++) { | |||
| String pathname = f[j]; | |||
| if ( pathname.endsWith(".java") ){ | |||
| File file = new File( ds.getBasedir(), pathname); | |||
| if (pathname.endsWith(".java")) { | |||
| File file = new File(ds.getBasedir(), pathname); | |||
| // file = project.resolveFile(file.getAbsolutePath()); | |||
| String classname = pathname.substring(0, pathname.length()-".java".length()); | |||
| String classname = pathname.substring(0, pathname.length() - ".java".length()); | |||
| classname = classname.replace(File.separatorChar, '.'); | |||
| files.put( file.getAbsolutePath(), classname ); // it's a java file, add it. | |||
| files.put(file.getAbsolutePath(), classname); // it's a java file, add it. | |||
| } | |||
| } | |||
| } | |||
| return files; | |||
| } | |||
| protected Hashtable scanSources(final Hashtable mapping, final String[] entries) { | |||
| final Vector javaFiles = new Vector(512); | |||
| for (int i = 0; i < entries.length; i++) { | |||
| final File f = new File(entries[i]); | |||
| if (f.isDirectory()) { | |||
| DirectoryScanner ds = new DirectoryScanner(); | |||
| ds.setBasedir(f); | |||
| ds.setIncludes(new String[]{"**/*.java"}); | |||
| ds.scan(); | |||
| String[] included = ds.getIncludedFiles(); | |||
| for (int j = 0; j < included.length; j++) { | |||
| javaFiles.addElement(new File(f, included[j])); | |||
| } | |||
| } else if (entries[i].endsWith(".java")) { | |||
| javaFiles.addElement(f); | |||
| } | |||
| } | |||
| // do the mapping paths/classname | |||
| final int count = javaFiles.size(); | |||
| for (int i = 0; i < count; i++) { | |||
| File file = (File) javaFiles.elementAt(i); | |||
| String pathname = Path.translateFile(file.getAbsolutePath()); | |||
| String classname = pathname.substring(0, pathname.length() - ".java".length()); | |||
| classname = classname.replace(File.separatorChar, '.'); | |||
| mapping.put(pathname, classname); | |||
| } | |||
| return mapping; | |||
| } | |||
| } | |||
| @@ -1,7 +1,7 @@ | |||
| /* | |||
| * The Apache Software License, Version 1.1 | |||
| * | |||
| * Copyright (c) 2001 The Apache Software Foundation. All rights | |||
| * Copyright (c) 2001-2002 The Apache Software Foundation. All rights | |||
| * reserved. | |||
| * | |||
| * Redistribution and use in source and binary forms, with or without | |||
| @@ -53,21 +53,19 @@ | |||
| */ | |||
| package org.apache.tools.ant.taskdefs.optional.metamata; | |||
| import java.io.File; | |||
| import java.io.FileOutputStream; | |||
| import java.io.IOException; | |||
| import java.io.OutputStream; | |||
| import java.util.Vector; | |||
| import org.apache.tools.ant.BuildException; | |||
| import org.apache.tools.ant.Project; | |||
| import org.apache.tools.ant.taskdefs.ExecuteStreamHandler; | |||
| import org.apache.tools.ant.taskdefs.LogStreamHandler; | |||
| import org.apache.tools.ant.types.FileSet; | |||
| import org.apache.tools.ant.types.Path; | |||
| import java.io.File; | |||
| import java.io.OutputStream; | |||
| import java.io.FileOutputStream; | |||
| import java.io.IOException; | |||
| import java.util.Vector; | |||
| /** | |||
| * Metamata Audit evaluates Java code for programming errors, weaknesses, and | |||
| * style violation. | |||
| @@ -81,7 +79,7 @@ import java.util.Vector; | |||
| * For more information, visit the website at | |||
| * <a href="http://www.metamata.com">www.metamata.com</a> | |||
| * | |||
| * @author <a href="mailto:sbailliez@imediation.com">Stephane Bailliez</a> | |||
| * @author <a href="mailto:sbailliez@apache.org">Stephane Bailliez</a> | |||
| */ | |||
| public class MAudit extends AbstractMetamataTask { | |||
| @@ -119,15 +117,24 @@ public class MAudit extends AbstractMetamataTask { | |||
| // (?:file:)?((?#filepath).+):((?#line)\\d+)\\s*:\\s+((?#message).*) | |||
| final static String AUDIT_PATTERN = "(?:file:)?(.+):(\\d+)\\s*:\\s+(.*)"; | |||
| protected File outFile = null; | |||
| protected Path searchPath = null; | |||
| protected boolean fix = false; | |||
| protected boolean list = false; | |||
| protected boolean unused = false; | |||
| private File outFile = null; | |||
| private Path searchPath = null; | |||
| private Path rulesPath = null; | |||
| private boolean fix = false; | |||
| private boolean list = false; | |||
| private boolean unused = false; | |||
| // add a bunch of undocumented options for the task | |||
| private boolean quiet = false; | |||
| private boolean exit = false; | |||
| private boolean offsets = false; | |||
| private boolean verbose = false; | |||
| private boolean fullsemanticize = false; | |||
| /** default constructor */ | |||
| public MAudit() { | |||
| @@ -135,49 +142,101 @@ public class MAudit extends AbstractMetamataTask { | |||
| } | |||
| /** set the destination file which should be an xml file */ | |||
| public void setTofile(File outFile){ | |||
| public void setTofile(File outFile) { | |||
| this.outFile = outFile; | |||
| } | |||
| public void setFix(boolean flag){ | |||
| public void setFix(boolean flag) { | |||
| this.fix = flag; | |||
| } | |||
| public void setList(boolean flag){ | |||
| public void setList(boolean flag) { | |||
| this.list = flag; | |||
| } | |||
| public void setUnused(boolean flag){ | |||
| public void setUnused(boolean flag) { | |||
| this.unused = flag; | |||
| } | |||
| public Path createSearchpath(){ | |||
| if (searchPath == null){ | |||
| searchPath = new Path(project); | |||
| public void setQuiet(boolean flag) { | |||
| this.quiet = flag; | |||
| } | |||
| public void setExit(boolean flag) { | |||
| this.exit = flag; | |||
| } | |||
| public void setOffsets(boolean flag) { | |||
| this.offsets = flag; | |||
| } | |||
| public void setVerbose(boolean flag) { | |||
| this.verbose = flag; | |||
| } | |||
| public void setFullsemanticize(boolean flag) { | |||
| this.fullsemanticize = flag; | |||
| } | |||
| /** one or more path for rules that must be placed before metamata.jar !! */ | |||
| public Path createRulespath() { | |||
| if (rulesPath == null) { | |||
| rulesPath = new Path(getProject()); | |||
| } | |||
| return rulesPath; | |||
| } | |||
| /** search path to use for unused global declarations */ | |||
| public Path createSearchpath() { | |||
| if (searchPath == null) { | |||
| searchPath = new Path(getProject()); | |||
| } | |||
| return searchPath; | |||
| } | |||
| protected Vector getOptions(){ | |||
| protected Vector getOptions() { | |||
| Vector options = new Vector(512); | |||
| // add the source path automatically from the fileset. | |||
| // to avoid redundancy... | |||
| for (int i = 0; i < fileSets.size(); i++) { | |||
| FileSet fs = (FileSet) fileSets.elementAt(i); | |||
| Path path = createSourcepath(); | |||
| File dir = fs.getDir(getProject()); | |||
| path.setLocation(dir); | |||
| } | |||
| // there is a bug in Metamata 2.0 build 37. The sourcepath argument does | |||
| // not work. So we will use the sourcepath prepended to classpath. (order | |||
| // is important since Metamata looks at .class and .java) | |||
| if (sourcePath != null){ | |||
| if (sourcePath != null) { | |||
| sourcePath.append(classPath); // srcpath is prepended | |||
| classPath = sourcePath; | |||
| sourcePath = null; // prevent from using -sourcepath | |||
| } | |||
| } | |||
| // don't forget to modify the pattern if you change the options reporting | |||
| if (classPath != null){ | |||
| if (classPath != null) { | |||
| options.addElement("-classpath"); | |||
| options.addElement(classPath.toString()); | |||
| } | |||
| // suppress copyright msg when running, we will let it so that this | |||
| // will be the only output to the console if in xml mode | |||
| // options.addElement("-quiet"); | |||
| if (fix){ | |||
| if (quiet) { | |||
| options.addElement("-quiet"); | |||
| } | |||
| if (fullsemanticize) { | |||
| options.addElement("-full-semanticize"); | |||
| } | |||
| if (verbose) { | |||
| options.addElement("-verbose"); | |||
| } | |||
| if (offsets) { | |||
| options.addElement("-offsets"); | |||
| } | |||
| if (exit) { | |||
| options.addElement("-exit"); | |||
| } | |||
| if (fix) { | |||
| options.addElement("-fix"); | |||
| } | |||
| options.addElement("-fullpath"); | |||
| @@ -185,43 +244,45 @@ public class MAudit extends AbstractMetamataTask { | |||
| // generate .maudit files much more detailed than the report | |||
| // I don't like it very much, I think it could be interesting | |||
| // to get all .maudit files and include them in the XML. | |||
| if (list){ | |||
| if (list) { | |||
| options.addElement("-list"); | |||
| } | |||
| if (sourcePath != null){ | |||
| if (sourcePath != null) { | |||
| options.addElement("-sourcepath"); | |||
| options.addElement(sourcePath.toString()); | |||
| } | |||
| if (unused){ | |||
| addAllVector(options, includedFiles.keys()); | |||
| if (unused) { | |||
| options.addElement("-unused"); | |||
| options.addElement(searchPath.toString()); | |||
| } | |||
| addAllVector(options, includedFiles.keys()); | |||
| return options; | |||
| } | |||
| protected void checkOptions() throws BuildException { | |||
| super.checkOptions(); | |||
| if (unused && searchPath == null){ | |||
| if (unused && searchPath == null) { | |||
| throw new BuildException("'searchpath' element must be set when looking for 'unused' declarations."); | |||
| } | |||
| if (!unused && searchPath != null){ | |||
| if (!unused && searchPath != null) { | |||
| log("'searchpath' element ignored. 'unused' attribute is disabled.", Project.MSG_WARN); | |||
| } | |||
| if (rulesPath != null) { | |||
| cmdl.createClasspath(getProject()).addExisting(rulesPath); | |||
| } | |||
| } | |||
| protected ExecuteStreamHandler createStreamHandler() throws BuildException { | |||
| ExecuteStreamHandler handler = null; | |||
| // if we didn't specify a file, then use a screen report | |||
| if (outFile == null){ | |||
| handler = new LogStreamHandler(this, Project.MSG_INFO, Project.MSG_INFO); | |||
| if (outFile == null) { | |||
| handler = new LogStreamHandler(this, Project.MSG_INFO, Project.MSG_INFO); | |||
| } else { | |||
| try { | |||
| //XXX | |||
| OutputStream out = new FileOutputStream( outFile ); | |||
| OutputStream out = new FileOutputStream(outFile); | |||
| handler = new MAuditStreamHandler(this, out); | |||
| } catch (IOException e){ | |||
| } catch (IOException e) { | |||
| throw new BuildException(e); | |||
| } | |||
| } | |||
| @@ -242,18 +303,9 @@ public class MAudit extends AbstractMetamataTask { | |||
| /** the inner class used to report violation information */ | |||
| final static class Violation { | |||
| int line; | |||
| String line; | |||
| String error; | |||
| } | |||
| /** handy factory to create a violation */ | |||
| static final Violation createViolation(int line, String msg){ | |||
| Violation violation = new Violation(); | |||
| violation.line = line; | |||
| violation.error = msg; | |||
| return violation; | |||
| } | |||
| } | |||
| @@ -1,7 +1,7 @@ | |||
| /* | |||
| * The Apache Software License, Version 1.1 | |||
| * | |||
| * Copyright (c) 2001 The Apache Software Foundation. All rights | |||
| * Copyright (c) 2001-2002 The Apache Software Foundation. All rights | |||
| * reserved. | |||
| * | |||
| * Redistribution and use in source and binary forms, with or without | |||
| @@ -53,31 +53,34 @@ | |||
| */ | |||
| package org.apache.tools.ant.taskdefs.optional.metamata; | |||
| import org.apache.tools.ant.Project; | |||
| import org.apache.tools.ant.taskdefs.ExecuteStreamHandler; | |||
| import org.apache.tools.ant.util.regexp.RegexpMatcher; | |||
| import org.apache.tools.ant.util.regexp.RegexpMatcherFactory; | |||
| import org.apache.tools.ant.util.DOMElementWriter; | |||
| import org.w3c.dom.Document; | |||
| import org.w3c.dom.Element; | |||
| import java.io.BufferedReader; | |||
| import java.io.OutputStream; | |||
| import java.io.InputStream; | |||
| import java.io.File; | |||
| import java.io.IOException; | |||
| import java.io.InputStream; | |||
| import java.io.InputStreamReader; | |||
| import java.io.Writer; | |||
| import java.io.OutputStream; | |||
| import java.io.OutputStreamWriter; | |||
| import java.util.Hashtable; | |||
| import java.io.Writer; | |||
| import java.util.Date; | |||
| import java.util.Enumeration; | |||
| import java.util.Hashtable; | |||
| import java.util.Vector; | |||
| import javax.xml.parsers.DocumentBuilder; | |||
| import javax.xml.parsers.DocumentBuilderFactory; | |||
| import org.w3c.dom.Document; | |||
| import org.w3c.dom.Element; | |||
| import org.apache.tools.ant.Project; | |||
| import org.apache.tools.ant.taskdefs.ExecuteStreamHandler; | |||
| import org.apache.tools.ant.taskdefs.LogOutputStream; | |||
| import org.apache.tools.ant.taskdefs.StreamPumper; | |||
| import org.apache.tools.ant.util.DOMElementWriter; | |||
| import org.apache.tools.ant.util.DateUtils; | |||
| import org.apache.tools.ant.util.StringUtils; | |||
| import org.apache.tools.ant.util.regexp.RegexpMatcher; | |||
| import org.apache.tools.ant.util.regexp.RegexpMatcherFactory; | |||
| /** | |||
| * This is a very bad stream handler for the MAudit task. | |||
| @@ -93,31 +96,41 @@ import javax.xml.parsers.DocumentBuilderFactory; | |||
| * <li>it does not report error that goes to stderr. | |||
| * </ul> | |||
| * | |||
| * @author <a href="sbailliez@imediation.com">Stephane Bailliez</a> | |||
| * @author <a href="mailto:sbailliez@apache.org">Stephane Bailliez</a> | |||
| */ | |||
| class MAuditStreamHandler implements ExecuteStreamHandler { | |||
| protected MAudit task; | |||
| /** parent task */ | |||
| private MAudit task; | |||
| /** reader for stdout */ | |||
| protected BufferedReader br; | |||
| private BufferedReader br; | |||
| /** matcher that will be used to extract the info from the line */ | |||
| protected RegexpMatcher matcher; | |||
| private RegexpMatcher matcher; | |||
| /** | |||
| * this is where the XML output will go, should mostly be a file | |||
| * the caller is responsible for flushing and closing this stream | |||
| */ | |||
| protected OutputStream xmlOut = null; | |||
| private OutputStream xmlOut = null; | |||
| /** error stream, might be useful to spit out error messages */ | |||
| private OutputStream errStream; | |||
| /** thread pumping out error stream */ | |||
| private Thread errThread; | |||
| /** | |||
| * the multimap. The key in the map is the filepath that caused the audit | |||
| * error and the value is a vector of MAudit.Violation entries. | |||
| */ | |||
| protected Hashtable auditedFiles = new Hashtable(); | |||
| private Hashtable auditedFiles = new Hashtable(); | |||
| MAuditStreamHandler(MAudit task, OutputStream xmlOut){ | |||
| /** program start timestamp for reporting purpose */ | |||
| private Date program_start; | |||
| MAuditStreamHandler(MAudit task, OutputStream xmlOut) { | |||
| this.task = task; | |||
| this.xmlOut = xmlOut; | |||
| /** the matcher should be the Oro one. I don't know about the other one */ | |||
| @@ -126,10 +139,14 @@ class MAuditStreamHandler implements ExecuteStreamHandler { | |||
| } | |||
| /** Ignore. */ | |||
| public void setProcessInputStream(OutputStream os) {} | |||
| public void setProcessInputStream(OutputStream os) { | |||
| } | |||
| /** Ignore. */ | |||
| public void setProcessErrorStream(InputStream is) {} | |||
| public void setProcessErrorStream(InputStream is) { | |||
| errStream = new LogOutputStream(task, Project.MSG_ERR); | |||
| errThread = createPump(is, errStream); | |||
| } | |||
| /** Set the inputstream */ | |||
| public void setProcessOutputStream(InputStream is) throws IOException { | |||
| @@ -138,6 +155,8 @@ class MAuditStreamHandler implements ExecuteStreamHandler { | |||
| /** Invokes parseOutput. This will block until the end :-(*/ | |||
| public void start() throws IOException { | |||
| program_start = new Date(); | |||
| errThread.start(); | |||
| parseOutput(br); | |||
| } | |||
| @@ -146,19 +165,32 @@ class MAuditStreamHandler implements ExecuteStreamHandler { | |||
| * the MAudit output and write it to the output. | |||
| */ | |||
| public void stop() { | |||
| // make sure to flush err stream | |||
| try { | |||
| errThread.join(); | |||
| } catch (InterruptedException e) { | |||
| } | |||
| try { | |||
| errStream.flush(); | |||
| } catch (IOException e) { | |||
| } | |||
| // serialize the content as XML, move this to another method | |||
| // this is the only code that could be needed to be overrided | |||
| // this is the only code that could be needed to be overriden | |||
| Document doc = getDocumentBuilder().newDocument(); | |||
| Element rootElement = doc.createElement("classes"); | |||
| Enumeration keys = auditedFiles.keys(); | |||
| Hashtable filemapping = task.getFileMapping(); | |||
| final Date now = new Date(); | |||
| rootElement.setAttribute("snapshot_created", DateUtils.format(now, DateUtils.ISO8601_DATETIME_PATTERN)); | |||
| rootElement.setAttribute("elapsed_time", String.valueOf(now.getTime() - program_start.getTime())); | |||
| rootElement.setAttribute("program_start", DateUtils.format(now, DateUtils.ISO8601_DATETIME_PATTERN)); | |||
| rootElement.setAttribute("audited", String.valueOf(filemapping.size())); | |||
| rootElement.setAttribute("reported", String.valueOf(auditedFiles.size())); | |||
| int errors = 0; | |||
| while (keys.hasMoreElements()){ | |||
| String filepath = (String)keys.nextElement(); | |||
| Vector v = (Vector)auditedFiles.get(filepath); | |||
| String fullclassname = (String)filemapping.get(filepath); | |||
| while (keys.hasMoreElements()) { | |||
| String filepath = (String) keys.nextElement(); | |||
| Vector v = (Vector) auditedFiles.get(filepath); | |||
| String fullclassname = (String) filemapping.get(filepath); | |||
| if (fullclassname == null) { | |||
| task.getProject().log("Could not find class mapping for " + filepath, Project.MSG_WARN); | |||
| continue; | |||
| @@ -169,12 +201,13 @@ class MAuditStreamHandler implements ExecuteStreamHandler { | |||
| Element clazz = doc.createElement("class"); | |||
| clazz.setAttribute("package", pkg); | |||
| clazz.setAttribute("name", clazzname); | |||
| clazz.setAttribute("violations", String.valueOf(v.size())); | |||
| errors += v.size(); | |||
| for (int i = 0; i < v.size(); i++){ | |||
| MAudit.Violation violation = (MAudit.Violation)v.elementAt(i); | |||
| final int violationCount = v.size(); | |||
| clazz.setAttribute("violations", String.valueOf(violationCount)); | |||
| errors += violationCount; | |||
| for (int i = 0; i < violationCount; i++) { | |||
| MAudit.Violation violation = (MAudit.Violation) v.elementAt(i); | |||
| Element error = doc.createElement("violation"); | |||
| error.setAttribute("line", String.valueOf(violation.line)); | |||
| error.setAttribute("line", violation.line); | |||
| error.setAttribute("message", violation.error); | |||
| clazz.appendChild(error); | |||
| } | |||
| @@ -183,54 +216,67 @@ class MAuditStreamHandler implements ExecuteStreamHandler { | |||
| rootElement.setAttribute("violations", String.valueOf(errors)); | |||
| // now write it to the outputstream, not very nice code | |||
| if (xmlOut != null) { | |||
| Writer wri = null; | |||
| try { | |||
| wri = new OutputStreamWriter(xmlOut, "UTF-8"); | |||
| wri.write("<?xml version=\"1.0\"?>\n"); | |||
| (new DOMElementWriter()).write(rootElement, wri, 0, " "); | |||
| wri.flush(); | |||
| } catch(IOException exc) { | |||
| task.log("Unable to write log file", Project.MSG_ERR); | |||
| } finally { | |||
| if (xmlOut != System.out && xmlOut != System.err) { | |||
| if (wri != null) { | |||
| try { | |||
| wri.close(); | |||
| } catch (IOException e) {} | |||
| } | |||
| Writer wri = null; | |||
| try { | |||
| wri = new OutputStreamWriter(xmlOut, "UTF-8"); | |||
| wri.write("<?xml version=\"1.0\"?>\n"); | |||
| (new DOMElementWriter()).write(rootElement, wri, 0, " "); | |||
| wri.flush(); | |||
| } catch (IOException exc) { | |||
| task.log("Unable to write log file", Project.MSG_ERR); | |||
| } finally { | |||
| if (wri != null) { | |||
| try { | |||
| wri.close(); | |||
| } catch (IOException e) { | |||
| } | |||
| } | |||
| } | |||
| } | |||
| protected static DocumentBuilder getDocumentBuilder() { | |||
| try { | |||
| return DocumentBuilderFactory.newInstance().newDocumentBuilder(); | |||
| } | |||
| catch(Exception exc) { | |||
| } catch (Exception exc) { | |||
| throw new ExceptionInInitializerError(exc); | |||
| } | |||
| } | |||
| /** | |||
| * Creates a stream pumper to copy the given input stream to the given output stream. | |||
| */ | |||
| protected Thread createPump(InputStream is, OutputStream os) { | |||
| final Thread result = new Thread(new StreamPumper(is, os)); | |||
| result.setDaemon(true); | |||
| return result; | |||
| } | |||
| /** read each line and process it */ | |||
| protected void parseOutput(BufferedReader br) throws IOException { | |||
| String line = null; | |||
| while ( (line = br.readLine()) != null ){ | |||
| while ((line = br.readLine()) != null) { | |||
| processLine(line); | |||
| } | |||
| } | |||
| // we suppose here that there is only one report / line. | |||
| // There will obviouslly be a problem if the message is on several lines... | |||
| protected void processLine(String line){ | |||
| protected void processLine(String line) { | |||
| Vector matches = matcher.getGroups(line); | |||
| if (matches != null) { | |||
| String file = (String)matches.elementAt(1); | |||
| int lineNum = Integer.parseInt((String)matches.elementAt(2)); | |||
| String msg = (String)matches.elementAt(3); | |||
| addViolationEntry(file, MAudit.createViolation(lineNum, msg) ); | |||
| String file = (String) matches.elementAt(1); | |||
| MAudit.Violation violation = new MAudit.Violation(); | |||
| violation.line = (String) matches.elementAt(2); | |||
| violation.error = (String) matches.elementAt(3); | |||
| // remove the pathname from any messages and let the classname only. | |||
| final int pos = file.lastIndexOf(File.separatorChar); | |||
| if ((pos != -1) && (pos != file.length() - 1)) { | |||
| String filename = file.substring(pos + 1); | |||
| violation.error = StringUtils.replace(violation.error, | |||
| "file:" + file, filename); | |||
| } | |||
| addViolationEntry(file, violation); | |||
| } else { | |||
| // this doesn't match..report it as info, it could be | |||
| // either the copyright, summary or a multiline message (damn !) | |||
| @@ -239,14 +285,14 @@ class MAuditStreamHandler implements ExecuteStreamHandler { | |||
| } | |||
| /** add a violation entry for the file */ | |||
| protected void addViolationEntry(String file, MAudit.Violation entry){ | |||
| Vector violations = (Vector)auditedFiles.get(file); | |||
| // if there is no decl for this file yet, create it. | |||
| if (violations == null){ | |||
| violations = new Vector(); | |||
| auditedFiles.put(file, violations); | |||
| } | |||
| violations.add( entry ); | |||
| protected void addViolationEntry(String file, MAudit.Violation entry) { | |||
| Vector violations = (Vector) auditedFiles.get(file); | |||
| // if there is no decl for this file yet, create it. | |||
| if (violations == null) { | |||
| violations = new Vector(); | |||
| auditedFiles.put(file, violations); | |||
| } | |||
| violations.addElement(entry); | |||
| } | |||
| } | |||
| @@ -53,21 +53,19 @@ | |||
| */ | |||
| package org.apache.tools.ant.taskdefs.optional.metamata; | |||
| import java.io.File; | |||
| import java.io.FileInputStream; | |||
| import java.io.FileOutputStream; | |||
| import java.io.IOException; | |||
| import java.util.Vector; | |||
| import org.apache.tools.ant.BuildException; | |||
| import org.apache.tools.ant.Project; | |||
| import org.apache.tools.ant.taskdefs.ExecuteStreamHandler; | |||
| import org.apache.tools.ant.taskdefs.LogStreamHandler; | |||
| import org.apache.tools.ant.types.EnumeratedAttribute; | |||
| import org.apache.tools.ant.types.Path; | |||
| import java.io.File; | |||
| import java.io.FileInputStream; | |||
| import java.io.IOException; | |||
| import java.io.FileOutputStream; | |||
| import java.util.Vector; | |||
| /** | |||
| * Calculates global complexity and quality metrics on Java source code. | |||
| * | |||
| @@ -114,15 +112,15 @@ Format Options | |||
| */ | |||
| /** the granularity mode. Should be one of 'files', 'methods' and 'types'. */ | |||
| protected String granularity = null; | |||
| private String granularity = null; | |||
| /** the XML output file */ | |||
| protected File outFile = null; | |||
| private File outFile = null; | |||
| /** the location of the temporary txt report */ | |||
| protected File tmpFile = createTmpFile(); | |||
| private File tmpFile; | |||
| protected Path path = null; | |||
| private Path path = null; | |||
| //--------------------------- PUBLIC METHODS ------------------------------- | |||
| @@ -131,20 +129,29 @@ Format Options | |||
| super("com.metamata.sc.MMetrics"); | |||
| } | |||
| /** | |||
| * Attributes for granularity. | |||
| */ | |||
| public static class GranularityAttribute extends EnumeratedAttribute { | |||
| public String[] getValues() { | |||
| return new String[]{"compilation-units", "files", "methods", "types", "packages"}; | |||
| } | |||
| } | |||
| /** | |||
| * set the granularity of the audit. Should be one of 'files', 'methods' | |||
| * or 'types'. | |||
| * @param granularity the audit reporting mode. | |||
| */ | |||
| public void setGranularity(String granularity){ | |||
| this.granularity = granularity; | |||
| public void setGranularity(GranularityAttribute granularity) { | |||
| this.granularity = granularity.getValue(); | |||
| } | |||
| /** | |||
| * Set the output XML file | |||
| * @param file the xml file to write the XML report to. | |||
| */ | |||
| public void setTofile(File file){ | |||
| public void setTofile(File file) { | |||
| this.outFile = file; | |||
| } | |||
| @@ -152,7 +159,7 @@ Format Options | |||
| * Set a new path (directory) to measure metrics from. | |||
| * @return the path instance to use. | |||
| */ | |||
| public Path createPath(){ | |||
| public Path createPath() { | |||
| if (path == null) { | |||
| path = new Path(project); | |||
| } | |||
| @@ -167,27 +174,24 @@ Format Options | |||
| protected void checkOptions() throws BuildException { | |||
| super.checkOptions(); | |||
| if ( !"files".equals(granularity) && !"methods".equals(granularity) | |||
| && !"types".equals(granularity) ){ | |||
| throw new BuildException("Metrics reporting granularity is invalid. Must be one of 'files', 'methods', 'types'"); | |||
| } | |||
| if (outFile == null){ | |||
| if (outFile == null) { | |||
| throw new BuildException("Output XML file must be set via 'tofile' attribute."); | |||
| } | |||
| if (path == null && fileSets.size() == 0){ | |||
| if (path == null && fileSets.size() == 0) { | |||
| throw new BuildException("Must set either paths (path element) or files (fileset element)"); | |||
| } | |||
| // I don't accept dirs and files at the same time, I cannot recognize the semantic in the result | |||
| if (path != null && fileSets.size() > 0){ | |||
| if (path != null && fileSets.size() > 0) { | |||
| throw new BuildException("Cannot set paths (path element) and files (fileset element) at the same time"); | |||
| } | |||
| tmpFile = createTmpFile(); | |||
| } | |||
| protected void execute0(ExecuteStreamHandler handler) throws BuildException { | |||
| super.execute0(handler); | |||
| transformFile(); | |||
| } | |||
| /** | |||
| * transform the generated file via the handler | |||
| * This function can either be called if the result is written to the output | |||
| @@ -197,8 +201,8 @@ Format Options | |||
| protected void transformFile() throws BuildException { | |||
| FileInputStream tmpStream = null; | |||
| try { | |||
| tmpStream = new FileInputStream( tmpFile ); | |||
| } catch (IOException e){ | |||
| tmpStream = new FileInputStream(tmpFile); | |||
| } catch (IOException e) { | |||
| throw new BuildException("Error reading temporary file: " + tmpFile, e); | |||
| } | |||
| FileOutputStream xmlStream = null; | |||
| @@ -208,29 +212,31 @@ Format Options | |||
| xmlHandler.setProcessOutputStream(tmpStream); | |||
| xmlHandler.start(); | |||
| xmlHandler.stop(); | |||
| } catch (IOException e){ | |||
| } catch (IOException e) { | |||
| throw new BuildException("Error creating output file: " + outFile, e); | |||
| } finally { | |||
| if (xmlStream != null){ | |||
| if (xmlStream != null) { | |||
| try { | |||
| xmlStream.close(); | |||
| } catch (IOException ignored){} | |||
| } catch (IOException ignored) { | |||
| } | |||
| } | |||
| if (tmpStream != null){ | |||
| if (tmpStream != null) { | |||
| try { | |||
| tmpStream.close(); | |||
| } catch (IOException ignored){} | |||
| } catch (IOException ignored) { | |||
| } | |||
| } | |||
| } | |||
| } | |||
| /** cleanup the temporary txt report */ | |||
| protected void cleanUp() throws BuildException { | |||
| try { | |||
| super.cleanUp(); | |||
| } finally { | |||
| if (tmpFile != null){ | |||
| if (tmpFile != null) { | |||
| tmpFile.delete(); | |||
| tmpFile = null; | |||
| } | |||
| @@ -242,52 +248,52 @@ Format Options | |||
| * a normal logger here, otherwise we could use the metrics handler | |||
| * directly to capture and transform the output on stdout to XML. | |||
| */ | |||
| protected ExecuteStreamHandler createStreamHandler(){ | |||
| protected ExecuteStreamHandler createStreamHandler() { | |||
| // write the report directtly to an XML stream | |||
| // return new MMetricsStreamHandler(this, xmlStream); | |||
| return new LogStreamHandler(this, Project.MSG_INFO, Project.MSG_INFO); | |||
| } | |||
| protected Vector getOptions(){ | |||
| protected Vector getOptions() { | |||
| Vector options = new Vector(512); | |||
| // there is a bug in Metamata 2.0 build 37. The sourcepath argument does | |||
| // not work. So we will use the sourcepath prepended to classpath. (order | |||
| // is important since Metamata looks at .class and .java) | |||
| if (sourcePath != null){ | |||
| if (sourcePath != null) { | |||
| sourcePath.append(classPath); // srcpath is prepended | |||
| classPath = sourcePath; | |||
| sourcePath = null; // prevent from using -sourcepath | |||
| } | |||
| // don't forget to modify the pattern if you change the options reporting | |||
| if (classPath != null){ | |||
| if (classPath != null) { | |||
| options.addElement("-classpath"); | |||
| options.addElement(classPath); | |||
| options.addElement(classPath.toString()); | |||
| } | |||
| options.addElement( "-output" ); | |||
| options.addElement( tmpFile.toString() ); | |||
| options.addElement( "-" + granularity); | |||
| options.addElement("-output"); | |||
| options.addElement(tmpFile.toString()); | |||
| options.addElement("-" + granularity); | |||
| // display the metamata copyright | |||
| // options.addElement( "-quiet"); | |||
| options.addElement( "-format"); | |||
| options.addElement("-format"); | |||
| // need this because that's what the handler is using, it's | |||
| // way easier to process than any other separator | |||
| options.addElement( "tab"); | |||
| options.addElement("tab"); | |||
| // specify a / as the indent character, used by the handler. | |||
| options.addElement( "-i"); | |||
| options.addElement( "/"); | |||
| options.addElement("-i"); | |||
| options.addElement("/"); | |||
| // directories | |||
| String[] dirs = path.list(); | |||
| for (int i = 0; i < dirs.length; i++){ | |||
| options.addElement( dirs[i] ); | |||
| for (int i = 0; i < dirs.length; i++) { | |||
| options.addElement(dirs[i]); | |||
| } | |||
| // files next. | |||
| // files next. | |||
| addAllVector(options, includedFiles.keys()); | |||
| return options; | |||
| } | |||
| @@ -54,32 +54,36 @@ | |||
| package org.apache.tools.ant.taskdefs.optional.metamata; | |||
| import org.xml.sax.SAXException; | |||
| import org.xml.sax.Attributes; | |||
| import org.xml.sax.helpers.AttributesImpl; | |||
| import javax.xml.transform.TransformerFactory; | |||
| import javax.xml.transform.Transformer; | |||
| import javax.xml.transform.OutputKeys; | |||
| import javax.xml.transform.stream.StreamResult; | |||
| import javax.xml.transform.sax.TransformerHandler; | |||
| import javax.xml.transform.sax.SAXTransformerFactory; | |||
| import java.util.Stack; | |||
| import java.util.EmptyStackException; | |||
| import java.util.Enumeration; | |||
| import java.util.Vector; | |||
| import java.io.BufferedReader; | |||
| import java.io.IOException; | |||
| import java.io.InputStream; | |||
| import java.io.InputStreamReader; | |||
| import java.io.OutputStream; | |||
| import java.io.IOException; | |||
| import java.io.OutputStreamWriter; | |||
| import java.io.BufferedReader; | |||
| import java.io.InputStreamReader; | |||
| import java.text.ParseException; | |||
| import java.text.NumberFormat; | |||
| import java.text.DecimalFormat; | |||
| import java.text.NumberFormat; | |||
| import java.text.ParseException; | |||
| import java.util.Date; | |||
| import java.util.EmptyStackException; | |||
| import java.util.Enumeration; | |||
| import java.util.Stack; | |||
| import java.util.Vector; | |||
| import javax.xml.transform.OutputKeys; | |||
| import javax.xml.transform.Transformer; | |||
| import javax.xml.transform.TransformerFactory; | |||
| import javax.xml.transform.sax.SAXTransformerFactory; | |||
| import javax.xml.transform.sax.TransformerHandler; | |||
| import javax.xml.transform.stream.StreamResult; | |||
| import org.apache.tools.ant.taskdefs.ExecuteStreamHandler; | |||
| import org.apache.tools.ant.Task; | |||
| import org.xml.sax.Attributes; | |||
| import org.xml.sax.SAXException; | |||
| import org.xml.sax.helpers.AttributesImpl; | |||
| import org.apache.tools.ant.BuildException; | |||
| import org.apache.tools.ant.Project; | |||
| import org.apache.tools.ant.Task; | |||
| import org.apache.tools.ant.taskdefs.ExecuteStreamHandler; | |||
| import org.apache.tools.ant.util.DateUtils; | |||
| /** | |||
| * A handy metrics handler. Most of this code was done only with the | |||
| @@ -95,44 +99,44 @@ import org.apache.tools.ant.Project; | |||
| public class MMetricsStreamHandler implements ExecuteStreamHandler { | |||
| /** CLASS construct, it should be named something like 'MyClass' */ | |||
| protected final static String CLASS = "class"; | |||
| private final static String CLASS = "class"; | |||
| /** package construct, it should be look like 'com.mycompany.something' */ | |||
| protected final static String PACKAGE = "package"; | |||
| private final static String PACKAGE = "package"; | |||
| /** FILE construct, it should look like something 'MyClass.java' or 'MyClass.class' */ | |||
| protected final static String FILE = "file"; | |||
| private final static String FILE = "file"; | |||
| /** METHOD construct, it should looke like something 'doSomething(...)' or 'doSomething()' */ | |||
| protected final static String METHOD = "method"; | |||
| private final static String METHOD = "method"; | |||
| protected final static String[] ATTRIBUTES = { "name", "vg", "loc", | |||
| "dit", "noa", "nrm", "nlm", "wmc", "rfc", "dac", "fanout", "cbo", "lcom", "nocl" | |||
| }; | |||
| private final static String[] ATTRIBUTES = { | |||
| "name", "vg", "loc", "dit", "noa", "nrm", "nlm", "wmc", | |||
| "rfc", "dac", "fanout", "cbo", "lcom", "nocl"}; | |||
| /** reader for stdout */ | |||
| protected InputStream metricsOutput; | |||
| private InputStream metricsOutput; | |||
| /** | |||
| * this is where the XML output will go, should mostly be a file | |||
| * the caller is responsible for flushing and closing this stream | |||
| */ | |||
| protected OutputStream xmlOutputStream; | |||
| private OutputStream xmlOutputStream; | |||
| /** metrics handler */ | |||
| protected TransformerHandler metricsHandler; | |||
| private TransformerHandler metricsHandler; | |||
| /** the task */ | |||
| protected Task task; | |||
| private Task task; | |||
| /** | |||
| * the stack where are stored the metrics element so that they we can | |||
| * know if we have to close an element or not. | |||
| */ | |||
| protected Stack stack = new Stack(); | |||
| private Stack stack = new Stack(); | |||
| /** initialize this handler */ | |||
| MMetricsStreamHandler(Task task, OutputStream xmlOut){ | |||
| MMetricsStreamHandler(Task task, OutputStream xmlOut) { | |||
| this.task = task; | |||
| this.xmlOutputStream = xmlOut; | |||
| } | |||
| @@ -147,34 +151,39 @@ public class MMetricsStreamHandler implements ExecuteStreamHandler { | |||
| /** Set the inputstream */ | |||
| public void setProcessOutputStream(InputStream is) throws IOException { | |||
| metricsOutput = is; | |||
| metricsOutput = is; | |||
| } | |||
| public void start() throws IOException { | |||
| // create the transformer handler that will be used to serialize | |||
| // the output. | |||
| TransformerFactory factory = TransformerFactory.newInstance(); | |||
| if ( !factory.getFeature(SAXTransformerFactory.FEATURE) ){ | |||
| if (!factory.getFeature(SAXTransformerFactory.FEATURE)) { | |||
| throw new IllegalStateException("Invalid Transformer factory feature"); | |||
| } | |||
| try { | |||
| metricsHandler = ((SAXTransformerFactory)factory).newTransformerHandler(); | |||
| metricsHandler.setResult( new StreamResult( new OutputStreamWriter(xmlOutputStream, "UTF-8")) ); | |||
| metricsHandler = ((SAXTransformerFactory) factory).newTransformerHandler(); | |||
| metricsHandler.setResult(new StreamResult(new OutputStreamWriter(xmlOutputStream, "UTF-8"))); | |||
| Transformer transformer = metricsHandler.getTransformer(); | |||
| transformer.setOutputProperty(OutputKeys.INDENT, "yes"); | |||
| // start the document with a 'metrics' root | |||
| final Date now = new Date(); | |||
| metricsHandler.startDocument(); | |||
| AttributesImpl attr = new AttributesImpl(); | |||
| attr.addAttribute("", "company", "company", "CDATA", "metamata"); | |||
| attr.addAttribute("", "snapshot_created", "snapshot_created", "CDATA", | |||
| DateUtils.format(now, DateUtils.ISO8601_DATETIME_PATTERN)); | |||
| // attr.addAttribute("", "elapsed_time", "elapsed_time", "CDATA", String.valueOf(now.getTime() - program_start.getTime())); | |||
| attr.addAttribute("", "program_start", "program_start", "CDATA", | |||
| DateUtils.format(new Date(), DateUtils.ISO8601_DATETIME_PATTERN)); | |||
| metricsHandler.startElement("", "metrics", "metrics", attr); | |||
| // now parse the whole thing | |||
| parseOutput(); | |||
| } catch (Exception e){ | |||
| e.printStackTrace(); | |||
| throw new IOException(e.getMessage()); | |||
| } catch (Exception e) { | |||
| throw new BuildException(e); | |||
| } | |||
| } | |||
| @@ -185,15 +194,15 @@ public class MMetricsStreamHandler implements ExecuteStreamHandler { | |||
| try { | |||
| // we need to pop everything and close elements that have not been | |||
| // closed yet. | |||
| while ( stack.size() > 0){ | |||
| ElementEntry elem = (ElementEntry)stack.pop(); | |||
| while (stack.size() > 0) { | |||
| ElementEntry elem = (ElementEntry) stack.pop(); | |||
| metricsHandler.endElement("", elem.getType(), elem.getType()); | |||
| } | |||
| // close the root | |||
| metricsHandler.endElement("", "metrics", "metrics"); | |||
| // document is finished for good | |||
| metricsHandler.endDocument(); | |||
| } catch (SAXException e){ | |||
| } catch (SAXException e) { | |||
| e.printStackTrace(); | |||
| throw new IllegalStateException(e.getMessage()); | |||
| } | |||
| @@ -203,7 +212,7 @@ public class MMetricsStreamHandler implements ExecuteStreamHandler { | |||
| protected void parseOutput() throws IOException, SAXException { | |||
| BufferedReader br = new BufferedReader(new InputStreamReader(metricsOutput)); | |||
| String line = null; | |||
| while ( (line = br.readLine()) != null ){ | |||
| while ((line = br.readLine()) != null) { | |||
| processLine(line); | |||
| } | |||
| } | |||
| @@ -214,16 +223,16 @@ public class MMetricsStreamHandler implements ExecuteStreamHandler { | |||
| * @param line the line to process, it is normally a line full of metrics. | |||
| */ | |||
| protected void processLine(String line) throws SAXException { | |||
| if ( line.startsWith("Construct\tV(G)\tLOC\tDIT\tNOA\tNRM\tNLM\tWMC\tRFC\tDAC\tFANOUT\tCBO\tLCOM\tNOCL") ){ | |||
| if (line.startsWith("Construct\tV(G)\tLOC\tDIT\tNOA\tNRM\tNLM\tWMC\tRFC\tDAC\tFANOUT\tCBO\tLCOM\tNOCL")) { | |||
| return; | |||
| } | |||
| try { | |||
| MetricsElement elem = MetricsElement.parse(line); | |||
| startElement(elem); | |||
| } catch (ParseException e) { | |||
| e.printStackTrace(); | |||
| //e.printStackTrace(); | |||
| // invalid lines are sent to the output as information, it might be anything, | |||
| task.log(line, Project.MSG_INFO); | |||
| task.log(line, Project.MSG_INFO); | |||
| } | |||
| } | |||
| @@ -238,16 +247,17 @@ public class MMetricsStreamHandler implements ExecuteStreamHandler { | |||
| // if there are elements in the stack we possibly need to close one or | |||
| // more elements previous to this one until we got its parent | |||
| int indent = elem.getIndent(); | |||
| if ( stack.size() > 0 ){ | |||
| ElementEntry previous = (ElementEntry)stack.peek(); | |||
| if (stack.size() > 0) { | |||
| ElementEntry previous = (ElementEntry) stack.peek(); | |||
| // close nodes until you got the parent. | |||
| try { | |||
| while ( indent <= previous.getIndent() && stack.size() > 0){ | |||
| while (indent <= previous.getIndent() && stack.size() > 0) { | |||
| stack.pop(); | |||
| metricsHandler.endElement("", previous.getType(), previous.getType()); | |||
| previous = (ElementEntry)stack.peek(); | |||
| previous = (ElementEntry) stack.peek(); | |||
| } | |||
| } catch (EmptyStackException ignored){} | |||
| } catch (EmptyStackException ignored) { | |||
| } | |||
| } | |||
| // ok, now start the new construct | |||
| @@ -256,7 +266,7 @@ public class MMetricsStreamHandler implements ExecuteStreamHandler { | |||
| metricsHandler.startElement("", type, type, attrs); | |||
| // make sure we keep track of what we did, that's history | |||
| stack.push( new ElementEntry(type, indent) ); | |||
| stack.push(new ElementEntry(type, indent)); | |||
| } | |||
| /** | |||
| @@ -267,36 +277,36 @@ public class MMetricsStreamHandler implements ExecuteStreamHandler { | |||
| * @return the type of the metrics element, either PACKAGE, FILE, CLASS or | |||
| * METHOD. | |||
| */ | |||
| protected String getConstructType(MetricsElement elem){ | |||
| protected String getConstructType(MetricsElement elem) { | |||
| // ok no doubt, it's a file | |||
| if ( elem.isCompilationUnit() ){ | |||
| if (elem.isCompilationUnit()) { | |||
| return FILE; | |||
| } | |||
| // same, we're sure it's a method | |||
| if ( elem.isMethod() ){ | |||
| if (elem.isMethod()) { | |||
| return METHOD; | |||
| } | |||
| // if it's empty, and none of the above it should be a package | |||
| if ( stack.size() == 0 ){ | |||
| if (stack.size() == 0) { | |||
| return PACKAGE; | |||
| } | |||
| // ok, this is now black magic time, we will guess the type based on | |||
| // the previous type and its indent... | |||
| final ElementEntry previous = (ElementEntry)stack.peek(); | |||
| final ElementEntry previous = (ElementEntry) stack.peek(); | |||
| final String prevType = previous.getType(); | |||
| final int prevIndent = previous.getIndent(); | |||
| final int indent = elem.getIndent(); | |||
| // we're just under a file with a bigger indent so it's a class | |||
| if ( prevType.equals(FILE) && indent > prevIndent ){ | |||
| if (prevType.equals(FILE) && indent > prevIndent) { | |||
| return CLASS; | |||
| } | |||
| // we're just under a class with a greater or equals indent, it's a class | |||
| // (there might be several classes in a compilation unit and inner classes as well) | |||
| if ( prevType.equals(CLASS) && indent >= prevIndent ){ | |||
| if (prevType.equals(CLASS) && indent >= prevIndent) { | |||
| return CLASS; | |||
| } | |||
| @@ -308,17 +318,16 @@ public class MMetricsStreamHandler implements ExecuteStreamHandler { | |||
| /** | |||
| * Create all attributes of a MetricsElement skipping those who have an | |||
| * empty string | |||
| * @param elem | |||
| */ | |||
| protected Attributes createAttributes(MetricsElement elem){ | |||
| protected Attributes createAttributes(MetricsElement elem) { | |||
| AttributesImpl impl = new AttributesImpl(); | |||
| int i = 0; | |||
| String name = ATTRIBUTES[i++]; | |||
| impl.addAttribute("", name, name, "CDATA", elem.getName()); | |||
| Enumeration metrics = elem.getMetrics(); | |||
| for (; metrics.hasMoreElements(); i++){ | |||
| String value = (String)metrics.nextElement(); | |||
| if ( value.length() > 0 ){ | |||
| for (; metrics.hasMoreElements(); i++) { | |||
| String value = (String) metrics.nextElement(); | |||
| if (value.length() > 0) { | |||
| name = ATTRIBUTES[i]; | |||
| impl.addAttribute("", name, name, "CDATA", value); | |||
| } | |||
| @@ -333,13 +342,16 @@ public class MMetricsStreamHandler implements ExecuteStreamHandler { | |||
| private final static class ElementEntry { | |||
| private String type; | |||
| private int indent; | |||
| ElementEntry(String type, int indent){ | |||
| ElementEntry(String type, int indent) { | |||
| this.type = type; | |||
| this.indent = indent; | |||
| } | |||
| public String getType(){ | |||
| public String getType() { | |||
| return type; | |||
| } | |||
| public int getIndent() { | |||
| return indent; | |||
| } | |||
| @@ -351,6 +363,7 @@ class MetricsElement { | |||
| private final static NumberFormat METAMATA_NF; | |||
| private final static NumberFormat NEUTRAL_NF; | |||
| static { | |||
| METAMATA_NF = NumberFormat.getInstance(); | |||
| METAMATA_NF.setMaximumFractionDigits(1); | |||
| @@ -367,30 +380,30 @@ class MetricsElement { | |||
| private Vector metrics; | |||
| MetricsElement(int indent, String construct, Vector metrics){ | |||
| MetricsElement(int indent, String construct, Vector metrics) { | |||
| this.indent = indent; | |||
| this.construct = construct; | |||
| this.metrics = metrics; | |||
| } | |||
| public int getIndent(){ | |||
| public int getIndent() { | |||
| return indent; | |||
| } | |||
| public String getName(){ | |||
| public String getName() { | |||
| return construct; | |||
| } | |||
| public Enumeration getMetrics(){ | |||
| public Enumeration getMetrics() { | |||
| return metrics.elements(); | |||
| } | |||
| public boolean isCompilationUnit(){ | |||
| return ( construct.endsWith(".java") || construct.endsWith(".class") ); | |||
| public boolean isCompilationUnit() { | |||
| return (construct.endsWith(".java") || construct.endsWith(".class")); | |||
| } | |||
| public boolean isMethod(){ | |||
| return ( construct.endsWith("(...)") || construct.endsWith("()") ); | |||
| public boolean isMethod() { | |||
| return (construct.endsWith("(...)") || construct.endsWith("()")); | |||
| } | |||
| public static MetricsElement parse(String line) throws ParseException { | |||
| @@ -399,32 +412,32 @@ class MetricsElement { | |||
| // i'm using indexOf since I need to know if there are empty strings | |||
| // between tabs and I find it easier than with StringTokenizer | |||
| while ( (pos = line.indexOf('\t')) != -1 ){ | |||
| while ((pos = line.indexOf('\t')) != -1) { | |||
| String token = line.substring(0, pos); | |||
| // only parse what coudl be a valid number. ie not constructs nor no value | |||
| /*if (metrics.size() != 0 || token.length() != 0){ | |||
| Number num = METAMATA_NF.parse(token); // parse with Metamata NF | |||
| token = NEUTRAL_NF.format(num.doubleValue()); // and format with a neutral NF | |||
| }*/ | |||
| metrics.addElement( token ); | |||
| metrics.addElement(token); | |||
| line = line.substring(pos + 1); | |||
| } | |||
| metrics.addElement( line ); | |||
| metrics.addElement(line); | |||
| // there should be exactly 14 tokens (1 name + 13 metrics), if not, there is a problem ! | |||
| if ( metrics.size() != 14 ){ | |||
| throw new ParseException("Could not parse the following line as a metrics: -->" + line +"<--", -1); | |||
| if (metrics.size() != 14) { | |||
| throw new ParseException("Could not parse the following line as a metrics: -->" + line + "<--", -1); | |||
| } | |||
| // remove the first token it's made of the indentation string and the | |||
| // construct name, we'll need all this to figure out what type of | |||
| // construct it is since we lost all semantics :( | |||
| // (#indent[/]*)(#construct.*) | |||
| String name = (String)metrics.elementAt(0); | |||
| String name = (String) metrics.elementAt(0); | |||
| metrics.removeElementAt(0); | |||
| int indent = 0; | |||
| pos = name.lastIndexOf('/'); | |||
| if (pos != -1){ | |||
| if (pos != -1) { | |||
| name = name.substring(pos + 1); | |||
| indent = pos + 1; // indentation is last position of token + 1 | |||
| } | |||