package com.yahoo.dtf.plugin;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import org.apache.xerces.parsers.DOMParser;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import com.yahoo.dtf.plugin.JarUtil;
import com.yahoo.dtf.exception.DTFException;
import com.yahoo.dtf.init.JarFileFilter;
import com.yahoo.dtf.logger.DTFLogger;
/**
* Class used by build.xml to add plug-ins to existing DTF framework. This class
* is responsible for merging XSDs and any other content that is necessary to
* make a plug-in part of the DTF framework.
*
* @author Rodney Gomes
*
*/
public class PluginMerger {
private static DTFLogger _logger = DTFLogger.getLogger(PluginMerger.class);
public static void printUsage() {
_logger.info(" PluginMerger ");
_logger.info("**************");
_logger.info(" PluginMerger plugin_directory dtf_xsd_location build_location");
}
private static Node findFirstChild(Node parent, String element) {
Node child = parent.getFirstChild();
while (child != null) {
String name = child.getLocalName();
if (name != null && name.equals(element)) {
return child;
}
child = child.getNextSibling();
}
return null;
}
public static void main(String[] args) {
if (args.length != 3) {
printUsage();
return;
}
try {
String pluginLocation = args[0];
String dtfFilename = args[1];
String buildLocation = args[2];
_logger.info("outputting to " + buildLocation + "/dtf.xsd");
FileOutputStream fos = new FileOutputStream(buildLocation + "/dtf.xsd");
File lib = new File(pluginLocation + File.separatorChar + "lib");
DOMParser parser = new DOMParser();
FileInputStream fis = new FileInputStream(dtfFilename);
InputSource is = new InputSource(fis);
parser.parse(is);
Document dtfDoc = parser.getDocument();
Element dtfRoot = dtfDoc.getDocumentElement();
// Get all jar files in the directories lib directory.
File[] jars = lib.listFiles(new JarFileFilter());
_logger.info("Looking for jars in: " + lib.getAbsolutePath());
//* Get the jar file and figure out if there is a XSD to be merged.
for(int i = 0; i < jars.length; i++) {
// Check for the XSDFile property value from the Manifest
File pluginJar = jars[i].getAbsoluteFile();
String xsdFilename =
JarUtil.getXSDPropertyValue(pluginJar,
XSDConstants.XSD_FILE_PROPERTY);
/*
* There is an xsd file in this jar file which means this is a DTF
* plugin that needs to be merged with.
*/
if (xsdFilename != null) {
_logger.info("Handling " + xsdFilename);
InputStream rIS = JarUtil.getXSDInputStream(pluginJar);
InputSource ris = new InputSource(rIS);
parser.parse(ris);
Element pluginRoot = parser.getDocument().getDocumentElement();
/*
* Merge the groups of the same name and throw an exception if
* conflicting elements are found.
*/
Node pNode = pluginRoot.getFirstChild();
while (pNode != null) {
/*
* Ignore comments or anything other than an xml element
* node.
*/
if (isElementNode(pNode)) {
NamedNodeMap pAttribs = pNode.getAttributes();
Node pAttrib = pAttribs.getNamedItem("name");
String pName = pAttrib.getNodeValue();
Node rNode = dtfRoot.getFirstChild();
boolean merged = false;
do {
// not element node then skip it.
if (!isElementNode(rNode))
continue;
NamedNodeMap rAttribs = rNode.getAttributes();
Node rAttrib = rAttribs.getNamedItem("name");
if (rAttrib == null)
throw new DTFException("element without 'name' attribute, [" + rNode + "]");
String rName = rAttrib.getNodeValue();
String localName = pNode.getLocalName();
// didn't find a plugin node with the same name
// then skip it.
if (!pName.equals(rName))
continue;
if (!localName.equals("group")) {
String msg = "Unsupported merging " +
"of XSD elements [" +
localName + "]";
throw new DTFException(msg);
}
_logger.info("Merging common [" + pName + "]");
/*
* merge the two common group elements that
* should have 1 choice child with the various
* XML elements to merge.
*/
/*
* Find choice element from DTF XSD group.
*/
Node child = null;
Node rChoice = findFirstChild(rNode, "choice");
Node choice = findFirstChild(pNode, "choice");
if ( choice != null ) {
child = choice.getFirstChild();
/*
* XXX: need a cleaner merging algorithm
* than the current one.
*/
/*
* If they're both sequences then we should
* merge those two.
*/
Node seqr = findFirstChild(rChoice,"sequence");
Node seqp = findFirstChild(choice, "sequence");
if (seqp != null && seqr != null) {
child = seqp.getFirstChild();
rChoice = seqr;
}
while (child != null) {
Node copy = dtfDoc.importNode(child, true);
rChoice.appendChild(copy);
child = child.getNextSibling();
}
} else {
throw new DTFException("Group " + pNode +
" does not have a xs:choice child!");
}
merged = true;
} while ((rNode = rNode.getNextSibling()) != null);
if (!merged) {
_logger.info("Adding element [" +
pNode.getLocalName() +
"] " + pName);
Node newNode = dtfDoc.importNode(pNode, true);
dtfDoc.getFirstChild().appendChild(newNode);
}
}
pNode = pNode.getNextSibling();
}
}
}
// Write the DOM document to the file
Transformer xformer =
TransformerFactory.newInstance().newTransformer();
Source source = new DOMSource(dtfDoc);
xformer.transform(source, new StreamResult(fos));
} catch (IOException e) {
_logger.error("Error parsing xsd files.",e);
System.exit(-1);
} catch (SAXException e) {
_logger.error("Error parsing xsd files.",e);
System.exit(-1);
} catch (DTFException e) {
_logger.error("Error parsing xsd files.",e);
System.exit(-1);
} catch (TransformerException e) {
_logger.error("Error writing out xsd file.",e);
System.exit(-1);
}
}
private static boolean isElementNode(Node node) {
return node.getNodeType() == Node.ELEMENT_NODE &&
node.getAttributes() != null;
}
}