--- xml-xindice/java/src/org/apache/xindice/core/xupdate/XUpdateImpl.java 2004-02-07 19:50:54.000000000 -0700 +++ /home/byrne/jbproject/xindice-cvs-src/xml-xindice/java/src/org/apache/xindice/core/xupdate/XUpdateImpl.java 2004-08-26 13:50:53.000000000 -0600 @@ -27,6 +27,7 @@ import org.w3c.dom.Document; import org.w3c.dom.Node; +import org.w3c.dom.NodeList; import org.xml.sax.SAXException; import org.xmldb.xupdate.lexus.XUpdateQueryImpl; import org.xmldb.xupdate.lexus.commands.CommandConstants; @@ -35,6 +36,7 @@ import java.util.Enumeration; import java.util.Hashtable; +import org.apache.xindice.core.data.Key; /** * Provides Collection and document based XUpdate capabilities. @@ -44,148 +46,194 @@ * * @version CVS $Revision: 1.12 $, $Date: 2004/02/08 02:50:54 $ */ -public class XUpdateImpl extends XUpdateQueryImpl { +public class XUpdateImpl + extends XUpdateQueryImpl { - protected int nodesModified = 0; - protected NamespaceMap nsMap; + protected int nodesModified = 0; + protected NamespaceMap nsMap; - /** - * If set to true, then namespaces set explicitly via an API call will take precendence. - * If set to false, then namespaces set implicitly within query string will take precedence. - */ - private static final boolean API_NS_PRECEDENCE = true; - - /** - * Set the namespace map to be used when resolving queries - */ - public void setNamespaceMap(NamespaceMap nsMap) { - if (nsMap == null) { - return; - } - - if (this.nsMap == null) { - this.nsMap = nsMap; - } else { - this.nsMap.includeNamespaces(nsMap, API_NS_PRECEDENCE); - } + /** + * If set to true, then namespaces set explicitly via an API call will take precendence. + * If set to false, then namespaces set implicitly within query string will take precedence. + */ + private static final boolean API_NS_PRECEDENCE = true; + + /** + * Set the namespace map to be used when resolving queries + */ + public void setNamespaceMap(NamespaceMap nsMap) { + if (nsMap == null) { + return; } - - /** - * Sets the query string to be used when executing update - */ - public void setQString(String query) throws SAXException { - super.setQString(query); - if (nsMap == null) { - nsMap = new NamespaceMap(); - } - nsMap.includeNamespaces(super.namespaces, !API_NS_PRECEDENCE); + if (this.nsMap == null) { + this.nsMap = nsMap; } + else { + this.nsMap.includeNamespaces(nsMap, API_NS_PRECEDENCE); + } + } + /** + * Sets the query string to be used when executing update + */ + public void setQString(String query) throws SAXException { + super.setQString(query); + if (nsMap == null) { + nsMap = new NamespaceMap(); + } + nsMap.includeNamespaces(super.namespaces, !API_NS_PRECEDENCE); + } - /** - * Execute the XUpdate commands against a document. - */ - public void execute(Node contextNode) throws Exception { - CommandObject currentCommand = new DefaultCommand(contextNode); - Enumeration commands = super.query[0].elements(); - Enumeration attributes = super.query[1].elements(); - Enumeration characters = super.query[2].elements(); - Node origNode = contextNode; - CommandObject.getXPath().setNamespace(nsMap.getContextNode()); - - while (commands.hasMoreElements()) { - int id = ((Integer) commands.nextElement()).intValue(); - - if (id == CommandConstants.ATTRIBUTES) { - currentCommand.submitAttributes((Hashtable) attributes.nextElement()); - } else if (id == CommandConstants.CHARACTERS) { - currentCommand.submitCharacters((String) characters.nextElement()); - } else if (id > 0) { - if (!currentCommand.submitInstruction(id)) { - super.commandConstants.setContextNode(contextNode); - currentCommand = super.commandConstants.commandForID(id); - if (currentCommand == null) { - throw new Exception("Operation can not have any XUpdate-instruction!"); - } - currentCommand.reset(); - } - } else { - if (!currentCommand.executeInstruction()) { - try { - contextNode = currentCommand.execute(); - } catch (Exception e) { - // While not ideal, CommandObject.execute throws - // Exception("no nodes selected !") if nothing is - // selected for modification we trap that case - // and ignore allowing continued processing - // of remaining xupdate instructions that may be present - if (!"no nodes selected !".equals(e.getMessage())) { - throw e; - } - } - // Default do-nothing command will soak up anything - // (characters, attributes, etc.) encountered until we - // come across the next xupdate instruction - // (e.g. remove, append, insert, etc.) - currentCommand = new DefaultCommand(contextNode); - } + /** + * Execute the XUpdate commands against a document. + */ + public void execute(Node contextNode) throws Exception { + CommandObject currentCommand = new DefaultCommand(contextNode); + Enumeration commands = super.query[0].elements(); + Enumeration attributes = super.query[1].elements(); + Enumeration characters = super.query[2].elements(); + Node origNode = contextNode; + CommandObject.getXPath().setNamespace(nsMap.getContextNode()); + boolean contextNodeNeedsCleaning = false; + + while (commands.hasMoreElements()) { + int id = ( (Integer) commands.nextElement()).intValue(); + + if (id == CommandConstants.ATTRIBUTES) { + currentCommand.submitAttributes( (Hashtable) attributes.nextElement()); + } + else if (id == CommandConstants.CHARACTERS) { + currentCommand.submitCharacters( (String) characters.nextElement()); + } + else if (id > 0) { + if (!currentCommand.submitInstruction(id)) { + super.commandConstants.setContextNode(contextNode); + currentCommand = super.commandConstants.commandForID(id); + if (currentCommand == null) { + throw new Exception( + "Operation can not have any XUpdate-instruction!"); + } + currentCommand.reset(); + } + } + else { + if (!currentCommand.executeInstruction()) { + try { + contextNode = currentCommand.execute(); + } + catch (Exception e) { + // While not ideal, CommandObject.execute throws + // Exception("no nodes selected !") if nothing is + // selected for modification we trap that case + // and ignore allowing continued processing + // of remaining xupdate instructions that may be present + if (!"no nodes selected !".equals(e.getMessage())) { + throw e; } - } - - if (origNode instanceof CompressedNode) { - CompressedNode cn = (CompressedNode) origNode; - if (cn.isDirty()) { - nodesModified++; + // from my tests I figure out it adds one temporary node per + // command, but when the exception gets thrown it never cleans it self up + // this one node is always at the end + String lastNodeName = origNode.getLastChild().getNodeName(); + if (lastNodeName.equals("temporaryXUpdateTree")) { + // assuming that one command one temporary node. + origNode.removeChild(origNode.getLastChild()); + } + else { // search for it? + contextNodeNeedsCleaning = true; } + } + // Default do-nothing command will soak up anything + // (characters, attributes, etc.) encountered until we + // come across the next xupdate instruction + // (e.g. remove, append, insert, etc.) + currentCommand = new DefaultCommand(contextNode); } + } } + if (contextNodeNeedsCleaning) { + // search entire tree and to verify no extra nodes where left around. + cleanUpNode(origNode); + } + if (origNode instanceof CompressedNode) { + CompressedNode cn = (CompressedNode) origNode; + if (cn.isDirty()) { + nodesModified++; + } + } + } + + /** + * Execute the set of XUpdate commands against a collection. + * + * @param col The collection against which the command will be executed + * @exception Exception Description of Exception + */ + public void execute(Collection col) throws Exception { + + int attribIndex = 0; + int temp = 0; + Hashtable docsUpdated = new Hashtable(); + + for (int i = 0; i < super.query[0].size(); i++) { + int cmdID = ( (Integer)super.query[0].elementAt(i)).intValue(); + + if (cmdID == CommandConstants.ATTRIBUTES) { + Hashtable attribs = (Hashtable)super.query[1].elementAt(attribIndex); + attribIndex++; + String selector = (String) attribs.get("select"); + + // If we found an XPath selector we need to execute the commands. + // save to skip variable because at this point we have already run the commands on the + // files. + if (selector != null && !selector.startsWith("$")) { + NodeSet ns = col.queryCollection("XPath", selector, nsMap); + + while (ns != null && ns.hasMoreNodes()) { + DBNode node = (DBNode) ns.getNextNode(); + Document doc = node.getOwnerDocument(); + NodeSource source = node.getSource(); + Node contextNode = doc.getDocumentElement(); - /** - * Execute the set of XUpdate commands against a collection. - * - * @param col The collection against which the command will be executed - * @exception Exception Description of Exception - */ - public void execute(Collection col) throws Exception { - - int attribIndex = 0; - for (int i = 0; i < super.query[0].size(); i++) { - int cmdID = ((Integer) super.query[0].elementAt(i)).intValue(); - - if (cmdID == CommandConstants.ATTRIBUTES) { - Hashtable attribs = (Hashtable) super.query[1].elementAt(attribIndex); - attribIndex++; - String selector = (String) attribs.get("select"); - - // If we found an XPath selector we need to execute the commands. - if (selector != null) { - NodeSet ns = col.queryCollection("XPath", selector, nsMap); - Document lastDoc = null; - while (ns != null && ns.hasMoreNodes()) { - DBNode node = (DBNode) ns.getNextNode(); - Document doc = node.getOwnerDocument(); - - if (doc == lastDoc) { - continue; // We only have to process it once - } else { - lastDoc = doc; - } - - NodeSource source = node.getSource(); - - Node contextNode = doc.getDocumentElement(); - execute(contextNode); - - col.setDocument(source.getKey(), doc); - } - } + if (docsUpdated.containsKey(source.getKey())) { + continue; // We only have to process it once } + else { + docsUpdated.put(source.getKey(), doc); + } + execute(contextNode); + } } + } + } + Enumeration keys = docsUpdated.keys(); + while (keys.hasMoreElements()) { + Key docID = (Key) keys.nextElement(); + col.setDocument(docID, (Document) docsUpdated.get(docID)); } + } + + public int getModifiedCount() { + return nodesModified; + } +/** + * Search the document tree for nodes left around from the xupdate commands. + * + */ - public int getModifiedCount() { - return nodesModified; + private void cleanUpNode(Node n) { + NodeList list = n.getChildNodes(); + int listLength = list.getLength(); + + for (int i = listLength - 1; i >= 0; i--) { + Node currentNode = list.item(i); + if (currentNode.getNodeName().equals("temporaryXUpdateTree")) { + n.removeChild(currentNode); + } + else { + cleanUpNode(currentNode); + } } -} + } +} \ No newline at end of file