com.marklogic.dom.DocumentImpl Maven / Gradle / Ivy
/*
* Copyright (c) 2020 MarkLogic Corporation
*
* 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.
*/
package com.marklogic.dom;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.w3c.dom.Attr;
import org.w3c.dom.CDATASection;
import org.w3c.dom.Comment;
import org.w3c.dom.DOMConfiguration;
import org.w3c.dom.DOMException;
import org.w3c.dom.DOMImplementation;
import org.w3c.dom.Document;
import org.w3c.dom.DocumentFragment;
import org.w3c.dom.DocumentType;
import org.w3c.dom.Element;
import org.w3c.dom.EntityReference;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.w3c.dom.ProcessingInstruction;
import org.w3c.dom.Text;
import com.marklogic.tree.ExpandedTree;
/**
* A read-only W3C DOM Node implementation of MarkLogic's internal
* representation of an XML or text document as stored in the expanded tree
* cache of a forest on disk.
*
*
* This interface is effectively read-only: Setters and update methods inherited
* from org.w3c.Node
are not supported and will raise an exception
* if called. To create a modifiable copy of a node for XML document, use
* {@link #cloneNode}. Text document cannot be cloned.
*
*
* @author jchen
*/
public class DocumentImpl extends NodeImpl implements Document {
public static final Log LOG = LogFactory.getLog(DocumentImpl.class);
private Element documentElement;
/**
* owner document for cloneNode
*/
private Document ownerDocCloned;
private static DocumentBuilderFactory dbf = null;
private int isXMLDoc;
static final int UNKNOWN_TYPE = -1;
static final int VALID_XML = 0;
static final int NON_XML = 1;
public DocumentImpl(ExpandedTree tree, int node) {
super(tree, node);
isXMLDoc = UNKNOWN_TYPE;
}
private static synchronized DocumentBuilderFactory getDocumentBuilderFactory() {
if (dbf == null) {
dbf = DocumentBuilderFactory.newInstance();
dbf.setNamespaceAware(true);
}
return dbf;
}
/**
* {@inheritDoc}
*
* Text documents in MarkLogic cannot be cloned.
* UnsupportedOperationException will be thrown if cloneNode is call on text
* document. >
*
* DocumentType node will not be cloned as it is not part of the Expanded
* Tree.>
*
*
*/
public Node cloneNode(boolean deep) {
try {
if (isXMLDoc == UNKNOWN_TYPE) isXMLDoc = getDocumentType();
if (isXMLDoc == NON_XML) {
throw new UnsupportedOperationException(
"Text document cannot be cloned");
}
// initialize a new doc owner node
initClonedOwnerDoc();
} catch (ParserConfigurationException e) {
throw new RuntimeException("Internal Error:" + e);
}
if (deep) {
for (NodeImpl n = (NodeImpl) getFirstChild(); n != null; n = (NodeImpl) n
.getNextSibling()) {
ownerDocCloned.appendChild(n.cloneNode(ownerDocCloned, true));
}
}
return ownerDocCloned;
}
protected void initClonedOwnerDoc() throws ParserConfigurationException {
ownerDocCloned = getDocumentBuilderFactory().newDocumentBuilder().newDocument();
ownerDocCloned.setDocumentURI(getDocumentURI());
ownerDocCloned.setXmlVersion(getXmlVersion());
}
/**
* Document can be an XML document or a text document.
* @return true if XML document; otherwise false.
*/
public boolean isXMLDoc() {
if (isXMLDoc == UNKNOWN_TYPE) isXMLDoc = getDocumentType();
return isXMLDoc == VALID_XML;
}
/**
* Check root node of a document to see if it conform to DOM Structure
* Model. The root node can only be ELEMENT_NODE,
* PROCESSING_INSTRUCTION_NODE or COMMENT_NODE.
*
* @return 1(NON_XML) if root node violates DOM Structure Model; otherwise
* 0(VALID_XML).
*/
private int getDocumentType() {
NodeList children = getChildNodes();
int elemCount = 0;
for (int i = 0; i < children.getLength(); i++) {
Node n = children.item(i);
switch (n.getNodeType()) {
case Node.ELEMENT_NODE:
elemCount++;
break;
case Node.PROCESSING_INSTRUCTION_NODE:
case Node.COMMENT_NODE:
continue;
default:
return NON_XML;
}
}
return elemCount <= 1 ? VALID_XML : NON_XML;
}
@Override
public String getNodeName() {
return "#document";
}
@Override
public Document getOwnerDocument() {
return null;
}
protected int getNumChildren() {
return tree.docNodeNumChildren[tree.nodeRepID[node]];
}
public int getFirstChildIndex() {
return tree.docNodeChildNodeRepID[tree.nodeRepID[node]];
}
public NodeList getChildNodes() {
return new NodeList() {
public int getLength() {
return getNumChildren();
}
public Node item(int index) {
return (index < getNumChildren()) ? tree
.node(getFirstChildIndex() + index) : null;
}
};
}
public Node getFirstChild() {
int i = getFirstChildIndex();
return (i != Integer.MAX_VALUE) ? tree.node(i) : null;
}
public Node getLastChild() {
int i = tree.docNodeChildNodeRepID[tree.nodeRepID[node]];
return (i != Integer.MAX_VALUE) ? tree.node(i
+ tree.docNodeNumChildren[node] - 1) : null;
}
public boolean hasChildNodes() {
return (getFirstChildIndex() != Integer.MAX_VALUE);
}
protected Node getNextChild(int child) {
if (child - getFirstChildIndex() + 1 < getNumChildren()) {
return tree.node(child + 1);
} else {
return null;
}
}
protected Node getPreviousChild(int node) {
if (node != getFirstChildIndex()) {
return tree.node(node - 1);
} else {
return null;
}
}
/** Unsupported. */
public Node adoptNode(Node arg0) throws DOMException {
throw new DOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR, null);
}
/** Unsupported. */
public Attr createAttribute(String arg0) throws DOMException {
throw new DOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR, null);
}
/** Unsupported. */
public Attr createAttributeNS(String arg0, String arg1)
throws DOMException {
throw new DOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR, null);
}
/** Unsupported. */
public CDATASection createCDATASection(String arg0) throws DOMException {
throw new DOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR, null);
}
/** Unsupported. */
public Comment createComment(String arg0) {
throw new DOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR, null);
}
/** Unsupported. */
public DocumentFragment createDocumentFragment() {
throw new DOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR, null);
}
/** Unsupported. */
public Element createElement(String arg0) throws DOMException {
throw new DOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR, null);
}
/** Unsupported. */
public Element createElementNS(String arg0, String arg1)
throws DOMException {
throw new DOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR, null);
}
/** Unsupported. */
public EntityReference createEntityReference(String arg0)
throws DOMException {
throw new DOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR, null);
}
/** Unsupported. */
public ProcessingInstruction createProcessingInstruction(String arg0,
String arg1) throws DOMException {
throw new DOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR, null);
}
/** Unsupported. */
public Text createTextNode(String arg0) {
throw new DOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR, null);
}
/**
* Returns a dummy DocumentTypeImpl object that contains nothing. It is not
* expected to do anything with the returned object.
*/
public DocumentType getDoctype() {
return new DocumentTypeImpl(tree, node);
}
public Element getDocumentElement() {
if (documentElement != null)
return documentElement;
NodeList children = getChildNodes();
for (int i = 0; i < children.getLength(); i++) {
Node n = children.item(i);
if (n.getNodeType() == Node.ELEMENT_NODE) {
documentElement = (Element) n;
break;
}
}
return documentElement;
}
@Override
public boolean isDefaultNamespace(String namespaceURI) {
Element e = getDocumentElement();
return e != null ? e.isDefaultNamespace(namespaceURI) : false;
}
public String getDocumentURI() {
return tree.getDocumentURI();
}
/** Unsupported. */
public DOMConfiguration getDomConfig() {
return null;
}
/** Unsupported. */
public Element getElementById(String arg0) {
return null;
}
public NodeList getElementsByTagNameNS(String namespaceURI, String name) {
return getElementsByTagNameNSOrNodeName(namespaceURI, name, false);
}
public NodeList getElementsByTagName(String localName) {
return getElementsByTagNameNSOrNodeName(null, localName, true);
}
/** Unsupported. */
public DOMImplementation getImplementation() {
return null;
}
/** Unsupported. */
public String getInputEncoding() {
return null;
}
/** Unsupported. */
public boolean getStrictErrorChecking() {
return false;
}
/** Unsupported. */
public String getXmlEncoding() {
return null;
}
/** Unsupported. */
public boolean getXmlStandalone() {
return true;
}
public String getXmlVersion() {
return "1.0";
}
/** Unsupported. */
public Node importNode(Node arg0, boolean arg1) throws DOMException {
return null;
}
@Override
public String lookupNamespaceURI(String prefix) {
return getDocumentElement().lookupNamespaceURI(prefix);
}
@Override
public String lookupPrefix(String namespaceURI) {
return getDocumentElement().lookupPrefix(namespaceURI);
}
/** Unsupported. */
public void normalizeDocument() {
throw new DOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR, null);
}
/** Unsupported. */
public Node renameNode(Node arg0, String arg1, String arg2)
throws DOMException {
throw new DOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR, null);
}
/** Unsupported. */
public void setDocumentURI(String arg0) {
throw new DOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR, null);
}
/** Unsupported. */
public void setStrictErrorChecking(boolean arg0) {
throw new DOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR, null);
}
/** Unsupported. */
public void setXmlStandalone(boolean arg0) throws DOMException {
throw new DOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR, null);
}
/** Unsupported. */
public void setXmlVersion(String arg0) throws DOMException {
throw new DOMException(DOMException.NO_MODIFICATION_ALLOWED_ERR, null);
}
}