#ODFTOOLKIT-435# TextSelection.createSpanElement() added invalid line-breaks. Patched by Georg Fuechsle

git-svn-id: https://svn.apache.org/repos/asf/incubator/odf/trunk@1792545 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/simple/src/main/java/org/odftoolkit/simple/common/navigation/TextSelection.java b/simple/src/main/java/org/odftoolkit/simple/common/navigation/TextSelection.java
index 9a9e558..a6275c3 100644
--- a/simple/src/main/java/org/odftoolkit/simple/common/navigation/TextSelection.java
+++ b/simple/src/main/java/org/odftoolkit/simple/common/navigation/TextSelection.java
@@ -1,1299 +1,1301 @@
-/* 
-Licensed to the Apache Software Foundation (ASF) under one
-or more contributor license agreements.  See the NOTICE file
-distributed with this work for additional information
-regarding copyright ownership.  The ASF licenses this file
-to you 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.odftoolkit.simple.common.navigation;
-
-import java.net.URI;
-import java.net.URL;
-import java.text.SimpleDateFormat;
-import java.util.Date;
-import java.util.Map;
-import java.util.TreeMap;
-
-import org.odftoolkit.odfdom.dom.OdfContentDom;
-import org.odftoolkit.odfdom.dom.OdfDocumentNamespace;
-import org.odftoolkit.odfdom.dom.OdfStylesDom;
-import org.odftoolkit.odfdom.dom.element.OdfStylableElement;
-import org.odftoolkit.odfdom.dom.element.OdfStyleBase;
-import org.odftoolkit.odfdom.dom.element.dc.DcCreatorElement;
-import org.odftoolkit.odfdom.dom.element.dc.DcDateElement;
-import org.odftoolkit.odfdom.dom.element.office.OfficeAnnotationElement;
-import org.odftoolkit.odfdom.dom.element.style.StyleParagraphPropertiesElement;
-import org.odftoolkit.odfdom.dom.element.style.StyleTextPropertiesElement;
-import org.odftoolkit.odfdom.dom.element.text.TextAElement;
-import org.odftoolkit.odfdom.dom.element.text.TextConditionalTextElement;
-import org.odftoolkit.odfdom.dom.element.text.TextPElement;
-import org.odftoolkit.odfdom.dom.element.text.TextParagraphElementBase;
-import org.odftoolkit.odfdom.dom.element.text.TextSElement;
-import org.odftoolkit.odfdom.dom.element.text.TextSpanElement;
-import org.odftoolkit.odfdom.dom.element.text.TextUserFieldDeclElement;
-import org.odftoolkit.odfdom.dom.element.text.TextVariableDeclElement;
-import org.odftoolkit.odfdom.dom.style.OdfStyleFamily;
-import org.odftoolkit.odfdom.dom.style.props.OdfStylePropertiesSet;
-import org.odftoolkit.odfdom.dom.style.props.OdfStyleProperty;
-import org.odftoolkit.odfdom.incubator.doc.office.OdfOfficeAutomaticStyles;
-import org.odftoolkit.odfdom.incubator.doc.style.OdfStyle;
-import org.odftoolkit.odfdom.incubator.doc.text.OdfTextHeading;
-import org.odftoolkit.odfdom.incubator.doc.text.OdfTextParagraph;
-import org.odftoolkit.odfdom.incubator.doc.text.OdfTextSpan;
-import org.odftoolkit.odfdom.pkg.OdfElement;
-import org.odftoolkit.odfdom.pkg.OdfFileDom;
-import org.odftoolkit.simple.Document;
-import org.odftoolkit.simple.TextDocument;
-import org.odftoolkit.simple.common.TextExtractor;
-import org.odftoolkit.simple.common.field.ConditionField;
-import org.odftoolkit.simple.common.field.Field;
-import org.odftoolkit.simple.common.field.Field.FieldType;
-import org.odftoolkit.simple.common.field.Fields;
-import org.odftoolkit.simple.common.field.VariableField;
-import org.odftoolkit.simple.draw.Image;
-import org.odftoolkit.simple.table.Table;
-import org.odftoolkit.simple.text.Paragraph;
-import org.w3c.dom.Element;
-import org.w3c.dom.NamedNodeMap;
-import org.w3c.dom.Node;
-import org.w3c.dom.NodeList;
-
-/**
- * <code>TextSelection</code> describes a sub element in a paragraph element or
- * a heading element. It is recognized by the container element, which type
- * should be {@link org.odftoolkit.odfdom.incubator.doc.text.OdfTextParagraph
- * OdfTextParagraph} or
- * {@link org.odftoolkit.odfdom.incubator.doc.text.OdfTextHeading
- * OdfTextHeading}, the start index of text content in container element and the
- * text content of this <code>Selection</code>.
- */
-public class TextSelection extends Selection {
-
-	String mMatchedText;
-	private OdfTextParagraph mParagraph;
-	private OdfTextHeading mHeading;
-	private int mIndexInContainer;
-	private boolean mIsInserted;
-	private boolean isSelectionReplaced = false;
-
-	/**
-	 * Constructor of <code>TextSelection</code>.
-	 * 
-	 * @param text
-	 *            the text content of this <code>TextSelection</code>
-	 * @param containerElement
-	 *            the paragraph element or heading element that contains this
-	 *            <code>TextSelection</code>
-	 * @param index
-	 *            the start index of the text content in container element
-	 * 
-	 */
-	TextSelection(Navigation search, String text, OdfElement containerElement,
-			int index) {
-		this.search = search;
-		mMatchedText = text;
-		if (containerElement instanceof OdfTextParagraph) {
-			mParagraph = (OdfTextParagraph) containerElement;
-		} else if (containerElement instanceof OdfTextHeading) {
-			mHeading = (OdfTextHeading) containerElement;
-		}
-		mIndexInContainer = index;
-	}
-
-	public TextNavigation getTextNavigation() {
-		if (search instanceof TextNavigation) {
-			return (TextNavigation) search;
-		}
-		return null;
-	}
-	/**
-	 * Create a new <code>TextSelection</code>.
-	 * 
-	 * @param text
-	 *            the text content of this <code>TextSelection</code>
-	 * @param containerElement
-	 *            the paragraph element or heading element that contains this
-	 *            <code>TextSelection</code>
-	 * @param index
-	 *            the start index of the text content in container element
-	 * 
-	 * @since 0.5.5
-	 */
-	public static TextSelection newTextSelection(Navigation search,
-			String text, OdfElement containerElement, int index) {
-		TextSelection selection = new TextSelection(search, text,
-				containerElement, index);
-		Selection.SelectionManager.registerItem(selection);
-		return selection;
-	}
-
-	/**
-	 * Get the paragraph element or heading element that contains this
-	 * <code>TextSelection</code>.
-	 * 
-	 * @return OdfElement the container element
-	 */
-	@Override
-	public OdfElement getElement() {
-		return getContainerElement();
-	}
-
-	/**
-	 * Get the paragraph element or heading element that contains this text.
-	 * 
-	 * @return OdfElement
-	 */
-	public OdfElement getContainerElement() {
-		if (mParagraph != null) {
-			return mParagraph;
-		} else {
-			return mHeading;
-		}
-	}
-
-	/**
-	 * Get the start index of the text content of its container element.
-	 * 
-	 * @return index the start index of the text content of its container
-	 *         element
-	 */
-	@Override
-	public int getIndex() {
-		return mIndexInContainer;
-	}
-
-	/**
-	 * Get the text content of this <code>TextSelection</code>.
-	 * 
-	 * @return text the text content
-	 */
-	public String getText() {
-		return mMatchedText;
-	}
-
-	/**
-	 * 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.
-	 */
-	@Override
-	public void cut() throws InvalidNavigationException {
-		if (validate() == false) {
-			throw new InvalidNavigationException("No matched string at this position");
-		}
-		OdfElement container = getContainerElement();
-		delete(mIndexInContainer, mMatchedText.length(), container);
-		SelectionManager.refreshAfterCut(this);
-		mMatchedText = "";
-	}
-
-	/**
-	 * Apply a style to the selection so that the text style of this selection
-	 * will append the specified style.
-	 * 
-	 * @param style
-	 *            the style can be from the current document or user defined
-	 * @throws InvalidNavigationException
-	 *             if the selection is unavailable.
-	 */
-	public void applyStyle(OdfStyleBase style) throws 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 = mIndexInContainer;
-
-		appendStyle(index, leftLength, parentElement, style);
-
-	}
-
-	/**
-	 * Replace the text content of selection with a new string.
-	 * 
-	 * @param newText
-	 *            the replace text String
-	 * @throws InvalidNavigationException
-	 *             if the selection is unavailable.
-	 */
-	public void replaceWith(String newText) throws InvalidNavigationException {
-		if (validate() == false) {
-			throw new InvalidNavigationException("No matched string at this position");
-		}
-		OdfElement parentElement = getContainerElement();
-		int leftLength = getText().length();
-		int index = mIndexInContainer;
-		delete(index, leftLength, parentElement);
-		OdfTextSpan textSpan = new OdfTextSpan((OdfFileDom) parentElement.getOwnerDocument());
-		textSpan.addContentWhitespace(newText);
-		mIsInserted = false;
-		insertOdfElement(textSpan, index, parentElement);
-		// optimize the parent element
-		optimize(parentElement);
-		int offset = newText.length() - leftLength;
-		SelectionManager.refresh(getContainerElement(), offset, index + getText().length());
-		mMatchedText = newText;
-	}
-	
-	/**
-	 * Replace the text content of selection with a new Table.
-	 * 
-	 * @param newTable
-	 *            the replace Table
-	 * @return 
-	 * @throws InvalidNavigationException
-	 *             if the selection is unavailable.
-	 * @return the new Table in the TextDocument
-	 */
-	public Table replaceWith(Table newTable) throws InvalidNavigationException {
-		if (validate() == false) {
-			throw new InvalidNavigationException("No matched string at this position");
-		}
-		TableSelection nextTableSelection=new TableSelection(this);
-		return nextTableSelection.replaceWithTable(newTable);
-	}
-	/**
-	 * Replace the text content of selection with a new Image.
-	 * 
-	 * @param newImage
-	 *            the replace Image
-	 * @return 
-	 * @throws InvalidNavigationException
-	 *             if the selection is unavailable.
-	 * @return the new Image in the TextDocument,the image name is set to "replace" + System.currentTimeMillis(), please update the name to others by yourself.
-	 */
-	public Image replaceWith(Image newImage) throws InvalidNavigationException {
-		if (validate() == false) {
-			throw new InvalidNavigationException("No matched string at this position");
-		}
-		ImageSelection nextImageSelection=new ImageSelection(this);
-		return nextImageSelection.replaceWithImage(newImage);
-	}
-	/**
-	 * Replace the text content of selection with a new Image.
-	 * 
-	 * @param imageUri
-	 *            the replace Image URI
-	 * @throws InvalidNavigationException
-	 *             if the selection is unavailable.
-	 * @return the new Image in the TextDocument,the image name is set to "replace" + System.currentTimeMillis(), please update the name to others by yourself.
-	 */
-	public Image replaceWith(URI imageUri) throws InvalidNavigationException {
-		if (validate() == false) {
-			throw new InvalidNavigationException("No matched string at this position");
-		}
-		ImageSelection nextImageSelection=new ImageSelection(this);
-		return nextImageSelection.replaceWithImage(imageUri);
-	}
-	/**
-	 * Replace the content with a Field
-	 * 
-	 * @param orgField
-	 *            the reference Field to replace.
-     * @throws InvalidNavigationException
-	 *             if the selection is unavailable.   
-	 * @return the created field.         
-	 */
-	public Field replaceWith(Field orgField) throws InvalidNavigationException {
-		if (validate() == false) {
-			throw new InvalidNavigationException("No matched string at this position");
-		}
-		Field newfield=null;
-		OdfElement parentElement = getContainerElement();
-		Paragraph orgparagraph = Paragraph.getInstanceof((TextParagraphElementBase) parentElement);
-		TextDocument document = (TextDocument) orgparagraph.getOwnerDocument();
-		
-		FieldSelection nextFieldSelection=new FieldSelection(this);
-		FieldType fieldType = orgField.getFieldType();
-	
-		switch (fieldType) {
-		case DATE_FIELD:
-			newfield=nextFieldSelection.replaceWithSimpleField(fieldType);
-			break;
-		case FIXED_DATE_FIELD:
-			newfield=nextFieldSelection.replaceWithSimpleField(fieldType);
-			break;
-		case TIME_FIELD:
-			newfield=nextFieldSelection.replaceWithSimpleField(fieldType);
-			break;
-		case FIXED_TIME_FIELD:
-			newfield=nextFieldSelection.replaceWithSimpleField(fieldType);
-			break;
-		case PREVIOUS_PAGE_NUMBER_FIELD:
-			newfield=nextFieldSelection.replaceWithSimpleField(fieldType);
-			break;
-		case CURRENT_PAGE_NUMBER_FIELD:
-			newfield=nextFieldSelection.replaceWithSimpleField(fieldType);
-			break;
-		case NEXT_PAGE_NUMBER_FIELD:
-			newfield=nextFieldSelection.replaceWithSimpleField(fieldType);
-			break;
-		case PAGE_COUNT_FIELD:
-			newfield=nextFieldSelection.replaceWithSimpleField(fieldType);
-			break;
-		case TITLE_FIELD:
-			newfield=nextFieldSelection.replaceWithSimpleField(fieldType);
-			break;
-		case SUBJECT_FIELD:
-			newfield=nextFieldSelection.replaceWithSimpleField(fieldType);
-			break;
-		case AUTHOR_NAME_FIELD:
-			newfield=nextFieldSelection.replaceWithSimpleField(fieldType);
-			break;
-		case AUTHOR_INITIALS_FIELD:
-			newfield=nextFieldSelection.replaceWithSimpleField(fieldType);
-			break;
-		case CHAPTER_FIELD:
-			newfield=nextFieldSelection.replaceWithSimpleField(fieldType);
-			break;
-		case SIMPLE_VARIABLE_FIELD:
-			VariableField SimpleVariableField = (VariableField)orgField;
-			String simplefieldname = SimpleVariableField.getVariableName();
-			VariableField simplefield=Fields.createSimpleVariableField(document, simplefieldname);
-			nextFieldSelection.replaceWithVariableField(simplefield);
-			newfield=simplefield;
-			break;
-		case USER_VARIABLE_FIELD:
-			VariableField userVariableField = (VariableField)orgField;
-			TextUserFieldDeclElement textUserFieldDeclElement =(TextUserFieldDeclElement) userVariableField.getOdfElement();
-			String fieldname = userVariableField.getVariableName();
-			String value=textUserFieldDeclElement.getOfficeStringValueAttribute();
-			VariableField variableField=Fields.createUserVariableField(document, fieldname,value);
-			nextFieldSelection.replaceWithVariableField(variableField);
-			newfield=variableField;
-			break;
-		case CONDITION_FIELD:
-			ConditionField conditionField = (ConditionField)orgField;
-			TextConditionalTextElement textConditionalTextElement =(TextConditionalTextElement) conditionField.getOdfElement();
-			String StringValueIfFalse=textConditionalTextElement.getTextStringValueIfFalseAttribute();
-			String StringValueIfTrue=textConditionalTextElement.getTextStringValueIfTrueAttribute();
-			String StringCondition=textConditionalTextElement.getTextConditionAttribute();
-			boolean CurrentValue=textConditionalTextElement.getTextCurrentValueAttribute();
-			ConditionField newdConditionField = nextFieldSelection.replaceWithConditionField(StringCondition, StringValueIfTrue, StringValueIfFalse);
-			TextConditionalTextElement newTextConditionalTextElement=(TextConditionalTextElement)newdConditionField.getOdfElement();
-			newTextConditionalTextElement.setTextCurrentValueAttribute(CurrentValue);
-			newfield=newdConditionField;
-			break;
-		case HIDDEN_TEXT_FIELD:
-			ConditionField conditionFieldHIDDEN = (ConditionField)orgField;
-			TextConditionalTextElement textConditionalTextElementHIDDEN =(TextConditionalTextElement) conditionFieldHIDDEN.getOdfElement();
-			String StringValueIfFalseHIDDEN=textConditionalTextElementHIDDEN.getTextStringValueIfFalseAttribute();
-			String StringConditionHIDDEN=textConditionalTextElementHIDDEN.getTextConditionAttribute();
-			boolean CurrentValueHIDDEN=textConditionalTextElementHIDDEN.getTextCurrentValueAttribute();
-			ConditionField newdConditionFieldHIDDEN = nextFieldSelection.replaceWithHiddenTextField(StringConditionHIDDEN, StringValueIfFalseHIDDEN);
-			TextConditionalTextElement newTextConditionalTextElementHIDDEN=(TextConditionalTextElement)newdConditionFieldHIDDEN.getOdfElement();
-			newTextConditionalTextElementHIDDEN.setTextCurrentValueAttribute(CurrentValueHIDDEN);
-			newfield=newdConditionFieldHIDDEN;
-			break;
-		case REFERENCE_FIELD:
-		default: throw new IllegalArgumentException("Simple Java API for ODF doesn't support this type now.");
-		}
-		return newfield;
-	}
-	
-	/**
-	 * Replace the content with a paragraph, the paragraph can be in the same TextDocument or in a different Document.
-	 * 
-	 * @param newParagraph
-	 *            the reference paragraph to replace.
-     * @throws InvalidNavigationException
-	 *             if the selection is unavailable.   
-	 * @return the replaced Paragraph.             
-	 */
-	public Paragraph replaceWith(Paragraph newParagraph) throws InvalidNavigationException {
-		if (validate() == false) {
-			throw new InvalidNavigationException("No matched string at this position");
-		}
-		ParagraphSelection nextParagraphSelection=new ParagraphSelection(this);
-		return nextParagraphSelection.replaceWithParagraph(newParagraph);
-	}
-	/**
-	 * Replace the content with a TextDocument with Styles.
-	 * Note: You need cache the TextNavigation.nextSelection item because after
-	 * you replace currtenTextSelection with TextDocument, TextNavigation.nextSelection will search from the inserted Content, 
-	 * it will make you into a loop if the Search keyword also can be found in the new inserted Content.
-	 * </p>
-	 * The right way to use this replaceWithTextDocument(TextDocument textDocument) method should like this: 
-	 * <Code>
-	 * <p>	search = new TextNavigation("SIMPLE", doc);    </p>
-	 * <p>	TextSelection currtenTextSelection,nextTextSelection=null;</p>
-	 * <p>		while (search.hasNext()) {</p>
-	 * <p>			if(nextTextSelection!=null){</p>
-	 * <p>				currtenTextSelection=nextTextSelection;</p>
-	 * <p>			}else {</p>
-	 * <p>			 	currtenTextSelection = (TextSelection) search.nextSelection();</p>
-	 * <p>			}</p>
-	 * <p>			nextTextSelection = (TextSelection) search.nextSelection();</p>
-	 * <p>			if(currtenTextSelection!=null){</p>
-	 * <p>				try {</p>
-	 * <p>					nextTextSelection.replaceWithTextDocument(sourcedoc);</p>
-	 * <p>				} catch (Exception e) {</p>
-	 * <p>					e.printStackTrace();</p>
-	 * <p>				}</p>
-	 * <p>			}</p>
-	 * <p>		}</p>
-	 * <p>		if(nextTextSelection!=null){</p>
-	 * <p>			try {</p>
-	 * <p>				nextTextSelection.replaceWithTextDocument(sourcedoc);</p>
-	 * <p>			} catch (Exception e) {</p>
-	 * <p>				e.printStackTrace();</p>
-	 * <p>			}</p>
-	 * <p>		}</p>
-	 * </Code>
-	 * 
-	 * @param newTextDocument
-	 *            the reference TextDocument to replace.
-	 * @throws InvalidNavigationException 
-	 */
-	public void replaceWith(TextDocument newTextDocument) throws InvalidNavigationException{
-		if (validate() == false) {
-			throw new InvalidNavigationException("No matched string at this position");
-		}
-		TextDocumentSelection nextTextDocumentSelection=new TextDocumentSelection(this);
-		try {
-			nextTextDocumentSelection.replaceWithTextDocument(newTextDocument);
-		} catch (Exception e) {
-			e.printStackTrace();
-		}
-	}
-	/**
-	 * Create a span element for this text selection.
-	 * 
-	 * @return the created text span element for this selection
-	 * @throws InvalidNavigationException
-	 *             if the selection is unavailable.
-	 * @since 0.5.5
-	 */
-	public TextSpanElement createSpanElement() throws InvalidNavigationException {
-		if (validate() == false) {
-			throw new InvalidNavigationException("No matched string at this position");
-		}
-		OdfElement parentElement = getContainerElement();
-		int leftLength = getText().length();
-		int index = mIndexInContainer;
-		delete(index, leftLength, parentElement);
-		OdfTextSpan textSpan = new OdfTextSpan((OdfFileDom) parentElement.getOwnerDocument());
-		textSpan.addContentWhitespace(getText());
-		mIsInserted = false;
-		insertOdfElement(textSpan, index, parentElement);
-		// optimize the parent element
-		optimize(parentElement);
-
-		return textSpan;
-	}
-
-	/**
-	 * Paste this selection just before a specific selection.
-	 * 
-	 * @param positionItem
-	 *            a selection that is used to point out the position
-	 * @throws InvalidNavigationException
-	 *             if the selection is unavailable.
-	 */
-	@Override
-	public void pasteAtFrontOf(Selection positionItem) throws 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 = ((TextSelection) positionItem).getIndex();
-			newElement = ((TextSelection) positionItem).getContainerElement();
-		}
-
-		OdfTextSpan textSpan = getSpan((OdfFileDom) positionItem.getElement().getOwnerDocument());
-		mIsInserted = false;
-		insertOdfElement(textSpan, indexOfNew, newElement);
-		adjustStyle(newElement, textSpan, null);
-		SelectionManager.refreshAfterPasteAtFrontOf(this, positionItem);
-	}
-
-	/**
-	 * Paste this selection just after a specific selection.
-	 * 
-	 * @param positionItem
-	 *            a selection that is used to point out the position
-	 * @throws InvalidNavigationException
-	 *             if the selection is unavailable.
-	 */
-	@Override
-	public void pasteAtEndOf(Selection positionItem) throws 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 = ((TextSelection) positionItem).getIndex() + ((TextSelection) positionItem).getText().length();
-			newElement = ((TextSelection) positionItem).getContainerElement();
-		}
-		OdfTextSpan textSpan = getSpan((OdfFileDom) positionItem.getElement().getOwnerDocument());
-		mIsInserted = false;
-		insertOdfElement(textSpan, indexOfNew, newElement);
-		adjustStyle(newElement, textSpan, null);
-		SelectionManager.refreshAfterPasteAtEndOf(this, positionItem);
-	}
-	public void setSelectionReplaced(boolean b) {
-		this.isSelectionReplaced = b;
-	}
-	public boolean isSelectionReplaced() {
-		return this.isSelectionReplaced;
-	}
-
-	/**
-	 * Add a hypertext reference to the selection.
-	 * 
-	 * @param url
-	 *            the URL of this hypertext reference
-	 * @throws InvalidNavigationException
-	 *             if the selection is unavailable.
-	 */
-	public void addHref(URL url) throws InvalidNavigationException {
-		if (validate() == false) {
-			throw new InvalidNavigationException("No matched string at this position");
-		}
-		OdfElement parentElement = getContainerElement();
-		int leftLength = getText().length();
-		int index = mIndexInContainer;
-		addHref(index, leftLength, parentElement, url.toString());
-	}
-	
-	/**
-	 * Add a comment to the selection.
-	 * 
-	 * @param content
-	 *            the content of this comment.
-	 * @param creator
-	 *            the creator of this comment, if <code>creator</code> is null,
-	 *            the value of <code>System.getProperty("user.name")</code> will
-	 *            be used.
-	 * @throws InvalidNavigationException
-	 *            if the selection is unavailable.
-	 * @since 0.6.5
-	 */
-	public void addComment(String content, String creator) throws InvalidNavigationException {
-		if (validate() == false) {
-			throw new InvalidNavigationException("No matched string at this position");
-		}
-		// create annotation element
-		OdfElement parentElement = getContainerElement();
-		OdfFileDom dom = (OdfFileDom) parentElement.getOwnerDocument();
-		OfficeAnnotationElement annotationElement = dom.newOdfElement(OfficeAnnotationElement.class);
-		// set creator
-		DcCreatorElement dcCreatorElement = annotationElement.newDcCreatorElement();
-		if (creator == null) {
-			creator = System.getProperty("user.name");
-		}
-		dcCreatorElement.setTextContent(creator);
-		// set date
-		String dcDate = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss").format(new Date());
-		DcDateElement dcDateElement = annotationElement.newDcDateElement();
-		dcDateElement.setTextContent(dcDate);
-		TextPElement notePElement = annotationElement.newTextPElement();
-		TextSpanElement noteSpanElement = notePElement.newTextSpanElement();
-		// set comment style
-		OdfOfficeAutomaticStyles styles = null;
-		if (dom instanceof OdfContentDom) {
-			styles = ((OdfContentDom) dom).getAutomaticStyles();
-		} else if (dom instanceof OdfStylesDom) {
-			styles = ((OdfStylesDom) dom).getAutomaticStyles();
-		}
-		OdfStyle textStyle = styles.newStyle(OdfStyleFamily.Text);
-		StyleTextPropertiesElement styleTextPropertiesElement = textStyle.newStyleTextPropertiesElement(null);
-		styleTextPropertiesElement.setStyleFontNameAttribute("Tahoma");
-		styleTextPropertiesElement.setFoFontSizeAttribute("10pt");
-		styleTextPropertiesElement.setStyleFontNameAsianAttribute("Lucida Sans Unicode");
-		styleTextPropertiesElement.setStyleFontSizeAsianAttribute("12pt");
-		noteSpanElement.setStyleName(textStyle.getStyleNameAttribute());
-		// set comment content
-		noteSpanElement.setTextContent(content);
-		// insert comment to its position
-		insertOdfElement(annotationElement, mIndexInContainer, parentElement);
-		// three text length plus two '\r'
-		int offset = content.length() + 1 + dcDate.length() + 1 + creator.length();
-		SelectionManager.refresh(getContainerElement(), offset, getIndex());
-	}
-	
-	/**
-	 * 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.
-	 * 
-	 * @return a String representation of the value of this
-	 *         <code>TextSelection</code>
-	 */
-	@Override
-	public String toString() {
-		return "[" + mMatchedText + "] started from " + mIndexInContainer + " in paragraph:"
-				+ TextExtractor.getText(getContainerElement());
-	}
-
-	@Override
-	protected void refreshAfterFrontalDelete(Selection deleteItem) {
-		if (deleteItem instanceof TextSelection) {
-			mIndexInContainer -= ((TextSelection) deleteItem).getText().length();
-		}
-	}
-
-	@Override
-	protected void refreshAfterFrontalInsert(Selection pasteItem) {
-		if (pasteItem instanceof TextSelection) {
-			mIndexInContainer += ((TextSelection) pasteItem).getText().length();
-		}
-	}
-
-	@Override
-	protected void refresh(int offset) {
-		mIndexInContainer += offset;
-		if (mIndexInContainer < 0) {
-			mIndexInContainer = 0;
-		}
-	}
-
-	void cleanBreakProperty(Paragraph paragraph) {
-		TextNavigation search = this.getTextNavigation();
-		if (search == null)
-			throw new IllegalStateException("Navigation is null");
-		OdfStyleBase styleElement = paragraph.getStyleHandler()
-				.getStyleElementForRead();
-		String name = styleElement.getAttribute("style:name");
-		String newName = null;
-		OdfElement modifiedStyleElement = search
-				.getModifiedStyleElement(styleElement);
-		if (modifiedStyleElement == null) {
-			modifiedStyleElement = (OdfElement) styleElement.cloneNode(true);
-			search.addModifiedStyleElement(styleElement, modifiedStyleElement);
-			NodeList paragraphProperties = modifiedStyleElement
-					.getElementsByTagName("style:paragraph-properties");
-			if (paragraphProperties != null
-					&& paragraphProperties.getLength() > 0) {
-				StyleParagraphPropertiesElement property = (StyleParagraphPropertiesElement) paragraphProperties
-						.item(0);
-				property.removeAttribute("fo:break-before");
-				property.removeAttribute("fo:break-after");
-				property.removeAttribute("style:page-number");
-			}
-			modifiedStyleElement.removeAttribute("style:master-page-name");
-			newName = name + "-" + makeUniqueName();
-			NamedNodeMap attributes = modifiedStyleElement.getAttributes();
-			if (attributes != null) {
-				for (int i = 0; i < attributes.getLength(); i++) {
-					Node item = attributes.item(i);
-					String value = item.getNodeValue();
-					if (name.equals(value)) {
-						item.setNodeValue(newName);
-						break;
-					}
-				}
-			}
-			styleElement.getParentNode().appendChild(modifiedStyleElement);
-		} else {
-			newName = modifiedStyleElement.getAttribute("style:name");
-		}
-		NamedNodeMap attributes = paragraph.getOdfElement().getAttributes();
-		if (attributes != null) {
-			for (int i = 0; i < attributes.getLength(); i++) {
-				Node item = attributes.item(i);
-				String value = item.getNodeValue();
-				if (name.equals(value)) {
-					item.setNodeValue(newName);
-					break;
-				}
-			}
-		}
-		this.getTextNavigation().setHandlePageBreak(true);
-	}
-	String makeUniqueName() {
-		return String.format("p%06x", (int) (Math.random() * 0xffffff));
-	}
-	/*
-	 * 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 = (OdfElement) parentElement.cloneNode(true);
-			if (ownerDoc != parentElement.getOwnerDocument()) {
-				copyParentNode = (OdfElement) ownerDoc.adoptNode(copyParentNode);
-			}
-			OdfTextSpan textSpan = new OdfTextSpan(ownerDoc);
-			int sIndex = mIndexInContainer;
-			int eIndex = sIndex + mMatchedText.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() - eIndex, copyParentNode);
-			delete(0, sIndex, copyParentNode);
-			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) {
-				applyTextStyleProperties(getTextStylePropertiesDeep((OdfStylableElement) copyParentNode), textSpan);
-			}
-			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((OdfTextSpan) node).length() == 0) {
-					node.getParentNode().removeChild(node);
-				} 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<OdfStyleProperty, String> styleMap, OdfStylableElement toElement) {
-		if (styleMap != null) {
-			// preserve the style property of toElement if it is also exist in
-			// styleMap
-			OdfStyle resultStyleElement = toElement.getAutomaticStyles().newStyle(OdfStyleFamily.Text);
-			for (Map.Entry<OdfStyleProperty, String> entry : 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>odfElement</code>, span or annotation, into the from index of <code>pNode<code>.
-	 */
-	private void insertOdfElement(OdfElement odfElement, int fromIndex, Node pNode) {
-		if (fromIndex < 0) {
-			fromIndex = 0;
-		}
-		if (fromIndex == 0 && mIsInserted) {
-			return;
-		}
-		int nodeLength = 0;
-		Node node = pNode.getFirstChild();
-		while (node != null) {
-			if (fromIndex <= 0 && mIsInserted) {
-				return;
-			}
-			if (node.getNodeType() == Node.TEXT_NODE) {
-				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(fromIndex, value.length()));
-					if (nextNode != null) {
-						parNode.insertBefore(odfElement, nextNode);
-						parNode.insertBefore(newNode, nextNode);
-					} else {
-						parNode.appendChild(odfElement);
-						parNode.appendChild(newNode);
-					}
-					mIsInserted = true;
-					return;
-				}
-			} else if (node.getNodeType() == Node.ELEMENT_NODE) {
-				// text:s
-				if (node.getLocalName().equals("s")) {
-					try {
-						nodeLength = Integer.parseInt(((Element) node).getAttributeNS(OdfDocumentNamespace.TEXT
-								.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((OdfElement) node).length();
-					insertOdfElement(odfElement, fromIndex, node);
-					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 parentNode, OdfTextSpan textSpan, Map<OdfStyleProperty, String> styleMap) {
-		if (parentNode instanceof OdfStylableElement) {
-			OdfStylableElement pStyleNode = (OdfStylableElement) parentNode;
-			if (styleMap == null) {
-				styleMap = getTextStylePropertiesDeep(pStyleNode);
-			}
-			Node node = parentNode.getFirstChild();
-			while (node != null) {
-				if (node.getNodeType() == Node.TEXT_NODE) {
-					if (node.getTextContent().length() > 0) {
-						Node nextNode = node.getNextSibling();
-						OdfTextSpan span = new OdfTextSpan((OdfFileDom) node.getOwnerDocument());
-						span.appendChild(node);
-						if (nextNode != null) {
-							parentNode.insertBefore(span, nextNode);
-						} else {
-							parentNode.appendChild(span);
-						}
-						node = span;
-						applyTextStyleProperties(styleMap, (OdfStylableElement) node);
-					}
-				} else if ((node instanceof OdfStylableElement)) {
-					if (!node.equals(textSpan)) {
-						Map<OdfStyleProperty, String> styles = getTextStylePropertiesDeep(pStyleNode);
-						Map<OdfStyleProperty, String> styles1 = getTextStylePropertiesDeep((OdfStylableElement) node);
-						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 & Node.DOCUMENT_POSITION_CONTAINED_BY) > 0) {
-							adjustStyle(node, textSpan, styles);
-						} else {
-							applyTextStyleProperties(styles, (OdfStylableElement) node);
-						}
-					}
-				}
-				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(OdfDocumentNamespace.TEXT.getUri(), "style-name");
-		}
-	}
-
-	/*
-	 * Delete the <code>pNode<code> from the <code>fromIndex</code> text, and
-	 * delete <code>leftLength</code> text.
-	 */
-	private void delete(int fromIndex, int leftLength, Node 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() == Node.TEXT_NODE) {
-				nodeLength = node.getNodeValue().length();
-			} else if (node.getNodeType() == Node.ELEMENT_NODE) {
-				// text:s
-				if (node.getLocalName().equals("s")) {
-					try {
-						nodeLength = Integer.parseInt(((Element) node).getAttributeNS(OdfDocumentNamespace.TEXT
-								.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((OdfElement) node).length();
-				}
-			}
-			if (nodeLength <= fromIndex) {
-				fromIndex -= nodeLength;
-			} else {
-				// the start index is in this node
-				if (node.getNodeType() == Node.TEXT_NODE) {
-					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(endLength, value.length()));
-						leftLength = 0;
-					} else {
-						leftLength = endLength - value.length();
-					}
-					node.setNodeValue(buffer.toString());
-
-				} else if (node.getNodeType() == Node.ELEMENT_NODE) {
-					// if text:s?????????
-					// text:s
-					if (node.getLocalName().equals("s")) {
-						// delete space
-						((TextSElement) node).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(fromIndex, leftLength, node);
-						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 fromIndex, int leftLength, Node pNode, String 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() == Node.TEXT_NODE) {
-				nodeLength = node.getNodeValue().length();
-			} else if (node.getNodeType() == Node.ELEMENT_NODE) {
-				// text:s
-				if (node.getLocalName().equals("s")) {
-					try {
-						nodeLength = Integer.parseInt(((Element) node).getAttributeNS(OdfDocumentNamespace.TEXT
-								.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((OdfElement) node).length();
-				}
-
-			}
-			if (nodeLength <= fromIndex) {
-				fromIndex -= nodeLength;
-			} else {
-				// the start index is in this node
-				if (node.getNodeType() == Node.TEXT_NODE) {
-					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
-					TextAElement textLink = new TextAElement((OdfFileDom) node.getOwnerDocument());
-					Node newNode = null;
-					if (nextLength >= 0) {
-						textLink.setTextContent(value.substring(fromIndex, endLength));
-						newNode = node.cloneNode(true);
-						newNode.setNodeValue(value.substring(endLength, value.length()));
-						leftLength = 0;
-					} else {
-						textLink.setTextContent(value.substring(fromIndex, value.length()));
-						leftLength = endLength - value.length();
-					}
-					textLink.setXlinkTypeAttribute("simple");
-					textLink.setXlinkHrefAttribute(href);
-
-					if (nextNode != null) {
-						parNode.insertBefore(textLink, nextNode);
-						if (newNode != null) {
-							parNode.insertBefore(newNode, nextNode);
-						}
-					} else {
-						parNode.appendChild(textLink);
-						if (newNode != null) {
-							parNode.appendChild(newNode);
-						}
-					}
-					fromIndex = 0;
-					if (nextNode != null) {
-						node = nextNode;
-					} else {
-						node = textLink;
-					}
-
-				} else if (node.getNodeType() == Node.ELEMENT_NODE) {
-					// if text:s?????????
-					// text:s
-					if (node.getLocalName().equals("s")) {
-						// delete space
-						((TextSElement) node).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(fromIndex, leftLength, node, href);
-						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.
-	 */
-	private Map<OdfStyleProperty, String> getTextStyleProperties(OdfStylableElement element) {
-		String styleName = element.getStyleName();
-		OdfStyleBase styleElement = element.getAutomaticStyles().getStyle(styleName, element.getStyleFamily());
-
-		if (styleElement == null) {
-			styleElement = element.getDocumentStyle();
-		}
-		if (styleElement != null) {
-			// check if it is the style:defaut-style
-			if ((styleElement.getPropertiesElement(OdfStylePropertiesSet.ParagraphProperties) == null)
-					&& (styleElement.getPropertiesElement(OdfStylePropertiesSet.TextProperties) == null)) {
-				styleElement = ((Document) ((OdfFileDom) styleElement.getOwnerDocument()).getDocument())
-						.getDocumentStyles().getDefaultStyle(styleElement.getFamily());
-			}
-			TreeMap<OdfStyleProperty, String> result = new TreeMap<OdfStyleProperty, String>();
-			OdfStyleFamily family = OdfStyleFamily.Text;
-			if (family != null) {
-				for (OdfStyleProperty property : family.getProperties()) {
-					if (styleElement.hasProperty(property)) {
-						result.put(property, styleElement.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.
-	 */
-	private Map<OdfStyleProperty, String> getTextStylePropertiesDeep(OdfStylableElement element) {
-		String styleName = element.getStyleName();
-		OdfStyleBase styleElement = element.getAutomaticStyles().getStyle(styleName, element.getStyleFamily());
-		if (styleElement == null) {
-			styleElement = element.getDocumentStyle();
-		}
-		TreeMap<OdfStyleProperty, String> result = new TreeMap<OdfStyleProperty, String>();
-		while (styleElement != null) {
-			// check if it is the style:defaut-style
-			if ((styleElement.getPropertiesElement(OdfStylePropertiesSet.ParagraphProperties) == null)
-					&& (styleElement.getPropertiesElement(OdfStylePropertiesSet.TextProperties) == null)) {
-				styleElement = ((Document) ((OdfFileDom) styleElement.getOwnerDocument()).getDocument())
-						.getDocumentStyles().getDefaultStyle(styleElement.getFamily());
-			}
-			OdfStyleFamily family = OdfStyleFamily.Text;
-			if (family != null) {
-				for (OdfStyleProperty property : family.getProperties()) {
-					if (styleElement.hasProperty(property)) {
-						result.put(property, styleElement.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;
-		}
-		OdfElement container = getContainerElement();
-		if (container == null) {
-			return false;
-		}
-		String content = TextExtractor.getText(container);
-		if (content.indexOf(mMatchedText, mIndexInContainer) == mIndexInContainer) {
-			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 fromIndex, int leftLength, Node pNode, OdfStyleBase 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() == Node.TEXT_NODE) {
-				nodeLength = node.getNodeValue().length();
-			} else if (node.getNodeType() == Node.ELEMENT_NODE) {
-				// text:s
-				if (node.getLocalName().equals("s")) {
-					try {
-						nodeLength = Integer.parseInt(((Element) node).getAttributeNS(OdfDocumentNamespace.TEXT
-								.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((OdfElement) node).length();
-				}
-			}
-			if (nodeLength <= fromIndex) {
-				fromIndex -= nodeLength;
-			} else {
-				// the start index is in this node
-				if (node.getNodeType() == Node.TEXT_NODE) {
-					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((OdfFileDom) node.getOwnerDocument());
-					Node newNode = null;
-					if (nextLength >= 0) {
-						textSpan.setTextContent(value.substring(fromIndex, endLength));
-						newNode = node.cloneNode(true);
-						newNode.setNodeValue(value.substring(endLength, value.length()));
-						leftLength = 0;
-					} else {
-						textSpan.setTextContent(value.substring(fromIndex, value.length()));
-						leftLength = endLength - value.length();
-					}
-					textSpan.setProperties(style.getStyleProperties());
-
-					if (nextNode != null) {
-						parNode.insertBefore(textSpan, nextNode);
-						if (newNode != null) {
-							parNode.insertBefore(newNode, nextNode);
-						}
-					} else {
-						parNode.appendChild(textSpan);
-						if (newNode != null) {
-							parNode.appendChild(newNode);
-						}
-					}
-					fromIndex = 0;
-					if (nextNode != null) {
-						node = nextNode;
-					} else {
-						node = textSpan;
-					}
-
-				} else if (node.getNodeType() == Node.ELEMENT_NODE) {
-					// if text:s?????????
-					// text:s
-					if (node.getLocalName().equals("s")) {
-						// delete space
-						((TextSElement) node).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(fromIndex, leftLength, node, style);
-						int length = (fromIndex + leftLength) - nodeLength;
-						leftLength = length > 0 ? length : 0;
-						fromIndex = 0;
-					}
-				}
-			}
-			node = node.getNextSibling();
-		}
-	}
-}
+/*

+Licensed to the Apache Software Foundation (ASF) under one

+or more contributor license agreements.  See the NOTICE file

+distributed with this work for additional information

+regarding copyright ownership.  The ASF licenses this file

+to you 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.odftoolkit.simple.common.navigation;

+

+import java.net.URI;

+import java.net.URL;

+import java.text.SimpleDateFormat;

+import java.util.Date;

+import java.util.Map;

+import java.util.TreeMap;

+import org.odftoolkit.odfdom.dom.OdfContentDom;

+import org.odftoolkit.odfdom.dom.OdfDocumentNamespace;

+import org.odftoolkit.odfdom.dom.OdfStylesDom;

+import org.odftoolkit.odfdom.dom.element.OdfStylableElement;

+import org.odftoolkit.odfdom.dom.element.OdfStyleBase;

+import org.odftoolkit.odfdom.dom.element.dc.DcCreatorElement;

+import org.odftoolkit.odfdom.dom.element.dc.DcDateElement;

+import org.odftoolkit.odfdom.dom.element.office.OfficeAnnotationElement;

+import org.odftoolkit.odfdom.dom.element.style.StyleParagraphPropertiesElement;

+import org.odftoolkit.odfdom.dom.element.style.StyleTextPropertiesElement;

+import org.odftoolkit.odfdom.dom.element.text.TextAElement;

+import org.odftoolkit.odfdom.dom.element.text.TextConditionalTextElement;

+import org.odftoolkit.odfdom.dom.element.text.TextPElement;

+import org.odftoolkit.odfdom.dom.element.text.TextParagraphElementBase;

+import org.odftoolkit.odfdom.dom.element.text.TextSElement;

+import org.odftoolkit.odfdom.dom.element.text.TextSpanElement;

+import org.odftoolkit.odfdom.dom.element.text.TextUserFieldDeclElement;

+import org.odftoolkit.odfdom.dom.style.OdfStyleFamily;

+import org.odftoolkit.odfdom.dom.style.props.OdfStylePropertiesSet;

+import org.odftoolkit.odfdom.dom.style.props.OdfStyleProperty;

+import org.odftoolkit.odfdom.incubator.doc.office.OdfOfficeAutomaticStyles;

+import org.odftoolkit.odfdom.incubator.doc.style.OdfStyle;

+import org.odftoolkit.odfdom.incubator.doc.text.OdfTextHeading;

+import org.odftoolkit.odfdom.incubator.doc.text.OdfTextParagraph;

+import org.odftoolkit.odfdom.incubator.doc.text.OdfTextSpan;

+import org.odftoolkit.odfdom.pkg.OdfElement;

+import org.odftoolkit.odfdom.pkg.OdfFileDom;

+import org.odftoolkit.simple.Document;

+import org.odftoolkit.simple.TextDocument;

+import org.odftoolkit.simple.common.TextExtractor;

+import org.odftoolkit.simple.common.field.ConditionField;

+import org.odftoolkit.simple.common.field.Field;

+import org.odftoolkit.simple.common.field.Field.FieldType;

+import org.odftoolkit.simple.common.field.Fields;

+import org.odftoolkit.simple.common.field.VariableField;

+import org.odftoolkit.simple.draw.Image;

+import org.odftoolkit.simple.table.Table;

+import org.odftoolkit.simple.text.Paragraph;

+import org.w3c.dom.Element;

+import org.w3c.dom.NamedNodeMap;

+import org.w3c.dom.Node;

+import org.w3c.dom.NodeList;

+

+/**

+ * <code>TextSelection</code> describes a sub element in a paragraph element or

+ * a heading element. It is recognized by the container element, which type

+ * should be {@link org.odftoolkit.odfdom.incubator.doc.text.OdfTextParagraph

+ * OdfTextParagraph} or

+ * {@link org.odftoolkit.odfdom.incubator.doc.text.OdfTextHeading

+ * OdfTextHeading}, the start index of text content in container element and the

+ * text content of this <code>Selection</code>.

+ */

+public class TextSelection extends Selection {

+

+	String mMatchedText;

+	private OdfTextParagraph mParagraph;

+	private OdfTextHeading mHeading;

+	private int mIndexInContainer;

+	private boolean mIsInserted;

+	private boolean isSelectionReplaced = false;

+

+	/**

+	 * Constructor of <code>TextSelection</code>.

+	 *

+	 * @param text

+	 *            the text content of this <code>TextSelection</code>

+	 * @param containerElement

+	 *            the paragraph element or heading element that contains this

+	 *            <code>TextSelection</code>

+	 * @param index

+	 *            the start index of the text content in container element

+	 *

+	 */

+	TextSelection(Navigation search, String text, OdfElement containerElement,

+			int index) {

+		this.search = search;

+		mMatchedText = text;

+		if (containerElement instanceof OdfTextParagraph) {

+			mParagraph = (OdfTextParagraph) containerElement;

+		} else if (containerElement instanceof OdfTextHeading) {

+			mHeading = (OdfTextHeading) containerElement;

+		}

+		mIndexInContainer = index;

+	}

+

+	public TextNavigation getTextNavigation() {

+		if (search instanceof TextNavigation) {

+			return (TextNavigation) search;

+		}

+		return null;

+	}

+	/**

+	 * Create a new <code>TextSelection</code>.

+	 *

+	 * @param text

+	 *            the text content of this <code>TextSelection</code>

+	 * @param containerElement

+	 *            the paragraph element or heading element that contains this

+	 *            <code>TextSelection</code>

+	 * @param index

+	 *            the start index of the text content in container element

+	 *

+	 * @since 0.5.5

+	 */

+	public static TextSelection newTextSelection(Navigation search,

+			String text, OdfElement containerElement, int index) {

+		TextSelection selection = new TextSelection(search, text,

+				containerElement, index);

+		Selection.SelectionManager.registerItem(selection);

+		return selection;

+	}

+

+	/**

+	 * Get the paragraph element or heading element that contains this

+	 * <code>TextSelection</code>.

+	 *

+	 * @return OdfElement the container element

+	 */

+	@Override

+	public OdfElement getElement() {

+		return getContainerElement();

+	}

+

+	/**

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

+	 *

+	 * @return OdfElement

+	 */

+	public OdfElement getContainerElement() {

+		if (mParagraph != null) {

+			return mParagraph;

+		} else {

+			return mHeading;

+		}

+	}

+

+	/**

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

+	 *

+	 * @return index the start index of the text content of its container

+	 *         element

+	 */

+	@Override

+	public int getIndex() {

+		return mIndexInContainer;

+	}

+

+	/**

+	 * Get the text content of this <code>TextSelection</code>.

+	 *

+	 * @return text the text content

+	 */

+	public String getText() {

+		return mMatchedText;

+	}

+

+	/**

+	 * 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.

+	 */

+	@Override

+	public void cut() throws InvalidNavigationException {

+		if (validate() == false) {

+			throw new InvalidNavigationException("No matched string at this position");

+		}

+		OdfElement container = getContainerElement();

+		delete(mIndexInContainer, mMatchedText.length(), container);

+		SelectionManager.refreshAfterCut(this);

+		mMatchedText = "";

+	}

+

+	/**

+	 * Apply a style to the selection so that the text style of this selection

+	 * will append the specified style.

+	 *

+	 * @param style

+	 *            the style can be from the current document or user defined

+	 * @throws InvalidNavigationException

+	 *             if the selection is unavailable.

+	 */

+	public void applyStyle(OdfStyleBase style) throws 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 = mIndexInContainer;

+

+		appendStyle(index, leftLength, parentElement, style);

+

+	}

+

+	/**

+	 * Replace the text content of selection with a new string.

+	 *

+	 * @param newText

+	 *            the replace text String

+	 * @throws InvalidNavigationException

+	 *             if the selection is unavailable.

+	 */

+	public void replaceWith(String newText) throws InvalidNavigationException {

+		if (validate() == false) {

+			throw new InvalidNavigationException("No matched string at this position");

+		}

+		OdfElement parentElement = getContainerElement();

+		int leftLength = getText().length();

+		int index = mIndexInContainer;

+		delete(index, leftLength, parentElement);

+		OdfTextSpan textSpan = new OdfTextSpan((OdfFileDom) parentElement.getOwnerDocument());

+		textSpan.addContentWhitespace(newText);

+		mIsInserted = false;

+		insertOdfElement(textSpan, index, parentElement);

+		// optimize the parent element

+		optimize(parentElement);

+		int offset = newText.length() - leftLength;

+		SelectionManager.refresh(getContainerElement(), offset, index + getText().length());

+		mMatchedText = newText;

+	}

+

+	/**

+	 * Replace the text content of selection with a new Table.

+	 *

+	 * @param newTable

+	 *            the replace Table

+	 * @return

+	 * @throws InvalidNavigationException

+	 *             if the selection is unavailable.

+	 * @return the new Table in the TextDocument

+	 */

+	public Table replaceWith(Table newTable) throws InvalidNavigationException {

+		if (validate() == false) {

+			throw new InvalidNavigationException("No matched string at this position");

+		}

+		TableSelection nextTableSelection=new TableSelection(this);

+		return nextTableSelection.replaceWithTable(newTable);

+	}

+	/**

+	 * Replace the text content of selection with a new Image.

+	 *

+	 * @param newImage

+	 *            the replace Image

+	 * @return

+	 * @throws InvalidNavigationException

+	 *             if the selection is unavailable.

+	 * @return the new Image in the TextDocument,the image name is set to "replace" + System.currentTimeMillis(), please update the name to others by yourself.

+	 */

+	public Image replaceWith(Image newImage) throws InvalidNavigationException {

+		if (validate() == false) {

+			throw new InvalidNavigationException("No matched string at this position");

+		}

+		ImageSelection nextImageSelection=new ImageSelection(this);

+		return nextImageSelection.replaceWithImage(newImage);

+	}

+	/**

+	 * Replace the text content of selection with a new Image.

+	 *

+	 * @param imageUri

+	 *            the replace Image URI

+	 * @throws InvalidNavigationException

+	 *             if the selection is unavailable.

+	 * @return the new Image in the TextDocument,the image name is set to "replace" + System.currentTimeMillis(), please update the name to others by yourself.

+	 */

+	public Image replaceWith(URI imageUri) throws InvalidNavigationException {

+		if (validate() == false) {

+			throw new InvalidNavigationException("No matched string at this position");

+		}

+		ImageSelection nextImageSelection=new ImageSelection(this);

+		return nextImageSelection.replaceWithImage(imageUri);

+	}

+	/**

+	 * Replace the content with a Field

+	 *

+	 * @param orgField

+	 *            the reference Field to replace.

+     * @throws InvalidNavigationException

+	 *             if the selection is unavailable.

+	 * @return the created field.

+	 */

+	public Field replaceWith(Field orgField) throws InvalidNavigationException {

+		if (validate() == false) {

+			throw new InvalidNavigationException("No matched string at this position");

+		}

+		Field newfield=null;

+		OdfElement parentElement = getContainerElement();

+		Paragraph orgparagraph = Paragraph.getInstanceof((TextParagraphElementBase) parentElement);

+		TextDocument document = (TextDocument) orgparagraph.getOwnerDocument();

+

+		FieldSelection nextFieldSelection=new FieldSelection(this);

+		FieldType fieldType = orgField.getFieldType();

+

+		switch (fieldType) {

+		case DATE_FIELD:

+			newfield=nextFieldSelection.replaceWithSimpleField(fieldType);

+			break;

+		case FIXED_DATE_FIELD:

+			newfield=nextFieldSelection.replaceWithSimpleField(fieldType);

+			break;

+		case TIME_FIELD:

+			newfield=nextFieldSelection.replaceWithSimpleField(fieldType);

+			break;

+		case FIXED_TIME_FIELD:

+			newfield=nextFieldSelection.replaceWithSimpleField(fieldType);

+			break;

+		case PREVIOUS_PAGE_NUMBER_FIELD:

+			newfield=nextFieldSelection.replaceWithSimpleField(fieldType);

+			break;

+		case CURRENT_PAGE_NUMBER_FIELD:

+			newfield=nextFieldSelection.replaceWithSimpleField(fieldType);

+			break;

+		case NEXT_PAGE_NUMBER_FIELD:

+			newfield=nextFieldSelection.replaceWithSimpleField(fieldType);

+			break;

+		case PAGE_COUNT_FIELD:

+			newfield=nextFieldSelection.replaceWithSimpleField(fieldType);

+			break;

+		case TITLE_FIELD:

+			newfield=nextFieldSelection.replaceWithSimpleField(fieldType);

+			break;

+		case SUBJECT_FIELD:

+			newfield=nextFieldSelection.replaceWithSimpleField(fieldType);

+			break;

+		case AUTHOR_NAME_FIELD:

+			newfield=nextFieldSelection.replaceWithSimpleField(fieldType);

+			break;

+		case AUTHOR_INITIALS_FIELD:

+			newfield=nextFieldSelection.replaceWithSimpleField(fieldType);

+			break;

+		case CHAPTER_FIELD:

+			newfield=nextFieldSelection.replaceWithSimpleField(fieldType);

+			break;

+		case SIMPLE_VARIABLE_FIELD:

+			VariableField SimpleVariableField = (VariableField)orgField;

+			String simplefieldname = SimpleVariableField.getVariableName();

+			VariableField simplefield=Fields.createSimpleVariableField(document, simplefieldname);

+			nextFieldSelection.replaceWithVariableField(simplefield);

+			newfield=simplefield;

+			break;

+		case USER_VARIABLE_FIELD:

+			VariableField userVariableField = (VariableField)orgField;

+			TextUserFieldDeclElement textUserFieldDeclElement =(TextUserFieldDeclElement) userVariableField.getOdfElement();

+			String fieldname = userVariableField.getVariableName();

+			String value=textUserFieldDeclElement.getOfficeStringValueAttribute();

+			VariableField variableField=Fields.createUserVariableField(document, fieldname,value);

+			nextFieldSelection.replaceWithVariableField(variableField);

+			newfield=variableField;

+			break;

+		case CONDITION_FIELD:

+			ConditionField conditionField = (ConditionField)orgField;

+			TextConditionalTextElement textConditionalTextElement =(TextConditionalTextElement) conditionField.getOdfElement();

+			String StringValueIfFalse=textConditionalTextElement.getTextStringValueIfFalseAttribute();

+			String StringValueIfTrue=textConditionalTextElement.getTextStringValueIfTrueAttribute();

+			String StringCondition=textConditionalTextElement.getTextConditionAttribute();

+			boolean CurrentValue=textConditionalTextElement.getTextCurrentValueAttribute();

+			ConditionField newdConditionField = nextFieldSelection.replaceWithConditionField(StringCondition, StringValueIfTrue, StringValueIfFalse);

+			TextConditionalTextElement newTextConditionalTextElement=(TextConditionalTextElement)newdConditionField.getOdfElement();

+			newTextConditionalTextElement.setTextCurrentValueAttribute(CurrentValue);

+			newfield=newdConditionField;

+			break;

+		case HIDDEN_TEXT_FIELD:

+			ConditionField conditionFieldHIDDEN = (ConditionField)orgField;

+			TextConditionalTextElement textConditionalTextElementHIDDEN =(TextConditionalTextElement) conditionFieldHIDDEN.getOdfElement();

+			String StringValueIfFalseHIDDEN=textConditionalTextElementHIDDEN.getTextStringValueIfFalseAttribute();

+			String StringConditionHIDDEN=textConditionalTextElementHIDDEN.getTextConditionAttribute();

+			boolean CurrentValueHIDDEN=textConditionalTextElementHIDDEN.getTextCurrentValueAttribute();

+			ConditionField newdConditionFieldHIDDEN = nextFieldSelection.replaceWithHiddenTextField(StringConditionHIDDEN, StringValueIfFalseHIDDEN);

+			TextConditionalTextElement newTextConditionalTextElementHIDDEN=(TextConditionalTextElement)newdConditionFieldHIDDEN.getOdfElement();

+			newTextConditionalTextElementHIDDEN.setTextCurrentValueAttribute(CurrentValueHIDDEN);

+			newfield=newdConditionFieldHIDDEN;

+			break;

+		case REFERENCE_FIELD:

+		default: throw new IllegalArgumentException("Simple Java API for ODF doesn't support this type now.");

+		}

+		return newfield;

+	}

+

+	/**

+	 * Replace the content with a paragraph, the paragraph can be in the same TextDocument or in a different Document.

+	 *

+	 * @param newParagraph

+	 *            the reference paragraph to replace.

+     * @throws InvalidNavigationException

+	 *             if the selection is unavailable.

+	 * @return the replaced Paragraph.

+	 */

+	public Paragraph replaceWith(Paragraph newParagraph) throws InvalidNavigationException {

+		if (validate() == false) {

+			throw new InvalidNavigationException("No matched string at this position");

+		}

+		ParagraphSelection nextParagraphSelection=new ParagraphSelection(this);

+		return nextParagraphSelection.replaceWithParagraph(newParagraph);

+	}

+	/**

+	 * Replace the content with a TextDocument with Styles.

+	 * Note: You need cache the TextNavigation.nextSelection item because after

+	 * you replace currtenTextSelection with TextDocument, TextNavigation.nextSelection will search from the inserted Content,

+	 * it will make you into a loop if the Search keyword also can be found in the new inserted Content.

+	 * </p>

+	 * The right way to use this replaceWithTextDocument(TextDocument textDocument) method should like this:

+	 * <Code>

+	 * <p>	search = new TextNavigation("SIMPLE", doc);    </p>

+	 * <p>	TextSelection currtenTextSelection,nextTextSelection=null;</p>

+	 * <p>		while (search.hasNext()) {</p>

+	 * <p>			if(nextTextSelection!=null){</p>

+	 * <p>				currtenTextSelection=nextTextSelection;</p>

+	 * <p>			}else {</p>

+	 * <p>			 	currtenTextSelection = (TextSelection) search.nextSelection();</p>

+	 * <p>			}</p>

+	 * <p>			nextTextSelection = (TextSelection) search.nextSelection();</p>

+	 * <p>			if(currtenTextSelection!=null){</p>

+	 * <p>				try {</p>

+	 * <p>					nextTextSelection.replaceWithTextDocument(sourcedoc);</p>

+	 * <p>				} catch (Exception e) {</p>

+	 * <p>					e.printStackTrace();</p>

+	 * <p>				}</p>

+	 * <p>			}</p>

+	 * <p>		}</p>

+	 * <p>		if(nextTextSelection!=null){</p>

+	 * <p>			try {</p>

+	 * <p>				nextTextSelection.replaceWithTextDocument(sourcedoc);</p>

+	 * <p>			} catch (Exception e) {</p>

+	 * <p>				e.printStackTrace();</p>

+	 * <p>			}</p>

+	 * <p>		}</p>

+	 * </Code>

+	 *

+	 * @param newTextDocument

+	 *            the reference TextDocument to replace.

+	 * @throws InvalidNavigationException

+	 */

+	public void replaceWith(TextDocument newTextDocument) throws InvalidNavigationException{

+		if (validate() == false) {

+			throw new InvalidNavigationException("No matched string at this position");

+		}

+		TextDocumentSelection nextTextDocumentSelection=new TextDocumentSelection(this);

+		try {

+			nextTextDocumentSelection.replaceWithTextDocument(newTextDocument);

+		} catch (Exception e) {

+			e.printStackTrace();

+		}

+	}

+	/**

+	 * Create a span element for this text selection.

+	 *

+	 * @return the created text span element for this selection

+	 * @throws InvalidNavigationException

+	 *             if the selection is unavailable.

+	 * @since 0.5.5

+	 */

+	public TextSpanElement createSpanElement() throws InvalidNavigationException {

+		if (validate() == false) {

+			throw new InvalidNavigationException("No matched string at this position");

+		}

+		OdfElement parentElement = getContainerElement();

+		int leftLength = getText().length();

+		int index = mIndexInContainer;

+		delete(index, leftLength, parentElement);

+		OdfTextSpan textSpan = new OdfTextSpan((OdfFileDom) parentElement.getOwnerDocument());

+		textSpan.addContentWhitespace(getText());

+		mIsInserted = false;

+		insertOdfElement(textSpan, index, parentElement);

+		// optimize the parent element

+		optimize(parentElement);

+

+		return textSpan;

+	}

+

+	/**

+	 * Paste this selection just before a specific selection.

+	 *

+	 * @param positionItem

+	 *            a selection that is used to point out the position

+	 * @throws InvalidNavigationException

+	 *             if the selection is unavailable.

+	 */

+	@Override

+	public void pasteAtFrontOf(Selection positionItem) throws 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 = ((TextSelection) positionItem).getIndex();

+			newElement = ((TextSelection) positionItem).getContainerElement();

+		}

+

+		OdfTextSpan textSpan = getSpan((OdfFileDom) positionItem.getElement().getOwnerDocument());

+		mIsInserted = false;

+		insertOdfElement(textSpan, indexOfNew, newElement);

+		adjustStyle(newElement, textSpan, null);

+		SelectionManager.refreshAfterPasteAtFrontOf(this, positionItem);

+	}

+

+	/**

+	 * Paste this selection just after a specific selection.

+	 *

+	 * @param positionItem

+	 *            a selection that is used to point out the position

+	 * @throws InvalidNavigationException

+	 *             if the selection is unavailable.

+	 */

+	@Override

+	public void pasteAtEndOf(Selection positionItem) throws 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 = ((TextSelection) positionItem).getIndex() + ((TextSelection) positionItem).getText().length();

+			newElement = ((TextSelection) positionItem).getContainerElement();

+		}

+		OdfTextSpan textSpan = getSpan((OdfFileDom) positionItem.getElement().getOwnerDocument());

+		mIsInserted = false;

+		insertOdfElement(textSpan, indexOfNew, newElement);

+		adjustStyle(newElement, textSpan, null);

+		SelectionManager.refreshAfterPasteAtEndOf(this, positionItem);

+	}

+	public void setSelectionReplaced(boolean b) {

+		this.isSelectionReplaced = b;

+	}

+	public boolean isSelectionReplaced() {

+		return this.isSelectionReplaced;

+	}

+

+	/**

+	 * Add a hypertext reference to the selection.

+	 *

+	 * @param url

+	 *            the URL of this hypertext reference

+	 * @throws InvalidNavigationException

+	 *             if the selection is unavailable.

+	 */

+	public void addHref(URL url) throws InvalidNavigationException {

+		if (validate() == false) {

+			throw new InvalidNavigationException("No matched string at this position");

+		}

+		OdfElement parentElement = getContainerElement();

+		int leftLength = getText().length();

+		int index = mIndexInContainer;

+		addHref(index, leftLength, parentElement, url.toString());

+	}

+

+	/**

+	 * Add a comment to the selection.

+	 *

+	 * @param content

+	 *            the content of this comment.

+	 * @param creator

+	 *            the creator of this comment, if <code>creator</code> is null,

+	 *            the value of <code>System.getProperty("user.name")</code> will

+	 *            be used.

+	 * @throws InvalidNavigationException

+	 *            if the selection is unavailable.

+	 * @since 0.6.5

+	 */

+	public void addComment(String content, String creator) throws InvalidNavigationException {

+		if (validate() == false) {

+			throw new InvalidNavigationException("No matched string at this position");

+		}

+		// create annotation element

+		OdfElement parentElement = getContainerElement();

+		OdfFileDom dom = (OdfFileDom) parentElement.getOwnerDocument();

+		OfficeAnnotationElement annotationElement = dom.newOdfElement(OfficeAnnotationElement.class);

+		// set creator

+		DcCreatorElement dcCreatorElement = annotationElement.newDcCreatorElement();

+		if (creator == null) {

+			creator = System.getProperty("user.name");

+		}

+		dcCreatorElement.setTextContent(creator);

+		// set date

+		String dcDate = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss").format(new Date());

+		DcDateElement dcDateElement = annotationElement.newDcDateElement();

+		dcDateElement.setTextContent(dcDate);

+		TextPElement notePElement = annotationElement.newTextPElement();

+		TextSpanElement noteSpanElement = notePElement.newTextSpanElement();

+		// set comment style

+		OdfOfficeAutomaticStyles styles = null;

+		if (dom instanceof OdfContentDom) {

+			styles = ((OdfContentDom) dom).getAutomaticStyles();

+		} else if (dom instanceof OdfStylesDom) {

+			styles = ((OdfStylesDom) dom).getAutomaticStyles();

+		}

+		OdfStyle textStyle = styles.newStyle(OdfStyleFamily.Text);

+		StyleTextPropertiesElement styleTextPropertiesElement = textStyle.newStyleTextPropertiesElement(null);

+		styleTextPropertiesElement.setStyleFontNameAttribute("Tahoma");

+		styleTextPropertiesElement.setFoFontSizeAttribute("10pt");

+		styleTextPropertiesElement.setStyleFontNameAsianAttribute("Lucida Sans Unicode");

+		styleTextPropertiesElement.setStyleFontSizeAsianAttribute("12pt");

+		noteSpanElement.setStyleName(textStyle.getStyleNameAttribute());

+		// set comment content

+		noteSpanElement.setTextContent(content);

+		// insert comment to its position

+		insertOdfElement(annotationElement, mIndexInContainer, parentElement);

+		// three text length plus two '\r'

+		int offset = content.length() + 1 + dcDate.length() + 1 + creator.length();

+		SelectionManager.refresh(getContainerElement(), offset, getIndex());

+	}

+

+	/**

+	 * 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.

+	 *

+	 * @return a String representation of the value of this

+	 *         <code>TextSelection</code>

+	 */

+	@Override

+	public String toString() {

+		return "[" + mMatchedText + "] started from " + mIndexInContainer + " in paragraph:"

+				+ TextExtractor.getText(getContainerElement());

+	}

+

+	@Override

+	protected void refreshAfterFrontalDelete(Selection deleteItem) {

+		if (deleteItem instanceof TextSelection) {

+			mIndexInContainer -= ((TextSelection) deleteItem).getText().length();

+		}

+	}

+

+	@Override

+	protected void refreshAfterFrontalInsert(Selection pasteItem) {

+		if (pasteItem instanceof TextSelection) {

+			mIndexInContainer += ((TextSelection) pasteItem).getText().length();

+		}

+	}

+

+	@Override

+	protected void refresh(int offset) {

+		mIndexInContainer += offset;

+		if (mIndexInContainer < 0) {

+			mIndexInContainer = 0;

+		}

+	}

+

+	void cleanBreakProperty(Paragraph paragraph) {

+		TextNavigation search = this.getTextNavigation();

+		if (search == null)

+			throw new IllegalStateException("Navigation is null");

+		OdfStyleBase styleElement = paragraph.getStyleHandler()

+				.getStyleElementForRead();

+		String name = styleElement.getAttribute("style:name");

+		String newName = null;

+		OdfElement modifiedStyleElement = search

+				.getModifiedStyleElement(styleElement);

+		if (modifiedStyleElement == null) {

+			modifiedStyleElement = (OdfElement) styleElement.cloneNode(true);

+			search.addModifiedStyleElement(styleElement, modifiedStyleElement);

+			NodeList paragraphProperties = modifiedStyleElement

+					.getElementsByTagName("style:paragraph-properties");

+			if (paragraphProperties != null

+					&& paragraphProperties.getLength() > 0) {

+				StyleParagraphPropertiesElement property = (StyleParagraphPropertiesElement) paragraphProperties

+						.item(0);

+				property.removeAttribute("fo:break-before");

+				property.removeAttribute("fo:break-after");

+				property.removeAttribute("style:page-number");

+			}

+			modifiedStyleElement.removeAttribute("style:master-page-name");

+			newName = name + "-" + makeUniqueName();

+			NamedNodeMap attributes = modifiedStyleElement.getAttributes();

+			if (attributes != null) {

+				for (int i = 0; i < attributes.getLength(); i++) {

+					Node item = attributes.item(i);

+					String value = item.getNodeValue();

+					if (name.equals(value)) {

+						item.setNodeValue(newName);

+						break;

+					}

+				}

+			}

+			styleElement.getParentNode().appendChild(modifiedStyleElement);

+		} else {

+			newName = modifiedStyleElement.getAttribute("style:name");

+		}

+		NamedNodeMap attributes = paragraph.getOdfElement().getAttributes();

+		if (attributes != null) {

+			for (int i = 0; i < attributes.getLength(); i++) {

+				Node item = attributes.item(i);

+				String value = item.getNodeValue();

+				if (name.equals(value)) {

+					item.setNodeValue(newName);

+					break;

+				}

+			}

+		}

+		this.getTextNavigation().setHandlePageBreak(true);

+	}

+	String makeUniqueName() {

+		return String.format("p%06x", (int) (Math.random() * 0xffffff));

+	}

+	/*

+	 * 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 = (OdfElement) parentElement.cloneNode(true);

+			if (ownerDoc != parentElement.getOwnerDocument()) {

+				copyParentNode = (OdfElement) ownerDoc.adoptNode(copyParentNode);

+			}

+			OdfTextSpan textSpan = new OdfTextSpan(ownerDoc);

+			int sIndex = mIndexInContainer;

+			int eIndex = sIndex + mMatchedText.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() - eIndex, copyParentNode);

+			delete(0, sIndex, copyParentNode);

+			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) {

+				applyTextStyleProperties(getTextStylePropertiesDeep((OdfStylableElement) copyParentNode), textSpan);

+			}

+			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((OdfTextSpan) node).length() == 0) {

+					node.getParentNode().removeChild(node);

+				} 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<OdfStyleProperty, String> styleMap, OdfStylableElement toElement) {

+		if (styleMap != null) {

+			// preserve the style property of toElement if it is also exist in

+			// styleMap

+			OdfStyle resultStyleElement = toElement.getAutomaticStyles().newStyle(OdfStyleFamily.Text);

+			for (Map.Entry<OdfStyleProperty, String> entry : 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>odfElement</code>, span or annotation, into the from index of <code>pNode<code>.

+	 */

+	private void insertOdfElement(OdfElement odfElement, int fromIndex, Node pNode) {

+		if (fromIndex < 0) {

+			fromIndex = 0;

+		}

+		if (fromIndex == 0 && mIsInserted) {

+			return;

+		}

+		int nodeLength = 0;

+		Node node = pNode.getFirstChild();

+		while (node != null) {

+			if (fromIndex <= 0 && mIsInserted) {

+				return;

+			}

+			if (node.getNodeType() == Node.TEXT_NODE) {

+				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(fromIndex, value.length()));

+					if (nextNode != null) {

+						parNode.insertBefore(odfElement, nextNode);

+						parNode.insertBefore(newNode, nextNode);

+					} else {

+						parNode.appendChild(odfElement);

+						parNode.appendChild(newNode);

+					}

+					mIsInserted = true;

+					return;

+				}

+			} else if (node.getNodeType() == Node.ELEMENT_NODE) {

+				// text:s

+				if (node.getLocalName().equals("s")) {

+					try {

+						nodeLength = Integer.parseInt(((Element) node).getAttributeNS(OdfDocumentNamespace.TEXT

+								.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((OdfElement) node).length();

+					insertOdfElement(odfElement, fromIndex, node);

+					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 parentNode, OdfTextSpan textSpan, Map<OdfStyleProperty, String> styleMap) {

+		if (parentNode instanceof OdfStylableElement) {

+			OdfStylableElement pStyleNode = (OdfStylableElement) parentNode;

+			if (styleMap == null) {

+				styleMap = getTextStylePropertiesDeep(pStyleNode);

+			}

+			Node node = parentNode.getFirstChild();

+			while (node != null) {

+				if (node.getNodeType() == Node.TEXT_NODE) {

+					if (node.getTextContent().length() > 0) {

+						Node nextNode = node.getNextSibling();

+						OdfTextSpan span = new OdfTextSpan((OdfFileDom) node.getOwnerDocument());

+						span.appendChild(node);

+						if (nextNode != null) {

+							parentNode.insertBefore(span, nextNode);

+						} else {

+							parentNode.appendChild(span);

+						}

+						node = span;

+						applyTextStyleProperties(styleMap, (OdfStylableElement) node);

+					}

+				} else if ((node instanceof OdfStylableElement)) {

+					if (!node.equals(textSpan)) {

+						Map<OdfStyleProperty, String> styles = getTextStylePropertiesDeep(pStyleNode);

+						Map<OdfStyleProperty, String> styles1 = getTextStylePropertiesDeep((OdfStylableElement) node);

+						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 & Node.DOCUMENT_POSITION_CONTAINED_BY) > 0) {

+							adjustStyle(node, textSpan, styles);

+						} else {

+							applyTextStyleProperties(styles, (OdfStylableElement) node);

+						}

+					}

+				}

+				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(OdfDocumentNamespace.TEXT.getUri(), "style-name");

+		}

+	}

+

+	/*

+	 * Delete the <code>pNode<code> from the <code>fromIndex</code> text, and

+	 * delete <code>leftLength</code> text.

+	 */

+	private void delete(int fromIndex, int leftLength, Node 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() == Node.TEXT_NODE) {

+				nodeLength = node.getNodeValue().length();

+			} else if (node.getNodeType() == Node.ELEMENT_NODE) {

+				// text:s

+				if (node.getLocalName().equals("s")) {

+					try {

+						nodeLength = Integer.parseInt(((Element) node).getAttributeNS(OdfDocumentNamespace.TEXT

+								.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((OdfElement) node).length();

+				}

+			}

+			if (nodeLength <= fromIndex) {

+				fromIndex -= nodeLength;

+			} else {

+				// the start index is in this node

+				if (node.getNodeType() == Node.TEXT_NODE) {

+					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(endLength, value.length()));

+						leftLength = 0;

+					} else {

+						leftLength = endLength - value.length();

+					}

+					node.setNodeValue(buffer.toString());

+

+				} else if (node.getNodeType() == Node.ELEMENT_NODE) {

+					// if text:s?????????

+					// text:s

+					if (node.getLocalName().equals("s")) {

+						// delete space

+						((TextSElement) node).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--;

+						Node nodeMerker = node.getNextSibling();

+						pNode.removeChild(node);

+						node = nodeMerker;

+						continue;

+					} else {

+						delete(fromIndex, leftLength, node);

+						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 fromIndex, int leftLength, Node pNode, String 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() == Node.TEXT_NODE) {

+				nodeLength = node.getNodeValue().length();

+			} else if (node.getNodeType() == Node.ELEMENT_NODE) {

+				// text:s

+				if (node.getLocalName().equals("s")) {

+					try {

+						nodeLength = Integer.parseInt(((Element) node).getAttributeNS(OdfDocumentNamespace.TEXT

+								.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((OdfElement) node).length();

+				}

+

+			}

+			if (nodeLength <= fromIndex) {

+				fromIndex -= nodeLength;

+			} else {

+				// the start index is in this node

+				if (node.getNodeType() == Node.TEXT_NODE) {

+					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

+					TextAElement textLink = new TextAElement((OdfFileDom) node.getOwnerDocument());

+					Node newNode = null;

+					if (nextLength >= 0) {

+						textLink.setTextContent(value.substring(fromIndex, endLength));

+						newNode = node.cloneNode(true);

+						newNode.setNodeValue(value.substring(endLength, value.length()));

+						leftLength = 0;

+					} else {

+						textLink.setTextContent(value.substring(fromIndex, value.length()));

+						leftLength = endLength - value.length();

+					}

+					textLink.setXlinkTypeAttribute("simple");

+					textLink.setXlinkHrefAttribute(href);

+

+					if (nextNode != null) {

+						parNode.insertBefore(textLink, nextNode);

+						if (newNode != null) {

+							parNode.insertBefore(newNode, nextNode);

+						}

+					} else {

+						parNode.appendChild(textLink);

+						if (newNode != null) {

+							parNode.appendChild(newNode);

+						}

+					}

+					fromIndex = 0;

+					if (nextNode != null) {

+						node = nextNode;

+					} else {

+						node = textLink;

+					}

+

+				} else if (node.getNodeType() == Node.ELEMENT_NODE) {

+					// if text:s?????????

+					// text:s

+					if (node.getLocalName().equals("s")) {

+						// delete space

+						((TextSElement) node).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(fromIndex, leftLength, node, href);

+						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.

+	 */

+	private Map<OdfStyleProperty, String> getTextStyleProperties(OdfStylableElement element) {

+		String styleName = element.getStyleName();

+		OdfStyleBase styleElement = element.getAutomaticStyles().getStyle(styleName, element.getStyleFamily());

+

+		if (styleElement == null) {

+			styleElement = element.getDocumentStyle();

+		}

+		if (styleElement != null) {

+			// check if it is the style:defaut-style

+			if ((styleElement.getPropertiesElement(OdfStylePropertiesSet.ParagraphProperties) == null)

+					&& (styleElement.getPropertiesElement(OdfStylePropertiesSet.TextProperties) == null)) {

+				styleElement = ((Document) ((OdfFileDom) styleElement.getOwnerDocument()).getDocument())

+						.getDocumentStyles().getDefaultStyle(styleElement.getFamily());

+			}

+			TreeMap<OdfStyleProperty, String> result = new TreeMap<OdfStyleProperty, String>();

+			OdfStyleFamily family = OdfStyleFamily.Text;

+			if (family != null) {

+				for (OdfStyleProperty property : family.getProperties()) {

+					if (styleElement.hasProperty(property)) {

+						result.put(property, styleElement.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.

+	 */

+	private Map<OdfStyleProperty, String> getTextStylePropertiesDeep(OdfStylableElement element) {

+		String styleName = element.getStyleName();

+		OdfStyleBase styleElement = element.getAutomaticStyles().getStyle(styleName, element.getStyleFamily());

+		if (styleElement == null) {

+			styleElement = element.getDocumentStyle();

+		}

+		TreeMap<OdfStyleProperty, String> result = new TreeMap<OdfStyleProperty, String>();

+		while (styleElement != null) {

+			// check if it is the style:defaut-style

+			if ((styleElement.getPropertiesElement(OdfStylePropertiesSet.ParagraphProperties) == null)

+					&& (styleElement.getPropertiesElement(OdfStylePropertiesSet.TextProperties) == null)) {

+				styleElement = ((Document) ((OdfFileDom) styleElement.getOwnerDocument()).getDocument())

+						.getDocumentStyles().getDefaultStyle(styleElement.getFamily());

+			}

+			OdfStyleFamily family = OdfStyleFamily.Text;

+			if (family != null) {

+				for (OdfStyleProperty property : family.getProperties()) {

+					if (styleElement.hasProperty(property)) {

+						result.put(property, styleElement.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;

+		}

+		OdfElement container = getContainerElement();

+		if (container == null) {

+			return false;

+		}

+		String content = TextExtractor.getText(container);

+		if (content.indexOf(mMatchedText, mIndexInContainer) == mIndexInContainer) {

+			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 fromIndex, int leftLength, Node pNode, OdfStyleBase 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() == Node.TEXT_NODE) {

+				nodeLength = node.getNodeValue().length();

+			} else if (node.getNodeType() == Node.ELEMENT_NODE) {

+				// text:s

+				if (node.getLocalName().equals("s")) {

+					try {

+						nodeLength = Integer.parseInt(((Element) node).getAttributeNS(OdfDocumentNamespace.TEXT

+								.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((OdfElement) node).length();

+				}

+			}

+			if (nodeLength <= fromIndex) {

+				fromIndex -= nodeLength;

+			} else {

+				// the start index is in this node

+				if (node.getNodeType() == Node.TEXT_NODE) {

+					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((OdfFileDom) node.getOwnerDocument());

+					Node newNode = null;

+					if (nextLength >= 0) {

+						textSpan.setTextContent(value.substring(fromIndex, endLength));

+						newNode = node.cloneNode(true);

+						newNode.setNodeValue(value.substring(endLength, value.length()));

+						leftLength = 0;

+					} else {

+						textSpan.setTextContent(value.substring(fromIndex, value.length()));

+						leftLength = endLength - value.length();

+					}

+					textSpan.setProperties(style.getStyleProperties());

+

+					if (nextNode != null) {

+						parNode.insertBefore(textSpan, nextNode);

+						if (newNode != null) {

+							parNode.insertBefore(newNode, nextNode);

+						}

+					} else {

+						parNode.appendChild(textSpan);

+						if (newNode != null) {

+							parNode.appendChild(newNode);

+						}

+					}

+					fromIndex = 0;

+					if (nextNode != null) {

+						node = nextNode;

+					} else {

+						node = textSpan;

+					}

+

+				} else if (node.getNodeType() == Node.ELEMENT_NODE) {

+					// if text:s?????????

+					// text:s

+					if (node.getLocalName().equals("s")) {

+						// delete space

+						((TextSElement) node).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(fromIndex, leftLength, node, style);

+						int length = (fromIndex + leftLength) - nodeLength;

+						leftLength = length > 0 ? length : 0;

+						fromIndex = 0;

+					}

+				}

+			}

+			node = node.getNextSibling();

+		}

+	}

+}

diff --git a/simple/src/test/java/org/odftoolkit/simple/text/SpanTest.java b/simple/src/test/java/org/odftoolkit/simple/text/SpanTest.java
index 5e7c3fa..b65c782 100644
--- a/simple/src/test/java/org/odftoolkit/simple/text/SpanTest.java
+++ b/simple/src/test/java/org/odftoolkit/simple/text/SpanTest.java
@@ -1,154 +1,233 @@
-/* 
-Licensed to the Apache Software Foundation (ASF) under one
-or more contributor license agreements.  See the NOTICE file
-distributed with this work for additional information
-regarding copyright ownership.  The ASF licenses this file
-to you 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.odftoolkit.simple.text;
-
-import java.net.URI;
-import java.util.Iterator;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-
-import junit.framework.Assert;
-
-import org.junit.Test;
-import org.odftoolkit.odfdom.type.Color;
-import org.odftoolkit.simple.TextDocument;
-import org.odftoolkit.simple.common.navigation.TextNavigation;
-import org.odftoolkit.simple.common.navigation.TextSelection;
-import org.odftoolkit.simple.style.DefaultStyleHandler;
-import org.odftoolkit.simple.style.Font;
-import org.odftoolkit.simple.style.StyleTypeDefinitions.FontStyle;
-import org.odftoolkit.simple.style.StyleTypeDefinitions.TextLinePosition;
-import org.odftoolkit.simple.utils.ResourceUtilities;
-
-public class SpanTest {
-
-	@Test
-	public void testSpan() {
-		try {
-			TextDocument doc = TextDocument.newTextDocument();
-			doc.addParagraph("This is a test paragraph!");
-			TextNavigation navigation = new TextNavigation("test", doc);
-			TextSelection sel = (TextSelection) navigation.nextSelection();
-			Span span = Span.newSpan(sel);
-			TextHyperlink link = span.applyHyperlink(new URI("http://www.ibm.com"));
-			DefaultStyleHandler handler = span.getStyleHandler();
-			Font font1Base = new Font("Arial", FontStyle.ITALIC, 10, Color.BLACK, TextLinePosition.THROUGH);
-			handler.getTextPropertiesForWrite().setFont(font1Base);
-			doc.save(ResourceUtilities.newTestOutputFile("spantest.odt"));
-
-			String content = span.getTextContent();
-			Assert.assertEquals("test", content);
-			span.setTextContent("new test");
-			Assert.assertEquals("new test", span.getTextContent());
-			doc.save(ResourceUtilities.newTestOutputFile("spantest.odt"));
-
-		} catch (Exception e) {
-			Logger.getLogger(SpanTest.class.getName()).log(Level.SEVERE, null, e);
-			Assert.fail();
-		}
-
-	}
-	
-	@Test
-	public void testRemoveTextContent() {
-		try {
-			TextDocument doc = TextDocument.newTextDocument();
-			
-			Iterator<Paragraph> paraA = doc.getParagraphIterator();
-			while(paraA.hasNext()){
-				Paragraph pp = paraA.next();
-				doc.removeParagraph(pp);
-			}
-			
-			doc.addParagraph("This is a test paragraph!");
-			
-			TextNavigation navigation = new TextNavigation("test", doc);
-			TextSelection sel = (TextSelection) navigation.nextSelection();
-			Span span = Span.newSpan(sel);
-			
-			span.removeTextContent();
-			boolean flag = false;
-			Iterator<Paragraph> parai = doc.getParagraphIterator();
-			while(parai.hasNext()){
-				Paragraph pp = parai.next();
-				if("This is a  paragraph!".equals(pp.getTextContent()))
-					flag = true;
-			}
-			Assert.assertTrue(flag);
-
-			//save
-			doc.save(ResourceUtilities.newTestOutputFile("spantest.odt"));
-
-		} catch (Exception e) {
-			Logger.getLogger(SpanTest.class.getName()).log(Level.SEVERE, null, e);
-			Assert.fail();
-		}
-
-	}
-	
-	@Test
-	public void testAppendTextContent() {
-		try {
-			TextDocument doc = TextDocument.newTextDocument();
-			doc.addParagraph("This is a test paragraph!");
-			
-			TextNavigation navigation = new TextNavigation("test", doc);
-			TextSelection sel = (TextSelection) navigation.nextSelection();
-			Span span = Span.newSpan(sel);
-			
-			span.appendTextContent("hello world.");
-			Assert.assertEquals("testhello world.", span.getTextContent());
-
-			//save
-			//doc.save(ResourceUtilities.newTestOutputFile("spantest.odt"));
-		} catch (Exception e) {
-			Logger.getLogger(SpanTest.class.getName()).log(Level.SEVERE, null, e);
-			Assert.fail();
-		}
-
-	}
-	
-	
-	@Test
-	public void testAppendTextContentPara() {
-		try {
-			TextDocument doc = TextDocument.newTextDocument();
-			doc.addParagraph("This is a test paragraph!");
-			
-			TextNavigation navigation = new TextNavigation("test", doc);
-			TextSelection sel = (TextSelection) navigation.nextSelection();
-			Span span = Span.newSpan(sel);
-			
-			span.appendTextContent("hello world.", true);
-			Assert.assertEquals("testhello world.", span.getTextContent());
-			
-			span.appendTextContent("hello world.", false);
-			Assert.assertEquals("testhello world.hello world.", span.getTextContent());
-
-			//save
-			//doc.save(ResourceUtilities.newTestOutputFile("spantest.odt"));
-		} catch (Exception e) {
-			Logger.getLogger(SpanTest.class.getName()).log(Level.SEVERE, null, e);
-			Assert.fail();
-		}
-
-	}
-	
-}
+/* 

+Licensed to the Apache Software Foundation (ASF) under one

+or more contributor license agreements.  See the NOTICE file

+distributed with this work for additional information

+regarding copyright ownership.  The ASF licenses this file

+to you 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.odftoolkit.simple.text;

+

+import java.net.URI;

+import java.util.Iterator;

+import java.util.logging.Level;

+import java.util.logging.Logger;

+

+import junit.framework.Assert;

+

+import org.junit.Test;

+import org.odftoolkit.odfdom.type.Color;

+import org.odftoolkit.simple.TextDocument;

+import org.odftoolkit.simple.common.navigation.TextNavigation;

+import org.odftoolkit.simple.common.navigation.TextSelection;

+import org.odftoolkit.simple.style.DefaultStyleHandler;

+import org.odftoolkit.simple.style.Font;

+import org.odftoolkit.simple.style.StyleTypeDefinitions.FontStyle;

+import org.odftoolkit.simple.style.StyleTypeDefinitions.TextLinePosition;

+import org.odftoolkit.simple.utils.ResourceUtilities;

+

+public class SpanTest {

+

+	@Test

+	public void testSpan() {

+		try {

+			TextDocument doc = TextDocument.newTextDocument();

+			doc.addParagraph("This is a test paragraph!");

+			TextNavigation navigation = new TextNavigation("test", doc);

+			TextSelection sel = (TextSelection) navigation.nextSelection();

+			Span span = Span.newSpan(sel);

+			TextHyperlink link = span.applyHyperlink(new URI("http://www.ibm.com"));

+			DefaultStyleHandler handler = span.getStyleHandler();

+			Font font1Base = new Font("Arial", FontStyle.ITALIC, 10, Color.BLACK, TextLinePosition.THROUGH);

+			handler.getTextPropertiesForWrite().setFont(font1Base);

+			doc.save(ResourceUtilities.newTestOutputFile("spantest.odt"));

+

+			String content = span.getTextContent();

+			Assert.assertEquals("test", content);

+			span.setTextContent("new test");

+			Assert.assertEquals("new test", span.getTextContent());

+			doc.save(ResourceUtilities.newTestOutputFile("spantest.odt"));

+

+		} catch (Exception e) {

+			Logger.getLogger(SpanTest.class.getName()).log(Level.SEVERE, null, e);

+			Assert.fail();

+		}

+

+	}

+	

+	@Test

+	public void testRemoveTextContent() {

+		try {

+			TextDocument doc = TextDocument.newTextDocument();

+			

+			Iterator<Paragraph> paraA = doc.getParagraphIterator();

+			while(paraA.hasNext()){

+				Paragraph pp = paraA.next();

+				doc.removeParagraph(pp);

+			}

+			

+			doc.addParagraph("This is a test paragraph!");

+			

+			TextNavigation navigation = new TextNavigation("test", doc);

+			TextSelection sel = (TextSelection) navigation.nextSelection();

+			Span span = Span.newSpan(sel);

+			

+			span.removeTextContent();

+			boolean flag = false;

+			Iterator<Paragraph> parai = doc.getParagraphIterator();

+			while(parai.hasNext()){

+				Paragraph pp = parai.next();

+				if("This is a  paragraph!".equals(pp.getTextContent()))

+					flag = true;

+			}

+			Assert.assertTrue(flag);

+

+			//save

+			doc.save(ResourceUtilities.newTestOutputFile("spantest.odt"));

+

+		} catch (Exception e) {

+			Logger.getLogger(SpanTest.class.getName()).log(Level.SEVERE, null, e);

+			Assert.fail();

+		}

+

+	}

+	

+	@Test

+	public void testAppendTextContent() {

+		try {

+			TextDocument doc = TextDocument.newTextDocument();

+			doc.addParagraph("This is a test paragraph!");

+			

+			TextNavigation navigation = new TextNavigation("test", doc);

+			TextSelection sel = (TextSelection) navigation.nextSelection();

+			Span span = Span.newSpan(sel);

+			

+			span.appendTextContent("hello world.");

+			Assert.assertEquals("testhello world.", span.getTextContent());

+

+			//save

+			//doc.save(ResourceUtilities.newTestOutputFile("spantest.odt"));

+		} catch (Exception e) {

+			Logger.getLogger(SpanTest.class.getName()).log(Level.SEVERE, null, e);

+			Assert.fail();

+		}

+

+	}

+	

+	

+	@Test

+	public void testAppendTextContentPara() {

+		try {

+			TextDocument doc = TextDocument.newTextDocument();

+			doc.addParagraph("This is a test paragraph!");

+			

+			TextNavigation navigation = new TextNavigation("test", doc);

+			TextSelection sel = (TextSelection) navigation.nextSelection();

+			Span span = Span.newSpan(sel);

+			

+			span.appendTextContent("hello world.", true);

+			Assert.assertEquals("testhello world.", span.getTextContent());

+			

+			span.appendTextContent("hello world.", false);

+			Assert.assertEquals("testhello world.hello world.", span.getTextContent());

+

+			//save

+			//doc.save(ResourceUtilities.newTestOutputFile("spantest.odt"));

+		} catch (Exception e) {

+			Logger.getLogger(SpanTest.class.getName()).log(Level.SEVERE, null, e);

+			Assert.fail();

+		}

+

+	}

+

+	@Test

+	public void testRemoveTextContentLb() {

+		try {

+

+			TextDocument doc = TextDocument.newTextDocument();

+

+			Iterator<Paragraph> paraA = doc.getParagraphIterator();

+			while (paraA.hasNext()) {

+				Paragraph pp = paraA.next();

+				doc.removeParagraph(pp);

+			}

+

+			Paragraph para = doc.addParagraph("This is a beforelb\nafterlb paragraph!");

+

+			System.out.println(para.getTextContent());

+

+			TextNavigation navigation = new TextNavigation("beforelb\nafterlb", doc);

+			TextSelection sel = (TextSelection) navigation.nextSelection();

+			Span span = Span.newSpan(sel);

+

+			span.removeTextContent();

+			boolean flag = false;

+			Iterator<Paragraph> parai = doc.getParagraphIterator();

+			while (parai.hasNext()) {

+				Paragraph pp = parai.next();

+				System.out.println(pp.getTextContent());

+				if ("This is a  paragraph!".equals(pp.getTextContent())) {

+					flag = true;

+				}

+			}

+			Assert.assertTrue("Linebreak is not removed from paragraph!", flag);

+

+		} catch (Exception e) {

+			Logger.getLogger(SpanTest.class.getName()).log(Level.SEVERE, null, e);

+			Assert.fail();

+		}

+

+	}

+

+	@Test

+	public void testRemoveTextContentTab() {

+		try {

+

+			TextDocument doc = TextDocument.newTextDocument();

+

+			Iterator<Paragraph> paraA = doc.getParagraphIterator();

+			while (paraA.hasNext()) {

+				Paragraph pp = paraA.next();

+				doc.removeParagraph(pp);

+			}

+

+			Paragraph para = doc.addParagraph("This is a beforetab\taftertab paragraph!");

+

+			System.out.println(para.getTextContent());

+

+			TextNavigation navigation = new TextNavigation("beforetab\taftertab", doc);

+			TextSelection sel = (TextSelection) navigation.nextSelection();

+			Span span = Span.newSpan(sel);

+

+			span.removeTextContent();

+			boolean flag = false;

+			Iterator<Paragraph> parai = doc.getParagraphIterator();

+			while (parai.hasNext()) {

+				Paragraph pp = parai.next();

+				System.out.println(pp.getTextContent());

+				if ("This is a  paragraph!".equals(pp.getTextContent())) {

+					flag = true;

+				}

+			}

+			Assert.assertTrue("Tabulator is not removed from paragraph!", flag);

+

+		} catch (Exception e) {

+			Logger.getLogger(SpanTest.class.getName()).log(Level.SEVERE, null, e);

+			Assert.fail();

+		}

+

+	}

+

+	

+}