package net.sf.lab3f.xml;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.io.IOException;
import java.io.StringWriter;
import java.io.File;
import java.io.FileOutputStream;
import java.io.FileInputStream;
import java.io.FilenameFilter;
import java.io.OutputStreamWriter;
import java.util.HashMap;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.TransformerException;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.traversal.NodeIterator;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;
import org.apache.xml.serializer.Serializer;
import org.apache.xml.serializer.SerializerFactory;
import org.apache.xml.serializer.OutputPropertiesFactory;
import org.apache.xalan.templates.OutputProperties;
import org.apache.xpath.XPathAPI;
//import net.sf.lab3f.util.TuttiFruttiable;
public class Main implements Xmlable {
private HashMap <File, Transformer> transformers = new HashMap <File, Transformer> ();
private HashMap <String, Document> docs = new HashMap <String, Document> ();
private DocumentBuilder dBuilder;
private TransformerFactory tFactory;
private final String docType = "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\"" +
" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">";
private boolean asHtml;
// private TuttiFruttiable tuttiFrutti;
public void removeDocument(String id){docs.remove("id");}
public void setAsHtml(boolean b){asHtml = b;}
public String getDocumentAsXml(String id){return toString(docs.get(id));}
public String getNodeAsXml(String id, String xpath){
String s = toString(getNodeByXPath(id, xpath));
if(s == null) return null;
int n = s.indexOf("?>");
return (n > 0 && (xpath.trim().endsWith("node()") || xpath.lastIndexOf('@') > xpath.lastIndexOf('/'))) ? s = s.substring(n + 2) : s;
}
public void start() throws Exception {
if(dBuilder == null){
DocumentBuilderFactory dFactory = DocumentBuilderFactory.newInstance();
dBuilder = dFactory.newDocumentBuilder();
}
System.out.println("[INFO] DOM holder starts.");
if(!new File("xsl").exists())return;
if(tFactory == null) tFactory = TransformerFactory.newInstance();
for(String s : new File("xsl").list( new FilenameFilter(){public boolean accept(File d, String n){return n.endsWith(".xsl");}})){
transformers.put(new File("xsl/" + s), tFactory.newTransformer(new StreamSource(new FileInputStream("xsl/" + s))));
}
System.out.println("[INFO] XSL-transformers loaded.");
}
public void stop() throws Exception{transformers.clear();}
public final void setInput(String id, InputStream is) throws IOException, SAXException{
if(is == null){
docs.remove(id);
return;
}
docs.put(id, dBuilder.parse(is));
}
public final void setInput(String id, String is){
try{setInput(id, new ByteArrayInputStream(is.getBytes("UTF8")));}
catch(IOException ex){System.out.println(ex);}
catch(SAXException ex){System.out.println(ex);}
}
private final String toString(Node node){
try{
StringWriter sw = new StringWriter();
Serializer serializer = SerializerFactory.getSerializer(OutputPropertiesFactory.getDefaultMethodProperties("xml"));
serializer.setWriter(sw);
serializer.asDOMSerializer().serialize(node);
String s = sw.toString().trim();
return "<?xml version=\"1.0\" encoding=\"UTF-8\"?>".equals(s) ? null : s;
}
catch(Exception ex){System.out.println(ex);return null;}
}
private final Node getNodeByXPath(String id, String xpath){
Document doc = docs.get(id);
try{return XPathAPI.eval(doc, xpath).nodeset().nextNode();}
catch(Exception ex){System.out.println(ex); return null;}
}
public void setAttribute(String id, String xpath, String name, String value){
((Element)getNodeByXPath(id, xpath)).setAttribute(name, value) ;
}
public final boolean remove(String id, String xpath){return doSomthing(id, xpath, null, Operations.REMOVE);}
public final boolean append(String id, String xpath, String newXml){ return doSomthing(id, xpath, newXml, Operations.APPEND);}
public final boolean insert(String id, String xpath, String newXml){ return doSomthing(id, xpath, newXml, Operations.INSERT); }
public final boolean replace(String id, String xpath, String newXml){ return doSomthing(id, xpath, newXml, Operations.REPLACE); }
private enum Operations {APPEND, INSERT, REPLACE, REMOVE};
private final boolean doSomthing(String id, String xPath, String xml, Operations op){
Node oldEl = getNodeByXPath(id, xPath);
if(oldEl == null) return false;
Document doc = docs.get(id);
try{
Node newEl = null;
if(xml != null){
Document d1 = dBuilder.parse(new ByteArrayInputStream(xml.getBytes("UTF8")));
newEl = doc.importNode(d1.getDocumentElement(), true);
}
switch(op){
case APPEND:
oldEl.appendChild(newEl);
return true;
case REPLACE:
oldEl.getParentNode().replaceChild(newEl, oldEl);
return true;
case INSERT:
oldEl.getParentNode().insertBefore(newEl, oldEl);
return true;
case REMOVE:
oldEl.getParentNode().removeChild(oldEl);
return true;
default: return false;
}
}
catch(Exception ex){System.out.println(ex);return false;}
}
public final void save(String id, String file){save(id, new File(file));}
public final void save(String id, File file){
try{
OutputStreamWriter fw = new OutputStreamWriter(new FileOutputStream(file), "UTF8");
String s = toString(docs.get(id));
if(asHtml)s = docType + s.substring(s.indexOf("<html"));
fw.write(s); fw.flush(); fw.close();
}
catch(Exception ex){System.out.println(ex);}
}
// XSLT
public final String transform(String name, String[] params, InputStream is){
try{
Transformer tr = transformers.get(new File("xsl/" + name));
if(tr == null) tr = transformers.get(new File("xsl/" + name + ".xsl"));
if(params != null) for(String p : params) tr.setParameter(p.substring(0, p.indexOf('=')), p.substring(p.indexOf('=') + 1));
StreamSource src = new StreamSource(is);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
StreamResult res = new StreamResult(baos);
tr.transform(src, res);
baos.flush();
return new String(baos.toByteArray(), "UTF8");
}
catch(Exception ex){System.out.println(ex);return null;}
}
public final String transform(String name, String[] params, String s){
try{return transform(name, params, new ByteArrayInputStream(s.getBytes("UTF8")));}
catch(Exception ex){System.out.println(ex);return null;}
}
public final String transform(String name, String s){
try{return transform(name, null, new ByteArrayInputStream(s.getBytes("UTF8")));}
catch(Exception ex){System.out.println(ex);return null;}
}
public final String transform(String name, InputStream is){
try{return transform(name, null, is);}
catch(Exception ex){System.out.println(ex);return null;}
}
public void transform(String from, String to, String xsl, String[] params){
String s = transform(xsl, params, getDocumentAsXml(from));
try{setInput(to, new ByteArrayInputStream(s.getBytes("UTF8")));}
catch(Exception ex){System.out.println(ex);}
}
public void transform(String from, String to, String xsl){transform(from, to, xsl, null);}
private SAXParserFactory saxParserFactory;
public boolean parse(InputStream is, Handlerable hnd){
try{
if(saxParserFactory == null)saxParserFactory = SAXParserFactory.newInstance();
DefaultHandler dh = new HandlerableHandler(hnd);
SAXParser sax = saxParserFactory.newSAXParser();
sax.parse(is, dh);
return true;
}
catch (Exception ex){System.out.println(ex);return false;}
}
private class HandlerableHandler extends DefaultHandler {
StringBuffer sb = new StringBuffer();
Handlerable handler;
HandlerableHandler(Handlerable h){handler = h;}
public void startElement(String s1, String s2, String s3, Attributes attrs) throws SAXException {
HashMap <String, String> map = new HashMap <String, String>();
for(int i = 0; i < attrs.getLength(); i++)map.put(attrs.getQName(i), attrs.getValue(i));
handler.startElement(s3, map);
}
public void endElement(String s1, String s2, String s3) throws SAXException {
handler.setTextNodeContent(sb.toString().trim());
sb = new StringBuffer();
handler.endElement(s3);
}
public void characters(char[] cha, int n, int l){
sb.append(new String(cha, n, l));
}
}
}