/******************************************************************************* * Copyright (c) 2010-2011 VIVO Harvester Team. For full list of contributors, please see the AUTHORS file provided. * All rights reserved. * This program and the accompanying materials are made available under the terms of the new BSD license which accompanies this distribution, and is available at http://www.opensource.org/licenses/bsd-license.html ******************************************************************************/ package org.vivoweb.harvester.translate; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import javax.xml.transform.Source; import javax.xml.transform.TransformerConfigurationException; import javax.xml.transform.TransformerException; import javax.xml.transform.TransformerFactory; import javax.xml.transform.stream.StreamResult; import javax.xml.transform.stream.StreamSource; import org.apache.commons.io.IOUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.vivoweb.harvester.util.InitLog; import org.vivoweb.harvester.util.FileAide; import org.vivoweb.harvester.util.args.ArgDef; import org.vivoweb.harvester.util.args.ArgList; import org.vivoweb.harvester.util.args.ArgParser; import org.vivoweb.harvester.util.args.UsageException; import org.vivoweb.harvester.util.repo.Record; import org.vivoweb.harvester.util.repo.RecordHandler; /** * Takes XML Files and uses an XSL file to translate the data into the desired ontology * @author Stephen V. Williams swilliams@ctrip.ufl.edu */ public class XSLTranslator { /** * the log property for logging errors, information, debugging */ private static Logger log = LoggerFactory.getLogger(XSLTranslator.class); /** * The translation xsl is the map that will reconstruct our input stream's document into the appropriate format */ private String translationString; /** * record handler for incoming records */ protected RecordHandler inStore; /** * record handler for storing records */ protected RecordHandler outStore; /** * force process records */ private boolean force; /** * Constructor * @param args commandline arguments * @throws IOException error creating task * @throws UsageException user requested usage message */ private XSLTranslator(String[] args) throws IOException, UsageException { this(getParser().parse(args)); } /** * Constructor * @param argList <ul> * <li>translationStream the file that details the translation from the original xml to the target format</li> * <li>inRecordHandler the files/records that require translation</li> * <li>outRecordHandler the output record for the translated files</li> * <li>force translate all input records, even if previously processed</li> * </ul> * @throws IOException error reading files */ private XSLTranslator(ArgList argList) throws IOException { this( RecordHandler.parseConfig(argList.get("i"), argList.getValueMap("I")), RecordHandler.parseConfig(argList.get("o"), argList.getValueMap("O")), FileAide.getInputStream(argList.get("x")), argList.has("f") ); } /** * Constructor * @param translationStream the file that details the translation from the original xml to the target format</li> * @param inRecordHandler the files/records that require translation * @param outRecordHandler the output record for the translated files * @param force translate all input records, even if previously processed * @throws IOException error reading files */ public XSLTranslator(RecordHandler inRecordHandler, RecordHandler outRecordHandler, InputStream translationStream, boolean force) throws IOException { // set Translation file setTranslation(translationStream); // create record handlers this.inStore = inRecordHandler; this.outStore = outRecordHandler; this.force = force; if(this.inStore == null) { throw new IllegalArgumentException("Must provide an input record handler"); } if(this.outStore == null) { throw new IllegalArgumentException("Must provide an output record handler"); } } /** * Set translation file from an inputSteam * @param transFileStream valid type of translation file is xslt * @throws IOException error reading from stream */ public void setTranslation(InputStream transFileStream) throws IOException { // copy xsl into memory for faster translations ByteArrayOutputStream baos = new ByteArrayOutputStream(); IOUtils.copy(transFileStream, baos); this.translationString = baos.toString(); } /** * Set translation file from a file * @param transFile valid type of translation file is xslt * @throws IOException error reading from file */ public void setTranslation(File transFile) throws IOException { setTranslation(new FileInputStream(transFile)); } /** * checks again for the necessary file and makes sure that they exist * @throws IOException error processing */ public void execute() throws IOException { // get from the in record and translate int translated = 0; int passed = 0; for(Record r : this.inStore) { if(this.force || r.needsProcessed(this.getClass())) { log.trace("Translating Record " + r.getID()); ByteArrayOutputStream baos = new ByteArrayOutputStream(); xmlTranslate(new ByteArrayInputStream(r.getData().getBytes("UTF-8")), baos, new ByteArrayInputStream(this.translationString.getBytes())); this.outStore.addRecord(r.getID(), baos.toString(), this.getClass()); r.setProcessed(this.getClass()); baos.close(); translated++; } else { log.trace("No Translation Needed: " + r.getID()); passed++; } } log.info(Integer.toString(translated) + " records translated."); log.info(Integer.toString(passed) + " records did not need translation"); } /** * using the javax xml transform factory this method uses the xsl to translate XML into the desired format * designated in the xsl. * @param inStream the input stream * @param outStream the output stream * @param translationStream the stream for the xsl * @throws IOException error translating */ public static void xmlTranslate(InputStream inStream, OutputStream outStream, InputStream translationStream) throws IOException { StreamResult outputResult = new StreamResult(outStream); // JAXP reads data using the Source interface Source xmlSource = new StreamSource(inStream); Source xslSource = new StreamSource(translationStream); try { // the factory pattern supports different XSLT processors // this outputs to outStream (through outputResult) TransformerFactory.newInstance("net.sf.saxon.TransformerFactoryImpl", null).newTransformer(xslSource).transform(xmlSource, outputResult); } catch(TransformerConfigurationException e) { throw new IOException(e); } catch(TransformerException e) { throw new IOException(e); } outStream.flush(); } /** * Get the ArgParser for this task * @return the ArgParser */ private static ArgParser getParser() { ArgParser parser = new ArgParser("XSLTranslator"); parser.addArgument(new ArgDef().setShortOption('i').setLongOpt("input").withParameter(true, "CONFIG_FILE").setDescription("config file for input record handler").setRequired(false)); parser.addArgument(new ArgDef().setShortOption('I').setLongOpt("inputOverride").withParameterValueMap("RH_PARAM", "VALUE").setDescription("override the RH_PARAM of input recordhandler using VALUE").setRequired(false)); parser.addArgument(new ArgDef().setShortOption('o').setLongOpt("output").withParameter(true, "CONFIG_FILE").setDescription("config file for output record handler").setRequired(false)); parser.addArgument(new ArgDef().setShortOption('O').setLongOpt("outputOverride").withParameterValueMap("RH_PARAM", "VALUE").setDescription("override the RH_PARAM of output recordhandler using VALUE").setRequired(false)); parser.addArgument(new ArgDef().setShortOption('x').setLongOpt("xslFile").withParameter(true, "XSL_FILE").setDescription("xsl file").setRequired(true)); parser.addArgument(new ArgDef().setShortOption('f').setLongOpt("force").setDescription("force translation of all input records, even if previously processed").setRequired(false)); return parser; } /** * Main method * @param args commandline arguments */ public static void main(String... args) { Exception error = null; try { InitLog.initLogger(args, getParser()); log.info(getParser().getAppName() + ": Start"); new XSLTranslator(args).execute(); } catch(IllegalArgumentException e) { log.error(e.getMessage()); log.debug("Stacktrace:",e); System.out.println(getParser().getUsage()); error = e; } catch(UsageException e) { log.info("Printing Usage:"); System.out.println(getParser().getUsage()); error = e; } catch(Exception e) { log.error(e.getMessage()); log.debug("Stacktrace:",e); error = e; } finally { log.info(getParser().getAppName() + ": End"); if(error != null) { System.exit(1); } } } }