mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
range marker lazy creation
This commit is contained in:
@@ -526,10 +526,6 @@ public class DocumentWindowImpl extends UserDataHolderBase implements Disposable
|
||||
return myDelegate.removeRangeMarker(((RangeMarkerWindow)rangeMarker).getDelegate());
|
||||
}
|
||||
|
||||
public void addRangeMarker(@NotNull RangeMarkerEx rangeMarker, int start, int end) {
|
||||
myDelegate.addRangeMarker(((RangeMarkerWindow)rangeMarker).getDelegate(), start, end);
|
||||
}
|
||||
|
||||
public boolean isInBulkUpdate() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -50,7 +50,6 @@ public interface DocumentEx extends Document {
|
||||
void clearLineModificationFlags();
|
||||
|
||||
boolean removeRangeMarker(@NotNull RangeMarkerEx rangeMarker);
|
||||
void addRangeMarker(@NotNull RangeMarkerEx rangeMarker, int start, int end);
|
||||
|
||||
boolean isInBulkUpdate();
|
||||
|
||||
|
||||
@@ -213,15 +213,19 @@ public class DocumentImpl extends UserDataHolderBase implements DocumentEx {
|
||||
return myRangeMarkers.removeInterval(rangeMarker);
|
||||
}
|
||||
|
||||
public void addRangeMarker(@NotNull RangeMarkerEx rangeMarker, int start, int end) {
|
||||
public void addRangeMarker(@NotNull RangeMarkerEx rangeMarker, int start, int end, boolean greedyToLeft, boolean greedyToRight, int layer) {
|
||||
ApplicationManagerEx.getApplicationEx().assertReadAccessToDocumentsAllowed();
|
||||
myRangeMarkers.addInterval(rangeMarker, start, end, null);
|
||||
myRangeMarkers.addInterval(rangeMarker, start, end, greedyToLeft, greedyToRight, layer);
|
||||
}
|
||||
|
||||
@TestOnly
|
||||
public int getRangeMarkersSize() {
|
||||
return myRangeMarkers.size();
|
||||
}
|
||||
@TestOnly
|
||||
public int getRangeMarkersNodeSize() {
|
||||
return myRangeMarkers.nodeSize();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public RangeMarker createGuardedBlock(int startOffset, int endOffset) {
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Copyright 2000-2011 JetBrains s.r.o.
|
||||
*
|
||||
* 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 com.intellij.openapi.editor.impl;
|
||||
|
||||
import java.lang.ref.ReferenceQueue;
|
||||
import java.lang.ref.WeakReference;
|
||||
|
||||
/**
|
||||
* User: cdr
|
||||
*/
|
||||
interface Getable<T> {
|
||||
T get();
|
||||
}
|
||||
class WeakReferencedGetable<T> extends WeakReference<T> implements Getable<T> {
|
||||
public WeakReferencedGetable(T referent, ReferenceQueue<? super T> q) {
|
||||
super(referent, q);
|
||||
}
|
||||
}
|
||||
@@ -26,6 +26,5 @@ public interface IntervalTree<T extends Interval> extends Iterable<T> {
|
||||
boolean processOverlappingWith(int start, int end, @NotNull Processor<? super T> processor);
|
||||
boolean processOverlappingWith(int offset, @NotNull Processor<? super T> processor);
|
||||
|
||||
Object addInterval(@NotNull T interval, int start, int end, Object data);
|
||||
boolean removeInterval(@NotNull T interval);
|
||||
}
|
||||
|
||||
+229
-144
@@ -15,17 +15,16 @@
|
||||
*/
|
||||
package com.intellij.openapi.editor.impl;
|
||||
|
||||
import com.intellij.openapi.editor.ex.RangeMarkerEx;
|
||||
import com.intellij.openapi.util.Ref;
|
||||
import com.intellij.openapi.util.Trinity;
|
||||
import com.intellij.util.IncorrectOperationException;
|
||||
import com.intellij.util.Processor;
|
||||
import com.intellij.util.SmartList;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import gnu.trove.TLongHashSet;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.lang.ref.Reference;
|
||||
import java.lang.ref.ReferenceQueue;
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.concurrent.locks.ReadWriteLock;
|
||||
@@ -35,13 +34,14 @@ import java.util.concurrent.locks.ReentrantReadWriteLock;
|
||||
* User: cdr
|
||||
*/
|
||||
public abstract class IntervalTreeImpl<T extends MutableInterval> extends RedBlackTree<T> implements IntervalTree<T> {
|
||||
protected int keySize; // number of all keys
|
||||
protected final ReadWriteLock l = new ReentrantReadWriteLock();
|
||||
|
||||
protected abstract EqualStartIntervalComparator<IntervalNode> getComparator();
|
||||
private final ReferenceQueue<T> myReferenceQueue = new ReferenceQueue<T>();
|
||||
private int deadReferenceCount;
|
||||
|
||||
public abstract static class IntervalNode extends RedBlackTree.Node implements MutableInterval {
|
||||
public abstract class IntervalNode extends RedBlackTree.Node<T> implements MutableInterval {
|
||||
protected int maxEnd; // max of all intervalEnd()s among all children.
|
||||
protected int delta; // delta of startOffset. getStartOffset() = myStartOffset + Sum of deltas up to root
|
||||
protected abstract int computeDeltaUpToRoot();
|
||||
@@ -53,28 +53,98 @@ public abstract class IntervalTreeImpl<T extends MutableInterval> extends RedBla
|
||||
public IntervalNode getRight() {
|
||||
return (IntervalNode)super.getRight();
|
||||
}
|
||||
|
||||
@Override
|
||||
public IntervalNode getParent() {
|
||||
return (IntervalNode)super.getParent();
|
||||
}
|
||||
|
||||
}
|
||||
protected class MyNode extends IntervalNode {
|
||||
|
||||
protected class MyNode extends IntervalNode {
|
||||
private volatile int myStart;
|
||||
private volatile int myEnd;
|
||||
private volatile boolean isValid = true;
|
||||
private final Reference<T> interval;
|
||||
protected final List<Getable<T>> intervals;
|
||||
public MyNode(@NotNull T key, int start, int end) {
|
||||
// maxEnd == 0 so to not disrupt existing maxes
|
||||
interval = new WeakReference<T>(key,myReferenceQueue);
|
||||
intervals = new SmartList<Getable<T>>(createGetable(key));
|
||||
myStart = start;
|
||||
myEnd = end;
|
||||
}
|
||||
@Override
|
||||
public T getKey() {
|
||||
return interval.get();
|
||||
public boolean processAliveKeys(@NotNull Processor<? super T> processor) {
|
||||
for (Getable<T> interval : intervals) {
|
||||
T key = interval.get();
|
||||
if (key != null && !processor.process(key)) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public List<T> getAliveKeys() {
|
||||
List<T> result = null;
|
||||
for (Getable<T> ref : intervals) {
|
||||
T interval = ref.get();
|
||||
if (interval == null) continue;
|
||||
if (result == null) {
|
||||
result = new ArrayList<T>(intervals.size());
|
||||
}
|
||||
result.add(interval);
|
||||
}
|
||||
return result == null ? Collections.<T>emptyList() : result;
|
||||
}
|
||||
|
||||
public boolean hasAliveKey(boolean purgeDead) {
|
||||
for (int i = intervals.size() - 1; i >= 0; i--) {
|
||||
Getable<T> interval = intervals.get(i);
|
||||
if (interval.get() != null) return true;
|
||||
if (purgeDead) {
|
||||
assertUnderWriteLock();
|
||||
intervals.remove(i);
|
||||
assert keySize > 0 : keySize;
|
||||
keySize--;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// removes interval and the node, if node become empty
|
||||
public boolean removeInterval(@NotNull T key) {
|
||||
checkBelongsToTheTree(key, true);
|
||||
assertUnderWriteLock();
|
||||
for (int i = intervals.size() - 1; i >= 0; i--) {
|
||||
Getable<T> interval = intervals.get(i);
|
||||
T t = interval.get();
|
||||
if (t == key) {
|
||||
intervals.remove(i);
|
||||
assert keySize > 0 : keySize;
|
||||
keySize--;
|
||||
if (intervals.isEmpty()) {
|
||||
removeNode(this);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
List<T> aliveKeys = getAliveKeys();
|
||||
assert false: "not found interval: "+key +"; "+ aliveKeys;
|
||||
return false;
|
||||
}
|
||||
|
||||
private void assertUnderWriteLock() {
|
||||
if (!VERIFY) return;
|
||||
String s = l.writeLock().toString();
|
||||
assert s.contains("Locked by thread") : s;
|
||||
}
|
||||
|
||||
public void addInterval(@NotNull T interval) {
|
||||
assertUnderWriteLock();
|
||||
intervals.add(createGetable(interval));
|
||||
keySize++;
|
||||
}
|
||||
|
||||
protected Getable<T> createGetable(@NotNull T interval) {
|
||||
return new WeakReferencedGetable<T>(interval, myReferenceQueue);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -134,38 +204,12 @@ public abstract class IntervalTreeImpl<T extends MutableInterval> extends RedBla
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected MyNode createNewNode(T key, int start, int end, Object data) {
|
||||
return new MyNode(key, start, end);
|
||||
}
|
||||
@NotNull
|
||||
protected abstract MyNode createNewNode(@NotNull T key, int start, int end, boolean greedyToLeft, boolean greedyToRight, int layer);
|
||||
protected abstract MyNode lookupNode(@NotNull T key);
|
||||
|
||||
@Override
|
||||
protected IntervalNode lookupNode(@NotNull T key, Node<T> interval) {
|
||||
return ((RangeMarkerImpl)key).myNode;
|
||||
//int delta = 0;
|
||||
//int delta1 = ((RangeMarkerImpl)key).myNode == null ? 0 : ((RangeMarkerImpl)key).myNode.computeDeltaUpToRoot();
|
||||
//List<IntervalNode> invalid = new ArrayList<IntervalNode>();
|
||||
//IntervalNode root = (IntervalNode)interval;
|
||||
//while (root != null) {
|
||||
// delta += root.delta;
|
||||
// int compResult = compareNodes(((RangeMarkerImpl)key).myNode, delta1, root, delta, invalid);
|
||||
// if (compResult < 0) {
|
||||
// root = root.getLeft();
|
||||
// }
|
||||
// else if (compResult > 0) {
|
||||
// root = root.getRight();
|
||||
// }
|
||||
// else {
|
||||
// return root;
|
||||
// }
|
||||
//}
|
||||
//deleteNodes(invalid);
|
||||
//return root;
|
||||
}
|
||||
|
||||
private int compareNodes(@NotNull IntervalNode i1, int delta1, @NotNull IntervalNode i2, int delta2, List<IntervalNode> invalid) {
|
||||
T i2Key = (T)i2.getKey();
|
||||
if (i2Key == null) {
|
||||
private int compareNodes(@NotNull IntervalNode i1, int delta1, @NotNull IntervalNode i2, int delta2, @NotNull List<IntervalNode> invalid) {
|
||||
if (!i2.hasAliveKey(false)) {
|
||||
invalid.add(i2); //gced
|
||||
}
|
||||
int start1 = i1.intervalStart() + delta1;
|
||||
@@ -179,8 +223,10 @@ public abstract class IntervalTreeImpl<T extends MutableInterval> extends RedBla
|
||||
protected interface EqualStartIntervalComparator<T extends MutableInterval> {
|
||||
|
||||
int compare(T i1, T i2);
|
||||
|
||||
}
|
||||
protected IntervalTreeImpl.IntervalNode getRoot() {
|
||||
|
||||
protected IntervalNode getRoot() {
|
||||
return (IntervalNode)root;
|
||||
}
|
||||
|
||||
@@ -200,11 +246,8 @@ public abstract class IntervalTreeImpl<T extends MutableInterval> extends RedBla
|
||||
if (root == null) return true;
|
||||
|
||||
if (!process(root.getLeft(), processor, modCountBefore)) return false;
|
||||
T key = (T)root.getKey();
|
||||
if (key != null) {
|
||||
if (!processor.process(key)) return false;
|
||||
assert modCount == modCountBefore;
|
||||
}
|
||||
if (!root.processAliveKeys(processor)) return false;
|
||||
assert modCount == modCountBefore;
|
||||
|
||||
return process(root.getRight(), processor, modCountBefore);
|
||||
}
|
||||
@@ -241,9 +284,8 @@ public abstract class IntervalTreeImpl<T extends MutableInterval> extends RedBla
|
||||
int myStartOffset = root.intervalStart() + delta;
|
||||
int myEndOffset = root.intervalEnd() + delta;
|
||||
boolean overlaps = Math.max(myStartOffset, start) <= Math.min(myEndOffset, end);
|
||||
T key = (T)root.getKey();
|
||||
if (key != null) {
|
||||
if (overlaps && !processor.process(key)) return false;
|
||||
if (overlaps) {
|
||||
if (!root.processAliveKeys(processor)) return false;
|
||||
assert modCount == modCountBefore;
|
||||
}
|
||||
|
||||
@@ -285,9 +327,8 @@ public abstract class IntervalTreeImpl<T extends MutableInterval> extends RedBla
|
||||
int myEndOffset = root.intervalEnd() + delta;
|
||||
boolean overlaps = myStartOffset <= offset && offset < myEndOffset;
|
||||
|
||||
T key = (T)root.getKey();
|
||||
if (key != null) {
|
||||
if (overlaps && !processor.process(key)) return false;
|
||||
if (overlaps) {
|
||||
if (!root.processAliveKeys(processor)) return false;
|
||||
assert modCount == modCountBefore;
|
||||
}
|
||||
|
||||
@@ -298,7 +339,7 @@ public abstract class IntervalTreeImpl<T extends MutableInterval> extends RedBla
|
||||
return processOverlapping(root.getRight(), offset, processor, modCountBefore, delta);
|
||||
}
|
||||
|
||||
protected void insert(@NotNull IntervalNode node) {
|
||||
protected MyNode findOrInsert(@NotNull IntervalNode node) {
|
||||
node.color = Color.RED;
|
||||
node.setParent(null);
|
||||
node.setValid(true);
|
||||
@@ -308,8 +349,6 @@ public abstract class IntervalTreeImpl<T extends MutableInterval> extends RedBla
|
||||
node.setRight(null);
|
||||
|
||||
List<IntervalNode> gced = new ArrayList<IntervalNode>();
|
||||
//T nodeKey = (T)node.getKey();
|
||||
//assert nodeKey != null;
|
||||
if (root == null) {
|
||||
root = node;
|
||||
}
|
||||
@@ -320,6 +359,9 @@ public abstract class IntervalTreeImpl<T extends MutableInterval> extends RedBla
|
||||
while (true) {
|
||||
delta += current.delta;
|
||||
int compResult = compareNodes(node, 0, current, delta, gced);
|
||||
if (compResult == 0) {
|
||||
return (MyNode)current;
|
||||
}
|
||||
if (compResult < 0) {
|
||||
if (current.getLeft() == null) {
|
||||
current.setLeft(node);
|
||||
@@ -327,44 +369,25 @@ public abstract class IntervalTreeImpl<T extends MutableInterval> extends RedBla
|
||||
}
|
||||
current = current.getLeft();
|
||||
}
|
||||
else if (compResult > 0) {
|
||||
else /*if (compResult > 0)*/ {
|
||||
if (current.getRight() == null) {
|
||||
current.setRight(node);
|
||||
break loop;
|
||||
}
|
||||
current = current.getRight();
|
||||
}
|
||||
else {
|
||||
T i1 = (T)node.getKey();
|
||||
T i2 = (T)current.getKey();
|
||||
int delta1 = 0;
|
||||
int delta2 = delta;
|
||||
assert false : "already inserted: " +
|
||||
i1 +
|
||||
":" +
|
||||
delta1 +
|
||||
"; " +
|
||||
((RangeMarkerEx)i1).getId() +
|
||||
" <-> " +
|
||||
i2 +
|
||||
":" +
|
||||
delta2 +
|
||||
"; " +
|
||||
((RangeMarkerEx)i2).getId() +
|
||||
" iden=" +
|
||||
(i1 == i2);
|
||||
return;
|
||||
}
|
||||
}
|
||||
node.delta = -delta;
|
||||
node.setParent(current);
|
||||
}
|
||||
size++;
|
||||
correctMaxUp(node);
|
||||
onInsertNode();
|
||||
keySize += ((MyNode)node).intervals.size();
|
||||
insertCase1(node);
|
||||
verifyProperties();
|
||||
|
||||
deleteNodes(gced);
|
||||
return (MyNode)node;
|
||||
}
|
||||
|
||||
private void deleteNodes(List<IntervalNode> collectedAway) {
|
||||
@@ -382,18 +405,21 @@ public abstract class IntervalTreeImpl<T extends MutableInterval> extends RedBla
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public IntervalNode addInterval(@NotNull T interval, int start, int end, Object data) {
|
||||
public MyNode addInterval(@NotNull T interval, int start, int end, boolean greedyToLeft, boolean greedyToRight, int layer) {
|
||||
try {
|
||||
l.writeLock().lock();
|
||||
checkMax(true);
|
||||
processReferenceQueue();
|
||||
modCount++;
|
||||
IntervalNode newNode = createNewNode(interval, start, end, data);
|
||||
insert(newNode);
|
||||
IntervalNode newNode = createNewNode(interval, start, end, greedyToLeft, greedyToRight, layer);
|
||||
MyNode insertedNode = findOrInsert(newNode);
|
||||
if (insertedNode != newNode) {
|
||||
// merged
|
||||
insertedNode.addInterval(interval);
|
||||
}
|
||||
|
||||
checkMax(true); // myNode assigned
|
||||
return newNode;
|
||||
checkMax(true);
|
||||
return insertedNode;
|
||||
}
|
||||
finally {
|
||||
l.writeLock().unlock();
|
||||
@@ -404,10 +430,13 @@ public abstract class IntervalTreeImpl<T extends MutableInterval> extends RedBla
|
||||
public boolean checkMax(boolean assertInvalid) {
|
||||
if (!VERIFY) return false;
|
||||
Ref<Boolean> allValid = new Ref<Boolean>(true);
|
||||
AtomicInteger counter = new AtomicInteger();
|
||||
checkMax(getRoot(), 0, assertInvalid, allValid, counter);
|
||||
AtomicInteger keyCounter = new AtomicInteger();
|
||||
AtomicInteger nodeCounter = new AtomicInteger();
|
||||
TLongHashSet ids = new TLongHashSet();
|
||||
checkMax(getRoot(), 0, assertInvalid, allValid, keyCounter, nodeCounter, ids);
|
||||
if (assertInvalid) {
|
||||
assert size == counter.get() : "size: "+size+"; actual: "+counter;
|
||||
assert nodeSize() == nodeCounter.get() : "node size: "+ nodeSize() +"; actual: "+nodeCounter;
|
||||
assert keySize == keyCounter.get() : "key size: "+ keySize +"; actual: "+keyCounter;
|
||||
}
|
||||
return allValid.get();
|
||||
}
|
||||
@@ -416,15 +445,23 @@ public abstract class IntervalTreeImpl<T extends MutableInterval> extends RedBla
|
||||
protected Trinity<Integer,Integer,Integer> checkMax(IntervalNode root,
|
||||
int deltaUpToRootExclusive,
|
||||
boolean assertInvalid,
|
||||
Ref<Boolean> allValid, AtomicInteger counter) {
|
||||
Ref<Boolean> allValid,
|
||||
AtomicInteger keyCounter,
|
||||
AtomicInteger nodeCounter,
|
||||
TLongHashSet ids) {
|
||||
if (root == null) return Trinity.create(Integer.MAX_VALUE,Integer.MIN_VALUE,Integer.MIN_VALUE);
|
||||
counter.getAndIncrement();
|
||||
for (T t : root.getAliveKeys()) {
|
||||
checkBelongsToTheTree(t, assertInvalid);
|
||||
assert ids.add(((RangeMarkerImpl)t).getId()) : t;
|
||||
}
|
||||
keyCounter.addAndGet(((MyNode)root).intervals.size());
|
||||
nodeCounter.incrementAndGet();
|
||||
int delta = deltaUpToRootExclusive + (root.isValid() ? root.delta : 0);
|
||||
Trinity<Integer, Integer, Integer> l = checkMax(root.getLeft(), delta, assertInvalid, allValid, counter);
|
||||
Trinity<Integer, Integer, Integer> l = checkMax(root.getLeft(), delta, assertInvalid, allValid, keyCounter, nodeCounter, ids);
|
||||
int minLeftStart = l.first;
|
||||
int maxLeftStart = l.second;
|
||||
int maxLeftEnd = l.third;
|
||||
Trinity<Integer, Integer, Integer> r = checkMax(root.getRight(), delta, assertInvalid, allValid, counter);
|
||||
Trinity<Integer, Integer, Integer> r = checkMax(root.getRight(), delta, assertInvalid, allValid, keyCounter, nodeCounter, ids);
|
||||
int maxRightEnd = r.third;
|
||||
int minRightStart = r.first;
|
||||
int maxRightStart = r.second;
|
||||
@@ -434,8 +471,7 @@ public abstract class IntervalTreeImpl<T extends MutableInterval> extends RedBla
|
||||
return Trinity.create(Math.min(minLeftStart, minRightStart), Math.max(maxLeftStart, maxRightStart), Math.max(maxRightEnd, maxLeftEnd));
|
||||
}
|
||||
IntervalNode parent = root.getParent();
|
||||
T rootKey = (T)root.getKey();
|
||||
if (parent != null && assertInvalid && rootKey != null) {
|
||||
if (parent != null && assertInvalid && root.hasAliveKey(false)) {
|
||||
int c = compareNodes(root, delta, parent, delta - root.delta, new ArrayList<IntervalNode>());
|
||||
assert c != 0;
|
||||
assert c < 0 && parent.getLeft() == root || c > 0 && parent.getRight() == root;
|
||||
@@ -465,17 +501,39 @@ public abstract class IntervalTreeImpl<T extends MutableInterval> extends RedBla
|
||||
return root;
|
||||
}
|
||||
|
||||
protected void checkBelongsToTheTree(T interval, boolean assertInvalid) {
|
||||
if (!VERIFY) return;
|
||||
MyNode root = lookupNode(interval);
|
||||
if (root == null) return;
|
||||
assert !root.intervals.isEmpty();
|
||||
|
||||
assert root.getTree() == this;
|
||||
List<T> keys = root.getAliveKeys();
|
||||
assert keys.contains(interval) : keys + "; " + interval;
|
||||
for (T key : keys) {
|
||||
MyNode node = lookupNode(key);
|
||||
assert assertInvalid && node == root || !assertInvalid && (node == null || node == root) : node;
|
||||
assert assertInvalid && node.getTree() == this || !assertInvalid && (node == null || node.getTree() == this) : node;
|
||||
}
|
||||
|
||||
IntervalNode e = root;
|
||||
while (e.getParent() != null) e = e.getParent();
|
||||
assert e == this.root; // assert the node belongs to our tree
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean removeInterval(@NotNull T interval) {
|
||||
if (!interval.isValid()) return false;
|
||||
try {
|
||||
l.writeLock().lock();
|
||||
checkBelongsToTheTree(interval, true);
|
||||
checkMax(true);
|
||||
processReferenceQueue();
|
||||
|
||||
IntervalNode node = lookupNode(interval, root);
|
||||
MyNode node = lookupNode(interval);
|
||||
if (node == null) return false;
|
||||
removeNode(node);
|
||||
|
||||
node.removeInterval(interval);
|
||||
checkMax(true);
|
||||
return true;
|
||||
}
|
||||
@@ -493,33 +551,51 @@ public abstract class IntervalTreeImpl<T extends MutableInterval> extends RedBla
|
||||
|
||||
@Override
|
||||
protected void deleteNode(Node<T> n) {
|
||||
pushDeltaFromRoot((IntervalNode)n);
|
||||
MyNode node = (MyNode)n;
|
||||
pushDeltaFromRoot(node);
|
||||
super.deleteNode(n);
|
||||
|
||||
keySize -= node.intervals.size();
|
||||
assert keySize >= 0 : keySize;
|
||||
}
|
||||
|
||||
protected static void pushDelta(IntervalNode root) {
|
||||
if (root == null || !root.isValid()) return;
|
||||
@Override
|
||||
public int size() {
|
||||
return keySize;
|
||||
}
|
||||
|
||||
// returns true if some delta was or became not null
|
||||
protected boolean pushDelta(IntervalNode root) {
|
||||
if (root == null || !root.isValid()) return false;
|
||||
int delta = root.delta;
|
||||
if (delta != 0) {
|
||||
root.setIntervalStart(root.intervalStart() + delta);
|
||||
root.setIntervalEnd(root.intervalEnd() + delta);
|
||||
root.maxEnd += delta;
|
||||
root.delta = 0;
|
||||
incDelta(root.getLeft(), delta);
|
||||
//noinspection NonShortCircuitBooleanExpression
|
||||
return
|
||||
incDelta(root.getLeft(), delta) |
|
||||
incDelta(root.getRight(), delta);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// returns true if some delta was or became not null
|
||||
private boolean incDelta(IntervalNode root, int delta) {
|
||||
if (root == null) return false;
|
||||
if (root.isValid()) {
|
||||
int newDelta = root.delta += delta;
|
||||
return newDelta != 0;
|
||||
}
|
||||
else {
|
||||
//noinspection NonShortCircuitBooleanExpression
|
||||
return
|
||||
incDelta(root.getLeft(), delta) |
|
||||
incDelta(root.getRight(), delta);
|
||||
}
|
||||
}
|
||||
|
||||
private static void incDelta(IntervalNode root, int delta) {
|
||||
if (root == null) return;
|
||||
if (root.isValid()) {
|
||||
root.delta += delta;
|
||||
}
|
||||
else {
|
||||
incDelta(root.getLeft(), delta);
|
||||
incDelta(root.getRight(), delta);
|
||||
}
|
||||
}
|
||||
@Override
|
||||
protected IntervalNode swapWithMaxPred(Node<T> root, Node<T> maxPred) {
|
||||
checkMax(false);
|
||||
@@ -546,7 +622,6 @@ public abstract class IntervalTreeImpl<T extends MutableInterval> extends RedBla
|
||||
checkMax(false);
|
||||
return a;
|
||||
}
|
||||
|
||||
private void swapNodes(IntervalNode n1, IntervalNode n2) {
|
||||
IntervalNode l1 = n1.getLeft();
|
||||
IntervalNode r1 = n1.getRight();
|
||||
@@ -580,7 +655,7 @@ public abstract class IntervalTreeImpl<T extends MutableInterval> extends RedBla
|
||||
if (r2 != null) r2.setParent(n1);
|
||||
}
|
||||
|
||||
protected static int maxEndOf(IntervalNode node, int deltaUpToRootExclusive) {
|
||||
private int maxEndOf(IntervalNode node, int deltaUpToRootExclusive) {
|
||||
if (node == null) {
|
||||
return 0;
|
||||
}
|
||||
@@ -591,14 +666,14 @@ public abstract class IntervalTreeImpl<T extends MutableInterval> extends RedBla
|
||||
}
|
||||
|
||||
// max of n.left's maxend, n.right's maxend and its own interval endOffset
|
||||
protected static void correctMax(@NotNull IntervalNode node, int deltaUpToRoot) {
|
||||
protected void correctMax(@NotNull IntervalNode node, int deltaUpToRoot) {
|
||||
if (!node.isValid()) return;
|
||||
int realMax = Math.max(Math.max(maxEndOf(node.getLeft(), deltaUpToRoot), maxEndOf(node.getRight(), deltaUpToRoot)),
|
||||
deltaUpToRoot + node.intervalEnd());
|
||||
node.maxEnd = realMax - deltaUpToRoot;
|
||||
}
|
||||
|
||||
private static void correctMaxUp(IntervalNode node) {
|
||||
private void correctMaxUp(IntervalNode node) {
|
||||
int delta = node == null ? 0 : node.computeDeltaUpToRoot();
|
||||
while (node != null) {
|
||||
if (node.isValid()) {
|
||||
@@ -720,37 +795,39 @@ public abstract class IntervalTreeImpl<T extends MutableInterval> extends RedBla
|
||||
final int modCountBefore = modCount;
|
||||
return new Iterator<T>() {
|
||||
private IntervalNode node = firstNode;
|
||||
private T element = (T)firstNode.getKey();
|
||||
private Iterator<T> iteratorInCurrentList = firstNode.getAliveKeys().iterator();
|
||||
|
||||
{
|
||||
// find first non-null key
|
||||
while (element == null) {
|
||||
while (!iteratorInCurrentList.hasNext()) {
|
||||
moveNext();
|
||||
if (node == null) break;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean hasNext() {
|
||||
return element != null;
|
||||
Iterator<T> it = iteratorInCurrentList;
|
||||
return it != null && it.hasNext();
|
||||
}
|
||||
|
||||
public T next() {
|
||||
assert modCount == modCountBefore : "Must not modify range markers during iterate";
|
||||
if (node == null || element == null) throw new NoSuchElementException();
|
||||
T current = element;
|
||||
if (node == null || iteratorInCurrentList == null || !iteratorInCurrentList.hasNext()) throw new NoSuchElementException();
|
||||
T current = iteratorInCurrentList.next();
|
||||
moveNext();
|
||||
return current;
|
||||
}
|
||||
|
||||
private void moveNext() {
|
||||
if (iteratorInCurrentList != null && iteratorInCurrentList.hasNext()) return;
|
||||
while (true) {
|
||||
node = getNextNode();
|
||||
if (node == null) {
|
||||
element = null;
|
||||
iteratorInCurrentList = null;
|
||||
break;
|
||||
}
|
||||
element = (T)node.getKey();
|
||||
if (element != null) break;
|
||||
iteratorInCurrentList = node.getAliveKeys().iterator();
|
||||
if (iteratorInCurrentList.hasNext()) break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -793,14 +870,38 @@ public abstract class IntervalTreeImpl<T extends MutableInterval> extends RedBla
|
||||
l.writeLock().unlock();
|
||||
}
|
||||
}
|
||||
|
||||
private static void normalize(IntervalNode root) {
|
||||
private void normalize(IntervalNode root) {
|
||||
if (root == null) return;
|
||||
pushDelta(root);
|
||||
normalize(root.getLeft());
|
||||
normalize(root.getRight());
|
||||
}
|
||||
|
||||
public void changeData(T interval, int start, int end, boolean greedyToLeft, boolean greedyToRight, int layer) {
|
||||
try {
|
||||
l.writeLock().lock();
|
||||
|
||||
MyNode node = lookupNode(interval);
|
||||
if (node == null) return;
|
||||
int before = size();
|
||||
boolean nodeRemoved = node.removeInterval(interval);
|
||||
assert nodeRemoved || !node.intervals.isEmpty();
|
||||
|
||||
MyNode insertedNode = addInterval(interval, start, end, greedyToLeft, greedyToRight, layer);
|
||||
assert node != insertedNode;
|
||||
|
||||
int after = size();
|
||||
// can be gced
|
||||
assert before >= after : before +";" + after;
|
||||
checkBelongsToTheTree(interval, true);
|
||||
checkMax(true);
|
||||
}
|
||||
finally {
|
||||
l.writeLock().unlock();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// called under write lock
|
||||
private void processReferenceQueue() {
|
||||
int dead = 0;
|
||||
@@ -821,35 +922,19 @@ public abstract class IntervalTreeImpl<T extends MutableInterval> extends RedBla
|
||||
deleteNodes(gced);
|
||||
}
|
||||
|
||||
private static void collectGced(IntervalNode root, List<IntervalNode> gced) {
|
||||
private void collectGced(IntervalNode root, List<IntervalNode> gced) {
|
||||
if (root == null) return;
|
||||
if (root.getKey() == null) gced.add(root);
|
||||
if (!root.hasAliveKey(true)) gced.add(root);
|
||||
collectGced(root.getLeft(), gced);
|
||||
collectGced(root.getRight(), gced);
|
||||
}
|
||||
|
||||
public void changeAttribute(@NotNull IntervalNode node, @NotNull Runnable changeAttributes) {
|
||||
normalize();
|
||||
try {
|
||||
l.writeLock().lock();
|
||||
checkMax(true);
|
||||
removeNode(node);
|
||||
changeAttributes.run();
|
||||
insert(node);
|
||||
checkMax(true);
|
||||
}
|
||||
finally {
|
||||
l.writeLock().unlock();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void printSorted() { printSorted(getRoot());}
|
||||
private static void printSorted(IntervalNode root) {
|
||||
private void printSorted(IntervalNode root) {
|
||||
if (root == null) return;
|
||||
printSorted(root.getLeft());
|
||||
System.out.println(root);
|
||||
printSorted(root.getRight());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -35,9 +35,7 @@ import com.intellij.openapi.editor.markup.HighlighterTargetArea;
|
||||
import com.intellij.openapi.editor.markup.RangeHighlighter;
|
||||
import com.intellij.openapi.editor.markup.TextAttributes;
|
||||
import com.intellij.openapi.util.UserDataHolderBase;
|
||||
import com.intellij.util.CommonProcessors;
|
||||
import com.intellij.util.Consumer;
|
||||
import com.intellij.util.Processor;
|
||||
import com.intellij.util.*;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
@@ -99,6 +97,7 @@ public class MarkupModelImpl extends UserDataHolderBase implements MarkupModelEx
|
||||
return offset;
|
||||
}
|
||||
|
||||
// NB: Can return invalid highlighters
|
||||
@NotNull
|
||||
public RangeHighlighter[] getAllHighlighters() {
|
||||
ApplicationManager.getApplication().assertIsDispatchThread();
|
||||
@@ -123,16 +122,11 @@ public class MarkupModelImpl extends UserDataHolderBase implements MarkupModelEx
|
||||
ApplicationManager.getApplication().assertIsDispatchThread();
|
||||
RangeHighlighterEx highlighter = isPersistent
|
||||
? new PersistentRangeHighlighterImpl(this, startOffset, layer, targetArea, textAttributes)
|
||||
: new RangeHighlighterImpl(this, startOffset, endOffset, layer, targetArea, textAttributes);
|
||||
: new RangeHighlighterImpl(this, startOffset, endOffset, layer, targetArea, textAttributes, false, false);
|
||||
|
||||
myCachedHighlighters = null;
|
||||
if (changeAttributesAction != null) {
|
||||
if (isPersistent) {
|
||||
((PersistentRangeHighlighterImpl)highlighter).changeAttributesInBatch(changeAttributesAction);
|
||||
}
|
||||
else {
|
||||
((RangeHighlighterImpl)highlighter).changeAttributesInBatch(changeAttributesAction);
|
||||
}
|
||||
((RangeHighlighterImpl)highlighter).changeAttributesNoEvents(changeAttributesAction);
|
||||
}
|
||||
fireAfterAdded(highlighter);
|
||||
return highlighter;
|
||||
@@ -141,17 +135,20 @@ public class MarkupModelImpl extends UserDataHolderBase implements MarkupModelEx
|
||||
@Override
|
||||
public void changeAttributesInBatch(@NotNull RangeHighlighterEx highlighter, @NotNull Consumer<RangeHighlighterEx> changeAttributesAction) {
|
||||
ApplicationManager.getApplication().assertIsDispatchThread();
|
||||
boolean changed = highlighter instanceof PersistentRangeHighlighterImpl
|
||||
? ((PersistentRangeHighlighterImpl)highlighter).changeAttributesInBatch(changeAttributesAction)
|
||||
: ((RangeHighlighterImpl)highlighter).changeAttributesInBatch(changeAttributesAction);
|
||||
boolean changed = ((RangeHighlighterImpl)highlighter).changeAttributesNoEvents(changeAttributesAction);
|
||||
if (changed) {
|
||||
fireAttributesChanged(highlighter);
|
||||
}
|
||||
}
|
||||
|
||||
void addRangeHighlighter(RangeHighlighterEx marker, int start, int end, RangeHighlighterData data) {
|
||||
IntervalTreeImpl<RangeHighlighterEx>.IntervalNode addRangeHighlighter(RangeHighlighterEx marker,
|
||||
int start,
|
||||
int end,
|
||||
boolean greedyToLeft,
|
||||
boolean greedyToRight,
|
||||
int layer) {
|
||||
ApplicationManager.getApplication().assertIsDispatchThread();
|
||||
myHighlighterTree.addInterval(marker, start, end, data);
|
||||
return myHighlighterTree.addInterval(marker, start, end, greedyToLeft, greedyToRight, layer);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
|
||||
+2
-1
@@ -34,7 +34,8 @@ class PersistentRangeHighlighterImpl extends RangeHighlighterImpl implements Ran
|
||||
int layer,
|
||||
@NotNull HighlighterTargetArea target,
|
||||
TextAttributes textAttributes) {
|
||||
super(model, model.getDocument().getLineStartOffset(model.getDocument().getLineNumber(offset)), model.getDocument().getLineEndOffset(model.getDocument().getLineNumber(offset)),layer, target, textAttributes);
|
||||
super(model, model.getDocument().getLineStartOffset(model.getDocument().getLineNumber(offset)), model.getDocument().getLineEndOffset(model.getDocument().getLineNumber(offset)),layer, target, textAttributes,
|
||||
false, false);
|
||||
setLine(model.getDocument().getLineNumber(offset));
|
||||
}
|
||||
|
||||
|
||||
+9
-15
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
package com.intellij.openapi.editor.impl;
|
||||
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.editor.Document;
|
||||
import com.intellij.openapi.editor.ex.RangeHighlighterEx;
|
||||
import com.intellij.openapi.editor.markup.*;
|
||||
@@ -30,7 +31,6 @@ import java.awt.*;
|
||||
*/
|
||||
abstract class RangeHighlighterData {
|
||||
private final MarkupModel myModel;
|
||||
private final int myLayer;
|
||||
private final HighlighterTargetArea myTargetArea;
|
||||
private TextAttributes myTextAttributes;
|
||||
private LineMarkerRenderer myLineMarkerRenderer;
|
||||
@@ -46,12 +46,10 @@ abstract class RangeHighlighterData {
|
||||
int myLine; // for PersistentRangeHighlighterImpl only
|
||||
|
||||
RangeHighlighterData(@NotNull MarkupModel model,
|
||||
int layer,
|
||||
@NotNull HighlighterTargetArea target,
|
||||
TextAttributes textAttributes) {
|
||||
myTextAttributes = textAttributes;
|
||||
myTargetArea = target;
|
||||
myLayer = layer;
|
||||
myModel = model;
|
||||
if (textAttributes != null) {
|
||||
myErrorStripeColor = textAttributes.getErrorStripeColor();
|
||||
@@ -73,10 +71,6 @@ abstract class RangeHighlighterData {
|
||||
}
|
||||
}
|
||||
|
||||
public int getLayer() {
|
||||
return myLayer;
|
||||
}
|
||||
|
||||
public HighlighterTargetArea getTargetArea() {
|
||||
return myTargetArea;
|
||||
}
|
||||
@@ -127,6 +121,7 @@ abstract class RangeHighlighterData {
|
||||
}
|
||||
|
||||
public void setErrorStripeTooltip(Object tooltipObject) {
|
||||
ApplicationManager.getApplication().assertIsDispatchThread();
|
||||
Object old = myErrorStripeTooltip;
|
||||
myErrorStripeTooltip = tooltipObject;
|
||||
if (!Comparing.equal(old, tooltipObject)) {
|
||||
@@ -139,6 +134,7 @@ abstract class RangeHighlighterData {
|
||||
}
|
||||
|
||||
public void setThinErrorStripeMark(boolean value) {
|
||||
ApplicationManager.getApplication().assertIsDispatchThread();
|
||||
boolean old = myErrorStripeMarkIsThin;
|
||||
myErrorStripeMarkIsThin = value;
|
||||
if (old != value) {
|
||||
@@ -223,16 +219,10 @@ abstract class RangeHighlighterData {
|
||||
return Math.min(textLength, document.getLineEndOffset(document.getLineNumber(endOffset)) + 1);
|
||||
}
|
||||
|
||||
public void registerMe(int start, int end) {
|
||||
((MarkupModelImpl)myModel).addRangeHighlighter(getRangeHighlighter(), start, end,this);
|
||||
}
|
||||
|
||||
public void unregisterMe() {
|
||||
myModel.removeHighlighter(getRangeHighlighter());
|
||||
}
|
||||
|
||||
// returns true if change was detected
|
||||
boolean changeAttributesInBatch(@NotNull Consumer<RangeHighlighterEx> change) {
|
||||
assert !inBatchChange;
|
||||
assert !changed;
|
||||
inBatchChange = true;
|
||||
boolean result;
|
||||
try {
|
||||
@@ -245,4 +235,8 @@ abstract class RangeHighlighterData {
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public MarkupModel getMarkupModel() {
|
||||
return myModel;
|
||||
}
|
||||
}
|
||||
|
||||
+32
-10
@@ -28,43 +28,64 @@ import java.awt.*;
|
||||
* Implementation of the markup element for the editor and document.
|
||||
* @author max
|
||||
*/
|
||||
class RangeHighlighterImpl extends RangeMarkerImpl implements RangeHighlighterEx {
|
||||
class RangeHighlighterImpl extends RangeMarkerImpl implements RangeHighlighterEx, Getable<RangeHighlighterImpl> {
|
||||
private final RangeHighlighterData data;
|
||||
|
||||
RangeHighlighterImpl(@NotNull MarkupModel model,
|
||||
int start,
|
||||
int end,
|
||||
int layer,
|
||||
@NotNull HighlighterTargetArea target,
|
||||
TextAttributes textAttributes) {
|
||||
TextAttributes textAttributes, boolean greedyToLeft, boolean greedyToRight) {
|
||||
super((DocumentEx)model.getDocument(), start, end,false);
|
||||
|
||||
RangeHighlighterData data = new RangeHighlighterData(model, layer, target, textAttributes) {
|
||||
data = new RangeHighlighterData(model, target, textAttributes) {
|
||||
@NotNull
|
||||
@Override
|
||||
public RangeHighlighterEx getRangeHighlighter() {
|
||||
return RangeHighlighterImpl.this;
|
||||
}
|
||||
};
|
||||
data.registerMe(start, end);
|
||||
|
||||
registerInTree(start, end, greedyToLeft, greedyToRight, layer);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setGreedyToLeft(boolean greedy) {
|
||||
if (!isValid() || greedy == isGreedyToLeft()) return;
|
||||
myNode.getTree().changeData(this, getStartOffset(), getEndOffset(), greedy, isGreedyToRight(), getLayer());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setGreedyToRight(boolean greedy) {
|
||||
if (!isValid() || greedy == isGreedyToRight()) return;
|
||||
myNode.getTree().changeData(this, getStartOffset(), getEndOffset(), isGreedyToLeft(), greedy, getLayer());
|
||||
}
|
||||
|
||||
protected RangeHighlighterData getData() {
|
||||
return ((RangeHighlighterTree.RHNode)myNode).data;
|
||||
return data;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void registerInDocument(int start, int end) {
|
||||
protected void registerInTree(int start, int end, boolean greedyToLeft, boolean greedyToRight, int layer) {
|
||||
// we store highlighters in MarkupModel
|
||||
((MarkupModelImpl)data.getMarkupModel()).addRangeHighlighter(this, start, end, greedyToLeft, greedyToRight, layer);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean unregisterInDocument() {
|
||||
protected boolean unregisterInTree() {
|
||||
if (myNode == null) return false;
|
||||
// we store highlighters in MarkupModel
|
||||
getData().unregisterMe();
|
||||
getData().getMarkupModel().removeHighlighter(this);
|
||||
myNode = null;
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RangeHighlighterImpl get() {
|
||||
return this;
|
||||
}
|
||||
|
||||
// delegates
|
||||
public TextAttributes getTextAttributes() {
|
||||
return getData().getTextAttributes();
|
||||
@@ -74,12 +95,13 @@ class RangeHighlighterImpl extends RangeMarkerImpl implements RangeHighlighterEx
|
||||
getData().setTextAttributes(textAttributes);
|
||||
}
|
||||
|
||||
boolean changeAttributesInBatch(@NotNull Consumer<RangeHighlighterEx> change) {
|
||||
boolean changeAttributesNoEvents(@NotNull Consumer<RangeHighlighterEx> change) {
|
||||
return getData().changeAttributesInBatch(change);
|
||||
}
|
||||
|
||||
public int getLayer() {
|
||||
return getData().getLayer();
|
||||
RangeHighlighterTree.RHNode node = (RangeHighlighterTree.RHNode)myNode;
|
||||
return node == null ? -1 : node.myLayer;
|
||||
}
|
||||
|
||||
public HighlighterTargetArea getTargetArea() {
|
||||
|
||||
+22
-14
@@ -28,8 +28,8 @@ public class RangeHighlighterTree extends RangeMarkerTree<RangeHighlighterEx> {
|
||||
public int compare(IntervalNode i1, IntervalNode i2) {
|
||||
RHNode o1 = (RHNode)i1;
|
||||
RHNode o2 = (RHNode)i2;
|
||||
if (o1.data.getLayer() != o2.data.getLayer()) {
|
||||
return o2.data.getLayer() - o1.data.getLayer();
|
||||
if (o1.myLayer != o2.myLayer) {
|
||||
return o2.myLayer - o1.myLayer;
|
||||
}
|
||||
boolean greedyL1 = o1.isGreedyToLeft();
|
||||
boolean greedyL2 = o2.isGreedyToLeft();
|
||||
@@ -42,9 +42,7 @@ public class RangeHighlighterTree extends RangeMarkerTree<RangeHighlighterEx> {
|
||||
boolean greedyR2 = o2.isGreedyToRight();
|
||||
if (greedyR1 != greedyR2) return greedyR1 ? -1 : 1;
|
||||
|
||||
// for now we tolerate equal range range markers (till lazy creation impl)
|
||||
d = (int)(o1.getId() - o2.getId());
|
||||
return d;
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -57,19 +55,29 @@ public class RangeHighlighterTree extends RangeMarkerTree<RangeHighlighterEx> {
|
||||
return myComparator;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
protected RHNode createNewNode(RangeHighlighterEx key, int start, int end, Object data) {
|
||||
RHNode node = new RHNode(key, start, end, (RangeHighlighterData)data);
|
||||
((RangeMarkerImpl)key).myNode = node;
|
||||
return node;
|
||||
protected RHNode createNewNode(@NotNull RangeHighlighterEx key, int start, int end, boolean greedyToLeft, boolean greedyToRight, int layer) {
|
||||
return new RHNode(key, start, end, greedyToLeft, greedyToRight,layer);
|
||||
}
|
||||
|
||||
class RHNode extends RMNode {
|
||||
final RangeHighlighterData data;
|
||||
public RHNode(@NotNull final RangeHighlighterEx key, int start, int end, RangeHighlighterData data) {
|
||||
super(key, start, end);
|
||||
this.data = data;
|
||||
final int myLayer;
|
||||
|
||||
public RHNode(@NotNull final RangeHighlighterEx key,
|
||||
int start,
|
||||
int end,
|
||||
boolean greedyToLeft,
|
||||
boolean greedyToRight,
|
||||
int layer) {
|
||||
super(key, start, end, greedyToLeft, greedyToRight);
|
||||
myLayer = layer;
|
||||
}
|
||||
|
||||
// range highlighters are strongly referenced
|
||||
@Override
|
||||
protected Getable<RangeHighlighterEx> createGetable(@NotNull RangeHighlighterEx interval) {
|
||||
return (Getable)interval;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -23,13 +23,22 @@ import com.intellij.openapi.util.UserDataHolderBase;
|
||||
import org.jetbrains.annotations.NonNls;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
|
||||
public class RangeMarkerImpl extends UserDataHolderBase implements RangeMarkerEx, MutableInterval {
|
||||
private static final Logger LOG = Logger.getInstance("#com.intellij.openapi.editor.impl.RangeMarkerImpl");
|
||||
|
||||
protected final DocumentEx myDocument;
|
||||
RangeMarkerTree.RMNode myNode;
|
||||
|
||||
private final long myId;
|
||||
//private static long counter;
|
||||
private static final AtomicLong counter = new AtomicLong();
|
||||
|
||||
protected RangeMarkerImpl(@NotNull DocumentEx document, int start, int end, boolean register) {
|
||||
this(document, start, end, register, false, false);
|
||||
}
|
||||
private RangeMarkerImpl(@NotNull DocumentEx document, int start, int end, boolean register, boolean greedyToLeft, boolean greedyToRight) {
|
||||
if (start < 0) {
|
||||
throw new IllegalArgumentException("Wrong start: " + start+"; end="+end);
|
||||
}
|
||||
@@ -41,29 +50,38 @@ public class RangeMarkerImpl extends UserDataHolderBase implements RangeMarkerEx
|
||||
}
|
||||
|
||||
myDocument = document;
|
||||
myId = counter.getAndIncrement();
|
||||
if (register) {
|
||||
registerInDocument(start, end);
|
||||
registerInTree(start, end, greedyToLeft, greedyToRight, 0);
|
||||
}
|
||||
}
|
||||
|
||||
protected void registerInDocument(int start, int end) {
|
||||
myNode = null;
|
||||
myDocument.addRangeMarker(this, start, end);
|
||||
assert myNode != null;
|
||||
private static Object createData(boolean greedyToLeft, boolean greedyToRight) {
|
||||
return (greedyToLeft ? 2 : 0) | (greedyToRight ? 1 : 0);
|
||||
}
|
||||
protected boolean unregisterInDocument() {
|
||||
|
||||
protected void registerInTree(int start, int end, boolean greedyToLeft, boolean greedyToRight, int layer) {
|
||||
((DocumentImpl)myDocument).addRangeMarker(this, start, end, greedyToLeft, greedyToRight, layer);
|
||||
}
|
||||
|
||||
protected boolean unregisterInTree() {
|
||||
IntervalTreeImpl tree = myNode.getTree();
|
||||
tree.checkMax(true);
|
||||
boolean b = myDocument.removeRangeMarker(this);
|
||||
myNode = null;
|
||||
tree.checkMax(true);
|
||||
return b;
|
||||
}
|
||||
|
||||
public long getId() {
|
||||
return myNode.getId();
|
||||
return myId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose() {
|
||||
unregisterInDocument();
|
||||
if(isValid()) {
|
||||
unregisterInTree();
|
||||
}
|
||||
}
|
||||
|
||||
public int getStartOffset() {
|
||||
@@ -85,22 +103,13 @@ public class RangeMarkerImpl extends UserDataHolderBase implements RangeMarkerEx
|
||||
|
||||
public void setGreedyToLeft(final boolean greedy) {
|
||||
if (!isValid() || greedy == isGreedyToLeft()) return;
|
||||
myNode.getTree().changeAttribute(myNode, new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
myNode.setGreedyToLeft(greedy);
|
||||
}
|
||||
});
|
||||
|
||||
myNode.getTree().changeData(this, getStartOffset(), getEndOffset(), greedy, isGreedyToRight(), 0);
|
||||
}
|
||||
|
||||
public void setGreedyToRight(final boolean greedy) {
|
||||
if (!isValid() || greedy == isGreedyToRight()) return;
|
||||
myNode.getTree().changeAttribute(myNode, new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
myNode.setGreedyToRight(greedy);
|
||||
}
|
||||
});
|
||||
myNode.getTree().changeData(this, getStartOffset(), getEndOffset(), isGreedyToLeft(), greedy, 0);
|
||||
}
|
||||
|
||||
public boolean isGreedyToLeft() {
|
||||
@@ -210,7 +219,7 @@ public class RangeMarkerImpl extends UserDataHolderBase implements RangeMarkerEx
|
||||
@NonNls
|
||||
public String toString() {
|
||||
return "RangeMarker" + (isGreedyToLeft() ? "[" : "(") + (isValid() ? "valid" : "invalid") + "," + getStartOffset() + "," + getEndOffset() + (
|
||||
isGreedyToRight() ? "]" : ")");
|
||||
isGreedyToRight() ? "]" : ")") + " " + getId();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -241,9 +250,7 @@ public class RangeMarkerImpl extends UserDataHolderBase implements RangeMarkerEx
|
||||
if (node == null) {
|
||||
return -1;
|
||||
}
|
||||
else {
|
||||
return node.intervalStart();
|
||||
}
|
||||
return node.intervalStart();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -252,8 +259,6 @@ public class RangeMarkerImpl extends UserDataHolderBase implements RangeMarkerEx
|
||||
if (node == null) {
|
||||
return -1;
|
||||
}
|
||||
else {
|
||||
return node.intervalEnd();
|
||||
}
|
||||
return node.intervalEnd();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,22 +20,18 @@ import com.intellij.openapi.editor.event.DocumentEvent;
|
||||
import com.intellij.openapi.editor.ex.MarkupModelEx;
|
||||
import com.intellij.openapi.editor.ex.PrioritizedDocumentListener;
|
||||
import com.intellij.openapi.editor.ex.RangeMarkerEx;
|
||||
import com.intellij.openapi.util.Ref;
|
||||
import com.intellij.openapi.util.Segment;
|
||||
import com.intellij.openapi.util.Trinity;
|
||||
import com.intellij.util.Processor;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
|
||||
/**
|
||||
* User: cdr
|
||||
*/
|
||||
public class RangeMarkerTree<T extends RangeMarkerEx> extends IntervalTreeImpl<T> {
|
||||
private final PrioritizedDocumentListener myListener;
|
||||
private final Document myDocument;
|
||||
final Document myDocument;
|
||||
private final EqualStartIntervalComparator<IntervalNode> myEqualStartIntervalComparator = new EqualStartIntervalComparator<IntervalNode>() {
|
||||
@Override
|
||||
public int compare(IntervalNode i1, IntervalNode i2) {
|
||||
@@ -54,9 +50,7 @@ public class RangeMarkerTree<T extends RangeMarkerEx> extends IntervalTreeImpl<T
|
||||
boolean greedyR2 = o2.isGreedyToRight();
|
||||
if (greedyR1 != greedyR2) return greedyR1 ? -1 : 1;
|
||||
|
||||
// for now we tolerate equal range range markers (till lazy creation impl)
|
||||
d = (int)(o1.getId() - o2.getId());
|
||||
return d;
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -88,53 +82,49 @@ public class RangeMarkerTree<T extends RangeMarkerEx> extends IntervalTreeImpl<T
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Trinity<Integer, Integer, Integer> checkMax(IntervalNode root,
|
||||
int deltaUpToRootExclusive,
|
||||
boolean assertInvalid,
|
||||
Ref<Boolean> allValid, AtomicInteger counter) {
|
||||
if (root != null) {
|
||||
RangeMarkerImpl r = (RangeMarkerImpl)root.getKey();
|
||||
if (r != null) {
|
||||
assert /*r.myNode == null || */r.myNode == root;
|
||||
assert r.myNode.getTree() == this;
|
||||
}
|
||||
}
|
||||
return super.checkMax(root, deltaUpToRootExclusive, assertInvalid, allValid, counter);
|
||||
}
|
||||
|
||||
@Override
|
||||
public IntervalNode addInterval(@NotNull T interval, int start, int end, Object data) {
|
||||
public MyNode addInterval(@NotNull T interval, int start, int end, boolean greedyToLeft, boolean greedyToRight, int layer) {
|
||||
RangeMarkerImpl marker = (RangeMarkerImpl)interval;
|
||||
marker.setValid(true);
|
||||
return super.addInterval(interval, start, end, data);
|
||||
}
|
||||
RMNode node = (RMNode)super.addInterval(interval, start, end, greedyToLeft, greedyToRight, layer);
|
||||
((RangeMarkerImpl)interval).myNode = node;
|
||||
|
||||
checkBelongsToTheTree(interval, true);
|
||||
|
||||
@Override
|
||||
protected RMNode createNewNode(T key, int start, int end, Object data) {
|
||||
RMNode node = new RMNode(key, start, end);
|
||||
((RangeMarkerImpl)key).myNode = node;
|
||||
return node;
|
||||
}
|
||||
|
||||
//private static long counter;
|
||||
private static final AtomicLong counter = new AtomicLong();
|
||||
@NotNull
|
||||
@Override
|
||||
protected RMNode createNewNode(@NotNull T key, int start, int end, boolean greedyToLeft, boolean greedyToRight, int layer) {
|
||||
return new RMNode(key, start, end, greedyToLeft, greedyToRight);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void checkBelongsToTheTree(T interval, boolean assertInvalid) {
|
||||
if (!VERIFY) return;
|
||||
assert ((RangeMarkerImpl)interval).myDocument == myDocument;
|
||||
|
||||
super.checkBelongsToTheTree(interval, assertInvalid);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected MyNode lookupNode(@NotNull T key) {
|
||||
return ((RangeMarkerImpl)key).myNode;
|
||||
}
|
||||
|
||||
public class RMNode extends MyNode {
|
||||
private boolean isExpandToLeft = false;
|
||||
private boolean isExpandToRight = false;
|
||||
private final long myId;
|
||||
private final boolean isExpandToLeft;
|
||||
private final boolean isExpandToRight;
|
||||
|
||||
public RMNode(@NotNull T key, int start, int end) {
|
||||
public RMNode(@NotNull T key, int start, int end, boolean greedyToLeft, boolean greedyToRight) {
|
||||
super(key, start, end);
|
||||
myId = counter.getAndIncrement();
|
||||
//myId = counter++;
|
||||
isExpandToLeft = greedyToLeft;
|
||||
isExpandToRight = greedyToRight;
|
||||
}
|
||||
|
||||
public void setGreedyToLeft(boolean greedy) {
|
||||
isExpandToLeft = greedy;
|
||||
}
|
||||
|
||||
public void setGreedyToRight(boolean greedy) {
|
||||
isExpandToRight = greedy;
|
||||
@Override
|
||||
public boolean processAliveKeys(@NotNull Processor processor) {
|
||||
return super.processAliveKeys(processor);
|
||||
}
|
||||
|
||||
public boolean isGreedyToLeft() {
|
||||
@@ -145,8 +135,18 @@ public class RangeMarkerTree<T extends RangeMarkerEx> extends IntervalTreeImpl<T
|
||||
return isExpandToRight;
|
||||
}
|
||||
|
||||
public long getId() {
|
||||
return myId;
|
||||
@Override
|
||||
public void addInterval(@NotNull T interval) {
|
||||
super.addInterval(interval);
|
||||
((RangeMarkerImpl)interval).myNode = this;
|
||||
checkBelongsToTheTree(interval, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean removeInterval(@NotNull T key) {
|
||||
boolean removedNode = super.removeInterval(key);
|
||||
((RangeMarkerImpl)key).myNode = null;
|
||||
return removedNode;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -159,11 +159,10 @@ public class RangeMarkerTree<T extends RangeMarkerEx> extends IntervalTreeImpl<T
|
||||
try {
|
||||
l.writeLock().lock();
|
||||
checkMax(true);
|
||||
normalized = false;
|
||||
|
||||
modCount++;
|
||||
List<IntervalNode> affected = new ArrayList<IntervalNode>();
|
||||
updateMarkersOnChange(getRoot(), e, affected);
|
||||
normalized &= !collectAffectedMarkers(getRoot(), e, affected);
|
||||
checkMax(false);
|
||||
|
||||
if (!affected.isEmpty()) {
|
||||
@@ -172,7 +171,7 @@ public class RangeMarkerTree<T extends RangeMarkerEx> extends IntervalTreeImpl<T
|
||||
int startOffset = node.intervalStart();
|
||||
int endOffset = node.intervalEnd();
|
||||
removeNode(node);
|
||||
node.delta = 0;
|
||||
node.delta = 0; // we can do it because all the deltas in the way were cleared in the collectAffectedMarkers
|
||||
node.setParent(null);
|
||||
node.setLeft(null);
|
||||
node.setRight(null);
|
||||
@@ -181,17 +180,26 @@ public class RangeMarkerTree<T extends RangeMarkerEx> extends IntervalTreeImpl<T
|
||||
}
|
||||
checkMax(true);
|
||||
for (IntervalNode node : affected) {
|
||||
RangeMarkerImpl marker = (RangeMarkerImpl)node.getKey();
|
||||
if (marker == null) continue; // collected
|
||||
List<T> aliveKeys = node.getAliveKeys();
|
||||
if (aliveKeys.isEmpty()) continue; // collected
|
||||
RangeMarkerImpl marker = (RangeMarkerImpl)aliveKeys.get(0);
|
||||
marker.setValid(true);
|
||||
//marker.myNode = null;
|
||||
marker.documentChanged(e);
|
||||
if (marker.isValid()) {
|
||||
insert(node);
|
||||
RMNode insertedNode = (RMNode)findOrInsert(node);
|
||||
// can change if two range become the one
|
||||
if (insertedNode != node) {
|
||||
// merge happened
|
||||
for (T interval : aliveKeys) {
|
||||
insertedNode.addInterval(interval);
|
||||
}
|
||||
}
|
||||
assert marker.isValid();
|
||||
}
|
||||
}
|
||||
checkMax(true);
|
||||
}
|
||||
checkMax(true);
|
||||
|
||||
IntervalNode root = getRoot();
|
||||
assert root == null || root.maxEnd + root.delta <= myDocument.getTextLength();
|
||||
@@ -201,19 +209,20 @@ public class RangeMarkerTree<T extends RangeMarkerEx> extends IntervalTreeImpl<T
|
||||
}
|
||||
}
|
||||
|
||||
private static void updateMarkersOnChange(IntervalNode root,
|
||||
@NotNull DocumentEvent e,
|
||||
@NotNull List<IntervalNode> affected) {
|
||||
if (root == null) return;
|
||||
pushDelta(root);
|
||||
// returns true if some delta was or became not null
|
||||
private boolean collectAffectedMarkers(IntervalNode root,
|
||||
@NotNull DocumentEvent e,
|
||||
@NotNull List<IntervalNode> affected) {
|
||||
if (root == null) return false;
|
||||
boolean denorm = pushDelta(root);
|
||||
|
||||
int maxEnd = root.maxEnd;
|
||||
assert root.isValid();
|
||||
|
||||
int offset = e.getOffset();
|
||||
int affectedEndOffset = offset + e.getOldLength();
|
||||
Object key = root.getKey();
|
||||
if (key == null) {
|
||||
boolean hasAliveKeys = root.hasAliveKey(false);
|
||||
if (!hasAliveKeys) {
|
||||
// marker was garbage collected
|
||||
affected.add(root);
|
||||
}
|
||||
@@ -222,28 +231,31 @@ public class RangeMarkerTree<T extends RangeMarkerEx> extends IntervalTreeImpl<T
|
||||
}
|
||||
else if (affectedEndOffset < root.intervalStart()) {
|
||||
int lengthDelta = e.getNewLength() - e.getOldLength();
|
||||
root.delta += lengthDelta;
|
||||
if (root.getLeft() != null) {
|
||||
root.getLeft().delta -= lengthDelta;
|
||||
int newD = root.delta += lengthDelta;
|
||||
denorm |= newD != 0;
|
||||
IntervalNode left = root.getLeft();
|
||||
if (left != null) {
|
||||
int newL = left.delta -= lengthDelta;
|
||||
denorm |= newL != 0;
|
||||
}
|
||||
pushDelta(root);
|
||||
updateMarkersOnChange(root.getLeft(), e, affected);
|
||||
denorm |= pushDelta(root);
|
||||
denorm |= collectAffectedMarkers(left, e, affected);
|
||||
correctMax(root, 0);
|
||||
}
|
||||
else {
|
||||
if (offset <= root.intervalEnd()) {
|
||||
// unlucky enough so that change affects the interval
|
||||
if (key != null) affected.add(root); // otherwise we've already added it
|
||||
if (hasAliveKeys) affected.add(root); // otherwise we've already added it
|
||||
root.setValid(false); //make invisible
|
||||
}
|
||||
|
||||
updateMarkersOnChange(root.getLeft(), e, affected);
|
||||
updateMarkersOnChange(root.getRight(), e, affected);
|
||||
denorm |= collectAffectedMarkers(root.getLeft(), e, affected);
|
||||
denorm |= collectAffectedMarkers(root.getRight(), e, affected);
|
||||
correctMax(root,0);
|
||||
}
|
||||
return denorm;
|
||||
}
|
||||
|
||||
|
||||
public boolean sweep(final int start, final int end, @NotNull final MarkupModelEx.SweepProcessor<T> sweepProcessor) {
|
||||
normalize();
|
||||
return sweep(new Generator<T>() {
|
||||
@@ -252,7 +264,6 @@ public class RangeMarkerTree<T extends RangeMarkerEx> extends IntervalTreeImpl<T
|
||||
return processOverlappingWith(start, end, processor);
|
||||
}
|
||||
}, sweepProcessor);
|
||||
|
||||
}
|
||||
|
||||
public interface Generator<T> {
|
||||
|
||||
@@ -15,8 +15,11 @@
|
||||
*/
|
||||
package com.intellij.openapi.editor.impl;
|
||||
|
||||
import com.intellij.util.Processor;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
|
||||
/**
|
||||
* User: cdr
|
||||
@@ -24,7 +27,7 @@ import org.jetbrains.annotations.NotNull;
|
||||
public abstract class RedBlackTree<K> {
|
||||
public static boolean VERIFY = false;
|
||||
private static final int INDENT_STEP = 4;
|
||||
protected int size;
|
||||
private int nodeSize; // number of nodes
|
||||
protected int modCount;
|
||||
protected Node<K> root;
|
||||
|
||||
@@ -33,8 +36,6 @@ public abstract class RedBlackTree<K> {
|
||||
verifyProperties();
|
||||
}
|
||||
|
||||
protected abstract Node<K> lookupNode(@NotNull K key, Node<K> root);
|
||||
|
||||
protected void rotateLeft(Node<K> n) {
|
||||
Node<K> r = n.getRight();
|
||||
replaceNode(n, r);
|
||||
@@ -78,7 +79,9 @@ public abstract class RedBlackTree<K> {
|
||||
//oldn.right = null;
|
||||
}
|
||||
|
||||
protected abstract Node<K> createNewNode(K key, int start, int end, Object data);
|
||||
protected void onInsertNode() {
|
||||
nodeSize++;
|
||||
}
|
||||
|
||||
protected void insertCase1(Node<K> n) {
|
||||
if (n.getParent() == null) {
|
||||
@@ -137,12 +140,6 @@ public abstract class RedBlackTree<K> {
|
||||
assert node1 == null || node1.getParent() == null || node1.getParent().getLeft() == node1 || node1.getParent().getRight() == node1;
|
||||
}
|
||||
|
||||
public Node<K> delete(@NotNull K key) {
|
||||
Node<K> n = lookupNode(key, root);
|
||||
deleteNode(n);
|
||||
return n;
|
||||
}
|
||||
|
||||
protected void deleteNode(Node<K> n) {
|
||||
modCount++;
|
||||
if (n == null) return; // Key not found, do nothing
|
||||
@@ -175,7 +172,9 @@ public abstract class RedBlackTree<K> {
|
||||
if (nodeColor(root) == Color.RED) {
|
||||
root.color = Color.BLACK;
|
||||
}
|
||||
size--;
|
||||
|
||||
assert nodeSize > 0 : nodeSize;
|
||||
nodeSize--;
|
||||
verifyProperties();
|
||||
}
|
||||
|
||||
@@ -286,10 +285,10 @@ public abstract class RedBlackTree<K> {
|
||||
System.err.print(" ");
|
||||
}
|
||||
if (n.color == Color.BLACK) {
|
||||
System.err.println(n.getKey());
|
||||
System.err.println(n);
|
||||
}
|
||||
else {
|
||||
System.err.println("<" + n.getKey() + ">");
|
||||
System.err.println("<" + n + ">");
|
||||
}
|
||||
if (n.getLeft() != null) {
|
||||
printHelper(n.getLeft(), indent + INDENT_STEP);
|
||||
@@ -347,7 +346,10 @@ public abstract class RedBlackTree<K> {
|
||||
this.parent = parent;
|
||||
}
|
||||
|
||||
public abstract K getKey();
|
||||
public abstract boolean processAliveKeys(@NotNull Processor<? super K> processor);
|
||||
@NotNull
|
||||
public abstract List<K> getAliveKeys();
|
||||
public abstract boolean hasAliveKey(boolean purgeDead);
|
||||
}
|
||||
|
||||
protected static enum Color {
|
||||
@@ -355,7 +357,10 @@ public abstract class RedBlackTree<K> {
|
||||
}
|
||||
|
||||
public int size() {
|
||||
return size;
|
||||
return nodeSize;
|
||||
}
|
||||
public int nodeSize() {
|
||||
return nodeSize;
|
||||
}
|
||||
|
||||
public void verifyProperties() {
|
||||
@@ -426,6 +431,6 @@ public abstract class RedBlackTree<K> {
|
||||
public void clear() {
|
||||
modCount++;
|
||||
root = null;
|
||||
size = 0;
|
||||
nodeSize = 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -287,10 +287,6 @@ public class MockDocument implements DocumentEx {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addRangeMarker(@NotNull RangeMarkerEx rangeMarker, int start, int end) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isInBulkUpdate() {
|
||||
return false;
|
||||
|
||||
@@ -768,11 +768,29 @@ public class ContainerUtil {
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@NotNull
|
||||
public static <T,V> List<V> map(@NotNull Collection<? extends T> iterable, @NotNull Function<T, V> mapping) {
|
||||
List<V> result = new ArrayList<V>(iterable.size());
|
||||
for (T t : iterable) {
|
||||
result.add(mapping.fun(t));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static <T, V> List<V> mapNotNull(@NotNull T[] array, Function<T, V> mapping) {
|
||||
return mapNotNull(Arrays.asList(array), mapping);
|
||||
}
|
||||
public static <T, V> V[] mapNotNull(@NotNull T[] arr, @NotNull Function<T, V> mapping, @NotNull V[] emptyArray) {
|
||||
List<V> result = new ArrayList<V>(arr.length);
|
||||
for (T t : arr) {
|
||||
V v = mapping.fun(t);
|
||||
if (v != null) {
|
||||
result.add(v);
|
||||
}
|
||||
}
|
||||
return result.toArray(emptyArray);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static <T, V> List<V> mapNotNull(Iterable<? extends T> iterable, Function<T, V> mapping) {
|
||||
@@ -785,6 +803,17 @@ public class ContainerUtil {
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@NotNull
|
||||
public static <T, V> List<V> mapNotNull(Collection<? extends T> iterable, Function<T, V> mapping) {
|
||||
List<V> result = new ArrayList<V>(iterable.size());
|
||||
for (T t : iterable) {
|
||||
final V o = mapping.fun(t);
|
||||
if (o != null) {
|
||||
result.add(o);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static <T> List<T> packNullables(@NotNull T... elements) {
|
||||
|
||||
Reference in New Issue
Block a user