/platform/platform-impl/src/com/intellij/openapi/editor/impl/softwrap/SoftWrapsStorage.java
https://bitbucket.org/nbargnesi/idea · Java · 206 lines · 112 code · 21 blank · 73 comment · 18 complexity · d22f2d3c60059624008246c8b24157e3 MD5 · raw file
- /*
- * Copyright 2000-2010 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.softwrap;
- import com.intellij.openapi.editor.SoftWrap;
- import com.intellij.openapi.editor.TextChange;
- import com.intellij.openapi.editor.ex.SoftWrapChangeListener;
- import org.jetbrains.annotations.NotNull;
- import org.jetbrains.annotations.Nullable;
- import java.util.ArrayList;
- import java.util.Collections;
- import java.util.List;
- /**
- * Holds registered soft wraps and provides monitoring and management facilities for them.
- * <p/>
- * Not thread-safe.
- *
- * @author Denis Zhdanov
- * @since Jun 29, 2010 3:04:20 PM
- */
- public class SoftWrapsStorage {
- private final List<SoftWrapImpl> myWraps = new ArrayList<SoftWrapImpl>();
- private final List<SoftWrapImpl> myWrapsView = Collections.unmodifiableList(myWraps);
- private final List<SoftWrapChangeListener> myListeners = new ArrayList<SoftWrapChangeListener>();
- /**
- * @return <code>true</code> if there is at least one soft wrap registered at the current storage; <code>false</code> otherwise
- */
- public boolean isEmpty() {
- return myWraps.isEmpty();
- }
- @Nullable
- public SoftWrap getSoftWrap(int offset) {
- int i = getSoftWrapIndex(offset);
- return i >= 0 ? myWraps.get(i) : null;
- }
- /**
- * @return view for registered soft wraps sorted by offset in ascending order if any; empty collection otherwise
- */
- @NotNull
- public List<SoftWrapImpl> getSoftWraps() {
- return myWrapsView;
- }
- /**
- * Tries to find index of the target soft wrap stored at {@link #myWraps} collection. <code>'Target'</code> soft wrap is the one
- * that starts at the given offset.
- *
- * @param offset target offset
- * @return index that conforms to {@link Collections#binarySearch(List, Object)} contract, i.e. non-negative returned
- * index points to soft wrap that starts at the given offset; <code>'-(negative value) - 1'</code> points
- * to position at {@link #myWraps} collection where soft wrap for the given index should be inserted
- */
- public int getSoftWrapIndex(int offset) {
- int start = 0;
- int end = myWraps.size() - 1;
- // We use custom inline implementation of binary search here because profiling shows that standard Collections.binarySearch()
- // is a bottleneck. The most probable reason is a big number of interface calls.
- while (start <= end) {
- int i = (start + end) >>> 1;
- SoftWrap softWrap = myWraps.get(i);
- int softWrapOffset = softWrap.getStart();
- if (softWrapOffset > offset) {
- end = i - 1;
- }
- else if (softWrapOffset < offset) {
- start = i + 1;
- }
- else {
- return i;
- }
- }
- return -(start + 1);
- }
- /**
- * Allows to answer how many soft wraps which {@link TextChange#getStart() start offsets} belong to given
- * <code>[start; end]</code> interval are registered withing the current storage.
- *
- * @param startOffset target start offset (inclusive)
- * @param endOffset target end offset (inclusive)
- * @return number of soft wraps which {@link TextChange#getStart() start offsets} belong to the target range
- */
- public int getNumberOfSoftWrapsInRange(int startOffset, int endOffset) {
- int startIndex = getSoftWrapIndex(startOffset);
- if (startIndex < 0) {
- startIndex = -startIndex - 1;
- }
- if (startIndex >= myWraps.size()) {
- return 0;
- }
- int result = 0;
- int endIndex = startIndex;
- for (; endIndex < myWraps.size(); endIndex++) {
- SoftWrap softWrap = myWraps.get(endIndex);
- if (softWrap.getStart() > endOffset) {
- break;
- }
- result++;
- }
- return result;
- }
-
- /**
- * Inserts given soft wrap to {@link #myWraps} collection at the given index.
- *
- * @param softWrap soft wrap to store
- * @param notifyListeners flag that indicates if registered listeners should be notified about soft wrap registration
- * @return previous soft wrap object stored for the same offset if any; <code>null</code> otherwise
- */
- @SuppressWarnings({"ForLoopReplaceableByForEach"})
- @Nullable
- public SoftWrap storeOrReplace(SoftWrapImpl softWrap, boolean notifyListeners) {
- int i = getSoftWrapIndex(softWrap.getStart());
- if (i >= 0) {
- return myWraps.set(i, softWrap);
- }
- i = -i - 1;
- myWraps.add(i, softWrap);
- if (notifyListeners) {
- // Use explicit loop as profiling shows that iterator-based processing is quite slow.
- for (int j = 0; j < myListeners.size(); j++) {
- myListeners.get(j).softWrapAdded(softWrap);
- }
- }
- return null;
- }
- /**
- * Allows to remove all soft wraps registered at the current storage with offsets from <code>[start; end)</code> range if any.
- *
- * @param startOffset start offset to use (inclusive)
- * @param endOffset end offset to use (exclusive)
- */
- public void removeInRange(int startOffset, int endOffset) {
- //CachingSoftWrapDataMapper.log(String.format("xxxxxxxxxx SoftWrapsStorage.removeInRange(%d, %d). Current number: %d", startOffset, endOffset, myWraps.size()));
- int startIndex = getSoftWrapIndex(startOffset);
- if (startIndex < 0) {
- startIndex = -startIndex - 1;
- }
- if (startIndex >= myWraps.size()) {
- return;
- }
- int endIndex = startIndex;
- for (; endIndex < myWraps.size(); endIndex++) {
- SoftWrap softWrap = myWraps.get(endIndex);
- if (softWrap.getStart() >= endOffset) {
- break;
- }
- }
- if (endIndex > startIndex) {
- myWraps.subList(startIndex, endIndex).clear();
- notifyListenersAboutRemoval();
- }
-
- //CachingSoftWrapDataMapper.log(String.format("xxxxxxxxxx SoftWrapsStorage.removeInRange(%d, %d). Remaining: %d", startOffset, endOffset, myWraps.size()));
- }
- /**
- * Removes all soft wraps registered at the current storage.
- */
- public void removeAll() {
- myWraps.clear();
- notifyListenersAboutRemoval();
- }
- /**
- * Registers given listener within the current model
- *
- * @param listener listener to register
- * @return <code>true</code> if given listener was not registered before; <code>false</code> otherwise
- */
- public boolean addSoftWrapChangeListener(@NotNull SoftWrapChangeListener listener) {
- return myListeners.add(listener);
- }
- private void notifyListenersAboutRemoval() {
- for (SoftWrapChangeListener listener : myListeners) {
- listener.softWrapsRemoved();
- }
- }
- }