package textbender.g.xml.dom; // Copyright 2007, Michael Allan. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Textbender Software"), to deal in the Textbender Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicence, and/or sell copies of the Textbender Software, and to permit persons to whom the Textbender Software is furnished to do so, subject to the following conditions: The preceding copyright notice and this permission notice shall be included in all copies or substantial portions of the Textbender Software. THE TEXTBENDER SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE TEXTBENDER SOFTWARE OR THE USE OR OTHER DEALINGS IN THE TEXTBENDER SOFTWARE. import org.w3c.dom.*; import textbender.g.lang.*; /** A pointer into a node. Points to a node, and an offset within it. *

* Cf. DOM Traversal and Range, and org.w3c.dom.ranges.Range. *

*/ public final class DOMPointer { /** Constructs a DOMPointer with an initial node of null. */ public DOMPointer() {} /** Positions the pointer at the last offset * among Text children of the specified parent. * If no such offset exists, node is set to null, and offset to -1. * * @return reference to this pointer */ public DOMPointer lastTextOffset( Node parent ) { lastTextOffsetOrNode( parent ); if( node == null || node instanceof Text ) return this; previousTextOffset(); return this; } /** Positions the pointer at the last offset * among Text children of the specified parent, * or the last non-Text child, whichever comes later. * If the resulting node is non-Text or null, offset is set to -1. * * @return reference to this pointer */ public DOMPointer lastTextOffsetOrNode( Node parent ) { node = parent.getLastChild(); if( node instanceof Text ) { offset = ((Text)node).getLength() - 1; if( offset == -1 ) previousTextOffsetOrNode(); // skip empty text node } else offset = -1; return this; } /** Moves this pointer forward to the next Text offset among sibling nodes. * All non-Text nodes are skipped. * If no such offset exists, node is set to null, and offset to -1. * * @return reference to this pointer */ public DOMPointer nextTextOffset() { for( ;; ) { nextTextOffsetOrNode(); if( node == null || node instanceof Text ) break; } return this; } /** Moves this pointer forward to the next Text offset, * or the next non-Text sibling node, whichever comes first. * If the resulting node is non-Text or null, offset is set to -1. * * @return reference to this pointer */ public DOMPointer nextTextOffsetOrNode() { if( node instanceof Text && offset < ((Text)node).getLength() - 1 ) ++offset; else { node = node.getNextSibling(); if( node instanceof Text ) { if( ((Text)node).getLength() == 0 ) nextTextOffsetOrNode(); // skip empty text node else offset = 0; } else offset = -1; } return this; } /** The node pointed at. */ public Node node; /** The offset pointed at, within the node. * For character based nodes, this is a character offset; * for other nodes, a child offset. */ public int offset; /** Moves this pointer back to the previous Text offset among sibling nodes. * All non-Text nodes are skipped. * If no such offset exists, node is set to null, and offset to -1. * * @return reference to this pointer */ public DOMPointer previousTextOffset() { for( ;; ) { previousTextOffsetOrNode(); if( node == null || node instanceof Text ) break; } return this; } /** Moves this pointer back to the previous Text offset, * or the previous non-Text sibling node, whichever comes first. * If the resulting node is non-Text or null, offset is set to -1. * * @return reference to this pointer */ public DOMPointer previousTextOffsetOrNode() { if( node instanceof Text && offset > 0 ) --offset; else { node = node.getPreviousSibling(); if( node instanceof Text ) { offset = ((Text)node).getLength() - 1; if( offset == -1 ) previousTextOffsetOrNode(); // skip empty text node } else offset = -1; } return this; } /** @param node per {@linkplain #node node} * @param offset per {@linkplain #offset offset} * * @return reference to this pointer */ public DOMPointer set( Node node, int offset ) { this.node = node; this.offset = offset; return this; } /** @param other pointer, whose node and offset to assign to this pointer * * @return reference to this pointer */ public DOMPointer setFrom( DOMPointer other ) { node = other.node; offset = other.offset; return this; } // - O b j e c t ---------------------------------------------------------------------- /** Returns true iff o is a DOMPointer, * and points to an 'equals' node and offset. */ public @Override boolean equals( Object o ) { if( !(o instanceof DOMPointer )) return false; DOMPointer that = (DOMPointer)o; return this.offset == that.offset && ObjectX.nullEquals( this.node, that.node ); } public @Override String toString() { String string; if( node == null ) string = "[ offset=" + offset + ", node=null]"; else string = "[ offset=" + offset + ", node=" + System.identityHashCode(node) + ":" + node + "]"; return string; } }