| /* 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.Path.PathEngine; |
| import org.apache.xmlbeans.impl.store.Saver.TextSaver; |
| import org.apache.xmlbeans.xml.stream.XMLInputStream; |
| 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 PathEngine _pathEngine; |
| private int _currentSelection; |
| |
| private ChangeListener _nextChangeListener; |
| |
| Cursor(Xobj x, int p) { |
| _cur = x._locale.weakCur(this); |
| _cur.moveTo(x, p); |
| _currentSelection = -1; |
| } |
| |
| 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 |
| // |
| |
| /** |
| * @deprecated XMLInputStream was deprecated by XMLStreamReader from STaX - jsr173 API. |
| */ |
| public XMLInputStream _newXMLInputStream() { |
| return _newXMLInputStream(null); |
| } |
| |
| 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); |
| } |
| |
| /** |
| * @deprecated XMLInputStream was deprecated by XMLStreamReader from STaX - jsr173 API. |
| */ |
| public XMLInputStream _newXMLInputStream(XmlOptions options) { |
| return new Saver.XmlInputStreamImpl(_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"); |
| |
| InputStream is = _newInputStream(options); |
| |
| try { |
| byte[] bytes = new byte[8192]; |
| |
| for (; ;) { |
| int n = is.read(bytes); |
| |
| if (n < 0) |
| break; |
| |
| os.write(bytes, 0, n); |
| } |
| } finally { |
| is.close(); |
| } |
| } |
| |
| public void _save(Writer w, XmlOptions options) throws IOException { |
| if (w == null) |
| throw new IllegalArgumentException("Null Writer specified"); |
| |
| if (options != null && options.hasOption( XmlOptions.SAVE_OPTIMIZE_FOR_SPEED )) |
| { |
| 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; |
| |
| XmlCursor c = newCursor(); |
| int token = c.toNextToken().intValue(); |
| |
| try { |
| 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; |
| } |
| } |
| } finally { |
| c.dispose(); |
| } |
| |
| return !seenElement; |
| } |
| |
| public Node _newDomNode(XmlOptions options) { |
| // Must ignore inner options for compat with v1. |
| |
| if (XmlOptions.hasOption(options, XmlOptions.SAVE_INNER)) { |
| options = new XmlOptions(options); |
| options.remove(XmlOptions.SAVE_INNER); |
| } |
| |
| 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 = Path.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, Locale.WS_PRESERVE, 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 Query.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 listToFill) { |
| if (listToFill != null) { |
| for (Bookmark b = _cur._xobj._bookmarks; b != null; b = b._next) { |
| if (b._value instanceof XmlBookmark) { |
| listToFill.add(b._value); |
| } |
| } |
| } |
| } |
| |
| public boolean _removeXml() { |
| if (_cur.isRoot()) { |
| throw new IllegalStateException("Can't remove a whole document."); |
| } |
| |
| if (_cur.isFinish()) { |
| return false; |
| } |
| |
| assert _cur.isText() || _cur.isNode(); |
| |
| if (_cur.isText()) |
| _cur.moveChars(null, -1); |
| else |
| _cur.moveNode(null); |
| |
| return true; |
| } |
| |
| public boolean _moveXml(Cursor to) { |
| to.checkInsertionValidity(_cur); |
| |
| // Check for a no-op |
| |
| if (_cur.isText()) { |
| int cchRight = _cur.cchRight(); |
| |
| assert cchRight > 0; |
| |
| if (_cur.inChars(to._cur, cchRight, true)) { |
| return false; |
| } |
| |
| _cur.moveChars(to._cur, cchRight); |
| |
| to._cur.nextChars(cchRight); |
| |
| return true; |
| } |
| |
| if (_cur.contains(to._cur)) { |
| return false; |
| } |
| |
| // Make a cur which will float to the right of the insertion |
| |
| Cur c = to.tempCur(); |
| |
| _cur.moveNode(to._cur); |
| |
| to._cur.moveToCur(c); |
| |
| c.release(); |
| |
| return true; |
| } |
| |
| public boolean _copyXml(Cursor to) { |
| to.checkInsertionValidity(_cur); |
| |
| assert _cur.isText() || _cur.isNode(); |
| |
| Cur c = to.tempCur(); |
| |
| if (_cur.isText()) { |
| to._cur.insertChars(_cur.getChars(-1), _cur._offSrc, _cur._cchSrc); |
| } else { |
| _cur.copyNode(to._cur); |
| } |
| |
| to._cur.moveToCur(c); |
| |
| c.release(); |
| |
| return true; |
| } |
| |
| public boolean _removeXmlContents() { |
| if (!_cur.isContainer()) { |
| return false; |
| } |
| |
| _cur.moveNodeContents(null, false); |
| |
| return true; |
| } |
| |
| private boolean checkContentInsertionValidity(Cursor to) { |
| _cur.push(); |
| |
| _cur.next(); |
| |
| if (_cur.isFinish()) { |
| _cur.pop(); |
| return false; |
| } |
| |
| try { |
| to.checkInsertionValidity(_cur); |
| } catch (IllegalArgumentException e) { |
| _cur.pop(); |
| throw e; |
| } |
| |
| _cur.pop(); |
| |
| return true; |
| } |
| |
| public boolean _moveXmlContents(Cursor to) { |
| if (!_cur.isContainer() || _cur.contains(to._cur)) { |
| return false; |
| } |
| |
| if (!checkContentInsertionValidity(to)) { |
| return false; |
| } |
| |
| Cur c = to.tempCur(); |
| |
| _cur.moveNodeContents(to._cur, false); |
| |
| to._cur.moveToCur(c); |
| |
| c.release(); |
| |
| return true; |
| } |
| |
| public boolean _copyXmlContents(Cursor to) { |
| if (!_cur.isContainer() || _cur.contains(to._cur)) { |
| return false; |
| } |
| |
| if (!checkContentInsertionValidity(to)) { |
| return false; |
| } |
| |
| // I don't have a primitive to copy contents, make a copy of the node and them move the |
| // contents |
| |
| Cur c = _cur._locale.tempCur(); |
| |
| _cur.copyNode(c); |
| |
| Cur c2 = to._cur.tempCur(); |
| |
| c.moveNodeContents(to._cur, false); |
| |
| c.release(); |
| |
| to._cur.moveToCur(c2); |
| |
| c2.release(); |
| |
| return true; |
| } |
| |
| public int _removeChars(int cch) { |
| int cchRight = _cur.cchRight(); |
| |
| if (cchRight == 0 || cch == 0) { |
| return 0; |
| } |
| |
| if (cch < 0 || cch > cchRight) { |
| cch = cchRight; |
| } |
| |
| _cur.moveChars(null, cch); |
| |
| return _cur._cchSrc; |
| } |
| |
| public int _moveChars(int cch, Cursor to) { |
| int cchRight = _cur.cchRight(); |
| |
| if (cchRight <= 0 || cch == 0) |
| return 0; |
| |
| if (cch < 0 || cch > cchRight) |
| cch = cchRight; |
| |
| to.checkInsertionValidity(_cur); |
| |
| _cur.moveChars(to._cur, cch); |
| |
| to._cur.nextChars(_cur._cchSrc); |
| |
| return _cur._cchSrc; |
| } |
| |
| public int _copyChars(int cch, Cursor to) { |
| int cchRight = _cur.cchRight(); |
| |
| if (cchRight <= 0 || cch == 0) |
| return 0; |
| |
| if (cch < 0 || cch > cchRight) |
| cch = cchRight; |
| |
| to.checkInsertionValidity(_cur); |
| |
| to._cur.insertChars(_cur.getChars(cch), _cur._offSrc, _cur._cchSrc); |
| |
| to._cur.nextChars(_cur._cchSrc); |
| |
| return _cur._cchSrc; |
| } |
| |
| public void _insertChars(String text) { |
| int l = text == null ? 0 : text.length(); |
| |
| if (l > 0) { |
| if (_cur.isRoot() || _cur.isAttr()) { |
| throw |
| new IllegalStateException("Can't insert before the document or an attribute."); |
| } |
| |
| _cur.insertChars(text, 0, l); |
| _cur.nextChars(l); |
| } |
| } |
| |
| // |
| // Inserting elements |
| // |
| |
| public void _beginElement(String localName) { |
| _insertElementWithText(localName, null, null); |
| _toPrevToken(); |
| } |
| |
| public void _beginElement(String localName, String uri) { |
| _insertElementWithText(localName, uri, null); |
| _toPrevToken(); |
| } |
| |
| public void _beginElement(QName name) { |
| _insertElementWithText(name, null); |
| _toPrevToken(); |
| } |
| |
| public void _insertElement(String localName) { |
| _insertElementWithText(localName, null, null); |
| } |
| |
| public void _insertElement(String localName, String uri) { |
| _insertElementWithText(localName, uri, null); |
| } |
| |
| public void _insertElement(QName name) { |
| _insertElementWithText(name, null); |
| } |
| |
| public void _insertElementWithText(String localName, String text) { |
| _insertElementWithText(localName, null, text); |
| } |
| |
| public void _insertElementWithText(String localName, String uri, String text) { |
| validateLocalName(localName); |
| |
| _insertElementWithText(_cur._locale.makeQName(uri, localName), text); |
| } |
| |
| public void _insertElementWithText(QName name, String text) { |
| validateLocalName(name.getLocalPart()); |
| |
| Cur c = _cur._locale.tempCur(); |
| |
| c.createElement(name); |
| |
| insertNode(c, text); |
| |
| c.release(); |
| } |
| |
| // |
| // |
| // |
| |
| public void _insertAttribute(String localName) { |
| _insertAttributeWithValue(localName, null); |
| } |
| |
| public void _insertAttribute(String localName, String uri) { |
| _insertAttributeWithValue(localName, uri, null); |
| } |
| |
| public void _insertAttribute(QName name) { |
| _insertAttributeWithValue(name, null); |
| } |
| |
| public void _insertAttributeWithValue(String localName, String value) { |
| _insertAttributeWithValue(localName, null, value); |
| } |
| |
| public void _insertAttributeWithValue(String localName, String uri, String value) { |
| validateLocalName(localName); |
| |
| _insertAttributeWithValue(_cur._locale.makeQName(uri, localName), value); |
| } |
| |
| public void _insertAttributeWithValue(QName name, String text) { |
| validateLocalName(name.getLocalPart()); |
| |
| Cur c = _cur._locale.tempCur(); |
| |
| c.createAttr(name); |
| |
| insertNode(c, text); |
| |
| c.release(); |
| } |
| |
| // |
| // |
| // |
| |
| public void _insertNamespace(String prefix, String namespace) { |
| _insertAttributeWithValue(_cur._locale.createXmlns(prefix), namespace); |
| } |
| |
| public void _insertComment(String text) { |
| Cur c = _cur._locale.tempCur(); |
| |
| c.createComment(); |
| |
| insertNode(c, text); |
| |
| c.release(); |
| } |
| |
| public void _insertProcInst(String target, String text) { |
| validateLocalName(target); |
| |
| if (Locale.beginsWithXml(target) && target.length() == 3) |
| throw new IllegalArgumentException("Target is 'xml'"); |
| |
| Cur c = _cur._locale.tempCur(); |
| |
| c.createProcinst(target); |
| |
| insertNode(c, text); |
| |
| c.release(); |
| } |
| |
| public void _dump() { |
| _cur.dump(); |
| } |
| |
| // |
| // |
| // |
| // |
| // |
| // |
| // |
| |
| private void checkThisCursor() { |
| if (_cur == null) |
| throw new IllegalStateException("This cursor has been disposed"); |
| } |
| |
| private Cursor checkCursors(XmlCursor xOther) { |
| checkThisCursor(); |
| |
| if (xOther == null) |
| throw new IllegalArgumentException("Other cursor is <null>"); |
| |
| if (!(xOther instanceof Cursor)) |
| throw new IllegalArgumentException("Incompatible cursors: " + xOther); |
| |
| Cursor other = (Cursor) xOther; |
| |
| if (other._cur == null) |
| throw new IllegalStateException("Other cursor has been disposed"); |
| |
| return other; |
| } |
| |
| // |
| // The following operations have two cursors, and can be in different documents |
| // |
| |
| private static final int MOVE_XML = 0; |
| private static final int COPY_XML = 1; |
| private static final int MOVE_XML_CONTENTS = 2; |
| private static final int COPY_XML_CONTENTS = 3; |
| private static final int MOVE_CHARS = 4; |
| private static final int COPY_CHARS = 5; |
| |
| private int twoLocaleOp(XmlCursor xOther, int op, int arg) { |
| Cursor other = checkCursors(xOther); |
| |
| Locale locale = _cur._locale; |
| Locale otherLocale = other._cur._locale; |
| |
| if (locale == otherLocale) { |
| return syncWrapNoEnter(() -> twoLocaleOp(other, op, arg)); |
| } |
| |
| if (locale.noSync()) { |
| if (otherLocale.noSync()) |
| return twoLocaleOp(other, op, arg); |
| else { |
| synchronized (otherLocale) { |
| return twoLocaleOp(other, op, arg); |
| } |
| } |
| } else if (otherLocale.noSync()) { |
| synchronized (locale) { |
| return twoLocaleOp(other, op, arg); |
| } |
| } |
| |
| boolean acquired = false; |
| |
| try { |
| GlobalLock.acquire(); |
| acquired = true; |
| |
| synchronized (locale) { |
| synchronized (otherLocale) { |
| GlobalLock.release(); |
| acquired = false; |
| |
| return twoLocaleOp(other, op, arg); |
| } |
| } |
| } catch (InterruptedException e) { |
| throw new RuntimeException(e.getMessage(), e); |
| } finally { |
| if (acquired) |
| GlobalLock.release(); |
| } |
| } |
| |
| private int twoLocaleOp(Cursor other, int op, int arg) { |
| Locale locale = _cur._locale; |
| Locale otherLocale = other._cur._locale; |
| |
| locale.enter(otherLocale); |
| |
| try { |
| switch (op) { |
| case MOVE_XML: |
| return _moveXml(other) ? 1 : 0; |
| case COPY_XML: |
| return _copyXml(other) ? 1 : 0; |
| case MOVE_XML_CONTENTS: |
| return _moveXmlContents(other) ? 1 : 0; |
| case COPY_XML_CONTENTS: |
| return _copyXmlContents(other) ? 1 : 0; |
| case MOVE_CHARS: |
| return _moveChars(arg, other); |
| case COPY_CHARS: |
| return _copyChars(arg, other); |
| |
| default : |
| throw new RuntimeException("Unknown operation: " + op); |
| } |
| } finally { |
| locale.exit(otherLocale); |
| } |
| } |
| |
| public boolean moveXml(XmlCursor xTo) { |
| return twoLocaleOp(xTo, MOVE_XML, 0) == 1; |
| } |
| |
| public boolean copyXml(XmlCursor xTo) { |
| return twoLocaleOp(xTo, COPY_XML, 0) == 1; |
| } |
| |
| public boolean moveXmlContents(XmlCursor xTo) { |
| return twoLocaleOp(xTo, MOVE_XML_CONTENTS, 0) == 1; |
| } |
| |
| public boolean copyXmlContents(XmlCursor xTo) { |
| return twoLocaleOp(xTo, COPY_XML_CONTENTS, 0) == 1; |
| } |
| |
| public int moveChars(int cch, XmlCursor xTo) { |
| return twoLocaleOp(xTo, MOVE_CHARS, cch); |
| } |
| |
| public int copyChars(int cch, XmlCursor xTo) { |
| return twoLocaleOp(xTo, COPY_CHARS, cch); |
| } |
| |
| |
| // |
| // Special methods involving multiple cursors which can be in different locales, but do not |
| // require sync on both locales. |
| // |
| |
| public boolean toCursor(XmlCursor xOther) { |
| // One may only move cursors within the same locale |
| |
| Cursor other = checkCursors(xOther); |
| |
| return _cur._locale == other._cur._locale && |
| syncWrap(() -> _toCursor(other)); |
| } |
| |
| public boolean isInSameDocument(XmlCursor xOther) { |
| return xOther != null && _cur.isInSameTree(checkCursors(xOther)._cur); |
| } |
| |
| // |
| // The following operations have two cursors, but they must be in the same document |
| // |
| |
| private Cursor preCheck(XmlCursor xOther) { |
| Cursor other = checkCursors(xOther); |
| |
| if (_cur._locale != other._cur._locale) { |
| throw new IllegalArgumentException("Cursors not in same document"); |
| } |
| |
| return other; |
| } |
| |
| public int comparePosition(XmlCursor xOther) { |
| Cursor other = preCheck(xOther); |
| return syncWrap(() -> _comparePosition(other)); |
| } |
| |
| public boolean isLeftOf(XmlCursor xOther) { |
| Cursor other = preCheck(xOther); |
| return syncWrap(() -> _isLeftOf(other)); |
| } |
| |
| public boolean isAtSamePositionAs(XmlCursor xOther) { |
| Cursor other = preCheck(xOther); |
| return syncWrap(() -> _isAtSamePositionAs(other)); |
| } |
| |
| public boolean isRightOf(XmlCursor xOther) { |
| Cursor other = preCheck(xOther); |
| return syncWrap(() -> _isRightOf(other)); |
| } |
| |
| // |
| // Create a cursor from an Xobj -- used for XmlBookmark.createCursor |
| // |
| |
| public static XmlCursor newCursor(Xobj x, int p) { |
| Locale l = x._locale; |
| if (l.noSync()) { |
| l.enter(); |
| try { |
| return new Cursor(x, p); |
| } finally { |
| l.exit(); |
| } |
| } else synchronized (l) { |
| l.enter(); |
| try { |
| return new Cursor(x, p); |
| } finally { |
| l.exit(); |
| } |
| } |
| } |
| |
| // |
| // The following operations involve only one cursor |
| // |
| |
| private boolean preCheck() { |
| checkThisCursor(); |
| return _cur._locale.noSync(); |
| } |
| |
| public void dispose() { |
| if (_cur != null) { |
| syncWrap(this::_dispose); |
| } |
| } |
| |
| public Object monitor() { |
| return syncWrap(this::_monitor); |
| } |
| |
| public XmlDocumentProperties documentProperties() { |
| return syncWrap(this::_documentProperties); |
| } |
| |
| public XmlCursor newCursor() { |
| return syncWrap(this::_newCursor); |
| } |
| |
| public XMLStreamReader newXMLStreamReader() { |
| return syncWrap((Supplier<XMLStreamReader>)this::_newXMLStreamReader); |
| } |
| |
| public XMLStreamReader newXMLStreamReader(XmlOptions options) { |
| return syncWrap(() -> _newXMLStreamReader(options)); |
| } |
| |
| /** |
| * @deprecated XMLInputStream was deprecated by XMLStreamReader from STaX - jsr173 API. |
| */ |
| public XMLInputStream newXMLInputStream() { |
| return syncWrap((Supplier<XMLInputStream>)this::_newXMLInputStream); |
| } |
| |
| public String xmlText() { |
| return syncWrap((Supplier<String>)this::_xmlText); |
| } |
| |
| public InputStream newInputStream() { |
| return syncWrap((Supplier<InputStream>)this::_newInputStream); |
| } |
| |
| public Reader newReader() { |
| return syncWrap((Supplier<Reader>)this::_newReader); |
| } |
| |
| public Node newDomNode() { |
| return syncWrap((Supplier<Node>)this::_newDomNode); |
| } |
| |
| public Node getDomNode() { |
| return syncWrap(this::_getDomNode); |
| } |
| |
| public void save(ContentHandler ch, LexicalHandler lh) throws SAXException { |
| syncWrapSAXEx(() -> _save(ch, lh)); |
| } |
| |
| public void save(File file) throws IOException { |
| syncWrapIOEx(() -> _save(file)); |
| } |
| |
| public void save(OutputStream os) throws IOException { |
| syncWrapIOEx(() -> _save(os)); |
| } |
| |
| public void save(Writer w) throws IOException { |
| syncWrapIOEx(() -> _save(w)); |
| } |
| |
| /** |
| * @deprecated XMLInputStream was deprecated by XMLStreamReader from STaX - jsr173 API. |
| */ |
| public XMLInputStream newXMLInputStream(XmlOptions options) { |
| return syncWrap(() -> _newXMLInputStream(options)); |
| } |
| |
| public String xmlText(XmlOptions options) { |
| return syncWrap(() -> _xmlText(options)); |
| } |
| |
| public InputStream newInputStream(XmlOptions options) { |
| return syncWrap(() -> _newInputStream(options)); |
| } |
| |
| public Reader newReader(XmlOptions options) { |
| return syncWrap(() -> _newReader(options)); |
| } |
| |
| public Node newDomNode(XmlOptions options) { |
| return syncWrap(() -> _newDomNode(options)); |
| } |
| |
| public void save(ContentHandler ch, LexicalHandler lh, XmlOptions options) throws SAXException { |
| syncWrapSAXEx(() -> _save(ch, lh, options)); |
| } |
| |
| public void save(File file, XmlOptions options) throws IOException { |
| syncWrapIOEx(() -> _save(file, options)); |
| } |
| |
| public void save(OutputStream os, XmlOptions options) throws IOException { |
| syncWrapIOEx(() -> _save(os, options)); |
| } |
| |
| public void save(Writer w, XmlOptions options) throws IOException { |
| syncWrapIOEx(() -> _save(w, options)); |
| } |
| |
| public void push() { |
| syncWrap(this::_push); |
| } |
| |
| public boolean pop() { |
| return syncWrap(this::_pop); |
| } |
| |
| public void selectPath(String path) { |
| syncWrap(() -> _selectPath(path)); |
| } |
| |
| public void selectPath(String path, XmlOptions options) { |
| syncWrap(() -> _selectPath(path, options)); |
| } |
| |
| public boolean hasNextSelection() { |
| return syncWrap(this::_hasNextSelection); |
| } |
| |
| public boolean toNextSelection() { |
| return syncWrap(this::_toNextSelection); |
| } |
| |
| public boolean toSelection(int i) { |
| return syncWrap(() -> _toSelection(i)); |
| } |
| |
| public int getSelectionCount() { |
| return syncWrap(this::_getSelectionCount); |
| } |
| |
| public void addToSelection() { |
| syncWrap(this::_addToSelection); |
| } |
| |
| public void clearSelections() { |
| syncWrap(this::_clearSelections); |
| } |
| |
| public boolean toBookmark(XmlBookmark bookmark) { |
| return syncWrap(() -> _toBookmark(bookmark)); |
| } |
| |
| public XmlBookmark toNextBookmark(Object key) { |
| return syncWrap(() -> _toNextBookmark(key)); |
| } |
| |
| public XmlBookmark toPrevBookmark(Object key) { |
| return syncWrap(() -> _toPrevBookmark(key)); |
| } |
| |
| public QName getName() { |
| return syncWrap(this::_getName); |
| } |
| |
| public void setName(QName name) { |
| syncWrap(() -> _setName(name)); |
| } |
| |
| public String namespaceForPrefix(String prefix) { |
| return syncWrap(() -> _namespaceForPrefix(prefix)); |
| } |
| |
| public String prefixForNamespace(String namespaceURI) { |
| return syncWrap(() -> _prefixForNamespace(namespaceURI)); |
| } |
| |
| public void getAllNamespaces(Map addToThis) { |
| syncWrap(() -> _getAllNamespaces(addToThis)); |
| } |
| |
| public XmlObject getObject() { |
| return syncWrap(this::_getObject); |
| } |
| |
| public TokenType currentTokenType() { |
| return syncWrapNoEnter(this::_currentTokenType); |
| } |
| |
| public boolean isStartdoc() { |
| return syncWrapNoEnter(this::_isStartdoc); |
| } |
| |
| public boolean isEnddoc() { |
| return syncWrapNoEnter(this::_isEnddoc); |
| } |
| |
| public boolean isStart() { |
| return syncWrapNoEnter(this::_isStart); |
| } |
| |
| public boolean isEnd() { |
| return syncWrapNoEnter(this::_isEnd); |
| } |
| |
| public boolean isText() { |
| return syncWrapNoEnter(this::_isText); |
| } |
| |
| public boolean isAttr() { |
| return syncWrapNoEnter(this::_isAttr); |
| } |
| |
| public boolean isNamespace() { |
| return syncWrapNoEnter(this::_isNamespace); |
| } |
| |
| public boolean isComment() { |
| return syncWrapNoEnter(this::_isComment); |
| } |
| |
| public boolean isProcinst() { |
| return syncWrapNoEnter(this::_isProcinst); |
| } |
| |
| public boolean isContainer() { |
| return syncWrapNoEnter(this::_isContainer); |
| } |
| |
| public boolean isFinish() { |
| return syncWrapNoEnter(this::_isFinish); |
| } |
| |
| public boolean isAnyAttr() { |
| return syncWrapNoEnter(this::_isAnyAttr); |
| } |
| |
| public TokenType prevTokenType() { |
| return syncWrap(this::_prevTokenType); |
| } |
| |
| public boolean hasNextToken() { |
| return syncWrapNoEnter(this::_hasNextToken); |
| } |
| |
| public boolean hasPrevToken() { |
| return syncWrap(this::_hasPrevToken); |
| } |
| |
| public TokenType toNextToken() { |
| return syncWrap(this::_toNextToken); |
| } |
| |
| public TokenType toPrevToken() { |
| return syncWrap(this::_toPrevToken); |
| } |
| |
| public TokenType toFirstContentToken() { |
| return syncWrap(this::_toFirstContentToken); |
| } |
| |
| public TokenType toEndToken() { |
| return syncWrap(this::_toEndToken); |
| } |
| |
| public int toNextChar(int cch) { |
| return syncWrap(() -> _toNextChar(cch)); |
| } |
| |
| public int toPrevChar(int cch) { |
| return syncWrap(() -> _toPrevChar(cch)); |
| } |
| |
| // public boolean _toNextSibling() |
| // { |
| // return Locale.toNextSiblingElement(_cur); |
| // } |
| |
| public boolean ___toNextSibling() { |
| if (!_cur.hasParent()) { |
| return false; |
| } |
| |
| Xobj parent = _cur.getParentNoRoot(); |
| |
| if (parent==null) { |
| _cur._locale.enter(); |
| try { |
| parent = _cur.getParent(); |
| } finally { |
| _cur._locale.exit(); |
| } |
| } |
| |
| return Locale.toNextSiblingElement(_cur, parent); |
| } |
| |
| public boolean toNextSibling() { |
| return syncWrapNoEnter(this::___toNextSibling); |
| } |
| |
| public boolean toPrevSibling() { |
| return syncWrap(this::_toPrevSibling); |
| } |
| |
| public boolean toParent() { |
| return syncWrap(this::_toParent); |
| } |
| |
| public boolean toFirstChild() { |
| return syncWrapNoEnter(this::_toFirstChild); |
| } |
| |
| public boolean toLastChild() { |
| return syncWrap(this::_toLastChild); |
| } |
| |
| public boolean toChild(String name) { |
| return syncWrap(() -> _toChild(name)); |
| } |
| |
| public boolean toChild(String namespace, String name) { |
| return syncWrap(() -> _toChild(namespace, name)); |
| } |
| |
| public boolean toChild(QName name) { |
| return syncWrap(() -> _toChild(name)); |
| } |
| |
| public boolean toChild(int index) { |
| return syncWrap(() -> _toChild(index)); |
| } |
| |
| public boolean toChild(QName name, int index) { |
| return syncWrap(() -> _toChild(name, index)); |
| } |
| |
| public boolean toNextSibling(String name) { |
| return syncWrap(() -> _toNextSibling(name)); |
| } |
| |
| public boolean toNextSibling(String namespace, String name) { |
| return syncWrap(() -> _toNextSibling(namespace, name)); |
| } |
| |
| public boolean toNextSibling(QName name) { |
| return syncWrap(() -> _toNextSibling(name)); |
| } |
| |
| public boolean toFirstAttribute() { |
| return syncWrapNoEnter(this::_toFirstAttribute); |
| } |
| |
| public boolean toLastAttribute() { |
| return syncWrap(this::_toLastAttribute); |
| } |
| |
| public boolean toNextAttribute() { |
| return syncWrap(this::_toNextAttribute); |
| } |
| |
| public boolean toPrevAttribute() { |
| return syncWrap(this::_toPrevAttribute); |
| } |
| |
| public String getAttributeText(QName attrName) { |
| return syncWrap(() -> _getAttributeText(attrName)); |
| } |
| |
| public boolean setAttributeText(QName attrName, String value) { |
| return syncWrap(() -> _setAttributeText(attrName, value)); |
| } |
| |
| public boolean removeAttribute(QName attrName) { |
| return syncWrap(() -> _removeAttribute(attrName)); |
| } |
| |
| public String getTextValue() { |
| return syncWrap((Supplier<String>)this::_getTextValue); |
| } |
| |
| public int getTextValue(char[] chars, int offset, int cch) { |
| return syncWrap(() -> _getTextValue(chars, offset, cch)); |
| } |
| |
| public void setTextValue(String text) { |
| syncWrap(() -> _setTextValue(text)); |
| } |
| |
| public void setTextValue(char[] sourceChars, int offset, int length) { |
| syncWrap(() -> _setTextValue(sourceChars, offset, length)); |
| } |
| |
| public String getChars() { |
| return syncWrap((Supplier<String>)this::_getChars); |
| } |
| |
| public int getChars(char[] chars, int offset, int cch) { |
| return syncWrap(() -> _getChars(chars, offset, cch)); |
| } |
| |
| public void toStartDoc() { |
| syncWrapNoEnter(this::_toStartDoc); |
| } |
| |
| public void toEndDoc() { |
| syncWrapNoEnter(this::_toEndDoc); |
| } |
| |
| public XmlCursor execQuery(String query) { |
| return syncWrap(() -> _execQuery(query)); |
| } |
| |
| public XmlCursor execQuery(String query, XmlOptions options) { |
| return syncWrap(() -> _execQuery(query, options)); |
| } |
| |
| public ChangeStamp getDocChangeStamp() { |
| return syncWrap(this::_getDocChangeStamp); |
| } |
| |
| public void setBookmark(XmlBookmark bookmark) { |
| syncWrap(() -> _setBookmark(bookmark)); |
| } |
| |
| public XmlBookmark getBookmark(Object key) { |
| return syncWrap(() -> _getBookmark(key)); |
| } |
| |
| public void clearBookmark(Object key) { |
| syncWrap(() -> _clearBookmark(key)); |
| } |
| |
| public void getAllBookmarkRefs(Collection listToFill) { |
| syncWrap(() -> _getAllBookmarkRefs(listToFill)); |
| } |
| |
| public boolean removeXml() { |
| return syncWrap(this::_removeXml); |
| } |
| |
| public boolean removeXmlContents() { |
| return syncWrap(this::_removeXmlContents); |
| } |
| |
| public int removeChars(int cch) { |
| return syncWrap(() -> _removeChars(cch)); |
| } |
| |
| public void insertChars(String text) { |
| syncWrap(() -> _insertChars(text)); |
| } |
| |
| public void insertElement(QName name) { |
| syncWrap(() -> _insertElement(name)); |
| } |
| |
| public void insertElement(String localName) { |
| syncWrap(() -> _insertElement(localName)); |
| } |
| |
| public void insertElement(String localName, String uri) { |
| syncWrap(() -> _insertElement(localName, uri)); |
| } |
| |
| public void beginElement(QName name) { |
| syncWrap(() -> _beginElement(name)); |
| } |
| |
| public void beginElement(String localName) { |
| syncWrap(() -> _beginElement(localName)); |
| } |
| |
| public void beginElement(String localName, String uri) { |
| syncWrap(() -> _beginElement(localName, uri)); |
| } |
| |
| public void insertElementWithText(QName name, String text) { |
| syncWrap(() -> _insertElementWithText(name, text)); |
| } |
| |
| public void insertElementWithText(String localName, String text) { |
| syncWrap(() -> _insertElementWithText(localName, text)); |
| } |
| |
| public void insertElementWithText(String localName, String uri, String text) { |
| syncWrap(() -> _insertElementWithText(localName, uri, text)); |
| } |
| |
| public void insertAttribute(String localName) { |
| syncWrap(() -> _insertAttribute(localName)); |
| } |
| |
| public void insertAttribute(String localName, String uri) { |
| syncWrap(() -> _insertAttribute(localName, uri)); |
| } |
| |
| public void insertAttribute(QName name) { |
| syncWrap(() -> _insertAttribute(name)); |
| } |
| |
| public void insertAttributeWithValue(String name, String value) { |
| syncWrap(() -> _insertAttributeWithValue(name, value)); |
| } |
| |
| public void insertAttributeWithValue(String name, String uri, String value) { |
| syncWrap(() -> _insertAttributeWithValue(name, uri, value)); |
| } |
| |
| public void insertAttributeWithValue(QName name, String value) { |
| syncWrap(() -> _insertAttributeWithValue(name, value)); |
| } |
| |
| public void insertNamespace(String prefix, String namespace) { |
| syncWrap(() -> _insertNamespace(prefix, namespace)); |
| } |
| |
| public void insertComment(String text) { |
| syncWrap(() -> _insertComment(text)); |
| } |
| |
| public void insertProcInst(String target, String text) { |
| syncWrap(() -> _insertProcInst(target, text)); |
| } |
| |
| public void dump() { |
| syncWrap(this::_dump); |
| } |
| |
| private interface WrapSAXEx { |
| void run() throws SAXException; |
| } |
| |
| private interface WrapIOEx { |
| void run() throws IOException; |
| } |
| |
| |
| @SuppressWarnings("SynchronizeOnNonFinalField") |
| private void syncWrap(Runnable inner) { |
| if (preCheck()) { |
| syncWrapHelper(inner, true); |
| } else { |
| synchronized (_cur._locale) { |
| syncWrapHelper(inner, true); |
| } |
| } |
| } |
| |
| @SuppressWarnings("SynchronizeOnNonFinalField") |
| private <T> T syncWrap(Supplier<T> inner) { |
| if (preCheck()) { |
| return syncWrapHelper(inner, true); |
| } else { |
| synchronized (_cur._locale) { |
| return syncWrapHelper(inner, true); |
| } |
| } |
| } |
| |
| @SuppressWarnings("SynchronizeOnNonFinalField") |
| private <T> T syncWrapNoEnter(Supplier<T> inner) { |
| if (preCheck()) { |
| return syncWrapHelper(inner, false); |
| } else { |
| synchronized (_cur._locale) { |
| return syncWrapHelper(inner, false); |
| } |
| } |
| } |
| |
| @SuppressWarnings("SynchronizeOnNonFinalField") |
| private void syncWrapNoEnter(Runnable inner) { |
| if (preCheck()) { |
| syncWrapHelper(inner, false); |
| } else { |
| synchronized (_cur._locale) { |
| syncWrapHelper(inner, false); |
| } |
| } |
| } |
| |
| @SuppressWarnings("SynchronizeOnNonFinalField") |
| private void syncWrapSAXEx(WrapSAXEx inner) throws SAXException { |
| if (preCheck()) { |
| syncWrapHelper(inner); |
| } else { |
| synchronized (_cur._locale) { |
| syncWrapHelper(inner); |
| } |
| } |
| } |
| |
| @SuppressWarnings("SynchronizeOnNonFinalField") |
| private void syncWrapIOEx(WrapIOEx inner) throws IOException { |
| if (preCheck()) { |
| syncWrapHelper(inner); |
| } else { |
| synchronized (_cur._locale) { |
| syncWrapHelper(inner); |
| } |
| } |
| } |
| |
| private void syncWrapHelper(Runnable inner, final boolean enterLocale) { |
| final Locale l = _cur._locale; |
| if (enterLocale) { |
| l.enter(); |
| } |
| try { |
| inner.run(); |
| } finally { |
| if (enterLocale) { |
| l.exit(); |
| } |
| } |
| } |
| |
| private <T> T syncWrapHelper(Supplier<T> inner, final boolean enterLocale) { |
| final Locale l = _cur._locale; |
| if (enterLocale) { |
| l.enter(); |
| } |
| try { |
| return inner.get(); |
| } finally { |
| if (enterLocale) { |
| l.exit(); |
| } |
| } |
| } |
| |
| private void syncWrapHelper(WrapSAXEx inner) throws SAXException { |
| final Locale l = _cur._locale; |
| l.enter(); |
| try { |
| inner.run(); |
| } finally { |
| l.exit(); |
| } |
| } |
| |
| private void syncWrapHelper(WrapIOEx inner) throws IOException { |
| final Locale l = _cur._locale; |
| l.enter(); |
| try { |
| inner.run(); |
| } finally { |
| l.exit(); |
| } |
| } |
| } |