/* * Copyright (c) 2002 Scott Lamb <slamb@slamb.org> * This code is released under the MIT license; see the file LICENSE. */ package net.spy.ant; import java.io.BufferedReader; import java.io.File; import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; import java.io.PrintWriter; import java.util.ArrayList; import java.util.HashSet; import java.util.Set; import java.util.StringTokenizer; import net.spy.util.CloseUtil; import net.spy.util.SPGen; import org.apache.tools.ant.BuildException; import org.apache.tools.ant.DirectoryScanner; import org.apache.tools.ant.taskdefs.MatchingTask; /** * Generates Java code from SPT files. * * This is an Ant task that recurses a directory and invokes * net.spy.util.SPGen on .spt files to generate Java code. It does the usual * stuff expected of a Make-style tool: leaving up-to-date .java files intact, * creating output with a temporary file until it is finished so partial * builds don't create problems, etc. * * <p>Example ant build entry: * <pre><code> * <target name="spfiles"> * <taskdef name="spgen" classname="net.spy.util.SPGenTask"> * <classpath refid="compile.classpath"/> * </taskdef> * <spgen srcdir="${spt.dir}" destdir="${spt.dir}" * superclass="com.foo.MyDbsp"/> * </target> * </pre></code> * </p> * * @author Scott Lamb * @version $Revision: 1.7 $ $Date: 2003/08/05 09:01:05 $ **/ public class SPGenTask extends MatchingTask { private File srcDir; private File destDir; private String superclass=null; private String dbcpSuperclass=null; private String dbspSuperclass=null; private Set<String> interfaces=null; private boolean verbose=false; /** * Sets the source directory to search for .spt files. **/ public void setSrcdir(File to) { this.srcDir = to; } /** * Sets the destination directory to place .java files. * If not specified, assumed to be the same as the source directory. **/ public void setDestdir(File to) { this.destDir = to; } /** * Set the superclass for the generated class. */ public void setSuperclass(String sc) { this.superclass=sc; } /** * Set the DBCP superclass for the generated class. */ public void setDbcpSuperclass(String sc) { this.dbcpSuperclass=sc; } /** * Set the DBSP superclass for the generated class. */ public void setDbspSuperclass(String sc) { this.dbspSuperclass=sc; } /** * True if we want verbose transformations. */ public void setVerbose(boolean to) { this.verbose=to; } /** * Performs the operation. **/ @Override public void execute() throws BuildException { if (srcDir == null) { throw new BuildException("srcdir attribute must be set!", getLocation()); } if (!srcDir.isDirectory()) { throw new BuildException("source directory \"" + srcDir + "\" is not valid."); } if (destDir == null) { destDir = srcDir; } if (!destDir.isDirectory()) { throw new BuildException("destination directory \"" + destDir + "\" is not valid.", getLocation()); } DirectoryScanner ds = getDirectoryScanner(srcDir); String[] includes = {"**\\*.spt"}; ds.setIncludes(includes); ds.setBasedir(srcDir); ds.scan(); String[] files = ds.getIncludedFiles(); String[] toDoFiles = trimSPTList(files); if(toDoFiles.length > 0) { log("Compiling " + toDoFiles.length + " of " + files.length + " spt file" + (files.length == 1 ? "" : "s") + " to " + destDir); // Do the work for (int i = 0; i < toDoFiles.length; i++) { processFile(toDoFiles[i]); } } } private String[] trimSPTList(String input[]) { ArrayList<String> a=new ArrayList<String>(); for(int i = 0; i < input.length; i++) { File srcFile = new File(srcDir, input[i]); File destFile = getDestFile(input[i]); if(!(destFile.exists() && destFile.lastModified() > srcFile.lastModified())) { a.add(input[i]); } } String[] rv=new String[a.size()]; for(int i=0; i<a.size(); i++) { rv[i]=a.get(i); } return(rv); } // Translate a source filename (.spt) to a dest File (.java) private File getDestFile(String filename) { File destFile = new File(destDir, filename.substring(0, filename.length()-4)+".java"); return(destFile); } /** * Processes an individual file. * @param filename The filename, relative to the source directory. **/ /** * @param filename */ protected void processFile(String filename) { File srcFile = new File(srcDir, filename); File tmpFile = null; File destFile = getDestFile(filename); BufferedReader in=null; PrintWriter out=null; try { // Open input file try { FileReader reader = new FileReader(srcFile); in = new BufferedReader(reader); } catch (IOException e) { throw new BuildException(e); } if (destFile.exists() && destFile.lastModified() > srcFile.lastModified()) { return; } // Open output file // Temporary so partial builds don't impact future ones try { tmpFile = File.createTempFile("sptgen", "java", destDir); FileWriter writer = new FileWriter(tmpFile); out = new PrintWriter(writer); String name = srcFile.getName(); // assert name.endsWith(".spt"); SPGen spg = new SPGen( name.substring(0, name.length()-4), in, out); if (this.superclass!=null) { spg.setSuperclass(this.superclass); } if (this.dbcpSuperclass!=null) { spg.setDbcpSuperclass(this.dbcpSuperclass); } if (this.dbspSuperclass!=null) { spg.setDbspSuperclass(this.dbspSuperclass); } if (this.interfaces != null) { spg.addInterfaces(this.interfaces); } spg.setVerbose(verbose); spg.generate(); } catch (Exception e) { throw new BuildException(e); } finally { CloseUtil.close(in); CloseUtil.close(out); } if (destFile.exists()) { if (!destFile.delete()) { throw new BuildException("Unable to delete " + destFile); } } else { checkParent(destFile); } if (!tmpFile.renameTo(destFile)) { throw new BuildException("Unable to rename " + tmpFile + " to " + destFile); } tmpFile = null; } finally { if (tmpFile != null) { tmpFile.delete(); } } } /** * Checks if a parent directory exists and, if not, creates it. * Stops at the destination directory level. **/ protected void checkParent(File f) throws BuildException { File parent = f.getParentFile(); if (parent.equals(destDir) && !parent.exists()) { throw new BuildException("Destination dir no longer exists"); } if (!parent.exists()) { checkParent(parent); if (!parent.mkdir()) { throw new BuildException("Unable to create directory " + parent); } } else if (!parent.isDirectory()) { throw new BuildException("Not a directory: " + parent); } } /** * Add a space separated set of interfaces to have generated classes * implement. * @param to a space separated list of fully qualified interface names. */ public void setInterfaces(String to) { interfaces = new HashSet<String>(); for(StringTokenizer st=new StringTokenizer(to); st.hasMoreTokens();) { interfaces.add(st.nextToken()); } } }