/*
* Copyright 2001-2002,2004 The Apache Software Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
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;
/**
* Invokes the Metamata Audit/ Webgain Quality Analyzer on a set of Java files.
* <p>
* <i>maudit</i> performs static analysis of the Java source code and byte
* code files to find and report errors of style and potential problems related
* to performance, maintenance and robustness. As a convenience, a stylesheet
* is given in <tt>etc</tt> directory, so that an HTML report can be generated
* from the XML file.
*
*/
public class MAudit extends AbstractMetamataTask {
/* As of Metamata 2.0, the command line of MAudit is as follows:
Usage
maudit <option>... <path>... [-unused <search-path>...]
Parameters
path File or directory to audit.
search-path File or directory to search for declaration uses.
Options
-arguments -A <file> Includes command line arguments from file.
-classpath -cp <path> Sets class path (also source path unless one
explicitly set). Overrides METAPATH/CLASSPATH.
-exit -x Exits after the first error.
-fix -f Automatically fixes certain errors.
-fullpath Prints full path for locations.
-help -h Prints help and exits.
-list -l Creates listing file for each audited file.
-offsets -off Offset and length for locations.
-output -o <file> Prints output to file.
-quiet -q Suppresses copyright and summary messages.
-sourcepath <path> Sets source path. Overrides SOURCEPATH.
-tab -t Prints a tab character after first argument.
-unused -u Finds declarations unused in search paths.
-verbose -v Prints all messages.
-version -V Prints version and exits.
*/
//---------------------- PUBLIC METHODS ------------------------------------
/** pattern used by maudit to report the error for a file */
/** RE does not seems to support regexp pattern with comments so i'm stripping it*/
// (?:file:)?((?#filepath).+):((?#line)\\d+)\\s*:\\s+((?#message).*)
static final String AUDIT_PATTERN = "(?:file:)?(.+):(\\d+)\\s*:\\s+(.*)";
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() {
super("com.metamata.gui.rc.MAudit");
}
/**
* The XML file to which the Audit result should be written to; required
*/
public void setTofile(File outFile) {
this.outFile = outFile;
}
/**
* Automatically fix certain errors
* (those marked as fixable in the manual);
* optional, default=false
*/
public void setFix(boolean flag) {
this.fix = flag;
}
/**
* Creates listing file for each audited file; optional, default false.
* When set, a .maudit file will be generated in the
* same location as the source file.
*/
public void setList(boolean flag) {
this.list = flag;
}
/**
* Finds declarations unused in search paths; optional, default false.
* It will look for unused global declarations
* in the source code within a use domain specified by the
* <tt>searchpath</tt> element.
*/
public void setUnused(boolean flag) {
this.unused = flag;
}
/**
* flag to suppress copyright and summary messages; default false.
* internal/testing only
* @ant.attribute ignore="true"
*/
public void setQuiet(boolean flag) {
this.quiet = flag;
}
/**
* flag to tell the task to exit after the first error.
* internal/testing only
* @ant.attribute ignore="true"
*/
public void setExit(boolean flag) {
this.exit = flag;
}
/**
* internal/testing only
* @ant.attribute ignore="true"
*/
public void setOffsets(boolean flag) {
this.offsets = flag;
}
/**
* flag to print all messages; optional, default false.
* internal/testing only
* @ant.attribute ignore="true"
*/
public void setVerbose(boolean flag) {
this.verbose = flag;
}
/**
* internal/testing only
* @ant.attribute ignore="true"
*/
public void setFullsemanticize(boolean flag) {
this.fullsemanticize = flag;
}
/**
* classpath for additional audit rules
* these 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;
* required when <tt>unused</tt> is set.
*/
public Path createSearchpath() {
if (searchPath == null) {
searchPath = new Path(getProject());
}
return searchPath;
}
/**
* create the option vector for the command
*/
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) {
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) {
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
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");
// 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) {
options.addElement("-list");
}
if (sourcePath != null) {
options.addElement("-sourcepath");
options.addElement(sourcePath.toString());
}
addAllVector(options, includedFiles.keys());
if (unused) {
options.addElement("-unused");
options.addElement(searchPath.toString());
}
return options;
}
/**
* validate the settings
*/
protected void checkOptions() throws BuildException {
super.checkOptions();
if (unused && searchPath == null) {
throw new BuildException("'searchpath' element must be set when "
+ "looking for 'unused' declarations.");
}
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 {
// if we didn't specify a file, then use a screen report
if (outFile == null) {
return new LogStreamHandler(this, Project.MSG_INFO, Project.MSG_ERR);
}
ExecuteStreamHandler handler = null;
OutputStream out = null;
try {
out = new FileOutputStream(outFile);
handler = new MAuditStreamHandler(this, out);
} catch (IOException e) {
throw new BuildException(e);
} finally {
if (out == null) {
try {
out.close();
} catch (IOException e) {
}
}
}
return handler;
}
protected void cleanUp() throws BuildException {
super.cleanUp();
// at this point if -list is used, we should move
// the .maudit file since we cannot choose their location :(
// the .maudit files match the .java files
// we'll use includedFiles to get the .maudit files.
/*if (out != null) {
// close it if not closed by the handler...
}*/
}
}