org.apache.xmlbeans.impl.store.Cursor Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of aem-sdk-api Show documentation
Show all versions of aem-sdk-api Show documentation
The Adobe Experience Manager SDK
/* Copyright 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.
*/
package org.apache.xmlbeans.impl.store;
import org.apache.xmlbeans.XmlCursor;
import org.apache.xmlbeans.XmlDocumentProperties;
import org.apache.xmlbeans.XmlObject;
import org.apache.xmlbeans.XmlOptions;
import org.apache.xmlbeans.impl.common.GlobalLock;
import org.apache.xmlbeans.impl.common.XMLChar;
import org.apache.xmlbeans.impl.store.Locale.ChangeListener;
import org.apache.xmlbeans.impl.store.Saver.TextSaver;
import org.apache.xmlbeans.impl.xpath.XPathEngine;
import org.apache.xmlbeans.impl.xpath.XPathFactory;
import org.w3c.dom.Node;
import org.xml.sax.ContentHandler;
import org.xml.sax.SAXException;
import org.xml.sax.ext.LexicalHandler;
import javax.xml.namespace.QName;
import javax.xml.stream.XMLStreamReader;
import java.io.*;
import java.util.Collection;
import java.util.Map;
import java.util.function.Supplier;
public final class Cursor implements XmlCursor, ChangeListener {
static final int ROOT = Cur.ROOT;
static final int ELEM = Cur.ELEM;
static final int ATTR = Cur.ATTR;
static final int COMMENT = Cur.COMMENT;
static final int PROCINST = Cur.PROCINST;
static final int TEXT = Cur.TEXT;
private Cur _cur;
private XPathEngine _pathEngine;
private int _currentSelection;
private ChangeListener _nextChangeListener;
Cursor(Xobj x, int p) {
_cur = x._locale.weakCur(this);
_cur.moveTo(x, p);
_currentSelection = -1;
}
public Cursor(Cur c) {
this(c._xobj, c._pos);
}
private static boolean isValid(Cur c) {
if (c.kind() <= 0) {
c.push();
if (c.toParentRaw()) {
int pk = c.kind();
if (pk == COMMENT || pk == PROCINST || pk == ATTR) {
return false;
}
}
c.pop();
}
return true;
}
private boolean isValid() {
return isValid(_cur);
}
Locale locale() {
return _cur._locale;
}
Cur tempCur() {
return _cur.tempCur();
}
public void dump(PrintStream o) {
_cur.dump(o);
}
static void validateLocalName(QName name) {
if (name == null) {
throw new IllegalArgumentException("QName is null");
}
validateLocalName(name.getLocalPart());
}
static void validateLocalName(String name) {
if (name == null) {
throw new IllegalArgumentException("Name is null");
}
if (name.length() == 0) {
throw new IllegalArgumentException("Name is empty");
}
if (!XMLChar.isValidNCName(name)) {
throw new IllegalArgumentException("Name is not valid");
}
}
static void validatePrefix(String name) {
if (name == null) {
throw new IllegalArgumentException("Prefix is null");
}
if (name.length() == 0) {
throw new IllegalArgumentException("Prefix is empty");
}
if (Locale.beginsWithXml(name)) {
throw new IllegalArgumentException("Prefix begins with 'xml'");
}
if (!XMLChar.isValidNCName(name)) {
throw new IllegalArgumentException("Prefix is not valid");
}
}
private static void complain(String msg) {
throw new IllegalArgumentException(msg);
}
private void checkInsertionValidity(Cur that) {
int thatKind = that.kind();
if (thatKind < 0) {
complain("Can't move/copy/insert an end token.");
}
if (thatKind == ROOT) {
complain("Can't move/copy/insert a whole document.");
}
int thisKind = _cur.kind();
if (thisKind == ROOT) {
complain("Can't insert before the start of the document.");
}
if (thatKind == ATTR) {
_cur.push();
_cur.prevWithAttrs();
int pk = _cur.kind();
_cur.pop();
if (pk != ELEM && pk != ROOT && pk != -ATTR) {
complain("Can only insert attributes before other attributes or after containers.");
}
}
if (thisKind == ATTR && thatKind != ATTR) {
complain("Can only insert attributes before other attributes or after containers.");
}
}
private void insertNode(Cur that, String text) {
assert !that.isRoot();
assert that.isNode();
assert isValid(that);
assert isValid();
if (text != null && text.length() > 0) {
that.next();
that.insertString(text);
that.toParent();
}
checkInsertionValidity(that);
that.moveNode(_cur);
_cur.toEnd();
_cur.nextWithAttrs();
}
//
//
//
// TODO - deal with cursors moving to other documents upon release?
// Can I move the ref from one q to another? If not I will have to
// change from a phantom ref to a soft/weak ref so I can know what
// to do when I dequeue from the old q.
public void _dispose() {
_cur.release();
_cur = null;
}
public XmlCursor _newCursor() {
return new Cursor(_cur);
}
public QName _getName() {
// TODO - consider taking this out of the gateway
switch (_cur.kind()) {
case ATTR:
if (_cur.isXmlns()) {
return
_cur._locale.makeQNameNoCheck(_cur.getXmlnsUri(), _cur.getXmlnsPrefix());
}
// Fall thru
case ELEM:
case PROCINST:
return _cur.getName();
}
return null;
}
public void _setName(QName name) {
if (name == null) {
throw new IllegalArgumentException("Name is null");
}
switch (_cur.kind()) {
case ELEM:
case ATTR: {
validateLocalName(name.getLocalPart());
break;
}
case PROCINST: {
validatePrefix(name.getLocalPart());
if (name.getNamespaceURI().length() > 0) {
throw new IllegalArgumentException("Procinst name must have no URI");
}
if (name.getPrefix().length() > 0) {
throw new IllegalArgumentException("Procinst name must have no prefix");
}
break;
}
default:
throw
new IllegalStateException("Can set name on element, atrtribute and procinst only");
}
_cur.setName(name);
}
public TokenType _currentTokenType() {
assert isValid();
switch (_cur.kind()) {
case ROOT:
return TokenType.STARTDOC;
case -ROOT:
return TokenType.ENDDOC;
case ELEM:
return TokenType.START;
case -ELEM:
return TokenType.END;
case TEXT:
return TokenType.TEXT;
case ATTR:
return _cur.isXmlns() ? TokenType.NAMESPACE : TokenType.ATTR;
case COMMENT:
return TokenType.COMMENT;
case PROCINST:
return TokenType.PROCINST;
default:
throw new IllegalStateException();
}
}
public boolean _isStartdoc() {
//return _currentTokenType().isStartdoc();
assert isValid();
return _cur.isRoot();
}
public boolean _isEnddoc() {
//return _currentTokenType().isEnddoc();
assert isValid();
return _cur.isEndRoot();
}
public boolean _isStart() {
//return _currentTokenType().isStart();
assert isValid();
return _cur.isElem();
}
public boolean _isEnd() {
//return _currentTokenType().isEnd();
assert isValid();
return _cur.isEnd();
}
public boolean _isText() {
//return _currentTokenType().isText();
assert isValid();
return _cur.isText();
}
public boolean _isAttr() {
//return _currentTokenType().isAttr();
assert isValid();
return _cur.isNormalAttr();
}
public boolean _isNamespace() {
//return _currentTokenType().isNamespace();
assert isValid();
return _cur.isXmlns();
}
public boolean _isComment() {
//return _currentTokenType().isComment();
assert isValid();
return _cur.isComment();
}
public boolean _isProcinst() {
//return _currentTokenType().isProcinst();
assert isValid();
return _cur.isProcinst();
}
public boolean _isContainer() {
//return _currentTokenType().isContainer();
assert isValid();
return _cur.isContainer();
}
public boolean _isFinish() {
//return _currentTokenType().isFinish();
assert isValid();
return _cur.isFinish();
}
public boolean _isAnyAttr() {
//return _currentTokenType().isAnyAttr();
assert isValid();
return _cur.isAttr();
}
public TokenType _toNextToken() {
assert isValid();
switch (_cur.kind()) {
case ROOT:
case ELEM: {
if (!_cur.toFirstAttr()) {
_cur.next();
}
break;
}
case ATTR: {
if (!_cur.toNextSibling()) {
_cur.toParent();
_cur.next();
}
break;
}
case COMMENT:
case PROCINST: {
_cur.skip();
break;
}
default: {
if (!_cur.next()) {
return TokenType.NONE;
}
break;
}
}
return _currentTokenType();
}
public TokenType _toPrevToken() {
assert isValid();
// This method is different than the Cur version of prev in a few ways. First,
// Cursor iterates over attrs inline with all the other content. Cur will skip attrs, or
// if the Cur in in attrs, it will not jump out of attrs. Also, if moving backwards and
// text is to the left and right, Cur will move to the beginning of that text, while
// Cursor will move further so that the token type to the right is not text.
boolean wasText = _cur.isText();
if (!_cur.prev()) {
assert _cur.isRoot() || _cur.isAttr();
if (_cur.isRoot()) {
return TokenType.NONE;
}
_cur.toParent();
} else {
int k = _cur.kind();
if (k == -COMMENT || k == -PROCINST || k == -ATTR) {
_cur.toParent();
} else if (_cur.isContainer()) {
_cur.toLastAttr();
} else if (wasText && _cur.isText()) {
return _toPrevToken();
}
}
return _currentTokenType();
}
public Object _monitor() {
// TODO - some of these methods need not be protected by a
// gatway. This is one of them. Inline this.
return _cur._locale;
}
public boolean _toParent() {
Cur c = _cur.tempCur();
if (!c.toParent()) {
return false;
}
_cur.moveToCur(c);
c.release();
return true;
}
private static final class ChangeStampImpl implements ChangeStamp {
ChangeStampImpl(Locale l) {
_locale = l;
_versionStamp = _locale.version();
}
public boolean hasChanged() {
return _versionStamp != _locale.version();
}
private final Locale _locale;
private final long _versionStamp;
}
public ChangeStamp _getDocChangeStamp() {
return new ChangeStampImpl(_cur._locale);
}
//
// These simply delegate to the version of the method which takes XmlOptions
//
public XMLStreamReader _newXMLStreamReader() {
return _newXMLStreamReader(null);
}
public Node _newDomNode() {
return _newDomNode(null);
}
public InputStream _newInputStream() {
return _newInputStream(null);
}
public String _xmlText() {
return _xmlText(null);
}
public Reader _newReader() {
return _newReader(null);
}
public void _save(File file) throws IOException {
_save(file, null);
}
public void _save(OutputStream os) throws IOException {
_save(os, null);
}
public void _save(Writer w) throws IOException {
_save(w, null);
}
public void _save(ContentHandler ch, LexicalHandler lh) throws SAXException {
_save(ch, lh, null);
}
//
//
//
public XmlDocumentProperties _documentProperties() {
return Locale.getDocProps(_cur, true);
}
public XMLStreamReader _newXMLStreamReader(XmlOptions options) {
return Jsr173.newXmlStreamReader(_cur, options);
}
public String _xmlText(XmlOptions options) {
assert isValid();
return new TextSaver(_cur, options, null).saveToString();
}
public InputStream _newInputStream(XmlOptions options) {
return new Saver.InputStreamSaver(_cur, options);
}
public Reader _newReader(XmlOptions options) {
return new Saver.TextReader(_cur, options);
}
public void _save(ContentHandler ch, LexicalHandler lh, XmlOptions options)
throws SAXException {
new Saver.SaxSaver(_cur, options, ch, lh);
}
public void _save(File file, XmlOptions options) throws IOException {
if (file == null) {
throw new IllegalArgumentException("Null file specified");
}
try (OutputStream os = new FileOutputStream(file)) {
_save(os, options);
}
}
public void _save(OutputStream os, XmlOptions options) throws IOException {
if (os == null) {
throw new IllegalArgumentException("Null OutputStream specified");
}
try (InputStream is = _newInputStream(options)) {
byte[] bytes = new byte[8192];
for (; ; ) {
int n = is.read(bytes);
if (n < 0) {
break;
}
os.write(bytes, 0, n);
}
}
}
public void _save(Writer w, XmlOptions options) throws IOException {
if (w == null) {
throw new IllegalArgumentException("Null Writer specified");
}
if (options != null && options.isSaveOptimizeForSpeed()) {
Saver.OptimizedForSpeedSaver.save(_cur, w); //ignore all other options
return;
}
try (Reader r = _newReader(options)) {
char[] chars = new char[8192];
for (; ; ) {
int n = r.read(chars);
if (n < 0) {
break;
}
w.write(chars, 0, n);
}
}
}
public Node _getDomNode() {
return (Node) _cur.getDom();
}
private boolean isDomFragment() {
if (!isStartdoc()) {
return true;
}
boolean seenElement = false;
try (XmlCursor c = newCursor()) {
int token = c.toNextToken().intValue();
LOOP:
for (; ; ) {
switch (token) {
case TokenType.INT_START:
if (seenElement) {
return true;
}
seenElement = true;
token = c.toEndToken().intValue();
break;
case TokenType.INT_TEXT:
if (!Locale.isWhiteSpace(c.getChars())) {
return true;
}
token = c.toNextToken().intValue();
break;
case TokenType.INT_NONE:
case TokenType.INT_ENDDOC:
break LOOP;
case TokenType.INT_ATTR:
case TokenType.INT_NAMESPACE:
return true;
case TokenType.INT_END:
case TokenType.INT_COMMENT:
case TokenType.INT_PROCINST:
token = c.toNextToken().intValue();
break;
case TokenType.INT_STARTDOC:
assert false;
break LOOP;
}
}
}
return !seenElement;
}
public Node _newDomNode(XmlOptions options) {
// Must ignore inner options for compat with v1.
if (options != null && options.isSaveInner()) {
options = new XmlOptions(options);
options.setSaveInner(false);
}
return new DomSaver(_cur, isDomFragment(), options).saveDom();
}
public boolean _toCursor(Cursor other) {
assert _cur._locale == other._cur._locale;
_cur.moveToCur(other._cur);
return true;
}
public void _push() {
_cur.push();
}
public boolean _pop() {
return _cur.pop();
}
public void notifyChange() {
// Force any path to get exausted, cursor may be disposed, but still be on the notification
// list.
if (_cur != null) {
_getSelectionCount();
}
}
public void setNextChangeListener(ChangeListener listener) {
_nextChangeListener = listener;
}
public ChangeListener getNextChangeListener() {
return _nextChangeListener;
}
public void _selectPath(String path) {
_selectPath(path, null);
}
public void _selectPath(String pathExpr, XmlOptions options) {
_clearSelections();
assert _pathEngine == null;
_pathEngine = XPathFactory.getCompiledPath(pathExpr, options).execute(_cur, options);
_cur._locale.registerForChange(this);
}
public boolean _hasNextSelection() {
int curr = _currentSelection;
push();
try {
return _toNextSelection();
} finally {
_currentSelection = curr;
pop();
}
}
public boolean _toNextSelection() {
return _toSelection(_currentSelection + 1);
}
public boolean _toSelection(int i) {
if (i < 0) {
return false;
}
while (i >= _cur.selectionCount()) {
if (_pathEngine == null) {
return false;
}
if (!_pathEngine.next(_cur)) {
_pathEngine.release();
_pathEngine = null;
return false;
}
}
_cur.moveToSelection(_currentSelection = i);
return true;
}
public int _getSelectionCount() {
// Should never get to MAX_VALUE selection index, so, state should not change
_toSelection(Integer.MAX_VALUE);
return _cur.selectionCount();
}
public void _addToSelection() {
_toSelection(Integer.MAX_VALUE);
_cur.addToSelection();
}
public void _clearSelections() {
if (_pathEngine != null) {
_pathEngine.release();
_pathEngine = null;
}
_cur.clearSelection();
_currentSelection = -1;
}
public String _namespaceForPrefix(String prefix) {
if (!_cur.isContainer()) {
throw new IllegalStateException("Not on a container");
}
return _cur.namespaceForPrefix(prefix, true);
}
public String _prefixForNamespace(String ns) {
if (ns == null || ns.length() == 0) {
throw new IllegalArgumentException("Must specify a namespace");
}
// Note: I loosen this requirement in v2, can call this from anywhere
// if (!_cur.isContainer())
// throw new IllegalStateException( "Not on a container" );
return _cur.prefixForNamespace(ns, null, true);
}
public void _getAllNamespaces(Map addToThis) {
if (!_cur.isContainer()) {
throw new IllegalStateException("Not on a container");
}
if (addToThis != null) {
Locale.getAllNamespaces(_cur, addToThis);
}
}
public XmlObject _getObject() {
return _cur.getObject();
}
public TokenType _prevTokenType() {
_cur.push();
TokenType tt = _toPrevToken();
_cur.pop();
return tt;
}
public boolean _hasNextToken() {
//return _cur.kind() != -ROOT;
return _cur._pos != Cur.END_POS || _cur._xobj.kind() != ROOT;
}
public boolean _hasPrevToken() {
return _cur.kind() != ROOT;
}
public TokenType _toFirstContentToken() {
if (!_cur.isContainer()) {
return TokenType.NONE;
}
_cur.next();
return currentTokenType();
}
public TokenType _toEndToken() {
if (!_cur.isContainer()) {
return TokenType.NONE;
}
_cur.toEnd();
return currentTokenType();
}
public boolean _toChild(String local) {
return _toChild(null, local);
}
public boolean _toChild(QName name) {
return _toChild(name, 0);
}
public boolean _toChild(int index) {
return _toChild(null, index);
}
public boolean _toChild(String uri, String local) {
validateLocalName(local);
return _toChild(_cur._locale.makeQName(uri, local), 0);
}
public boolean _toChild(QName name, int index) {
return Locale.toChild(_cur, name, index);
}
public int _toNextChar(int maxCharacterCount) {
return _cur.nextChars(maxCharacterCount);
}
public int _toPrevChar(int maxCharacterCount) {
return _cur.prevChars(maxCharacterCount);
}
public boolean _toPrevSibling() {
return Locale.toPrevSiblingElement(_cur);
}
public boolean _toLastChild() {
return Locale.toLastChildElement(_cur);
}
public boolean _toFirstChild() {
return Locale.toFirstChildElement(_cur);
}
public boolean _toNextSibling(String name) {
return _toNextSibling(new QName(name));
}
public boolean _toNextSibling(String uri, String local) {
validateLocalName(local);
return _toNextSibling(_cur._locale._qnameFactory.getQName(uri, local));
}
public boolean _toNextSibling(QName name) {
_cur.push();
while (___toNextSibling()) {
if (_cur.getName().equals(name)) {
_cur.popButStay();
return true;
}
}
_cur.pop();
return false;
}
public boolean _toFirstAttribute() {
return _cur.isContainer() && Locale.toFirstNormalAttr(_cur);
}
public boolean _toLastAttribute() {
if (_cur.isContainer()) {
_cur.push();
_cur.push();
boolean foundAttr = false;
while (_cur.toNextAttr()) {
if (_cur.isNormalAttr()) {
_cur.popButStay();
_cur.push();
foundAttr = true;
}
}
_cur.pop();
if (foundAttr) {
_cur.popButStay();
return true;
}
_cur.pop();
}
return false;
}
public boolean _toNextAttribute() {
return _cur.isAttr() && Locale.toNextNormalAttr(_cur);
}
public boolean _toPrevAttribute() {
return _cur.isAttr() && Locale.toPrevNormalAttr(_cur);
}
public String _getAttributeText(QName attrName) {
if (attrName == null) {
throw new IllegalArgumentException("Attr name is null");
}
if (!_cur.isContainer()) {
return null;
}
return _cur.getAttrValue(attrName);
}
public boolean _setAttributeText(QName attrName, String value) {
if (attrName == null) {
throw new IllegalArgumentException("Attr name is null");
}
validateLocalName(attrName.getLocalPart());
if (!_cur.isContainer()) {
return false;
}
_cur.setAttrValue(attrName, value);
return true;
}
public boolean _removeAttribute(QName attrName) {
if (attrName == null) {
throw new IllegalArgumentException("Attr name is null");
}
if (!_cur.isContainer()) {
return false;
}
return _cur.removeAttr(attrName);
}
public String _getTextValue() {
if (_cur.isText()) {
return _getChars();
}
if (!_cur.isNode()) {
throw new IllegalStateException("Can't get text value, current token can have no text value");
}
return _cur.hasChildren() ? Locale.getTextValue(_cur) : _cur.getValueAsString();
}
public int _getTextValue(char[] chars, int offset, int max) {
if (_cur.isText()) {
return _getChars(chars, offset, max);
}
if (chars == null) {
throw new IllegalArgumentException("char buffer is null");
}
if (offset < 0) {
throw new IllegalArgumentException("offset < 0");
}
if (offset >= chars.length) {
throw new IllegalArgumentException("offset off end");
}
if (max < 0) {
max = Integer.MAX_VALUE;
}
if (offset + max > chars.length) {
max = chars.length - offset;
}
if (!_cur.isNode()) {
throw new IllegalStateException("Can't get text value, current token can have no text value");
}
// If there are no children (hopefully the common case), I can get the text faster.
if (_cur.hasChildren()) {
return Locale.getTextValue(_cur, chars, offset, max);
}
// Fast way
Object src = _cur.getFirstChars();
if (_cur._cchSrc > max) {
_cur._cchSrc = max;
}
if (_cur._cchSrc <= 0) {
return 0;
}
CharUtil.getChars(chars, offset, src, _cur._offSrc, _cur._cchSrc);
return _cur._cchSrc;
}
private void setTextValue(Object src, int off, int cch) {
if (!_cur.isNode()) {
throw new IllegalStateException("Can't set text value, current token can have no text value");
}
_cur.moveNodeContents(null, false);
_cur.next();
_cur.insertChars(src, off, cch);
_cur.toParent();
}
public void _setTextValue(String text) {
if (text == null) {
text = "";
}
setTextValue(text, 0, text.length());
}
public void _setTextValue(char[] sourceChars, int offset, int length) {
if (length < 0) {
throw new IndexOutOfBoundsException("setTextValue: length < 0");
}
if (sourceChars == null) {
if (length > 0) {
throw new IllegalArgumentException("setTextValue: sourceChars == null");
}
setTextValue(null, 0, 0);
return;
}
if (offset < 0 || offset >= sourceChars.length) {
throw new IndexOutOfBoundsException("setTextValue: offset out of bounds");
}
if (offset + length > sourceChars.length) {
length = sourceChars.length - offset;
}
CharUtil cu = _cur._locale.getCharUtil();
setTextValue(cu.saveChars(sourceChars, offset, length), cu._offSrc, cu._cchSrc);
}
public String _getChars() {
return _cur.getCharsAsString();
}
public int _getChars(char[] buf, int off, int cch) {
int cchRight = _cur.cchRight();
if (cch < 0 || cch > cchRight) {
cch = cchRight;
}
if (buf == null || off >= buf.length) {
return 0;
}
if (buf.length - off < cch) {
cch = buf.length - off;
}
Object src = _cur.getChars(cch);
CharUtil.getChars(buf, off, src, _cur._offSrc, _cur._cchSrc);
return _cur._cchSrc;
}
public void _toStartDoc() {
_cur.toRoot();
}
public void _toEndDoc() {
_toStartDoc();
_cur.toEnd();
}
public int _comparePosition(Cursor other) {
int s = _cur.comparePosition(other._cur);
if (s == 2) {
throw new IllegalArgumentException("Cursors not in same document");
}
assert s >= -1 && s <= 1;
return s;
}
public boolean _isLeftOf(Cursor other) {
return _comparePosition(other) < 0;
}
public boolean _isAtSamePositionAs(Cursor other) {
return _cur.isSamePos(other._cur);
}
public boolean _isRightOf(Cursor other) {
return _comparePosition(other) > 0;
}
public XmlCursor _execQuery(String query) {
return _execQuery(query, null);
}
public XmlCursor _execQuery(String query, XmlOptions options) {
checkThisCursor();
return XPathFactory.cursorExecQuery(_cur, query, options);
}
public boolean _toBookmark(XmlBookmark bookmark) {
if (bookmark == null || !(bookmark._currentMark instanceof Bookmark)) {
return false;
}
Bookmark m = (Bookmark) bookmark._currentMark;
if (m._xobj == null || m._xobj._locale != _cur._locale) {
return false;
}
_cur.moveTo(m._xobj, m._pos);
return true;
}
public XmlBookmark _toNextBookmark(Object key) {
if (key == null) {
return null;
}
int cch;
_cur.push();
for (; ; ) {
// Move a minimal amount. If at text, move to a potential bookmark in the text.
if ((cch = _cur.cchRight()) > 1) {
_cur.nextChars(1);
_cur.nextChars((cch = _cur.firstBookmarkInChars(key, cch - 1)) >= 0 ? cch : -1);
} else if (_toNextToken().isNone()) {
_cur.pop();
return null;
}
XmlBookmark bm = getBookmark(key, _cur);
if (bm != null) {
_cur.popButStay();
return bm;
}
if (_cur.kind() == -ROOT) {
_cur.pop();
return null;
}
}
}
public XmlBookmark _toPrevBookmark(Object key) {
if (key == null) {
return null;
}
int cch;
_cur.push();
for (; ; ) {
// Move a minimal amount. If at text, move to a potential bookmark in the text.
if ((cch = _cur.cchLeft()) > 1) {
_cur.prevChars(1);
_cur.prevChars((cch = _cur.firstBookmarkInCharsLeft(key, cch - 1)) >= 0 ? cch : -1);
} else if (cch == 1) {
// _toPrevToken will not skip to the beginning of the text, it will go further
// so that the token to the right is not text. I need to simply skip to
// the beginning of the text ...
_cur.prevChars(1);
} else if (_toPrevToken().isNone()) {
_cur.pop();
return null;
}
XmlBookmark bm = getBookmark(key, _cur);
if (bm != null) {
_cur.popButStay();
return bm;
}
if (_cur.kind() == ROOT) {
_cur.pop();
return null;
}
}
}
public void _setBookmark(XmlBookmark bookmark) {
if (bookmark != null) {
if (bookmark.getKey() == null) {
throw new IllegalArgumentException("Annotation key is null");
}
// TODO - I Don't do weak bookmarks yet ... perhaps I'll never do them ....
bookmark._currentMark = _cur.setBookmark(bookmark.getKey(), bookmark);
}
}
static XmlBookmark getBookmark(Object key, Cur c) {
// TODO - I Don't do weak bookmarks yet ...
if (key == null) {
return null;
}
Object bm = c.getBookmark(key);
return bm instanceof XmlBookmark ? (XmlBookmark) bm : null;
}
public XmlBookmark _getBookmark(Object key) {
return key == null ? null : getBookmark(key, _cur);
}
public void _clearBookmark(Object key) {
if (key != null) {
_cur.setBookmark(key, null);
}
}
public void _getAllBookmarkRefs(Collection
© 2015 - 2025 Weber Informatics LLC | Privacy Policy