- CliBuilder for command line interface
- ValidatorDescriptor class
- Consuming the properties file
As I mentioned in part 1, this port will not be an exact port because I am creating a groovy script rather than an Ant/Gant task - partly because I haven't played with Gant yet! Some of the existing Ant task code is shown below. For those familiar with creating Ant tasks, you will notice the class extends Task which is an abstract class. The code overrides the execute() method. The validator task checks the input and output parameters and then calls the validate() method to validate the files.
import java.io.File; import java.io.FileFilter; import java.io.FileInputStream; import java.io.FileWriter; import java.io.IOException; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Properties; import java.util.regex.PatternSyntaxException; import org.apache.tools.ant.BuildException; import org.apache.tools.ant.Task; public class Validator extends Task { private static final String DEFAULT_EXCLUDE = ""; // constants used to create the property file keys // --> extension..regex or extension. .description private static final String REGEX = ".regex"; private static final String DESCRIPTION = ".description"; private static final String EXCLUDE = ".exclude"; private String extension; private String directory; private String propertyFile; private String reportFile; private int fileCnt; private int errorCnt; private List descriptors; private Properties props; private Map errorMap; /** * Default constructor */ public Validator() { props = new Properties(); descriptors = new ArrayList(); errorMap = new HashMap(); errorCnt = 0; fileCnt = 0; } /** * Sets the extension for the task * @param extension extension of the files to be validated */ public void setExtension(String extension) { this.extension = extension; } /** * Sets the root directory to be processed. This directory * and all of its subdirectories will be processed. * @param directory directory to be processed. */ public void setDirectory(String directory) { this.directory = directory; } /** * Sets the root directory to be processed. This directory * and all of its subdirectories will be processed. * @param directory directory to be processed. */ public void setPropertyFile(String fileName) { this.propertyFile = fileName; } /** * Set the report file to be created as output * @param reportFile name of the html file to be * generated to show any errors */ public void setReportFile(String reportFile) { this.reportFile = reportFile; } /** * Creates an xml file from the bugs file */ public void execute() throws BuildException { checkParameters(); try { validate(); } catch (BuildException e) { throw e; } } /** * Check that all required attributes have been set * @throws BuildException if any of the required attributes * have not been set. */ private void checkParameters() { if (extension == null || extension.length() == 0 || directory == null || directory.length() == 0 || reportFile == null || reportFile.length() == 0 || propertyFile == null || propertyFile.length() == 0) throw new BuildException("Extension, directory, reportFile and property file attributes " + "must be defined for task <" + getTaskName() + "/>" ); } private void validate() { File dir = new File(directory); log("Processing file extension : "+extension+"\n"); try { props.load(new FileInputStream(propertyFile)); setDescriptors(); visitAllDirsAndFiles(dir); writeReport(); log("Validator processed "+fileCnt+ " files and found "+errorCnt+" possible errors!"); } catch (Exception e) { log(e.toString()); } } private void writeReport() { try { FileWriter writer = new FileWriter(reportFile); writeHeaders(writer); writeContents(writer); writeTrailer(writer); writer.close(); } catch (IOException e) { throw new BuildException(e); } } // some code omitted here - but shown later!!! }
Groovy replacement using CliBuilder
While not a whole lot shorter than the Ant equivalent, it is short, concise and a good example of CliBuilder usage.
def cli = new CliBuilder (usage: 'groovy Validator -e extension -p propertyFile -d directory -r reportFile') cli.h(longOpt: 'help', 'usage information') cli.e(longOpt:'extension', args:1, required:true, 'file extension to be validated') cli.p(longOpt:'prop', args:1, required:true, 'property file containing regular expresssion') cli.d(longOpt:'directory', args:1, required:true, 'base directory to start file search') cli.r(longOpt:'reportFile', args:1, required:true, 'output file for validation report') def opt = cli.parse(args) if (!opt) return if (opt.h) cli.usage() println "start processing $opt.e files using $opt.p from $opt.d and output to $opt.r"
RegExDescriptor becomes ValidatorDescriptor
First the java...
import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.regex.PatternSyntaxException; /** * Regular Expression Descriptor used to help * validate source files. * */ public class RegExDescriptor { private String description; private Pattern excludePattern; private Pattern pattern; public RegExDescriptor(String regExString, String description, String exclude) throws PatternSyntaxException { this.description = description; pattern = Pattern.compile(regExString, Pattern.CASE_INSENSITIVE); if (exclude != null && exclude.length() > 0) excludePattern = Pattern.compile(exclude); else excludePattern = null; } public Matcher getMatcher(String line) { return pattern.matcher(line); } public String getDescription() { return description; } public boolean excludeFile(String name) { if (excludePattern == null) return false; Matcher matcher = excludePattern.matcher(name); return matcher.find(); } public String getPattern() { return pattern.pattern(); } public String toString() { StringBuffer sb = new StringBuffer(); sb.append("Validator description=").append(description); sb.append(" regEx=").append(pattern.pattern()); if (excludePattern != null) sb.append(" exclude regEx=").append(excludePattern.pattern()); return sb.toString(); } }Now the Groovy equivalent, which I renamed to ValidatorDescriptor. Like a lot of Java classes ported to Groovy, it is significantly shorter since we don't need to specify the getters and setters. The toString() method is really needed but I used it in testing to display the ValidatorDescriptor classes created from the properties file.
class ValidatorDescriptor { String description String exclude String regex String toString() { "regEx="+regex + " description=" + description + (exclude ? " exclude("+exclude+")" : "") } }Consuming the properties file
The next task is to read the properties file and create a list of objects representing each of the errors I am attempting to capture. If you remember from part 1 of the posting, there can be three properties: regex, description and exclude. The java processing to read the file and create the list of RegExDescriptors was located in two steps: the first was to read the file into a Properties object and then call the setDescriptors method in the Validator class as part of the main method called by Ant.
The idea is to group the properties together by the extension and the number that is part of the key. Each matching set is used to construct the RegExDescriptor. The RegExDescriptor creates a Pattern for the regular expression checking for the error and another for the file name exclusion check. The patterns are compiled at construction time.
props.load(new FileInputStream(propertyFile)); setDescriptors(); // later in Validator /* * Setup the regular expression descriptors * using the extension to determine which properties * are to be used when validating the files. */ private void setDescriptors() { // strip the leading . and add one to end String prefix = extension; char c = extension.charAt(0); if (c == '.') prefix = prefix.substring(1); prefix = prefix + "."; int idx = 1; boolean done = false; // build list of validators using the extension to create the property keys while(!done) { String regexKey = prefix + idx + REGEX; if (props.containsKey(regexKey)) { String descriptionKey = prefix + idx + DESCRIPTION; String excludeKey = prefix + idx + EXCLUDE; try { idx++; String exclude = props.getProperty(excludeKey, DEFAULT_EXCLUDE); descriptors.add( new RegExDescriptor(props.getProperty(regexKey), props.getProperty(descriptionKey), exclude) ); } catch (PatternSyntaxException e) { StringBuffer sb = new StringBuffer(100); sb.append("Exception compiling regular expression key(").append(regexKey); sb.append(") :").append(props.getProperty(regexKey)).append("\n").append(e); sb.append("\nSkipping this expression...\n"); log(sb.toString()); } } else { done = true; } } }
Now for the Groovy implementation. It starts out the same, loading a Properties object. Next, we use the ConfigSlurper class to group and parse the properties. An empty list to hold the descriptors is defined and then we iterate across the extension (sql in my case) keys and build the new ValidatorDescriptor objects. Notice the use of the GString aware string as part of the key for calling the each closure.
Properties properties = new Properties(); try { properties.load(new FileInputStream('validator.properties')); } catch (IOException e) {} def config = new ConfigSlurper().parse(properties) def descriptorList = [] config."$extension".each { descriptorList << new ValidatorDescriptor(it.value) }
In the next (and last) posting in this series, we will cover the following:
- Replace the FileValidator class
- Using the HTML Builder to generate a report
- Complete listing of the script and classes