/*
* @(#)$Id: DocumentTableModel.java 3619 2008-03-26 07:23:03Z yui $
*
* Copyright 2006-2008 Makoto YUI
*
* 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.
*
* Contributors:
* Makoto YUI - initial implementation
*/
package xbird.xquery.dm.instance;
import static xbird.xquery.dm.dtm.IDocumentTable.BLOCKS_PER_NODE;
import java.io.Closeable;
import java.io.Externalizable;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInput;
import java.io.ObjectOutput;
import java.io.ObjectStreamException;
import java.util.concurrent.ConcurrentMap;
import javax.xml.XMLConstants;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.xml.sax.EntityResolver;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;
import xbird.config.Settings;
import xbird.storage.io.DiskPagedLongCache;
import xbird.util.cache.ILongCache;
import xbird.util.codec.IntCodec;
import xbird.util.concurrent.jsr166.ConcurrentReferenceHashMap;
import xbird.util.concurrent.jsr166.ConcurrentReferenceHashMap.ReferenceType;
import xbird.util.datetime.StopWatch;
import xbird.util.io.FastBufferedInputStream;
import xbird.util.io.FileUtils;
import xbird.util.lang.ClassResolver;
import xbird.util.lang.ObjectUtils;
import xbird.util.xml.XMLUtils;
import xbird.xquery.DynamicError;
import xbird.xquery.XQRTException;
import xbird.xquery.XQueryConstants;
import xbird.xquery.XQueryException;
import xbird.xquery.dm.NodeKind;
import xbird.xquery.dm.XQEventReceiver;
import xbird.xquery.dm.coder.SerializationContext;
import xbird.xquery.dm.coder.XQEventDecoder;
import xbird.xquery.dm.coder.XQEventEncoder;
import xbird.xquery.dm.dtm.DocumentTable;
import xbird.xquery.dm.dtm.DocumentTableBuilder;
import xbird.xquery.dm.dtm.IDocumentTable;
import xbird.xquery.dm.dtm.MemoryMappedDocumentTable;
import xbird.xquery.dm.dtm.PagingProfile;
import xbird.xquery.dm.dtm.PagingProfile.Strategy;
import xbird.xquery.dm.dtm.hooked.IProfiledDocumentTable;
import xbird.xquery.dm.dtm.hooked.ProfiledDocumentTable;
import xbird.xquery.dm.value.Item;
import xbird.xquery.dm.value.Sequence;
import xbird.xquery.dm.value.XQNode;
import xbird.xquery.dm.value.xsi.BooleanValue;
import xbird.xquery.expr.path.NodeTest;
import xbird.xquery.meta.DynamicContext;
import xbird.xquery.misc.QNameTable;
import xbird.xquery.misc.QNameTable.QualifiedName;
/**
*
* <DIV lang="en"></DIV>
* <DIV lang="ja"></DIV>
*
* @author Makoto YUI (yuin405+xbird@gmail.com)
*/
public final class DocumentTableModel extends DataModel implements Externalizable {
private static final long serialVersionUID = 6670281140109393291L;
private static final Log LOG = LogFactory.getLog(DocumentTableModel.class);
private static final String profileAccessPattern = System.getProperty("xbird.profile_dtm");
private static final boolean resolveEntity = Boolean.parseBoolean(Settings.get("xbird.xml.resolve_entity"));
private static final String HTML_PARSER_CLASS = Settings.get("xbird.xml.html_saxparser", "xbird.util.xml.HTMLSAXParser");
private static final String TMP_DATA_DIR = Settings.get("xbird.database.tmpdir");
private static final boolean hasSunXerces;
static {
hasSunXerces = ClassResolver.isPresent("com.sun.org.apache.xerces.internal.jaxp.SAXParserFactoryImpl");
}
private transient final DocumentTableBuilder _handler;
private transient final XMLReader _reader;
private transient boolean _mmapedStore;
private/* final */IDocumentTable _store;
private int _docid = -1;
private static ConcurrentMap<String, MemoryMappedDocumentTable> _remoteDoctblCache = null;
/**
* Only for {@link Externalizable}.
*/
public DocumentTableModel() {
this._handler = null;
this._reader = null;
}
public DocumentTableModel(boolean parseAsHtml) {
this(parseAsHtml, resolveEntity);
}
public DocumentTableModel(boolean parseAsHtml, boolean resolveEntity) {
this(profileAccessPattern != null ? getProfiledDocumentTable() : new DocumentTable(), false, parseAsHtml, resolveEntity);
}
public DocumentTableModel(IDocumentTable store) {
this(store, false, false, resolveEntity);
}
public DocumentTableModel(IDocumentTable store, boolean loaded) {
this(store, loaded, false, resolveEntity);
}
private DocumentTableModel(IDocumentTable store, boolean loaded, boolean parseAsHtml, boolean resolveEntity) {
super();
this._mmapedStore = (store instanceof MemoryMappedDocumentTable);
this._store = store;
if(loaded) {
this._handler = null;
this._reader = null;
this._docid = nextDocumentId();
} else {
this._handler = new DocumentTableBuilder(store);
this._reader = getXMLReader(_handler, parseAsHtml, resolveEntity);
}
}
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
this._docid = in.readInt();
final IDocumentTable doctbl = (IDocumentTable) in.readObject();
if(doctbl instanceof MemoryMappedDocumentTable) {
this._mmapedStore = true;
MemoryMappedDocumentTable mmDoctbl = (MemoryMappedDocumentTable) doctbl;
String docId = mmDoctbl.getDocumentIdentifer();
if(docId != null) {
setPool(mmDoctbl, docId);
}
}
this._store = doctbl;
}
private static synchronized void setPool(MemoryMappedDocumentTable mmDoctbl, String docId)
throws IOException {
ConcurrentMap<String, MemoryMappedDocumentTable> cache = _remoteDoctblCache;
if(cache == null) {
cache = new ConcurrentReferenceHashMap<String, MemoryMappedDocumentTable>(16, ReferenceType.STRONG, ReferenceType.SOFT);
_remoteDoctblCache = cache;
}
MemoryMappedDocumentTable prevDoctbl = cache.putIfAbsent(docId, mmDoctbl);
if(prevDoctbl != null) {
ILongCache<int[]> prevCache = prevDoctbl.getBufferPool();
if(prevCache != null) {
mmDoctbl.setBufferPool(prevCache);
}
} else {
File tmpDir = (TMP_DATA_DIR == null) ? null : new File(TMP_DATA_DIR);
String docname = FileUtils.basename(docId);
File tmpFile = File.createTempFile(docname, ".tmp", tmpDir);
tmpFile.deleteOnExit();
ILongCache<int[]> pool = new DiskPagedLongCache<int[]>(tmpFile, MemoryMappedDocumentTable.CACHED_PAGES, new IntCodec());
mmDoctbl.setBufferPool(pool);
}
}
public void writeExternal(ObjectOutput out) throws IOException {
out.writeInt(_docid);
out.writeObject(_store);
}
@Deprecated
public static DocumentTableModel read(ObjectInput in) throws IOException,
ClassNotFoundException {
DocumentTableModel model = new DocumentTableModel();
model.readExternal(in);
return model;
}
@Override
public boolean isMemoryMappedStore() {
return _mmapedStore;
}
private static DocumentTable getProfiledDocumentTable() {
return new ProfiledDocumentTable(new File(profileAccessPattern));
}
public <T extends DTMNodeBase> T createNode(long id) {
final byte kind = _store.getNodeKindAt(id);
return this.<T> createNode(kind, id);
}
@SuppressWarnings("unchecked")
public <T extends DTMNodeBase> T createNode(byte kind, long id) {
switch(kind) {
case NodeKind.DOCUMENT:
return (T) new DTMDocument(this, id);
case NodeKind.ELEMENT:
return (T) new DTMElement(this, id);
case NodeKind.ATTRIBUTE:
return (T) new DTMAttribute(this, id);
case NodeKind.NAMESPACE:
return (T) new DTMNamespace(this, id);
case NodeKind.COMMENT:
return (T) new DTMComment(this, id);
case NodeKind.TEXT:
return (T) new DTMText(this, id);
case NodeKind.PROCESSING_INSTRUCTION:
return (T) new DTMProcessingInstruction(this, id);
default:
throw new IllegalStateException("Invalid node kind: " + kind + " for node#" + id);
}
}
@SuppressWarnings("unchecked")
public static <T extends DTMNodeBase> T getPrototype(DocumentTableModel model, byte kind, long nid) {
switch(kind) {
case NodeKind.ELEMENT:
return (T) new DTMElement(model, nid);
case NodeKind.TEXT:
return (T) new DTMText(model, nid);
case NodeKind.ATTRIBUTE:
return (T) new DTMAttribute(model, nid);
case NodeKind.DOCUMENT:
return (T) new DTMDocument(model, nid);
case NodeKind.NAMESPACE:
return (T) new DTMNamespace(model, nid);
case NodeKind.COMMENT:
return (T) new DTMComment(model, nid);
case NodeKind.PROCESSING_INSTRUCTION:
return (T) new DTMProcessingInstruction(model, nid);
default:
throw new IllegalStateException("Invalid node kind " + kind + " for nid #" + nid);
}
}
public DTMDocument documentNode() {
return new DTMDocument(this, 0);
}
/**
* Traverses tree in depth first order and reports events.
*/
public void export(final XQNode curNode, final XQEventReceiver receiver) throws XQueryException {
this.export(curNode.getPosition(), receiver);
}
public void export(final long nodeid, final XQEventReceiver receiver) throws XQueryException {
final PagingProfile profile = _store.getPagingProfile();
Strategy origStrategy = null;
if(profile != null) {
origStrategy = profile.getStrategy();
profile.setStrategy(Strategy.serialization);
}
final IDocumentTable doctbl = _store;
doctbl.ensureOpen();
switch(doctbl.getNodeKindAt(nodeid)) {
case NodeKind.DOCUMENT:
receiver.evStartDocument();
final long firstChild = doctbl.firstChild(nodeid);
if(firstChild != -1L) {
export(firstChild, receiver);
long nextSib = doctbl.nextSibling(firstChild);
while(nextSib != 0L) {
export(nextSib, receiver);
nextSib = doctbl.nextSibling(firstChild);
}
}
receiver.evEndDocument();
break;
case NodeKind.ELEMENT:
final QualifiedName qname = doctbl.getName(nodeid);
receiver.evStartElement(nodeid, qname);
// namespace decl
final int nsdeclCnt = doctbl.getNamespaceCountAt(nodeid);
for(int i = 0; i < nsdeclCnt; i++) {
final long nsid = doctbl.getNamespaceDecl(nodeid, i);
final QualifiedName nsName = doctbl.getAttributeName(nsid);
final String uri = doctbl.getText(nsid);
receiver.evNamespace(nsid, nsName, uri);
}
// attribute
final int attrCnt = doctbl.getAttributeCountAt(nodeid);
for(int i = 0; i < attrCnt; i++) {
final long attid = doctbl.getAttribute(nodeid, i);
final QualifiedName attName = doctbl.getAttributeName(attid);
final String attValue = doctbl.getText(attid);
receiver.evAttribute(attid, attName, attValue);
}
final long elemFirstChild = doctbl.firstChild(nodeid);
if(elemFirstChild != -1L) {
export(elemFirstChild, receiver);
long nextSib = doctbl.nextSibling(elemFirstChild);
while(nextSib != 0L) {
export(nextSib, receiver);
nextSib = doctbl.nextSibling(nextSib);
}
}
receiver.evEndElement(nodeid, qname);
break;
case NodeKind.ATTRIBUTE:
receiver.evAttribute(nodeid, doctbl.getAttributeName(nodeid), doctbl.getText(nodeid));
break;
case NodeKind.NAMESPACE:
receiver.evNamespace(nodeid, doctbl.getAttributeName(nodeid), doctbl.getText(nodeid));
break;
case NodeKind.TEXT:
receiver.evText(nodeid, doctbl.getText(nodeid));
break;
case NodeKind.COMMENT:
receiver.evComment(nodeid, doctbl.getText(nodeid));
break;
case NodeKind.PROCESSING_INSTRUCTION:
final QualifiedName pi = doctbl.getName(nodeid);
receiver.evProcessingInstruction(nodeid, pi.getLocalPart(), pi.getNamespaceURI());
break;
default:
throw new IllegalStateException("Invalid node kind '"
+ NodeKind.resolveName(doctbl.getNodeKindAt(nodeid)) + "' for node#"
+ nodeid);
}
if(profile != null) {
profile.setStrategy(origStrategy);
}
}
public void loadDocument(InputStream is) throws XQueryException {
if(!(is instanceof FastBufferedInputStream)) {
is = new FastBufferedInputStream(is);
}
_handler.init();
final StopWatch sw = new StopWatch();
try {
_reader.parse(new InputSource(is));
} catch (IOException ie) {
throw new DynamicError("Invalid source", ie);
} catch (SAXException se) {
throw new DynamicError("Parse failed", se);
}
if(LOG.isDebugEnabled()) {
LOG.debug("Elasped time of building a DTM: " + sw.elapsed() + " msec");
}
this._docid = nextDocumentId();
if(_store instanceof IProfiledDocumentTable) {
((IProfiledDocumentTable) _store).setProfiling(true);
}
}
public void loadDocument(final InputStream is, final DynamicContext dynEnv)
throws XQueryException {
this.loadDocument(is);
}
private static final XMLReader getXMLReader(final DocumentTableBuilder handler, final boolean parseAsHtml, final boolean resolveEntity) {
final XMLReader myReader;
try {
if(parseAsHtml) {
Class clazz = ClassResolver.get(HTML_PARSER_CLASS);
assert (clazz != null);
myReader = ObjectUtils.<XMLReader> instantiate(clazz);
} else {
final SAXParserFactory factory;
if(hasSunXerces) {
factory = ObjectUtils.instantiate("com.sun.org.apache.xerces.internal.jaxp.SAXParserFactoryImpl", null);
} else {
factory = SAXParserFactory.newInstance();
}
factory.setNamespaceAware(true);
SAXParser parser = factory.newSAXParser();
myReader = parser.getXMLReader();
}
} catch (Exception e) {
throw new XQRTException("Creating SAX XMLReader failed", e);
}
// setup handlers (requires saxHandler)
myReader.setContentHandler(handler);
try {
myReader.setProperty("http://xml.org/sax/properties/lexical-handler", handler);
myReader.setFeature("http://xml.org/sax/features/validation", true); // Validate the document and report validity errors.
if(!parseAsHtml) {
myReader.setFeature("http://apache.org/xml/features/validation/dynamic", true); // The parser will validate the document only if a grammar is specified.
myReader.setFeature("http://apache.org/xml/features/validation/schema", true); // Turn on XML Schema validation by inserting an XML Schema validator into the pipeline.
}
} catch (Exception e) {
throw new XQRTException("Configuaring SAX XMLReader failed.", e);
}
// setup entity resolver
if(resolveEntity) {
org.apache.xml.resolver.CatalogManager catalog = org.apache.xml.resolver.CatalogManager.getStaticManager();
catalog.setIgnoreMissingProperties(true);
catalog.setPreferPublic(true);
catalog.setUseStaticCatalog(false);
EntityResolver resolver = new org.apache.xml.resolver.tools.CatalogResolver(catalog);
myReader.setEntityResolver(resolver);
}
return myReader;
}
public IDocumentTable getDocumentTable() {
return _store;
}
public static abstract class DTMNodeBase extends XQNode implements Externalizable {
private static final long serialVersionUID = 1L;
protected DocumentTableModel _model;
protected IDocumentTable _store;
private transient Sequence<Item> _toReplace;
public DTMNodeBase(DocumentTableModel model, long id) {
super(id);
this._model = model;
this._store = model._store;
}
public DTMNodeBase() {//for serialization
super(-1L);
this._model = null;
this._store = null;
}
public void writeExternal(ObjectOutput out) throws IOException {
final XQEventEncoder encoder = new XQEventEncoder(out);
try {
encoder.emit(this);
} catch (XQueryException xqe) {
throw new XQRTException(xqe);
} catch (Throwable e) {
LOG.fatal(e);
throw new IllegalStateException("failed encoding", e);
}
}
public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException {
assert (_toReplace == null);
final XQEventDecoder decoder = new XQEventDecoder(in);
final Sequence<Item> decoded;
try {
decoded = decoder.decode();
} catch (IOException ioe) {
throw new XQRTException("decode failed", ioe);
} catch (XQueryException xqe) {
throw new XQRTException(xqe);
} catch (Throwable e) {
throw new IllegalStateException("decode failed", e);
}
this._toReplace = decoded;
}
public void writeTo(final ObjectOutput out, final SerializationContext context)
throws IOException {
out.writeObject(_model);
out.writeByte(nodeKind());
long curr = getPosition();
out.writeLong(curr);
long nextsib = _store.nextSibling(_id);
long last = (nextsib == 0L) ? curr : nextsib;
out.writeLong(last);
long[] textBlocks = ((MemoryMappedDocumentTable) _store).referredTextBlocks(curr, last, context);
out.writeInt(textBlocks.length);
for(long tb : textBlocks) {
out.writeLong(tb);
}
}
public static DTMNodeBase readFrom(final ObjectInput in, SerializationContext serContext)
throws IOException, ClassNotFoundException {
DocumentTableModel model = (DocumentTableModel) in.readObject();
byte nodeKind = in.readByte();
long curr = in.readLong();
DTMNodeBase node = getPrototype(model, nodeKind, curr);
long last = in.readLong();
final int tbSize = in.readInt();
final long[] textBlocks = new long[tbSize];
for(int i = 0; i < tbSize; i++) {
textBlocks[i] = in.readLong();
}
MemoryMappedDocumentTable mmtable = (MemoryMappedDocumentTable) node._store;
mmtable.markReferredBlocks(curr, last, textBlocks, serContext);
return node;
}
public static void redirect(ObjectInput in, ObjectOutput out) throws IOException,
ClassNotFoundException {
DocumentTableModel model = (DocumentTableModel) in.readObject();
out.writeObject(model);
byte nodeKind = in.readByte();
out.writeByte(nodeKind);
long curr = in.readLong();
out.writeLong(curr);
long last = in.readLong();
out.writeLong(last);
final int tbsize = in.readInt();
out.writeInt(tbsize);
final byte[] b = new byte[80];
int i = 0;
final int limit = tbsize - 9;
for(; i < limit; i += 10) {
in.readFully(b, 0, 80);
out.write(b, 0, 80);
}
for(; i < tbsize; i++) {
in.readFully(b, 0, 8);
out.write(b, 0, 8);
}
}
protected Object readResolve() throws ObjectStreamException {
Sequence replaced = _toReplace;
this._toReplace = null;
return replaced;
}
public void setInternalTable(DocumentTableModel model) {
this._model = model;
this._store = model._store;
}
public IDocumentTable documentTable() {
return _store;
}
public DocumentTableModel getDataModel() {
return _model;
}
public String baseUri() {
return null;
}
public DTMNodeBase firstChild() {
return null;
}
public String getContent() {
return null;
}
public int getDocumentId() {
final DocumentTableModel model = getDataModel();
return model._docid;
}
public DTMDocument getDocumentNode() {
final DocumentTableModel model = getDataModel();
return model.createNode(NodeKind.DOCUMENT, 0);
}
public String getNamespaceURI() {
return null;
}
public DTMNodeBase lastChild() {
return null;
}
@Override
public final XQNode following(final boolean firstcall) {
final PagingProfile profile = _store.getPagingProfile();
final XQNode node;
if(profile != null) {
Strategy origStrategy = profile.getStrategy();
if(origStrategy != Strategy.nextsib) {
assert (origStrategy != null);
profile.setStrategy(Strategy.nextsib);
node = super.following(firstcall);
profile.setStrategy(origStrategy);
} else {
node = super.following(firstcall);
}
} else {
node = super.following(firstcall);
}
return node;
}
@Override
public byte nodeKind() {
return 0;
}
public DTMNodeBase nextSibling() {
final IDocumentTable store = documentTable();
final long nextsib = store.nextSibling(_id);
if(nextsib == 0L) {
return null;
}
final DocumentTableModel model = getDataModel();
return model.createNode(store.getNodeKindAt(nextsib), nextsib);
}
public final DTMNodeBase nextSibling(NodeTest filter) {
if(_id == 0L) {
return null;
}
final IDocumentTable store = documentTable();
final DocumentTableModel model = getDataModel();
for(long nid = store.nextSibling(_id); nid != 0L; nid = store.nextSibling(nid)) {
byte nodekind = store.getNodeKindAt(nid);
DTMNodeBase probe = getPrototype(model, nodekind, nid);
if(filter.accepts(probe)) {
return model.createNode(probe.nodeKind(), probe.getPosition());
}
}
return null;
}
public QualifiedName nodeName() {
return null;
}
public int getNameCode() {
return -1;
}
public DTMElement parent() {
final DocumentTableModel model = getDataModel();
if(_id == BLOCKS_PER_NODE) {
return model.createNode(NodeKind.DOCUMENT, 0);
}
final IDocumentTable store = documentTable();
final long pid = store.parent(_id);
if(pid == 0L) {
return null;
}
return model.createNode(store.getNodeKindAt(pid), pid);
}
public DTMNodeBase previousSibling() {
final IDocumentTable store = documentTable();
final long prevSib = store.previousSibling(_id);
if(prevSib == -1L) {
return null;
}
final DocumentTableModel model = getDataModel();
return model.createNode(store.getNodeKindAt(prevSib), prevSib);
}
public String stringValue() {
final IDocumentTable store = documentTable();
return store.stringValue(_id);
}
@Override
public DTMNodeBase clone() {
if(_cloned++ == 0) {
return this;
}
return (DTMNodeBase) super.cloneWithoutIncr();
}
}
public static class DTMElement extends DTMNodeBase {
private static final long serialVersionUID = 7884779293497299039L;
public DTMElement() {//for serialization
super();
}
protected DTMElement(DocumentTableModel model, long id) {
super(model, id);
}
public DTMAttribute attribute(int i) {
final long attid = getAttribute(i);
if(attid == -1) {
return null;
}
final DocumentTableModel model = getDataModel();
return new DTMAttribute(model, attid);
}
@Override
public String baseUri() {
DTMAttribute base = getAttribute(XMLConstants.XML_NS_URI, "base");
if(base != null) {
String uri = base.getContent();
return uri;
} else {
XQNode p = parent();
while(p != null) {
String uri = p.baseUri();
if(uri != null) {
return uri;
}
p = p.parent();
}
return null;
}
}
@Override
public DTMNodeBase firstChild() {
final IDocumentTable store = documentTable();
final long cid = store.firstChild(_id);
if(cid == -1) {
return null;
}
final DocumentTableModel model = getDataModel();
return model.createNode(store.getNodeKindAt(cid), cid);
}
public long getAttribute(int i) {
assert (i >= 0);
final IDocumentTable store = documentTable();
return store.getAttribute(_id, i);
}
public DTMAttribute getAttribute(String nsuri, String attname) {
assert (attname != null);
final IDocumentTable store = documentTable();
int attcnt = store.getAttributeCountAt(_id);
for(int i = 0; i < attcnt; i++) {
long attid = store.getAttribute(_id, i);
assert (attid != -1);
assert (store.getNodeKindAt(attid) != NodeKind.ATTRIBUTE);
QualifiedName qname = store.getAttributeName(attid);
String attUri = qname.getNamespaceURI();
String attName = qname.getLocalPart();
if(nsuri == null || nsuri.equals(attUri)) {
if(attname.equals(attName)) {
final DocumentTableModel model = getDataModel();
return new DTMAttribute(model, attid);
}
}
}
return null;
}
public int getAttributesCount() {
final IDocumentTable store = documentTable();
return store.getAttributeCountAt(_id);
}
public long getNamespaceDecl(int i) {
assert (i >= 0);
final IDocumentTable store = documentTable();
return store.getNamespaceDecl(_id, i);
}
public DTMNamespace getNamespace(int i) {
final long nsid = getNamespaceDecl(i);
if(nsid == -1) {
return null;
}
final DocumentTableModel model = getDataModel();
return new DTMNamespace(model, nsid);
}
public int getNamespaceDeclCount() {
final IDocumentTable store = documentTable();
return store.getNamespaceCountAt(_id);
}
@Override
public String getNamespaceURI() {
return nodeName().getNamespaceURI();
}
@Override
public DTMNodeBase lastChild() {
final IDocumentTable store = documentTable();
final long cid = store.lastChild(_id);
if(cid == -1) {
return null;
}
final DocumentTableModel model = getDataModel();
return model.createNode(store.getNodeKindAt(cid), cid);
}
public boolean nilled() {
final IDocumentTable store = documentTable();
long attid;
for(int i = 0; (attid = getAttribute(i)) != -1; i++) {
assert (attid != -1);
byte attkind = store.getNodeKindAt(attid);
if(attkind == NodeKind.ATTRIBUTE) {
QualifiedName qname = store.getAttributeName(attid);
String nsuri = qname.getNamespaceURI();
String attname = qname.getLocalPart();
if(XQueryConstants.XSI_URI.equals(nsuri) && "nil".equals(attname)) {
String attval = store.getText(attid);
if(attval == null) {
return false;
}
return BooleanValue.toBoolean(attval);
}
}
}
return false;
}
@Override
public byte nodeKind() {
return NodeKind.ELEMENT;
}
@Override
public int getNameCode() {
final IDocumentTable store = documentTable();
return store.getNameCode(_id);
}
@Override
public QualifiedName nodeName() {
final IDocumentTable store = documentTable();
return store.getName(_id);
}
}
public static class DTMDocument extends DTMElement implements Closeable {
private static final long serialVersionUID = -457402431467614608L;
private String documentUri = null;
public DTMDocument() {//for serialization
super();
}
protected DTMDocument(DocumentTableModel model, long id) {
super(model, id);
}
public DTMAttribute attribute(DTMElement elem, int i) {
throw new UnsupportedOperationException();
}
@Override
public DTMAttribute attribute(int i) {
throw new UnsupportedOperationException();
}
public String documentUri() {
return documentUri;
}
@Override
public long getAttribute(int i) {
throw new UnsupportedOperationException();
}
@Override
public DTMAttribute getAttribute(String nsuri, String attname) {
throw new UnsupportedOperationException();
}
@Override
public int getDocumentId() {
return getDataModel()._docid;
}
@Override
public DTMDocument getDocumentNode() {
return this;
}
@Override
public String getNamespaceURI() {
return null;
}
@Override
public DTMElement getRoot() {
return this;
}
@Override
public DTMNodeBase nextSibling() {
return null;
}
@Override
public boolean nilled() {
return false;
}
@Override
public byte nodeKind() {
return NodeKind.DOCUMENT;
}
@Override
public QualifiedName nodeName() {
return null;
}
@Override
public DTMElement parent() {
return null;
}
@Override
public DTMNodeBase previousSibling() {
return null;
}
public void setDocumentUri(String docUri) {
this.documentUri = docUri;
}
public void close() throws IOException {
_store.close();
}
}
/**
* Note: may be used within namespace node.
*/
public static class DTMAttribute extends DTMNodeBase {
private static final long serialVersionUID = 7263434327980382798L;
public DTMAttribute() {//for serialization
super();
}
protected DTMAttribute(DocumentTableModel model, long id) {
super(model, id);
}
@Override
public String getContent() {
return _store.getText(_id);
}
@Override
public String getNamespaceURI() {
return nodeName().getNamespaceURI();
}
@Override
public DTMNodeBase nextSibling() {
return null;
}
@Override
public byte nodeKind() {
return NodeKind.ATTRIBUTE;
}
@Override
public QualifiedName nodeName() {
return _store.getAttributeName(_id);
}
@Override
public int getNameCode() {
return _store.getAttributeNameCode(_id);
}
@Override
public DTMElement parent() {
final long eid = _store.parent(_id);
return _model.createNode(NodeKind.ELEMENT, eid);
}
@Override
public DTMNodeBase previousSibling() {
return null;
}
}
public static final class DTMNamespace extends DTMAttribute {
private static final long serialVersionUID = 5955223395627163938L;
public DTMNamespace() {//for serialization
super();
}
protected DTMNamespace(DocumentTableModel model, long id) {
super(model, id);
}
@Override
public byte nodeKind() {
return NodeKind.NAMESPACE;
}
}
public static final class DTMProcessingInstruction extends DTMNodeBase {
private static final long serialVersionUID = -4129132047857524061L;
public DTMProcessingInstruction() {//for serialization
super();
}
protected DTMProcessingInstruction(DocumentTableModel model, long id) {
super(model, id);
}
@Override
public String getContent() {
return _store.getName(_id).getNamespaceURI();
}
public String getTarget() {
return _store.getName(_id).getLocalPart();
}
@Override
public byte nodeKind() {
return NodeKind.PROCESSING_INSTRUCTION;
}
@Override
public QualifiedName nodeName() {
final String target = getTarget();
return QNameTable.instantiate(XMLUtils.NULL_NS_URI, target);
}
}
public static final class DTMText extends DTMNodeBase {
private static final long serialVersionUID = -1832518864998703501L;
public DTMText() {//for serialization
super();
}
protected DTMText(DocumentTableModel model, long id) {
super(model, id);
}
@Override
public String getContent() {
return _store.getText(_id);
}
@Override
public byte nodeKind() {
return NodeKind.TEXT;
}
}
public static final class DTMComment extends DTMNodeBase {
private static final long serialVersionUID = -2555012444869523835L;
public DTMComment() {//for serialization
super();
}
protected DTMComment(DocumentTableModel model, long id) {
super(model, id);
}
@Override
public String getContent() {
return _store.getText(_id);
}
@Override
public byte nodeKind() {
return NodeKind.COMMENT;
}
}
}