package ca.umontreal.iro.rali; import java.io.PrintStream; import java.util.List; import java.util.regex.Pattern; import javax.xml.parsers.ParserConfigurationException; import javax.xml.transform.Result; import javax.xml.transform.Transformer; import javax.xml.transform.TransformerException; import javax.xml.transform.dom.DOMSource; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import org.xml.sax.Attributes; import org.xml.sax.SAXException; import org.xml.sax.SAXParseException; import org.xml.sax.ext.DefaultHandler2; /** * handle SAX parsing events to extract selected parts of parsing * * @author Guy Lapalme, Université de Montréal, 2009 * */ public class ExtractSAXHandler extends DefaultHandler2{ // output files private PrintStream out; private Transformer trans; private Result transResult; // document private SpineDocument spineDoc; // spine document private Document doc; // document node to create new nodes private StringBuilder chars; // buffer to collect consecutive text nodes // global flags private boolean skipMode; // true when skipping nodes private boolean keepMode; // true when keeping nodes in the spine document before testing private Node keepStart; // node at which we started keeping (useful to remove at the appropriate level) private int level; // recursion level for either keeping or skipping // pattern private XPathParts pattern; // pattern to match (for printing only) private List parts; // pattern separated in levels private int lastPart; // number of parts private int iPart; // index of the current part of pattern to match /** * handle SAX parsing events to extract selected parts of parsing * * @param out output file * @param trans transformation to apply on each result * @param transResult output of the transformation * @throws ParserConfigurationException */ ExtractSAXHandler(PrintStream out,Transformer trans,Result transResult) throws ParserConfigurationException { this.out=out; this.trans=trans; this.transResult=transResult; // initialize local variables spineDoc= new SpineDocument(); doc = spineDoc.getDoc(); chars=new StringBuilder(); skipMode=false; keepMode=false; keepStart=null; level=0; pattern=new XPathParts(Options.xpathStr,Options.nsContext); parts = pattern.getParts(); lastPart=parts.size()-1; iPart=0; } private Pattern nonWhiteSpaceP = Pattern.compile("\\S"); /** * Create a text node from the content of the chars array and check if it matches. * This is called whenever a non-text node is encountered in the input * * @throws SAXException */ private void flushText() throws SAXException{ if(nonWhiteSpaceP.matcher(chars).find()){ // there is at least a non whitespace char String s = chars.toString(); checkPart(doc.createTextNode(s),false); checkAll(false); } chars.setLength(0); // must clear the buffer in all cases } /** * Check if a node matches the current part of the pattern * It first adds the node to the bottom of the spine document * If called in keep mode then return true * * @param n new node to add to the bottom of the document node * @param isElem true when n is an element node * @return * @throws SAXException */ private boolean checkPart(Node n, boolean isElem) throws SAXException{ if(Options.debug) Debug.dumpElem("checkPart("+n.getNodeType()+","+isElem+")", n); spineDoc.addBottom(n); if(keepMode){ if(isElem)level++; return true; } Examine.nbTests++; if(iPart0){ iPart++; return true; } // no match return false; } else { // final match if(isElem){ keepMode=true; keepStart=spineDoc.getBottom(); level=1; } return true; } } /** * Check if the last part of the pattern matches the bottom node * if it does transforms the resulting nodes to the output stream * * @param isElem is the last node an element node * @throws SAXException */ private void checkAll(boolean isElem) throws SAXException{ if(Options.debug) System.out.println("checkAll()"); if(level>0){ if(!isElem)spineDoc.upBottom(); return; } NodeList resNodes=parts.get(iPart).matches(keepStart==null?spineDoc.getBottom():keepStart); int nb=resNodes.getLength(); if(nb>0){ Examine.count+=nb; if(Options.onlyCount){ if(Options.maxRes>0)Options.maxRes=Options.maxRes-Math.min(nb,Options.maxRes); } else { // transform and output each result if(Options.debug)out.print("$:"); for(int i=0;i=0;i--){ String qname=attrs.getQName(i); int dp=qname.indexOf(':'); if(dp>=0) e.setAttributeNS(qname.substring(0,dp),qname.substring(dp+1), attrs.getValue(i)); else e.setAttributeNS(null,qname, attrs.getValue(i)); } if(!checkPart(e,true)){ skipMode=true; level=1; } } } /* (non-Javadoc) * @see org.xml.sax.helpers.DefaultHandler#endElement(java.lang.String, java.lang.String, java.lang.String) */ public void endElement(String uri, String localName, String raw) throws SAXException { if(Options.debug) System.out.println("endElement:"+raw+":"+level+":"+keepMode+":"+skipMode); flushText(); if(skipMode){ level--; if (level==0){ skipMode=false; spineDoc.removeBottom(); } } else if (keepMode){ level--; if (level==0){ keepMode=false; checkAll(true); keepStart=null; } else { spineDoc.upBottom(); } } else { spineDoc.removeBottom(); iPart--; } } /* (non-Javadoc) * @see org.xml.sax.helpers.DefaultHandler#characters(char[], int, int) */ public void characters(char[] ch, int start, int length) throws SAXException { String s=null; if(Options.debug)s=new String(ch,start,length).trim(); if(skipMode){ if(Options.debug) System.out.println("skip chars:"+s); } else { if(Options.debug) System.out.println("characters:"+s+":"+level+":"+keepMode+":"+skipMode); chars.append(ch,start,length); } } /* (non-Javadoc) * @see org.xml.sax.helpers.DefaultHandler#processingInstruction(java.lang.String, java.lang.String) */ public void processingInstruction(String target, String data) throws SAXException { if(Options.debug) System.out.println("processingInstruction:"+target+":"+level+":"+keepMode+":"+skipMode); if (!skipMode){ flushText(); checkPart(doc.createProcessingInstruction(target, data),false); checkAll(false); } } /* (non-Javadoc) * @see org.xml.sax.ext.DefaultHandler2#comment(char[], int, int) */ public void comment(char[] ch, int start, int length) throws SAXException { String s=new String(ch,start,length); if(Options.debug) System.out.println("comment:"+s+":"+level+":"+keepMode+":"+skipMode); if (!skipMode){ flushText(); checkPart(doc.createComment(s),false); checkAll(false); } } // end of processing types of XML nodes /* (non-Javadoc) * @see org.xml.sax.helpers.DefaultHandler#startPrefixMapping(java.lang.String, java.lang.String) */ public void startPrefixMapping(String prefix,String uri) throws SAXException { if(Options.debug) System.out.println("startPrefixMapping:"+prefix+"="+uri); Options.nsContext.addPrefixURI(prefix, uri); } /* (non-Javadoc) * @see org.xml.sax.helpers.DefaultHandler#endPrefixMapping(java.lang.String) */ public void endPrefixMapping(String prefix) throws SAXException{ if(Options.debug) System.out.println("endPrefixMapping:"+prefix); // uncommenting the following line will prevent getting the list of prefixes // Options.nsContext.removePrefix(prefix); } // error handling... /** * Output error message giving the line number where it occurred and other relevant information * * @param mess String to print * @param e exception from which information is extracted * @throws SAXException */ private void message(String mess,SAXParseException e) throws SAXException{ System.out.println("\n"+mess+ "\n Line:"+e.getLineNumber()+ "\n URI:" +e.getSystemId()+ "\n Message:"+e.getMessage()); } /* (non-Javadoc) * @see org.xml.sax.helpers.DefaultHandler#fatalError(org.xml.sax.SAXParseException) */ public void fatalError(SAXParseException e) throws SAXException{ message("Fatal error",e); } /* (non-Javadoc) * @see org.xml.sax.helpers.DefaultHandler#error(org.xml.sax.SAXParseException) */ public void error(SAXParseException e) throws SAXException{ message("Error",e); } /* (non-Javadoc) * @see org.xml.sax.helpers.DefaultHandler#warning(org.xml.sax.SAXParseException) */ public void warning(SAXParseException e) throws SAXException{ message("Warning",e); } }