TextSelection.java
/*
* Copyright 2013 The Apache Software Foundation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.odftoolkit.odfdom.changes;
import java.util.List;
import org.odftoolkit.odfdom.pkg.OdfElement;
/** @author svante.schubertATgmail.com */
public abstract class TextSelection {
protected List<Integer> mStartPosition;
protected List<Integer> mEndPosition;
protected OdfElement mSelectionElement;
protected String mUrl;
/**
* Returns the startPosition of the Anchor element.
*
* @return the startPosition of the Anchor element.
*/
public List<Integer> getStartPosition() {
return mStartPosition;
}
/**
* Returns the endPosition of the Anchor element.
*
* @return the endPosition of the Anchor element.
*/
public List<Integer> getEndPosition() {
return mEndPosition;
}
/** @param endPosition end position of the Anchor element. */
public void setEndPosition(List<Integer> endPosition) {
this.mEndPosition = endPosition;
}
/** @return the element being used for selecting the text. */
public OdfElement getSelectionElement() {
return mSelectionElement;
}
/**
* Hyperlinks need to keep their URL even if merged with spans.
*
* @return true if a text hyperlink, or a span a hyperlink was merged into
*/
public boolean hasUrl() {
return mUrl != null;
}
/** @return the hyperlink URL */
public String getURL() {
return mUrl;
}
/** Even on a spanSelection a URL must be able to be set, in case an anchor is merged into it */
public void setURL(String url) {
mUrl = url;
}
/** Test if one of the two given TextSelection is overlapping */
public static boolean overLapping(TextSelection s1, TextSelection s2) {
boolean isOverlapping;
// S1 INBETWEEN S2
// is the start of s1 inbetween s2 positions
isOverlapping = isInbetween(s1.mStartPosition, s2);
// is the end of s1 inbetween s2 positions
if (!isOverlapping) {
isOverlapping = isInbetween(s1.mEndPosition, s2);
}
if (!isOverlapping) {
// S2 INBETWEEN S1
// is the start of s2 inbetween s1 positions
isOverlapping = isInbetween(s2.mStartPosition, s1);
// is the end of s2 inbetween s1 positions
if (!isOverlapping) {
isOverlapping = isInbetween(s2.mEndPosition, s1);
}
}
return isOverlapping;
}
/** Tests if the given position is in between the given TextSelection */
private static boolean isInbetween(List<Integer> firstPosition, TextSelection s2) {
boolean isInbetween = false;
boolean firstPositionIsAfter2ndStart = false;
boolean firstPositionIsBefore2ndEnd = false;
// first position after START position
if (1 == comparePosition(firstPosition, s2.mStartPosition)) {
firstPositionIsAfter2ndStart = true;
}
// first position before END position
if (-1 == comparePosition(firstPosition, s2.mEndPosition)) {
firstPositionIsBefore2ndEnd = true;
}
if (firstPositionIsAfter2ndStart && firstPositionIsBefore2ndEnd) {
isInbetween = true;
}
return isInbetween;
}
/**
* int compareTo(T o) Compares this object with the specified object for order. Returns a negative
* integer, zero, or a positive integer as this object is less than, equal to, or greater than the
* specified object. The implementor must ensure sgn(x.compareTo(y)) == -sgn(y.compareTo(x)) for
* all x and y. (This implies that x.compareTo(y) must throw an exception iff y.compareTo(x)
* throws an exception.)
*
* <p>The implementor must also ensure that the relation is transitive: (x.compareTo(y)>0 &&
* y.compareTo(z)>0) implies x.compareTo(z)>0.
*
* <p>Finally, the implementor must ensure that x.compareTo(y)==0 implies that sgn(x.compareTo(z))
* == sgn(y.compareTo(z)), for all z.
*
* <p>It is strongly recommended, but not strictly required that (x.compareTo(y)==0) ==
* (x.equals(y)). Generally speaking, any class that implements the Comparable interface and
* violates this condition should clearly indicate this fact. The recommended language is "Note:
* this class has a natural ordering that is inconsistent with equals."
*
* <p>In the foregoing description, the notation sgn(expression) designates the mathematical
* signum function, which is defined to return one of -1, 0, or 1 according to whether the value
* of expression is negative, zero or positive.
*
* <p>Parameters: o - the object to be compared. Returns: a negative integer, zero, or a positive
* integer as this object is less than, equal to, or greater than the specified object. Throws:
* ClassCastException - if the specified object's type prevents it from being compared to this
* object.
*/
public int compareTo(Object o) {
TextSelection s2 = (TextSelection) o;
int result = 0;
result = comparePosition(this.getStartPosition(), s2.getStartPosition());
if (result == 0) {
result = comparePosition(this.getEndPosition(), s2.getEndPosition());
}
return result;
}
/**
* @returns 1 if pos1 is after pos2, -1 if pos1 was before pos2 and 0 if both positions are equal
*/
private static int comparePosition(List<Integer> pos1, List<Integer> pos2) {
int result = 0;
int length1 = pos1.size();
int length2 = pos2.size();
int i = 0;
int value1;
int value2;
do {
// check if length is sufficent
if (length1 > i) {
value1 = pos1.get(i);
} else {
value1 = -1;
}
if (length2 > i) {
value2 = pos2.get(i);
} else {
value2 = -1;
}
if (value1 > value2) {
result = 1;
break;
} else if (value2 > value1) {
result = -1;
break;
}
if (value1 == -1 || value2 == -1) {
break;
}
i++;
} while (true);
return result;
}
}