
package com.swa.ant.taskdefs;

import java.io.*;
import java.util.*;
import org.apache.tools.ant.*;
import org.apache.tools.ant.util.*;
import org.apache.tools.ant.types.*;
import org.apache.tools.ant.taskdefs.*;
import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.DirectoryScanner;
import com.swa.ant.util.ZEUSMapper;

/**
 * Task to generate ZEUS DTD Object Model.
 */
public class ZEUS extends MatchingTask {

   private File dir;
   private Path src;
   private String source;
   private File outputdir;
   private String javapackage;
   private boolean fork = false;
   private Path compileClasspath;
   protected File[] dtdList = new File[0];

   public ZEUS() {
   }

   /**
    * Create a nested <src ...> element for multiple source path
    * support.
    *
    * @return a nexted src element.
    */
   public Path createSrc() {
      if (src == null) {
	 src = new Path(project);
      }
      return src.createPath();
   }

   /**
    * Set the source dirs to find the source antlr files.
    */
   public void setSrcdir(Path srcDir) {
      if (src == null) {
	 src = srcDir;
      } else {
	 src.append(srcDir);
      }
   }

   /** 
    * Gets the source dirs to find the source antlr files.
    */
   public Path getSrcdir() {
      return src;
   }

   /**
    * Set the destination directory into which the antlr source
    * files should be compiled.
    */
   public void setOutputdir(File outputdir) {
      log("Setting output directory to: " + outputdir.getAbsolutePath(),
	  Project.MSG_VERBOSE);
      this.outputdir = outputdir;
   } 

   /**
    * Set the package where the generated object model will exist.
    */
   public void setJavapackage(String s)
   {
      System.out.println("Searching package " + s + " ........");
      javapackage = s;
   }

   /**
    * Set the classpath to be used for this compilation.
    */
   public void setClasspath(Path classpath) {
      if (compileClasspath == null) {
	 compileClasspath = classpath;
      } else {
	 compileClasspath.append(classpath);
      }
   }

   /** Gets the classpath to be used for this compilation. */
   public Path getClasspath() {
      return compileClasspath;
   }

   /**
    * Maybe creates a nested classpath element.
    */
   public Path createClasspath() {
      if (compileClasspath == null) {
	 compileClasspath = new Path(project);
      }
      return compileClasspath.createPath();
   }

   /**
    * Adds a reference to a CLASSPATH defined elsewhere.
    */
   public void setClasspathRef(Reference r) {
        System.out.println("setting classpath!!");
      createClasspath().setRefid(r);
   }

   /**
    * Sets whether to fork the antlr compiler.
    */
   public void setFork(boolean s) {
      this.fork = s;
   }

   /**
    * The working directory of the process
    */
   public void setDir(File d) {
      this.dir = d;
   }

   /**
    * Executes the task.
    */
   public void execute() throws BuildException {
      // first off, make sure that we've got a srcdir

      if (src == null) {
	 throw new BuildException("srcdir attribute must be set!", location);
      }
      String [] list = src.list();
      if (list.length == 0) {
	 throw new BuildException("srcdir attribute must be set!", location);
      }

      if (javapackage == null)
      {
	 throw new BuildException("srcdir attribute must be set!", location);
      }

      if (outputdir != null && !outputdir.isDirectory()) {
	 throw new BuildException("Invalid output directory: " +
				  outputdir, location);
      }

      if (fork && (dir == null || !dir.isDirectory())) {
	 throw new BuildException("Invalid working directory: " + dir);
      }

      // scan source directories and dest directory to build up DTD lists
      resetFileLists();
      for (int i=0; i<list.length; i++) {
	 File srcDir = (File)project.resolveFile(list[i]);
	 if (!srcDir.exists()) {
	    throw new BuildException("srcdir \"" + srcDir.getPath() +
				     "\" does not exist!", location);
	 }

	 DirectoryScanner ds = this.getDirectoryScanner(srcDir);

	 String[] files = ds.getIncludedFiles();

	 dtdList = scanDir(srcDir, files, dtdList);
      }

      // process the Zeus dtd's
      if (dtdList.length > 0)
      {
	 log("Processing " + dtdList.length + " DTD"
	     + (dtdList.length == 1 ? "" : "s")
	     + (outputdir != null ? " to " + outputdir : ""));

         processDTDFiles(compileClasspath, outputdir, dtdList, fork);
      }
      else
      {
	 log("All DTD sources are up to date");
      }
   }

   private void processDTDFiles(Path classpath, File outputdir,
                                File[] dtdFiles, boolean fork)
      throws BuildException
   {
      for (int i = 0; i < dtdFiles.length; ++i)
      {
         File dtd = dtdFiles[i];         

         processDTDFile(classpath, outputdir, dtd, fork);
      }
   }
   

   private void processDTDFile(Path classpath, File outputdir, File dtd,
                               boolean fork)
      throws BuildException
   {
      CommandlineJava commandline = new CommandlineJava();
      commandline.setVm("java");
      if (classpath != null)
         commandline.createClasspath(getProject()).addExisting(classpath);
      commandline.setClassname("org.enhydra.zeus.util.DTDSourceGenerator");
      commandline.createArgument().setValue("-constraints=" + 
                                            dtd.getAbsolutePath());
      commandline.createArgument().setValue("-javaPackage=" + javapackage);
      commandline.createArgument().setValue("-outputDir="  +
        ((outputdir == null) ? dtd.getParentFile().getAbsolutePath() : 
         outputdir.toString()));

      log(dtd.getAbsolutePath(), Project.MSG_VERBOSE);
	      
      if (fork) {
         log("Forking " + commandline.toString(), Project.MSG_VERBOSE);
         int err = run(commandline.getCommandline());
         if (err == 1) {
            throw new BuildException("ZEUS returned: " + err, location);
         }
      }
      else {
         Execute.runCommand(this, commandline.getCommandline());
      }
   }

   /**
    * Clear the list of files to be processed and copied..
    */
   protected void resetFileLists() {
      dtdList = new File[0];
   }

   /**
    * Scans the directory looking for dtd files to be processed.
    */
   protected File[] scanDir(File srcDir, String files[], File[] initial) {
      ZEUSMapper m = new ZEUSMapper(srcDir, outputdir, javapackage);
      SourceFileScanner sfs = new SourceFileScanner(this);
      File[] newFiles = sfs.restrictAsFiles(files, srcDir, null, m);

      if (newFiles.length > 0) {
	 File[] newCompileList = new File[initial.length +
					 newFiles.length];
	 System.arraycopy(initial, 0, newCompileList, 0,
			  initial.length);
	 System.arraycopy(newFiles, 0, newCompileList,
			  initial.length, newFiles.length);
	 initial = newCompileList;
      }
      return initial;
   }

   private int run(String[] command) throws BuildException {
      Execute exe = new Execute(new LogStreamHandler(this, Project.MSG_INFO,
						     Project.MSG_WARN), null);
      exe.setAntRun(project);
      exe.setWorkingDirectory(dir);
      exe.setCommandline(command);
      try {
	 return exe.execute();
      } catch (IOException e) {
	 throw new BuildException(e, location);
      }
   }
}
