/*
* 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 net.sourceforge.chaperon.model.extended.ExtendedGrammar;
import net.sourceforge.chaperon.process.extended.ExtendedDirectParserProcessor;
import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.DirectoryScanner;
import org.apache.tools.ant.Project;
import org.apache.tools.ant.taskdefs.MatchingTask;
import org.apache.tools.ant.types.Mapper;
import org.apache.tools.ant.types.XMLCatalog;
import org.apache.tools.ant.util.FileNameMapper;
import org.apache.tools.ant.util.IdentityMapper;
import org.exolab.castor.mapping.Mapping;
import org.exolab.castor.xml.Unmarshaller;
import org.xml.sax.InputSource;
import org.xml.sax.SAXParseException;
import org.xml.sax.XMLReader;
import org.xml.sax.helpers.AttributesImpl;
import org.xml.sax.helpers.LocatorImpl;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileReader;
import java.io.InputStreamReader;
import java.io.LineNumberReader;
import java.util.Properties;
import javax.xml.parsers.SAXParserFactory;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.sax.SAXTransformerFactory;
import javax.xml.transform.sax.TransformerHandler;
import javax.xml.transform.stream.StreamResult;
/**
* A ant task for parsing text files
*
* @author <a href="mailto:stephan@apache.org">Stephan Michels </a>
* @version CVS $Id: ExtendedParserTask.java,v 1.3 2004/01/09 10:48:06 benedikta Exp $
*/
public class ExtendedParserTask extends MatchingTask
{
private File srcDir = null;
private File destDir = null;
private File baseDir = null;
private Mapper mapper = null;
/** for resolving entities such as dtds */
private XMLCatalog xmlCatalog = new XMLCatalog();
private File grammarFile = null;
private String parserFactory = null;
private SAXParserFactory parserFactoryImpl = null;
private String transformerFactory = null;
private SAXTransformerFactory transformerFactoryImpl = null;
private String encoding = "ISO-8859-1";
private boolean indent = false;
private boolean flatten = false;
private String inputtype = "text";
private int msgLevel = Project.MSG_ERR;
private AntLog log;
private ExtendedGrammar grammar = null;
private ExtendedDirectParserProcessor parser = null;
/**
* Constructs the task
*/
public ExtendedParserTask() {}
/**
* Executes the task
*
* @throws BuildException
*/
public void execute() throws BuildException
{
if (baseDir==null)
baseDir = project.resolveFile(".");
if (grammarFile==null)
throw new BuildException("No grammar file is specified", location);
if (!grammarFile.exists())
throw new BuildException("Grammar file doesn't exists:"+grammarFile.getAbsolutePath(),
location);
if (destDir==null)
throw new BuildException("No destdir specified!", location);
log = new AntLog(getProject(), msgLevel);
buildAutomata(grammarFile);
DirectoryScanner scanner = getDirectoryScanner(srcDir);
FileNameMapper mapperImpl;
if (mapper==null)
mapperImpl = new IdentityMapper();
else
mapperImpl = mapper.getImplementation();
String[] list = scanner.getIncludedFiles();
for (int i = 0; i<list.length; i++)
{
String[] dest = mapperImpl.mapFileName(list[i]);
if (dest!=null)
for (int j = 0; j<dest.length; j++)
{
log("Transforming "+list[i]+" to "+dest[j], Project.MSG_DEBUG);
process(new File(srcDir, list[i]), new File(destDir, dest[j]));
}
}
}
/**
* Set the base directory.
*
* @param dir Base directory
*/
public void setBasedir(File dir)
{
baseDir = dir;
}
/**
* Set the source directory
*
* @param dir Source directory
*/
public void setSrcdir(File dir)
{
srcDir = 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;
}
/**
* Creates a mapper.
*
* @return New mapper.
*
* @throws BuildException
*/
public Mapper createMapper() throws BuildException
{
if (mapper!=null)
throw new BuildException("Cannot define more than one mapper", location);
mapper = new Mapper(project);
return mapper;
}
/**
* Set the grammar, which should be used.
*
* @param grammarFile Grammar file.
*/
public void setGrammar(File grammarFile)
{
this.grammarFile = grammarFile;
}
/**
* Sets the message level.
*
* @param msgLevel Message level.
*/
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 be indented
*
* @param indent If the output should be indented
*/
public void setIndent(boolean indent)
{
this.indent = indent;
}
/**
* If the input document is a XML or a text document.
*
* @param inputtype Type of the input document.
*/
public void setInputtype(String inputtype)
{
this.inputtype = inputtype;
}
/**
* Name of the parser factory.
*
* @param parserFactory Name of the parser factory.
*/
public void setParser(String parserFactory)
{
this.parserFactory = parserFactory;
}
/**
* Name of the transformer factory.
*
* @param transformerFactory Name of the transformer factory.
*/
public void setTransformer(String transformerFactory)
{
this.transformerFactory = transformerFactory;
}
/**
* Add the catalog to our internal catalog
*
* @param xmlCatalog the XMLCatalog instance to use to look up DTDs
*/
public void addConfiguredXMLCatalog(XMLCatalog xmlCatalog)
{
this.xmlCatalog.addConfiguredXMLCatalog(xmlCatalog);
}
/**
* Initialize internal instance of XMLCatalog
*/
public void init() throws BuildException
{
super.init();
xmlCatalog.setProject(project);
}
/**
* 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
*
* @throws BuildException
*/
private void process(File inFile, File outFile) throws BuildException
{
try
{
if (!inFile.exists())
throw new BuildException("File "+inFile+" doesn't exists", location);
if (inFile.lastModified()>outFile.lastModified())
{
ensureDirectoryFor(outFile);
log("Parsing file "+inFile+" to "+outFile, Project.MSG_INFO);
Properties format = new Properties();
format.put(OutputKeys.ENCODING, encoding);
if (indent)
format.put(OutputKeys.INDENT, "yes");
format.put(OutputKeys.METHOD, "xml");
SAXTransformerFactory factory = getTransformerFactory();
TransformerHandler serializer = factory.newTransformerHandler();
serializer.getTransformer().setOutputProperties(format);
serializer.setResult(new StreamResult(outFile));
this.parser = new ExtendedDirectParserProcessor();
this.parser.setLog(log);
this.parser.setFlatten(this.flatten);
this.parser.setExtendedGrammar(this.grammar);
this.parser.setContentHandler(serializer);
if (!inputtype.equalsIgnoreCase("xml"))
pushTextFile(inFile);
else
pushXMLFile(inFile);
}
}
catch (Exception ex)
{
if (outFile!=null)
outFile.delete();
if (ex instanceof BuildException)
throw (BuildException)ex;
throw new BuildException("Failed to process "+inFile+" : "+ex.getMessage(), ex);
}
}
/**
* Build the automata for the lexicon and grammar.
*
* @param grammarFile Grammar file.
*
* @throws BuildException
*/
private void buildAutomata(File grammarFile) throws BuildException
{
try
{
log("Building grammar from "+grammarFile, Project.MSG_INFO);
SAXParserFactory factory = getParserFactory();
factory.setNamespaceAware(true);
XMLReader parser = factory.newSAXParser().getXMLReader();
parser.setEntityResolver(xmlCatalog);
Mapping mapping = new Mapping();
mapping.loadMapping(new InputSource(ExtendedGrammar.class.getResource("mapping.xml")
.openStream()));
Unmarshaller unmarshaller = new Unmarshaller(ExtendedGrammar.class);
unmarshaller.setMapping(mapping);
this.grammar = (ExtendedGrammar)unmarshaller.unmarshal(new FileReader(grammarFile));
if (log.isDebugEnabled())
log.debug("grammar:\n"+grammar);
}
catch (Exception ex)
{
if (ex instanceof BuildException)
throw (BuildException)ex;
throw new BuildException(ex);
}
}
private void pushTextFile(File inFile) throws Exception
{
try
{
LocatorImpl locator = new LocatorImpl();
locator.setSystemId(inFile.toURL().toString());
locator.setLineNumber(1);
locator.setColumnNumber(1);
this.parser.setDocumentLocator(locator);
this.parser.startDocument();
this.parser.startElement("http://chaperon.sourceforge.net/schema/text/1.0", "text", "text",
new AttributesImpl());
LineNumberReader reader =
new LineNumberReader(new InputStreamReader(new FileInputStream(inFile)));
String line;
String newline = null;
String separator = System.getProperty("line.separator");
while (true)
{
if (newline==null)
line = reader.readLine();
else
line = newline;
if (line==null)
break;
newline = reader.readLine();
line = (newline!=null) ? (line+separator) : line;
locator.setLineNumber(reader.getLineNumber());
locator.setColumnNumber(1);
this.parser.characters(line.toCharArray(), 0, line.length());
if (newline==null)
break;
}
reader.close();
this.parser.endElement("http://chaperon.sourceforge.net/schema/text/1.0", "text", "text");
this.parser.endDocument();
}
catch (SAXParseException se)
{
throw new BuildException("Exception occurs during parsing file "+inFile+" at line "+
se.getLineNumber()+" column "+se.getColumnNumber(), se);
}
}
private void pushXMLFile(File inFile) throws Exception
{
SAXParserFactory parserfactory = getParserFactory();
parserfactory.setNamespaceAware(true);
XMLReader parser = parserfactory.newSAXParser().getXMLReader();
parser.setEntityResolver(xmlCatalog);
parser.setContentHandler(this.parser);
try
{
parser.parse(inFile.toString());
}
catch (SAXParseException se)
{
throw new BuildException("Exception occurs during parsing file "+inFile+" at line "+
se.getLineNumber()+" column "+se.getColumnNumber(), se);
}
}
/**
* 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());
}
private SAXParserFactory getParserFactory() throws BuildException
{
if (parserFactoryImpl==null)
{
try
{
if (parserFactory==null)
parserFactoryImpl = SAXParserFactory.newInstance();
else
parserFactoryImpl = (SAXParserFactory)Class.forName(parserFactory).newInstance();
}
catch (Exception e)
{
throw new BuildException("Could not load parser factory", e);
}
}
return parserFactoryImpl;
}
private SAXTransformerFactory getTransformerFactory()
throws BuildException
{
if (transformerFactoryImpl==null)
{
try
{
if (transformerFactory==null)
transformerFactoryImpl = (SAXTransformerFactory)SAXTransformerFactory.newInstance();
else
transformerFactoryImpl =
(SAXTransformerFactory)Class.forName(transformerFactory).newInstance();
}
catch (Exception e)
{
throw new BuildException("Could not load transformer factory", e);
}
}
return transformerFactoryImpl;
}
}