Start line:  
End line:  

Snippet Preview

Snippet HTML Code

Stack Overflow Questions
DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER Copyright 2009, 2010 IBM. All rights reserved. Use is subject to license terms. 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. You can also obtain a copy of the License at http://odftoolkit.org/docs/license.txt 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.odftoolkit.simple.common.navigation;
 
 import java.net.URL;
 import java.util.Map;
 
TextSelection describes a sub element in a paragraph element or a heading element. It is recognized by the container element, which type should be OdfTextParagraph or OdfTextHeading, the start index of text content in container element and the text content of this Selection.
 
 public class TextSelection extends Selection {
 
 	private int mIndexInContainer;
 	private boolean mIsInserted;

Constructor of TextSelection.

Parameters:
text the text content of this TextSelection
containerElement the paragraph element or heading element that contains this TextSelection
index the start index of the text content in container element
 
 	TextSelection(String textOdfElement containerElementint index) {
 		 = text;
 		if (containerElement instanceof OdfTextParagraph) {
 			 = (OdfTextParagraphcontainerElement;
 		} else if (containerElement instanceof OdfTextHeading) {
 			 = (OdfTextHeadingcontainerElement;
 		}
 		 = index;
 	}

Create a new TextSelection.

Parameters:
text the text content of this TextSelection
containerElement the paragraph element or heading element that contains this TextSelection
index the start index of the text content in container element
Since:
0.5.5
 
	public static TextSelection newTextSelection(String textOdfElement containerElementint index) {
		TextSelection selection = new TextSelection(textcontainerElementindex);
		Selection.SelectionManager.registerItem(selection);
		return selection;
	}

Get the paragraph element or heading element that contains this TextSelection.

Returns:
OdfElement the container element
	public OdfElement getElement() {
	}

Get the paragraph element or heading element that contains this text.

Returns:
OdfElement
		if ( != null) {
			return ;
else {
			return ;
		}
	}

Get the start index of the text content of its container element.

Returns:
index the start index of the text content of its container element
	public int getIndex() {
	}

Get the text content of this TextSelection.

Returns:
text the text content
	public String getText() {
		return ;
	}

Delete the selection from the document the other matched selection in the same container element will be updated automatically because the start index of the following selections will be changed when the previous selection has been deleted.

Throws:
InvalidNavigationException if the selection is unavailable.
	public void cut() throws InvalidNavigationException {
		if (validate() == false) {
			throw new InvalidNavigationException("No matched string at this position");
		}
		SelectionManager.refreshAfterCut(this);
	}

Apply a style to the selection so that the text style of this selection will append the specified style.

Parameters:
style the style can be from the current document or user defined
Throws:
InvalidNavigationException if the selection is unavailable.
	public void applyStyle(OdfStyleBase stylethrows InvalidNavigationException {
		// append the specified style to the selection
		if (validate() == false) {
			throw new InvalidNavigationException("No matched string at this position");
		}
		OdfElement parentElement = getContainerElement();
		int leftLength = getText().length();
		int index = ;
		appendStyle(indexleftLengthparentElementstyle);
	}

Replace the text content of selection with a new string.

Parameters:
newText the replace text String
Throws:
InvalidNavigationException if the selection is unavailable.
	public void replaceWith(String newTextthrows InvalidNavigationException {
		if (validate() == false) {
			throw new InvalidNavigationException("No matched string at this position");
		}
		OdfElement parentElement = getContainerElement();
		int leftLength = getText().length();
		int index = ;
		delete(indexleftLengthparentElement);
		OdfTextSpan textSpan = new OdfTextSpan((OdfFileDomparentElement.getOwnerDocument());
		textSpan.addContentWhitespace(newText);
		 = false;
		insertSpan(textSpanindexparentElement);
		// optimize the parent element
		optimize(parentElement);
		int offset = newText.length() - leftLength;
		SelectionManager.refresh(getContainerElement(), offsetindex + getText().length());
		 = newText;
	}

Create a span element for this text selection.

Returns:
the created text span element for this selection
Throws:
InvalidNavigationException if the selection is unavailable.
Since:
0.5.5
		if (validate() == false) {
			throw new InvalidNavigationException("No matched string at this position");
		}
		OdfElement parentElement = getContainerElement();
		int leftLength = getText().length();
		int index = ;
		delete(indexleftLengthparentElement);
		OdfTextSpan textSpan = new OdfTextSpan((OdfFileDomparentElement.getOwnerDocument());
		 = false;
		insertSpan(textSpanindexparentElement);
		// optimize the parent element
		optimize(parentElement);
		return textSpan;
	}

Paste this selection just before a specific selection.

Parameters:
positionItem a selection that is used to point out the position
Throws:
InvalidNavigationException if the selection is unavailable.
	public void pasteAtFrontOf(Selection positionItemthrows InvalidNavigationException {
		if (validate() == false) {
			throw new InvalidNavigationException("No matched string at this position");
		}
		int indexOfNew = 0;
		OdfElement newElement = positionItem.getElement();
		if (positionItem instanceof TextSelection) {
			indexOfNew = ((TextSelectionpositionItem).getIndex();
			newElement = ((TextSelectionpositionItem).getContainerElement();
		}
		OdfTextSpan textSpan = getSpan((OdfFileDompositionItem.getElement().getOwnerDocument());
		 = false;
		insertSpan(textSpanindexOfNewnewElement);
		adjustStyle(newElementtextSpannull);
		SelectionManager.refreshAfterPasteAtFrontOf(thispositionItem);
	}

Paste this selection just after a specific selection.

Parameters:
positionItem a selection that is used to point out the position
Throws:
InvalidNavigationException if the selection is unavailable.
	public void pasteAtEndOf(Selection positionItemthrows InvalidNavigationException {
		if (validate() == false) {
			throw new InvalidNavigationException("No matched string at this position");
		}
		// TODO: think about and test if search item is a element selection
		int indexOfNew = 0;
		OdfElement newElement = positionItem.getElement();
		if (positionItem instanceof TextSelection) {
			indexOfNew = ((TextSelectionpositionItem).getIndex() + ((TextSelectionpositionItem).getText().length();
			newElement = ((TextSelectionpositionItem).getContainerElement();
		}
		OdfTextSpan textSpan = getSpan((OdfFileDompositionItem.getElement().getOwnerDocument());
		 = false;
		insertSpan(textSpanindexOfNewnewElement);
		adjustStyle(newElementtextSpannull);
		SelectionManager.refreshAfterPasteAtEndOf(thispositionItem);
	}

Add a hypertext reference to the selection.

Parameters:
url the URL of this hypertext reference
Throws:
InvalidNavigationException if the selection is unavailable.
	public void addHref(URL urlthrows InvalidNavigationException {
		if (validate() == false) {
			throw new InvalidNavigationException("No matched string at this position");
		}
		OdfElement parentElement = getContainerElement();
		int leftLength = getText().length();
		int index = ;
		addHref(indexleftLengthparentElementurl.toString());
	}

return a String Object representing this selection value the text content of the selection, start index in the container element and the text content of the container element will be provided.

Returns:
a String representation of the value of this TextSelection
	public String toString() {
		return "[" +  + "] started from " +  + " in paragraph:"
				+ TextExtractor.getText(getContainerElement());
	}
	protected void refreshAfterFrontalDelete(Selection deleteItem) {
		if (deleteItem instanceof TextSelection) {
		}
	}
	protected void refreshAfterFrontalInsert(Selection pasteItem) {
		if (pasteItem instanceof TextSelection) {
		}
	}
	protected void refresh(int offset) {
		 += offset;
		if ( < 0) {
		}
	}
	/*
	 * Return a new span that cover this selection and keep the original style
	 * of this <code>Selection</code>.
	 */
	private OdfTextSpan getSpan(OdfFileDom ownerDoc) {
		OdfElement parentElement = getContainerElement();
		if (parentElement != null) {
			OdfElement copyParentNode = (OdfElementparentElement.cloneNode(true);
			if (ownerDoc != parentElement.getOwnerDocument()) {
				copyParentNode = (OdfElementownerDoc.adoptNode(copyParentNode);
			}
			OdfTextSpan textSpan = new OdfTextSpan(ownerDoc);
			int sIndex = ;
			int eIndex = sIndex + .length();
			// delete the content except the selection string
			// delete from the end to start, so that the postion will not be
			// impact by delete action
			delete(eIndex, TextExtractor.getText(copyParentNode).length() - eIndexcopyParentNode);
			delete(0, sIndexcopyParentNode);
			optimize(copyParentNode);
			Node childNode = copyParentNode.getFirstChild();
			while (childNode != null) {
				textSpan.appendChild(childNode.cloneNode(true));
				childNode = childNode.getNextSibling();
			}
			// apply text style for the textSpan
			if (copyParentNode instanceof OdfStylableElement) {
			}
			return textSpan;
		}
		return null;
	}
	/*
	 * Optimize the text element by deleting the empty text node.
	 */
	private void optimize(Node pNode) {
		// check if the text:a can be optimized
		Node node = pNode.getFirstChild();
		while (node != null) {
			Node nextNode = node.getNextSibling();
			// if ((node.getNodeType() == Node.ELEMENT_NODE) &&
			// (node.getPrefix().equals("text"))) {
			if (node instanceof OdfTextSpan) {
				if (TextExtractor.getText((OdfTextSpannode).length() == 0) {
else {
					optimize(node);
				}
			}
			node = nextNode;
		}
	}
	/*
	 * Apply the <code>styleMap</code> to the <code>toElement</code> reserve the
	 * style property of toElement, if it is also exist in <code>styleMap</code>
	 */
	private void applyTextStyleProperties(Map<OdfStylePropertyStringstyleMapOdfStylableElement toElement) {
		if (styleMap != null) {
			// preserve the style property of toElement if it is also exist in
			// styleMap
			OdfStyle resultStyleElement = toElement.getAutomaticStyles().newStyle(.);
			for (Map.Entry<OdfStylePropertyStringentry : styleMap.entrySet()) {
				if (toElement.hasProperty(entry.getKey())) {
					resultStyleElement.setProperty(entry.getKey(), toElement.getProperty(entry.getKey()));
else {
					resultStyleElement.setProperty(entry.getKey(), entry.getValue());
				}
			}
			toElement.setStyleName(resultStyleElement.getStyleNameAttribute());
		}
	}
	/*
	 * Insert <code>textSpan</code> into the from index of <code>pNode<code>.
	 */
	private void insertSpan(OdfTextSpan textSpanint fromIndexNode pNode) {
		if (fromIndex < 0) {
			fromIndex = 0;
		}
		if (fromIndex == 0 && ) {
			return;
		}
		int nodeLength = 0;
		Node node = pNode.getFirstChild();
		while (node != null) {
			if (fromIndex <= 0 && ) {
				return;
			}
			if (node.getNodeType() == .) {
				nodeLength = node.getNodeValue().length();
				if ((fromIndex != 0) && (nodeLength < fromIndex)) {
					fromIndex -= nodeLength;
else {
					// insert result after node, and insert an new text node
					// after the result node
					String value = node.getNodeValue();
					StringBuffer buffer = new StringBuffer();
					buffer.append(value.substring(0, fromIndex));
					// insert the text span in appropriate position
					node.setNodeValue(buffer.toString());
					Node nextNode = node.getNextSibling();
					Node parNode = node.getParentNode();
					Node newNode = node.cloneNode(true);
					newNode.setNodeValue(value.substring(fromIndexvalue.length()));
					if (nextNode != null) {
						parNode.insertBefore(textSpannextNode);
						parNode.insertBefore(newNodenextNode);
else {
						parNode.appendChild(textSpan);
						parNode.appendChild(newNode);
					}
					 = true;
					return;
				}
else if (node.getNodeType() == .) {
				// text:s
				if (node.getLocalName().equals("s")) {
					try {
						nodeLength = Integer.parseInt(((Elementnode).getAttributeNS(.
								.getUri(), "c"));
catch (Exception e) {
						nodeLength = 1;
					}
					fromIndex -= nodeLength;
else if (node.getLocalName().equals("line-break")) {
					nodeLength = 1;
					fromIndex--;
else if (node.getLocalName().equals("tab")) {
					nodeLength = 1;
					fromIndex--;
else {
					nodeLength = TextExtractor.getText((OdfElementnode).length();
					insertSpan(textSpanfromIndexnode);
					fromIndex -= nodeLength;
				}
			}
			node = node.getNextSibling();
		}
	}
	/*
	 * The <code>textSpan</code> must be the child element of
	 * <code>parentNode</code> this method is used to keep the style of text
	 * span when it has been insert into the <code>parentNode</code> if we don't
	 * deal with the style, the inserted span will also have the style of
	 * <code>parentNode</code>.
	 */
	private void adjustStyle(Node parentNodeOdfTextSpan textSpanMap<OdfStylePropertyStringstyleMap) {
		if (parentNode instanceof OdfStylableElement) {
			OdfStylableElement pStyleNode = (OdfStylableElementparentNode;
			if (styleMap == null) {
				styleMap = getTextStylePropertiesDeep(pStyleNode);
			}
			Node node = parentNode.getFirstChild();
			while (node != null) {
				if (node.getNodeType() == .) {
					if (node.getTextContent().length() > 0) {
						Node nextNode = node.getNextSibling();
						span.appendChild(node);
						if (nextNode != null) {
							parentNode.insertBefore(spannextNode);
else {
							parentNode.appendChild(span);
						}
						node = span;
					}
else if ((node instanceof OdfStylableElement)) {
					if (!node.equals(textSpan)) {
						if (styles == null) {
							styles = styles1;
else if (styles1 != null) {
							styles.putAll(styles1);
						}
						int comp = node.compareDocumentPosition(textSpan);
						// if node contains textSpan, then recurse the node
						if ((comp & .) > 0) {
							adjustStyle(nodetextSpanstyles);
else {
						}
					}
				}
				node = node.getNextSibling();
			}
			// change the parentNode to default style
			// here we don't know the default style name, so here just
			// remove the text:style-name attribute
			pStyleNode.removeAttributeNS(..getUri(), "style-name");
		}
	}
	/*
	 * Delete the <code>pNode<code> from the <code>fromIndex</code> text, and
	 * delete <code>leftLength</code> text.
	 */
	private void delete(int fromIndexint leftLengthNode pNode) {
		if ((fromIndex == 0) && (leftLength == 0)) {
			return;
		}
		int nodeLength = 0;
		Node node = pNode.getFirstChild();
		while (node != null) {
			if ((fromIndex == 0) && (leftLength == 0)) {
				return;
			}
			if (node.getNodeType() == .) {
				nodeLength = node.getNodeValue().length();
else if (node.getNodeType() == .) {
				// text:s
				if (node.getLocalName().equals("s")) {
					try {
						nodeLength = Integer.parseInt(((Elementnode).getAttributeNS(.
								.getUri(), "c"));
catch (Exception e) {
						nodeLength = 1;
					}
else if (node.getLocalName().equals("line-break")) {
					nodeLength = 1;
else if (node.getLocalName().equals("tab")) {
					nodeLength = 1;
else {
					nodeLength = TextExtractor.getText((OdfElementnode).length();
				}
			}
			if (nodeLength <= fromIndex) {
				fromIndex -= nodeLength;
else {
				// the start index is in this node
				if (node.getNodeType() == .) {
					String value = node.getNodeValue();
					StringBuffer buffer = new StringBuffer();
					buffer.append(value.substring(0, fromIndex));
					int endLength = fromIndex + leftLength;
					int nextLength = value.length() - endLength;
					fromIndex = 0;
					if (nextLength >= 0) {
						// delete the result
						buffer.append(value.substring(endLengthvalue.length()));
						leftLength = 0;
else {
						leftLength = endLength - value.length();
					}
					node.setNodeValue(buffer.toString());
else if (node.getNodeType() == .) {
					// if text:s?????????
					// text:s
					if (node.getLocalName().equals("s")) {
						// delete space
						((TextSElementnode).setTextCAttribute(new Integer(nodeLength - fromIndex));
						leftLength = leftLength - (nodeLength - fromIndex);
						fromIndex = 0;
else if (node.getLocalName().equals("line-break") || node.getLocalName().equals("tab")) {
						fromIndex = 0;
						leftLength--;
else {
						delete(fromIndexleftLengthnode);
						int length = (fromIndex + leftLength) - nodeLength;
						leftLength = length > 0 ? length : 0;
						fromIndex = 0;
					}
				}
			}
			node = node.getNextSibling();
		}
	}
	/*
	 * Add href for a range text of <code>pNode<code> from the
	 * <code>fromIndex</code> text, and the href will cover
	 * <code>leftLength</code> text.
	 */
	private void addHref(int fromIndexint leftLengthNode pNodeString href) {
		if ((fromIndex == 0) && (leftLength == 0)) {
			return;
		}
		int nodeLength = 0;
		Node node = pNode.getFirstChild();
		while (node != null) {
			if ((fromIndex == 0) && (leftLength == 0)) {
				return;
			}
			if (node.getNodeType() == .) {
				nodeLength = node.getNodeValue().length();
else if (node.getNodeType() == .) {
				// text:s
				if (node.getLocalName().equals("s")) {
					try {
						nodeLength = Integer.parseInt(((Elementnode).getAttributeNS(.
								.getUri(), "c"));
catch (Exception e) {
						nodeLength = 1;
					}
else if (node.getLocalName().equals("line-break")) {
					nodeLength = 1;
else if (node.getLocalName().equals("tab")) {
					nodeLength = 1;
else {
					nodeLength = TextExtractor.getText((OdfElementnode).length();
				}
			}
			if (nodeLength <= fromIndex) {
				fromIndex -= nodeLength;
else {
				// the start index is in this node
				if (node.getNodeType() == .) {
					String value = node.getNodeValue();
					node.setNodeValue(value.substring(0, fromIndex));
					int endLength = fromIndex + leftLength;
					int nextLength = value.length() - endLength;
					Node nextNode = node.getNextSibling();
					Node parNode = node.getParentNode();
					// init text:a
					Node newNode = null;
					if (nextLength >= 0) {
						textLink.setTextContent(value.substring(fromIndexendLength));
						newNode = node.cloneNode(true);
						newNode.setNodeValue(value.substring(endLengthvalue.length()));
						leftLength = 0;
else {
						textLink.setTextContent(value.substring(fromIndexvalue.length()));
						leftLength = endLength - value.length();
					}
					textLink.setXlinkTypeAttribute("simple");
					textLink.setXlinkHrefAttribute(href);
					if (nextNode != null) {
						parNode.insertBefore(textLinknextNode);
						if (newNode != null) {
							parNode.insertBefore(newNodenextNode);
						}
else {
						parNode.appendChild(textLink);
						if (newNode != null) {
							parNode.appendChild(newNode);
						}
					}
					fromIndex = 0;
					if (nextNode != null) {
						node = nextNode;
else {
						node = textLink;
					}
else if (node.getNodeType() == .) {
					// if text:s?????????
					// text:s
					if (node.getLocalName().equals("s")) {
						// delete space
						((TextSElementnode).setTextCAttribute(new Integer(nodeLength - fromIndex));
						leftLength = leftLength - (nodeLength - fromIndex);
						fromIndex = 0;
else if (node.getLocalName().equals("line-break") || node.getLocalName().equals("tab")) {
						fromIndex = 0;
						leftLength--;
else {
						addHref(fromIndexleftLengthnodehref);
						int length = (fromIndex + leftLength) - nodeLength;
						leftLength = length > 0 ? length : 0;
						fromIndex = 0;
					}
				}
			}
			node = node.getNextSibling();
		}
	}
	/*
	 * Get a map containing text properties of the specified styleable
	 * <code>element</code>.
	 * 
	 * @return a map of text properties.
	 */
		String styleName = element.getStyleName();
		OdfStyleBase styleElement = element.getAutomaticStyles().getStyle(styleNameelement.getStyleFamily());
		if (styleElement == null) {
			styleElement = element.getDocumentStyle();
		}
		if (styleElement != null) {
			// check if it is the style:defaut-style
				styleElement = ((Document) ((OdfFileDomstyleElement.getOwnerDocument()).getDocument())
			}
			if (family != null) {
				for (OdfStyleProperty property : family.getProperties()) {
					if (styleElement.hasProperty(property)) {
						result.put(propertystyleElement.getProperty(property));
					}
				}
			}
			return result;
		}
		return null;
	}
	/*
	 * Get a map containing text properties of the specified styleable
	 * <code>element</code>. The map will also include any properties set by
	 * parent styles.
	 * 
	 * @return a map of text properties.
	 */
		String styleName = element.getStyleName();
		OdfStyleBase styleElement = element.getAutomaticStyles().getStyle(styleNameelement.getStyleFamily());
		if (styleElement == null) {
			styleElement = element.getDocumentStyle();
		}
		while (styleElement != null) {
			// check if it is the style:defaut-style
				styleElement = ((Document) ((OdfFileDomstyleElement.getOwnerDocument()).getDocument())
			}
			if (family != null) {
				for (OdfStyleProperty property : family.getProperties()) {
					if (styleElement.hasProperty(property)) {
						result.put(propertystyleElement.getProperty(property));
					}
				}
			}
			styleElement = styleElement.getParentStyle();
		}
		return result;
	}
	/*
	 * Validate if the <code>Selection</code> is still available.
	 * 
	 * @return true if the selection is available; false if the
	 * <code>Selection</code> is not available.
	 */
	private boolean validate() {
		if (getContainerElement() == null) {
			return false;
		}
		if (container == null) {
			return false;
		}
		String content = TextExtractor.getText(container);
			return true;
else {
			return false;
		}
	}
	/*
	 * Append specified style for a range text of <code>pNode<code> from
	 * <code>fromIndex</code> and cover <code>leftLength</code>
	 */
	private void appendStyle(int fromIndexint leftLengthNode pNodeOdfStyleBase style) {
		if ((fromIndex == 0) && (leftLength == 0)) {
			return;
		}
		int nodeLength = 0;
		Node node = pNode.getFirstChild();
		while (node != null) {
			if ((fromIndex == 0) && (leftLength == 0)) {
				return;
			}
			if (node.getNodeType() == .) {
				nodeLength = node.getNodeValue().length();
else if (node.getNodeType() == .) {
				// text:s
				if (node.getLocalName().equals("s")) {
					try {
						nodeLength = Integer.parseInt(((Elementnode).getAttributeNS(.
								.getUri(), "c"));
catch (Exception e) {
						nodeLength = 1;
					}
else if (node.getLocalName().equals("line-break")) {
					nodeLength = 1;
else if (node.getLocalName().equals("tab")) {
					nodeLength = 1;
else {
					nodeLength = TextExtractor.getText((OdfElementnode).length();
				}
			}
			if (nodeLength <= fromIndex) {
				fromIndex -= nodeLength;
else {
				// the start index is in this node
				if (node.getNodeType() == .) {
					String value = node.getNodeValue();
					node.setNodeValue(value.substring(0, fromIndex));
					int endLength = fromIndex + leftLength;
					int nextLength = value.length() - endLength;
					Node nextNode = node.getNextSibling();
					Node parNode = node.getParentNode();
					// init text:a
					OdfTextSpan textSpan = new OdfTextSpan((OdfFileDomnode.getOwnerDocument());
					Node newNode = null;
					if (nextLength >= 0) {
						textSpan.setTextContent(value.substring(fromIndexendLength));
						newNode = node.cloneNode(true);
						newNode.setNodeValue(value.substring(endLengthvalue.length()));
						leftLength = 0;
else {
						textSpan.setTextContent(value.substring(fromIndexvalue.length()));
						leftLength = endLength - value.length();
					}
					if (nextNode != null) {
						parNode.insertBefore(textSpannextNode);
						if (newNode != null) {
							parNode.insertBefore(newNodenextNode);
						}
else {
						parNode.appendChild(textSpan);
						if (newNode != null) {
							parNode.appendChild(newNode);
						}
					}
					fromIndex = 0;
					if (nextNode != null) {
						node = nextNode;
else {
						node = textSpan;
					}
else if (node.getNodeType() == .) {
					// if text:s?????????
					// text:s
					if (node.getLocalName().equals("s")) {
						// delete space
						((TextSElementnode).setTextCAttribute(new Integer(nodeLength - fromIndex));
						leftLength = leftLength - (nodeLength - fromIndex);
						fromIndex = 0;
else if (node.getLocalName().equals("line-break") || node.getLocalName().equals("tab")) {
						fromIndex = 0;
						leftLength--;
else {
						appendStyle(fromIndexleftLengthnodestyle);
						int length = (fromIndex + leftLength) - nodeLength;
						leftLength = length > 0 ? length : 0;
						fromIndex = 0;
					}
				}
			}
			node = node.getNextSibling();
		}
	}
New to GrepCode? Check out our FAQ X