blob: 5a620724369193dd7e39abe267b59f036c9ffd82 [file]
/*
* 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.
*/
/**
* @author Alexey A. Ivanov, Roman I. Chernyatchik
*/
package javax.swing.text;
import java.lang.ref.PhantomReference;
import java.lang.ref.Reference;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Vector;
import org.apache.harmony.x.swing.internal.nls.Messages;
abstract class ContentPositions {
public final class DocumentMark implements Position, Comparable {
protected int index;
protected final Reference ref;
private DocumentMark(final int offset) {
setOffset(offset);
ref = null;
}
private DocumentMark(final int offset, final Position pos) {
setOffset(offset);
this.ref = new MarkReference(pos, this, positionQueue);
insertPosition(this);
}
/*
* @see java.lang.Comparable#compareTo(java.lang.Object)
*/
public int compareTo(final Object obj) {
return compare((DocumentMark) obj);
}
public int getOffset() {
return getOffsetForDocumentMark(index);
}
protected int compare(final DocumentMark dm) {
return index - dm.index;
}
protected boolean isEqual(final DocumentMark dm) {
return compare(dm) == 0;
}
protected boolean isGreater(final DocumentMark dm) {
return compare(dm) > 0;
}
protected boolean isLess(final DocumentMark dm) {
return compare(dm) < 0;
}
protected void setOffset(final int offset) {
index = setOffsetForDocumentMark(offset);
}
}
private static final class MarkReference extends PhantomReference {
protected final DocumentMark hostMark;
public MarkReference(final Object userMark,
final DocumentMark hostMark, final ReferenceQueue queue) {
super(userMark, queue);
this.hostMark = hostMark;
}
}
private final class UndoPosRef {
private int documentOffset;
private WeakReference posWeakRef;
protected UndoPosRef(final WeakReference weakReference) {
posWeakRef = weakReference;
DocumentMark positionMark = (DocumentMark) posWeakRef.get();
if (positionMark != null) {
documentOffset = positionMark.getOffset();
}
}
protected void restorePos() {
DocumentMark positionMark = (DocumentMark) posWeakRef.get();
if (positionMark != null) {
positionMark.setOffset(documentOffset);
}
}
}
private final class WeakPosition implements Position {
private final DocumentMark mark;
public WeakPosition(final int offset) {
mark = new DocumentMark(offset, this);
}
public int getOffset() {
return mark.getOffset();
}
}
protected List positionList = new ArrayList();
private ReferenceQueue positionQueue = new ReferenceQueue();
private DocumentMark searchMark = new DocumentMark(0);
public Position createPosition(final int offset)
throws BadLocationException {
if (offset < 0) {
throw new BadLocationException(Messages.getString("swing.85"), offset); //$NON-NLS-1$
}
return new WeakPosition(offset);
}
/**
* Deletes position instances from the list that were cleared by the garbage
* collector. It is called (or must be called) in the methods modifying this
* list before the modification is done.
*/
public void deletePositions() {
MarkReference ref;
while ((ref = (MarkReference) positionQueue.poll()) != null) {
removePosition(ref.hostMark);
ref.clear();
}
}
public Vector getPositionsInRange(final Vector vector, final int offset,
final int len) {
Vector vect = vector;
if (vect == null) {
vect = new Vector();
}
deletePositions();
for (int i = getStartIndexByOffset(offset); i < positionList.size();
i++) {
DocumentMark documentMark = (DocumentMark) positionList.get(i);
final int markOffset = documentMark.getOffset();
assert offset <= markOffset : "Failed @ " + i + ": " + offset
+ " > " + markOffset;
if (markOffset <= offset + len) {
vect.add(new UndoPosRef(new WeakReference(documentMark)));
} else {
break;
}
}
return vect;
}
public void moveMarkIndexes(final int startIndex, final int diff) {
moveMarkIndexesByPosListIndexes(getStartIndexByIndex(startIndex),
positionList.size(), diff);
}
public void moveMarkIndexes(final int startIndex, final int endIndex,
final int diff) {
moveMarkIndexesByPosListIndexes(getStartIndexByIndex(startIndex),
getEndIndexByIndex(endIndex), diff);
}
public void setMarkIndexes(final int startIndex, final int endIndex,
final int newIndex) {
final int limit = getEndIndexByIndex(endIndex);
for (int i = getStartIndexByIndex(startIndex); i < limit; i++) {
DocumentMark dm = (DocumentMark) positionList.get(i);
dm.index = newIndex;
}
}
public void updateUndoPositions(final Vector positions) {
UndoPosRef undoPositionReference;
deletePositions();
for (int i = 0; i < positions.size(); i++) {
Object item = positions.get(i);
if (!(item instanceof UndoPosRef)) {
continue;
}
undoPositionReference = (UndoPosRef) item;
undoPositionReference.restorePos();
}
Collections.sort(positionList);
}
protected abstract int getOffsetForDocumentMark(final int index);
protected abstract int setOffsetForDocumentMark(final int offset);
private int getStartIndexByOffset(final int offset) {
searchMark.setOffset(offset);
return getStartIndexByIndex(searchMark.index);
}
private int getStartIndexByIndex(final int index) {
if (index == 0) {
// Marks at index 0 must never be updated
for (int i = 0; i < positionList.size(); i++) {
DocumentMark dm = (DocumentMark) positionList.get(i);
if (dm.index > 0) {
return i;
}
}
return positionList.size();
}
searchMark.index = index;
int result = findIndex(searchMark);
if (result > 0) {
DocumentMark dm;
do {
dm = (DocumentMark) positionList.get(result - 1);
} while (!dm.isLess(searchMark) && --result > 0);
}
return result;
}
private int getEndIndexByIndex(final int index) {
final int lastMark = positionList.size() - 1;
if (index == -1 || lastMark < 0) {
return lastMark + 1;
}
DocumentMark dm = (DocumentMark) positionList.get(lastMark);
if (index >= dm.index) {
return lastMark + 1;
}
searchMark.index = index;
int result = findIndex(searchMark);
if (result < lastMark) {
do {
dm = (DocumentMark) positionList.get(result);
} while (!dm.isGreater(searchMark) && ++result < lastMark);
}
return result;
}
private int findIndex(final DocumentMark documentMark) {
int result = Collections.binarySearch(positionList, documentMark);
return result < 0 ? -result - 1 : result + 1;
}
private void moveMarkIndexesByPosListIndexes(final int startPosListIndex,
final int endPosListIndex, final int diff) {
for (int i = startPosListIndex; i < endPosListIndex; i++) {
DocumentMark dm = (DocumentMark) positionList.get(i);
dm.index += diff;
}
}
private void insertPosition(final DocumentMark documentMark) {
deletePositions();
final int index = findIndex(documentMark);
positionList.add(index, documentMark);
}
private void removePosition(final DocumentMark position) {
int foundPos = Collections.binarySearch(positionList, position);
int pos = foundPos;
Position current = null;
while (pos >= 0
&& (current = (Position) positionList.get(pos)) != position
&& current.getOffset() == position.getOffset()) {
pos--;
}
if (current == position) {
positionList.remove(pos);
return;
}
current = null;
pos = foundPos + 1;
while (pos < positionList.size()
&& (current = (Position) positionList.get(pos)) != position
&& current.getOffset() == position.getOffset()) {
pos++;
}
if (current == position) {
positionList.remove(pos);
}
}
}