package org.infinispan.tools.xsd;
import gnu.getopt.Getopt;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.OutputKeys;
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 javax.xml.transform.stream.StreamSource;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.util.LinkedHashMap;
import java.util.Map;
public class XSDoc {
public class Schema {
final String namespace;
final String name;
final Document doc;
final int major;
final int minor;
public Schema(Document doc, String name) {
this.name = name;
this.doc = doc;
String versionedNamespace = getDocumentNamespace(doc);
int versionSeparator = versionedNamespace.lastIndexOf(':');
namespace = versionedNamespace.substring(0, versionSeparator);
String[] versionParts = versionedNamespace.substring(versionSeparator + 1).split("\\.");
major = Integer.parseInt(versionParts[0]);
minor = Integer.parseInt(versionParts[1]);
}
public boolean since(Schema schema) {
return (schema ==null) || (this.major > schema.major) || ((this.major == schema.major) && (this.minor >= schema.minor));
}
}
private final Map<String, Document> xmls = new LinkedHashMap<>();
private final Map<String, Schema> latestSchemas = new LinkedHashMap<>();
private final Transformer xslt;
private final DocumentBuilder docBuilder;
private final Document indexDoc;
private Element indexRoot;
private TransformerFactory factory;
XSDoc() throws Exception {
factory = TransformerFactory.newInstance();
factory.setURIResolver((href, base) -> {
Document doc = xmls.get(getBaseFileName(href));
if (doc != null) {
return new DOMSource(doc);
} else {
return null;
}
});
ClassLoader cl = XSDoc.class.getClassLoader();
try (InputStream xsl = cl.getResourceAsStream("xsd/xsdoc.xslt")) {
xslt = factory.newTransformer(new StreamSource(xsl));
}
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
dbf.setNamespaceAware(true);
docBuilder = dbf.newDocumentBuilder();
indexDoc = docBuilder.newDocument();
indexRoot = indexDoc.createElement("files");
indexDoc.appendChild(indexRoot);
}
void load(String fileName) throws Exception {
Document doc = docBuilder.parse(new File(fileName));
String name = getBaseFileName(fileName);
xmls.put(name, doc);
Schema schema = new Schema(doc, name);
Schema current = latestSchemas.get(schema.namespace);
if (schema.since(current)) {
latestSchemas.put(schema.namespace, schema);
}
}
private void transform(String name, Document doc, File outputDir) {
try {
xslt.transform(new DOMSource(doc), new StreamResult(new File(outputDir, name + ".html")));
Element item = indexDoc.createElement("file");
item.setAttribute("name", name + ".html");
String ns = getDocumentNamespace(doc);
item.setAttribute("ns", ns);
indexRoot.appendChild(item);
} catch (TransformerException e) {
throw new RuntimeException(e);
}
}
public static String getDocumentNamespace(Document doc) {
Node child = doc.getFirstChild();
while (!(child instanceof Element)) child = child.getNextSibling();
return ((Element)child).getAttribute("targetNamespace");
}
void transformAll(File outputDir) {
latestSchemas.values().forEach(schema -> {
transform(schema.name, schema.doc, outputDir);
});
}
private void generateIndex(File outputDir) throws Exception {
printDocument(indexDoc, System.out);
ClassLoader cl = XSDoc.class.getClassLoader();
try (InputStream xsl = cl.getResourceAsStream("xsd/index.xslt")) {
Transformer indexXSLT = factory.newTransformer(new StreamSource(xsl));
indexXSLT.transform(new DOMSource(indexDoc), new StreamResult(new File(outputDir, "index.html")));
}
}
public static String getBaseFileName(String absoluteFileName) {
int slash = absoluteFileName.lastIndexOf('/');
int dot = absoluteFileName.lastIndexOf('.');
return absoluteFileName.substring(slash + 1, dot);
}
public static void printDocument(Document doc, OutputStream out) throws IOException, TransformerException {
TransformerFactory tf = TransformerFactory.newInstance();
Transformer transformer = tf.newTransformer();
transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "no");
transformer.setOutputProperty(OutputKeys.METHOD, "xml");
transformer.setOutputProperty(OutputKeys.INDENT, "yes");
transformer.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "4");
transformer.transform(new DOMSource(doc),
new StreamResult(new OutputStreamWriter(out, "UTF-8")));
}
public static void main(String argv[]) throws Exception {
XSDoc xsDoc = new XSDoc();
String outputDir = System.getProperty("user.dir");
Getopt opts = new Getopt("xsdoc", argv, "o:");
for (int opt = opts.getopt(); opt > -1; opt = opts.getopt()) {
switch (opt) {
case 'o':
outputDir = opts.getOptarg();
break;
}
}
File outDir = new File(outputDir);
outDir.mkdirs();
for (int i = opts.getOptind(); i < argv.length; i++) {
xsDoc.load(argv[i]);
}
xsDoc.transformAll(outDir);
xsDoc.generateIndex(outDir);
}
}