/*
* Copyright (C) Chaperon. All rights reserved.
* -------------------------------------------------------------------------
* This software is published under the terms of the Apache Software License
* version 1.1, a copy of which has been included with this distribution in
* the LICENSE file.
*/
package net.sourceforge.chaperon.ant;
import java.io.*;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.StringTokenizer;
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.types.Path;
import org.apache.tools.ant.types.Reference;
import org.apache.tools.ant.util.FileUtils;
import org.apache.tools.ant.taskdefs.MatchingTask;
import org.apache.xml.serialize.Method;
import org.apache.xml.serialize.OutputFormat;
import org.apache.xml.serialize.XMLSerializer;
import org.xml.sax.InputSource;
import net.sourceforge.chaperon.parser.output.EventQueue;
import net.sourceforge.chaperon.parser.output.SAXEventAdapter;
import net.sourceforge.chaperon.parser.ParserException;
import net.sourceforge.chaperon.parser.Parser;
import net.sourceforge.chaperon.parser.ParserTable;
/**
* A ant task for parsing text files
*
* @author Stephan Michels
* @version %version%
*/
public class ChaperonParse extends MatchingTask
{
private File destDir = null;
private File baseDir = null;
private String parsertableFilename = null;
private String targetExtension = ".xml";
private Vector params = new Vector();
private File inFile = null;
private File outFile = null;
private String encoding = null;
private boolean ignorableTokens = false;
private boolean indent = false;
private FileUtils fileUtils;
private int msgLevel = Project.MSG_ERR;
private AntLogger logger;
private Parser parser;
private SAXEventAdapter adapter;
/**
* Constructs the task
*/
public ChaperonParse()
{
fileUtils = FileUtils.newFileUtils();
}
/**
* Executes the task
*
* @throws BuildException
*/
public void execute() throws BuildException
{
logger = new AntLogger(this, msgLevel);
parser = new Parser();
parser.enableLogging(logger);
DirectoryScanner scanner;
String[] list;
String[] dirs;
if (parsertableFilename == null)
throw new BuildException("no parsertable specified", location);
if (baseDir == null)
baseDir = project.resolveFile(".");
File parsertableFile = project.resolveFile(parsertableFilename);
if (!parsertableFile.exists())
{
parsertableFile = fileUtils.resolveFile(baseDir, parsertableFilename);
if (parsertableFile.exists())
{
log("DEPRECATED - the parsertable attribute should be relative to the project\'s");
log(" basedir, not the tasks\'s basedir.");
}
}
if ((inFile != null) && (outFile != null))
{
process(inFile, outFile, parsertableFile);
return;
}
if (destDir == null)
{
String msg = "destdir attributes must be set!";
throw new BuildException(msg);
}
scanner = getDirectoryScanner(baseDir);
log("Transforming into " + destDir, Project.MSG_INFO);
list = scanner.getIncludedFiles();
for (int i = 0; i < list.length; ++i)
process(baseDir, list[i], destDir, parsertableFile);
dirs = scanner.getIncludedDirectories();
for (int j = 0; j < dirs.length; ++j)
{
list = new File(baseDir, dirs[j]).list();
for (int i = 0; i < list.length; ++i)
process(baseDir, list[i], destDir, parsertableFile);
}
}
/**
* Set the base directory.
*
* @param dir Base directory
*/
public void setBasedir(File dir)
{
baseDir = dir;
}
/**
* Set the destination directory into which the result
* files should be copied to
*
* @param dir Destination directory
*/
public void setDestdir(File dir)
{
destDir = dir;
}
/**
* Set the desired file extension to be used for the target
*
* @param name The extension to use
*/
public void setExtension(String name)
{
targetExtension = name;
}
/**
* Sets the parsertable to use for parsing relative to the base directory
* of this task.
*
* @param parsertableFilename File name of the parser table
*/
public void setParserTable(String parsertableFilename)
{
this.parsertableFilename = parsertableFilename;
}
/**
* Sets an output file
*
* @param outFile The output file
*/
public void setOut(File outFile)
{
this.outFile = outFile;
}
/**
* Sets an input xml file to be parsed
*
* @param inFile The input file
*/
public void setIn(File inFile)
{
this.inFile = inFile;
}
/**
* Sets the message level
*
* @param inFile The grammar file
*/
public void setMsglevel(String msgLevel)
{
if (msgLevel.equalsIgnoreCase("debug"))
this.msgLevel = Project.MSG_DEBUG;
else if (msgLevel.equalsIgnoreCase("verbose"))
this.msgLevel = Project.MSG_VERBOSE;
else if (msgLevel.equalsIgnoreCase("info"))
this.msgLevel = Project.MSG_INFO;
else if (msgLevel.equalsIgnoreCase("warn"))
this.msgLevel = Project.MSG_WARN;
else if (msgLevel.equalsIgnoreCase("error"))
this.msgLevel = Project.MSG_ERR;
}
/**
* Sets the encoding for the input file
*
* @param encoding Encoding of the document
*/
public void setEncoding(String encoding)
{
this.encoding = encoding;
}
/**
* Set if the output document should include
* the ignorable tokens
*
* @param ignorableTokens If the output document should include
* the ignorable tokens
*/
public void setIgnorabletokens(boolean ignorableTokens)
{
this.ignorableTokens = ignorableTokens;
}
/**
* Set if the output document should be indented
*
* @param ignorableTokens If the output document should be indented
* the ignorable tokens
*/
public void setIndent(boolean indent)
{
this.indent = indent;
}
/**
* Processes the given input XML file and stores the result
* in the given resultFile.
*
* @param baseDir Base directory
* @param xmlFile File, which should parsed
* @param destDir Destination directory
* @param parsertableFile Parser table file
*
* @throws BuildException
*/
private void process(File baseDir, String xmlFile, File destDir,
File parsertableFile) throws BuildException
{
String fileExt = targetExtension;
File outFile = null;
File inFile = null;
try
{
long parsertableLastModified = parsertableFile.lastModified();
inFile = new File(baseDir, xmlFile);
int dotPos = xmlFile.lastIndexOf('.');
if (dotPos > 0)
outFile = new File(destDir,
xmlFile.substring(0, xmlFile.lastIndexOf('.'))
+ fileExt);
else
outFile = new File(destDir, xmlFile + fileExt);
if (!inFile.exists())
{
log("File " + inFile + " doesn't exists");
return;
}
if (!parsertableFile.exists())
{
log("File " + parsertableFile + " doesn't exists");
return;
}
if ((inFile.lastModified() > outFile.lastModified())
|| (parsertableLastModified > outFile.lastModified()))
{
ensureDirectoryFor(outFile);
log("Parsing files from " + destDir);
ObjectInputStream in = new ObjectInputStream(new FileInputStream(parsertableFile));
EventQueue queue = parser.parse((ParserTable)in.readObject(), new FileInputStream(inFile), encoding);
in.close();
OutputFormat format = new OutputFormat(Method.XML, "ASCII", indent); // Serialize DOM
if (indent)
{
format.setIndenting(true);
format.setIndent(1);
}
else
format.setPreserveSpace(true);
StringWriter stringOut = new StringWriter(); // Writer will be a String
XMLSerializer serial = new XMLSerializer(stringOut, format);
SAXEventAdapter adapter = new SAXEventAdapter(serial.asContentHandler(), ignorableTokens, false);
adapter.enableLogging(new AntLogger(this, msgLevel));
queue.fireEvents(adapter);
FileOutputStream outputstream = new FileOutputStream(outFile);
PrintWriter printstream = new PrintWriter(outputstream);
printstream.println(stringOut.toString());
printstream.flush();
printstream.close();
}
}
catch (Exception ex)
{
log("Failed to process " + inFile, Project.MSG_INFO);
if (outFile != null)
outFile.delete();
if (ex instanceof ParserException)
{
log(ex.toString());
throw new BuildException("Parsing faild");
}
else
{
ex.printStackTrace();
throw new BuildException(ex);
}
}
}
/**
* Processes the given input XML file and stores the result
* in the given resultFile.
*
* @param inFile The text file, which should parsed
* @param outFile The output file
* @param parsertableFile Parser table file
*
* @throws BuildException
*/
private void process(File inFile, File outFile,
File parsertableFile) throws BuildException
{
try
{
long parsertableLastModified = parsertableFile.lastModified();
log("In file " + inFile + " time: " + inFile.lastModified(),
Project.MSG_DEBUG);
log("Out file " + outFile + " time: " + outFile.lastModified(),
Project.MSG_DEBUG);
log("Parsertable file " + parsertableFile + " time: "
+ parsertableLastModified, Project.MSG_DEBUG);
if (!inFile.exists())
{
log("File " + inFile + " doesn't exists");
return;
}
if (!parsertableFile.exists())
{
log("File " + parsertableFile + " doesn't exists");
return;
}
if ((inFile.lastModified() > outFile.lastModified())
|| (parsertableLastModified > outFile.lastModified()))
{
ensureDirectoryFor(outFile);
log("Parsing file " + inFile + " to " + outFile, Project.MSG_INFO);
ObjectInputStream in = new ObjectInputStream(new FileInputStream(parsertableFile));
EventQueue queue = parser.parse((ParserTable)in.readObject(), new FileInputStream(inFile), encoding);
in.close();
OutputFormat format = new OutputFormat(Method.XML, "ASCII", indent); // Serialize DOM
if (indent)
{
format.setIndenting(true);
format.setIndent(1);
}
else
format.setPreserveSpace(true);
StringWriter stringOut = new StringWriter(); // Writer will be a String
XMLSerializer serial = new XMLSerializer(stringOut, format);
SAXEventAdapter adapter = new SAXEventAdapter(serial.asContentHandler(), ignorableTokens, false);
adapter.enableLogging(new AntLogger(this, msgLevel));
queue.fireEvents(adapter);
FileOutputStream outputstream = new FileOutputStream(outFile);
PrintWriter printstream = new PrintWriter(outputstream);
printstream.println(stringOut.toString());
printstream.flush();
printstream.close();
}
}
catch (Exception ex)
{
log("Failed to process " + inFile, Project.MSG_INFO);
if (outFile != null)
outFile.delete();
if (ex instanceof ParserException)
{
log(ex.toString());
throw new BuildException("Parsing faild");
}
else
{
ex.printStackTrace();
throw new BuildException(ex);
}
}
}
/**
* Ensures the directory for the output
*
* @param targetFile The directory
*
* @throws BuildException
*/
private void ensureDirectoryFor(File targetFile) throws BuildException
{
File directory = new File(targetFile.getParent());
if ((!directory.exists()) && (!directory.mkdirs()))
throw new BuildException("Unable to create directory: "
+ directory.getAbsolutePath());
}
}