package ca.umontreal.iro.rali; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.InputStream; import java.io.OutputStream; import java.io.PrintStream; import java.lang.management.ManagementFactory; import java.util.List; import java.util.Map; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import javax.xml.stream.XMLInputFactory; import javax.xml.stream.XMLOutputFactory; import javax.xml.stream.XMLStreamConstants; import javax.xml.stream.XMLStreamException; import javax.xml.stream.XMLStreamReader; import javax.xml.stream.XMLStreamWriter; import javax.xml.transform.OutputKeys; import javax.xml.transform.Result; 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.DOMResult; import javax.xml.transform.dom.DOMSource; import javax.xml.transform.stream.StreamResult; import javax.xml.transform.stream.StreamSource; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; /** * Extract nodes from an XML document corresponding to an XPath expression * using a Pull Parser * * @author Guy Lapalme, Université de Montréal, 2009 * */ public class Extract { private static XMLStreamReader xmlsr=null; // Pull parser private static XMLStreamWriter xmlsw=null; // Output writer private static OutputStream output=null; private static PrintStream out=null; // Print stream corresponding to the output /** * Set up and initialize the pull parser and output stream writer * * @return the number of bytes in the input file */ protected static long setInputOutput(){ long fileLength=-1; try{ // create input XMLInputFactory inputFactory=XMLInputFactory.newInstance(); inputFactory.setProperty(XMLInputFactory.IS_REPLACING_ENTITY_REFERENCES, true); inputFactory.setProperty(XMLInputFactory.IS_COALESCING,true); InputStream input; if(Options.inputFileName==null){ input=System.in; } else { File file=new File(Options.inputFileName); fileLength=file.length(); input=new FileInputStream(file); } // create input xmlsr=inputFactory.createXMLStreamReader(input); if(!xmlsr.hasNext()){ System.err.println("Empty input file:"+(Options.inputFileName==null?"Standard input" :Options.inputFileName)); System.exit(1); } while(xmlsr.hasNext()&&!xmlsr.isStartElement()){ // skip to root element Explore.ignore(xmlsr); xmlsr.next(); } // get namespaces at root node int nsCount=xmlsr.getNamespaceCount(); for(int i=0;i parts = pattern.getParts(); int lastPart=parts.size()-1; int iPart=0; Examine.count=0; // configure XML output XMLOutputFactory outputFactory = XMLOutputFactory.newInstance(); xmlsw=outputFactory.createXMLStreamWriter(output,"UTF-8"); // create the XSL transformer factory transFact = TransformerFactory.newInstance(); // local stylesheet if(Options.xsltFile==null){ trans = transFact.newTransformer(); // identity transformer } else { xsltSource = new StreamSource(Options.xsltFile); trans = transFact.newTransformer(xsltSource); } trans.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION,"yes"); trans.setOutputProperty(OutputKeys.INDENT,Options.prettyPrint?"yes":"no"); for(Map.Entry e:Options.xslParams.entrySet()) trans.setParameter(e.getKey(), e.getValue()); // global stylesheet if(Options.xsltFileFinal==null) transResult = new StreamResult(output); else { DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); DocumentBuilder builder = factory.newDocumentBuilder(); outputDoc = builder.newDocument(); outputRoot = outputDoc.createElementNS(null, Options.rootName); outputDoc.appendChild(outputRoot); transResult = new DOMResult(outputRoot); } // if(Options.xsltFileFinal==null){ if(Options.rootName!=null){ // create the root document xmlsw.writeStartDocument("UTF-8","1.0");xmlsw.writeCharacters("\n"); xmlsw.writeStartElement(Options.rootName); for(Map.Entrye:Options.nsContext.getAllPrefixesURI()) xmlsw.writeNamespace(e.getKey(), e.getValue()); xmlsw.writeCharacters("\n"); } xmlsw.setNamespaceContext(Options.nsContext); } // read the input file readLoop: while(xmlsr.hasNext()){ switch (xmlsr.getEventType()){ case XMLStreamConstants.CHARACTERS: case XMLStreamConstants.COMMENT: case XMLStreamConstants.START_ELEMENT: case XMLStreamConstants.PROCESSING_INSTRUCTION: if(xmlsr.isWhiteSpace()) Explore.ignore(xmlsr); else { // node to add to the spine document boolean isElem=xmlsr.isStartElement(); Examine.nbTests++; if(iPart0){ iPart++; xmlsr.next(); continue; // go back to the enclosing while loop } // no match if(isElem)skip(xmlsr,1); // ignore the rest of the current element } else { // final match spineDoc.addBottom(readFullNode(xmlsr,doc)); if(Options.debug && isElem) Debug.dumpElem("start element:"+iPart+"/"+lastPart+ " part:"+parts.get(iPart),spineDoc.getBottom()); NodeList resNodes=parts.get(iPart).matches(spineDoc.getBottom()); int n=resNodes.getLength(); if(n>0){ Examine.count+=n; if(Options.onlyCount){ if(Options.maxRes>0)Options.maxRes=Options.maxRes-Math.min(n,Options.maxRes); } else { // transform and output each result if(Options.debug)xmlsw.writeCharacters("$:"); for(int i=0;i e:Options.nsContext.getAllPrefixesURI()){ String key = e.getKey(); outputRoot.setAttributeNS("http://www.w3.org/2000/xmlns/", "xmlns"+((key==null|| key.length()==0)?"":(":"+key)), e.getValue()); } xsltSource = new StreamSource(Options.xsltFileFinal); trans = transFact.newTransformer(xsltSource); trans.setOutputProperty(OutputKeys.INDENT,Options.prettyPrint?"yes":"no"); for(Map.Entry e:Options.xslParams.entrySet()) trans.setParameter(e.getKey(), e.getValue()); trans.transform(new DOMSource(outputDoc), new StreamResult(output)); } else if(Options.rootName!=null) xmlsw.writeEndElement(); xmlsw.writeCharacters("\n"); xmlsw.close(); if(Options.statistics){ System.err.println("XPath processor:"+XPath.getXPathProvider()); System.err.println("XSL Transformer:"+trans.getClass().getName()); } } catch(Throwable e){ System.err.format("Line %d : %.1f M used\n",xmlsr.getLocation().getLineNumber(), ManagementFactory.getMemoryMXBean().getHeapMemoryUsage().getUsed()/1000000.0); e.printStackTrace(); } return fileLength; } /** * read a Node (created with the doc) from pull parser reader * read a complete element (including embedded elements if necessary) * * @param xmlsr the pull-parser * @param doc the document to create new nodes * @return the Document node created by the pull-parser * @throws XMLStreamException */ protected static Node readFullNode(XMLStreamReader xmlsr,Document doc) throws XMLStreamException{ Node n0=Explore.readNode(xmlsr,doc); // read the start Node bottom=n0; int level=n0.getNodeType()==Node.ELEMENT_NODE?1:0; while(level>0 && xmlsr.hasNext()){ switch (xmlsr.next()){ case XMLStreamConstants.START_ELEMENT: case XMLStreamConstants.CHARACTERS: case XMLStreamConstants.COMMENT: case XMLStreamConstants.PROCESSING_INSTRUCTION: if(xmlsr.isWhiteSpace()) Explore.ignore(xmlsr); else { Node n=Explore.readNode(xmlsr,doc); // read the start of an embedded node bottom.appendChild(n); if(n.getNodeType()==Node.ELEMENT_NODE){ level++; bottom=n; } } break; case XMLStreamConstants.END_ELEMENT: level--; bottom=bottom.getParentNode(); break; default: Explore.ignore(xmlsr); } } return n0; } /** * Skip the rest of an element, taking into account the possibility of embedded elements * * @param xmlsr pull-parser * @param level indicates how many starting elements have been encountered without closing * @throws XMLStreamException */ protected static void skip(XMLStreamReader xmlsr, int level) throws XMLStreamException { if(level==0)return; while (xmlsr.hasNext()) { switch(xmlsr.next()){ case XMLStreamConstants.START_ELEMENT: if(Options.debug) System.out.println("skip Start:"+xmlsr.getLocalName()); level++; break; case XMLStreamConstants.END_ELEMENT: if(Options.debug) System.out.println("skip End:"+xmlsr.getLocalName()); level--; if (level==0){ xmlsr.next(); return; } break; case XMLStreamConstants.CHARACTERS: if(Options.debug) System.out.println(xmlsr.isWhiteSpace()?"skip WHITESPACE":"skip chars:"+xmlsr.getText()); break; default: if(Options.debug) System.out.println("skipping "+Explore.XMLStreamConstantsNames[xmlsr.getEventType()]); } } } }