/******************************************************************************* * Copyright (c) 2006-2010 eBay Inc. All Rights Reserved. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 *******************************************************************************/ package org.ebayopensource.turmeric.plugins; import java.io.File; import java.io.FileInputStream; import java.io.InputStream; import java.io.PrintStream; import java.util.ArrayList; import java.util.LinkedList; import java.util.List; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.stream.XMLInputFactory; import javax.xml.stream.XMLStreamConstants; import javax.xml.stream.XMLStreamReader; import javax.xml.transform.Transformer; import javax.xml.transform.TransformerFactory; import javax.xml.transform.dom.DOMSource; import javax.xml.transform.stream.StreamResult; import org.apache.maven.plugin.MojoExecutionException; import org.apache.maven.plugin.MojoFailureException; import org.apache.maven.plugin.logging.Log; import org.codehaus.plexus.util.FileUtils; import org.w3c.dom.Attr; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.NamedNodeMap; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import com.sun.tools.xjc.Driver; /** * * suitable for use within a Maven Plugin. */ public class XjcEpisode { private List<String> m_typesDefined = new ArrayList<String>(); private String m_targetNamespace = null; private File xsdEpisodeFile; private File sourceOutputDir; private File resourceOutputDir; private File episodeFile; private File catalogFile; private Log log; private boolean verbose = false; public boolean isVerbose() { return verbose; } public void setVerbose(boolean verbose) { this.verbose = verbose; } public File getCatalogFile() { return catalogFile; } public void setCatalogFile(File catalogFile) { this.catalogFile = catalogFile; if( (catalogFile != null) && (catalogFile.exists())) { // Xerces Catalog Reference System.setProperty("xml.catalog.files", catalogFile.getAbsolutePath()); } } public File getSourceOutputDir() { return sourceOutputDir; } public File getXsdEpisodeFile() { return xsdEpisodeFile; } public void setXsdEpisodeFile(File xsdSourceFile) { this.xsdEpisodeFile = xsdSourceFile; } public void setSourceOutputDir(File sourceOutputDir) { this.sourceOutputDir = sourceOutputDir; } public File getResourceOutputDir() { return resourceOutputDir; } public void setResourceOutputDir(File resourceOutputDir) { this.resourceOutputDir = resourceOutputDir; } public File getEpisodeFile() { return episodeFile; } public void setEpisodeFile(File episodeFile) { this.episodeFile = episodeFile; } private String parseAttribute(XMLStreamReader xmlStreamReader, String attributeName) { String attributeValue = null; for (int i = 0; i < xmlStreamReader.getAttributeCount(); i++) { if (xmlStreamReader.getAttributeLocalName(i).equals(attributeName)) attributeValue = xmlStreamReader.getAttributeValue(i); } return attributeValue; } public void readXSD() throws Exception { log.info("Reading Episode Schema"); log.debug("Episode Schema: " + xsdEpisodeFile); assertFileExists(xsdEpisodeFile); XMLInputFactory inputFactory = XMLInputFactory.newInstance(); InputStream input = new FileInputStream(xsdEpisodeFile); XMLStreamReader xmlStreamReader = inputFactory.createXMLStreamReader(input); while (xmlStreamReader.hasNext()) { int event = xmlStreamReader.next(); if (event == XMLStreamConstants.START_ELEMENT) { if (xmlStreamReader.getLocalName().equals("schema")) { m_targetNamespace = parseAttribute(xmlStreamReader, "targetNamespace"); } if (xmlStreamReader.getLocalName().equals("complexType")) { m_typesDefined.add(parseAttribute(xmlStreamReader, "name")); } } } } public void filterEpisode() throws Exception { log.info("Filtering XJC Generated Episode"); log.debug("Episode File: " + episodeFile); File tempEpisodeFile = new File(episodeFile.getAbsolutePath() + ".temp"); FileUtils.copyFile(episodeFile, tempEpisodeFile); DocumentBuilderFactory episodeDocBuilderFactory = DocumentBuilderFactory.newInstance(); DocumentBuilder episodeDocBuilder = episodeDocBuilderFactory.newDocumentBuilder(); Document episodeDoc = episodeDocBuilder.parse(tempEpisodeFile); Element docElement = episodeDoc.getDocumentElement(); NodeList childNodeList = docElement.getChildNodes(); Element requiredBindingNode = null; for (int i = 0; i < childNodeList.getLength(); i++) { Node node = childNodeList.item(i); if (node.getNodeType() != Node.ELEMENT_NODE) { continue; // Only interested in element nodes. } Element elem = (Element) node; NamedNodeMap attributeNodeMap = elem.getAttributes(); if (attributeNodeMap != null) { for (int j = 0; j < attributeNodeMap.getLength(); j++) { Node mapNode = attributeNodeMap.item(j); String nsValue = mapNode.getNodeValue(); if (nsValue.equals(m_targetNamespace)) { requiredBindingNode = elem; if (log.isDebugEnabled()) { log.debug("Found required binding node: " + debug(requiredBindingNode)); } break; } } } } if (requiredBindingNode == null) { String err = String.format("Unable to find Required Binding Node matching target namespace: %s", m_targetNamespace); log.error(err); throw new MojoFailureException(err); } NodeList childNodesOfBinding = requiredBindingNode.getChildNodes(); for (int i = 0; i < childNodesOfBinding.getLength(); i++) { Node node = childNodesOfBinding.item(i); if (node == null) { continue; // skip null node. } if (node.getNodeType() == Node.COMMENT_NODE) { // Remove COMMENT nodes. requiredBindingNode.removeChild(node); i--; continue; } if (node.getNodeType() == Node.TEXT_NODE) { // Remove TEXT nodes. requiredBindingNode.removeChild(node); i--; continue; } if (node.getNodeType() == Node.ELEMENT_NODE) { Element elem = (Element) node; if (elem.getNodeName().equals("schemaBindings")) { log.debug("Removing <schemaBindings>"); requiredBindingNode.removeChild(elem); i--; continue; } NamedNodeMap attributeNodeMap = elem.getAttributes(); if (attributeNodeMap != null) { for (int j = 0; j < attributeNodeMap.getLength(); j++) { Node mapNode = attributeNodeMap.item(j); String nodeName = mapNode.getNodeValue(); if (nodeName != null && nodeName.contains(":")) { nodeName = nodeName.substring(nodeName.indexOf(":") + 1); } if (m_typesDefined.contains(nodeName)) { if (log.isDebugEnabled()) { log.debug("Removing Element: " + debug(elem)); } requiredBindingNode.removeChild(elem); i--; break; } } } } } if (childNodesOfBinding.getLength() == 0) { docElement.removeChild(requiredBindingNode); } TransformerFactory tFactory = TransformerFactory.newInstance(); Transformer transformer = tFactory.newTransformer(); DOMSource source = new DOMSource(episodeDoc); StreamResult result = new StreamResult(episodeFile); transformer.transform(source, result); tempEpisodeFile.delete(); } private String debug(Element elem) { StringBuilder buf = new StringBuilder(); buf.append('<'); if (elem.getPrefix() != null) { buf.append(elem.getPrefix()); buf.append(':'); } buf.append(elem.getNodeName()); NamedNodeMap attrs = elem.getAttributes(); if (attrs != null) { int len = attrs.getLength(); for (int i = 0; i < len; i++) { buf.append(' '); Attr attr = (Attr) attrs.item(i); if (attr.getPrefix() != null) { buf.append(attr.getPrefix()).append(':'); } buf.append(attr.getName()); buf.append("=\"").append(attr.getValue()).append('\"'); } } buf.append('>'); return buf.toString(); } public void xjcCreateEpisode() throws Exception { log.info("Creating Episode with XJC"); log.debug("Episode Input: " + xsdEpisodeFile); log.debug("Episode Output: " + episodeFile); LinkedList<String> args = new LinkedList<String>(); args.add("-nv"); args.add("-extension"); if (catalogFile != null) { args.add("-catalog"); args.add(catalogFile.getAbsolutePath()); } args.add("-d"); args.add(sourceOutputDir.getAbsolutePath()); args.add("-episode"); args.add(episodeFile.getAbsolutePath()); args.add(xsdEpisodeFile.getAbsolutePath()); executeXJC("Create Episode Files", args); } public void generateEpisodeSourceFiles() throws Exception { log.info("Creating Episode Source"); log.debug("Episode Schema: " + xsdEpisodeFile); log.debug("Episode File: " + episodeFile); log.debug("Output Dir: " + sourceOutputDir); LinkedList<String> args = new LinkedList<String>(); args.add("-nv"); args.add("-extension"); if (catalogFile != null) { args.add("-catalog"); args.add(catalogFile.getAbsolutePath()); } args.add("-d"); args.add(sourceOutputDir.getAbsolutePath()); args.add("-b"); args.add(episodeFile.getAbsolutePath()); args.add(xsdEpisodeFile.getAbsolutePath()); executeXJC("Creating Episode Source", args); } public void generateSourceFiles(File schemaFile) throws Exception { log.info("Creating Source Files: " + schemaFile.getName()); log.debug("Schema File: " + schemaFile); log.debug("Output Dir: " + sourceOutputDir); LinkedList<String> args = new LinkedList<String>(); args.add("-nv"); args.add("-extension"); if (catalogFile != null) { args.add("-catalog"); args.add(catalogFile.getAbsolutePath()); } args.add("-d"); args.add(sourceOutputDir.getAbsolutePath()); args.add(schemaFile.getAbsolutePath()); executeXJC("Creating Source Files: " + schemaFile.getName(), args); } // TODO: Should be able to specify the ObjectFactory to delete. public void deleteObjectFactory() throws MojoExecutionException { String objectFactoryLoc = sourceOutputDir.getAbsolutePath() + File.separator + "org" + File.separator + "ebayopensource" + File.separator + "turmeric" + File.separator + "common" + File.separator + "config" + File.separator + "ObjectFactory.java"; File objectFactory = new File(objectFactoryLoc); if (objectFactory.exists()) { log.debug("Deleting object factory: " + objectFactory); if (!objectFactory.delete()) { throw new MojoExecutionException("Unable to delete object factory: " + objectFactory.getAbsolutePath()); } } } private void executeXJC(String purpose, LinkedList<String> args) throws Exception { if (log.isDebugEnabled()) { args.addFirst("-debug"); StringBuilder msg = new StringBuilder(); msg.append("Passing the following args to "); msg.append(Driver.class.getName()).append("#run()"); for (String arg : args) { msg.append("\n \"").append(arg).append("\","); } log.debug(msg.toString()); } String[] xjcArguments = args.toArray(new String[0]); PrintStream status = null; if (verbose) { status = System.out; } int result = Driver.run(xjcArguments, status, System.out); if (result != 0) { String err = String.format("XJC Failure: Failed to %s (error code=%d) - See console for details", purpose, result); throw new MojoExecutionException(err); } } private void assertFileExists(File file) throws MojoExecutionException { if (!file.exists()) { log.warn("File not found: " + file.getPath()); throw new MojoExecutionException("File not found: " + file.getAbsolutePath()); } } public void setLog(Log log) { this.log = log; } }