/*
* Copyright 1999-2004 The Apache Software Foundation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* CVS $Id: XUpdateImpl.java,v 1.12 2004/02/08 02:50:54 vgritsenko Exp $
*/
package org.apache.xindice.core.xupdate;
import org.apache.xindice.core.Collection;
import org.apache.xindice.core.data.NodeSet;
import org.apache.xindice.xml.NamespaceMap;
import org.apache.xindice.xml.NodeSource;
import org.apache.xindice.xml.dom.CompressedNode;
import org.apache.xindice.xml.dom.DBNode;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.xml.sax.SAXException;
import org.xmldb.xupdate.lexus.XUpdateQueryImpl;
import org.xmldb.xupdate.lexus.commands.CommandConstants;
import org.xmldb.xupdate.lexus.commands.CommandObject;
import org.xmldb.xupdate.lexus.commands.DefaultCommand;
import java.util.Enumeration;
import java.util.Hashtable;
/**
* Provides Collection and document based XUpdate capabilities.
*
* For more detail about XUpdate look at the
* <a href="http://www.xmldb.org/xupdate/xupdate-wd.html">XUpdate Working Draft</a>.
*
* @version CVS $Revision: 1.12 $, $Date: 2004/02/08 02:50:54 $
*/
public class XUpdateImpl extends XUpdateQueryImpl {
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);
}
}
/**
* 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);
}
}
}
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;
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);
}
}
}
}
}
public int getModifiedCount() {
return nodesModified;
}
}