blob: 85172fbae4c1b4984e97944c5fd5495d21740382 [file] [log] [blame]
/*
* 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.formatting;
import com.intellij.openapi.util.TextRange;
import com.intellij.openapi.util.text.StringUtil;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.List;
/**
* Collection of {@link FormatTextRange} objects with utility methods for batch processing on aggregated objects.
*
* @see FormatTextRange
* @author yole
*/
public class FormatTextRanges {
/**
* Wraps {@link TextRange} object with {@link #myProcessHeadingWhitespace} flag and provides convenient services for checking if wrapped range intersects
* with the given one.
*/
public static class FormatTextRange {
private TextRange myRange;
private final boolean myProcessHeadingWhitespace;
public FormatTextRange(TextRange range, boolean processHeadingWhitespace) {
myRange = range;
myProcessHeadingWhitespace = processHeadingWhitespace;
}
/**
* Allows to answer if given range has intersections with the range wrapped by the current {@link FormatTextRange} object.
* <p/>
* I.e. this method returns <code>true</code> (no intersections) if any of the statements below is true:
* <ul>
* <li>given range starts after wrapped range;</li>
* <li>
* given range ends before wrapped range start (given range ends before or at the wrapped range start if
* <code>'processHeadingWhitespace'</code> flag is set to <code>false</code>);
* </li>
* </ul>
*
* @param range range to check
* @return <code>true</code> if given range has no intersections with the wrapped range; <code>false</code> otherwise
*/
public boolean isWhitespaceReadOnly(@Nullable TextRange range) {
if (myRange == null) {
return false;
}
if (range == null || range.getStartOffset() >= myRange.getEndOffset()) return true;
if (myProcessHeadingWhitespace) {
return range.getEndOffset() < myRange.getStartOffset();
}
else {
return range.getEndOffset() <= myRange.getStartOffset();
}
}
public int getStartOffset() {
return myRange.getStartOffset();
}
/**
* Allows to check if given range has intersections with the range wrapped by the current {@link FormatTextRange} object.
* <p/>
* I.e. this method returns <code>true</code> (no intersections) if and only if any of conditions below is satisfied:
* <ul>
* <li>given range starts after end of the wrapped range;</li>
* <li>
* given range ends before start of the wrapped range (there is a special case when given <code>'rootIsRightBlock'</code> flag
* is <code>true</code> - <code>false</code> is returned if given range ends before or at start of the wrapped range);
* </li>
* </ul>
*
* @param range range to check
* @param rootIsRightBlock meta-information about given range that is used during final answer calculation
* @return <code>true</code> if there are no intersections between given and wrapped ranges;
* <code>false</code> otherwise
*/
public boolean isReadOnly(TextRange range, boolean rootIsRightBlock) {
if (myRange == null || myRange.getStartOffset() >= range.getEndOffset() && rootIsRightBlock) {
return false;
}
return range.getStartOffset() > myRange.getEndOffset() || range.getEndOffset() < myRange.getStartOffset();
}
public TextRange getTextRange() {
return myRange;
}
public void setTextRange(TextRange range) {
myRange = range;
}
public TextRange getNonEmptyTextRange() {
return new TextRange(myRange.getStartOffset(), myRange.getStartOffset() == myRange.getEndOffset()
? myRange.getEndOffset()+1
: myRange.getEndOffset());
}
@Override
public String toString() {
return myRange.toString() + (myProcessHeadingWhitespace ? "+" : "");
}
}
private final List<FormatTextRange> myRanges = new ArrayList<FormatTextRange>();
public FormatTextRanges() {
}
public FormatTextRanges(TextRange range, boolean processHeadingWhitespace) {
add(range, processHeadingWhitespace);
}
public void add(TextRange range, boolean processHeadingWhitespace) {
myRanges.add(new FormatTextRange(range, processHeadingWhitespace));
}
/**
* Batches {@link FormatTextRange#isWhitespaceReadOnly(TextRange)} operation for all aggregated ranges.
* <p/>
* I.e. this method allows to check if given range has intersections with any of aggregated ranges.
*
* @param range range to check
* @return <code>true</code> if given range doesn't have intersections with all aggregated ranges;
* <code>false</code> if given range intersects at least one of aggregated ranges
*/
public boolean isWhitespaceReadOnly(TextRange range) {
for (FormatTextRange formatTextRange : myRanges) {
if (!formatTextRange.isWhitespaceReadOnly(range)) {
return false;
}
}
return true;
}
/**
* Batches {@link FormatTextRange#isReadOnly(TextRange, boolean)} operation for all aggregated ranges.
* <p/>
* I.e. this method allows to check if given range has intersections with any of aggregated ranges.
*
* @param range range to check
* @param rootIsRightBlock flag to use during {@link FormatTextRange#isReadOnly(TextRange, boolean)} processing
* @return <code>true</code> if given range doesn't have intersections with all aggregated ranges;
* <code>false</code> if given range intersects at least one of aggregated ranges
*/
public boolean isReadOnly(TextRange range, boolean rootIsRightBlock) {
for (FormatTextRange formatTextRange : myRanges) {
if (!formatTextRange.isReadOnly(range, rootIsRightBlock)) {
return false;
}
}
return true;
}
public List<FormatTextRange> getRanges() {
return myRanges;
}
public FormatTextRanges ensureNonEmpty() {
FormatTextRanges result = new FormatTextRanges();
for (FormatTextRange range : myRanges) {
if (range.myProcessHeadingWhitespace) {
result.add(range.getNonEmptyTextRange(), true);
}
else {
result.add(range.getTextRange(), false);
}
}
return result;
}
@Override
public String toString() {
return "FormatTextRanges{" + StringUtil.join(myRanges, StringUtil.createToStringFunction(FormatTextRange.class), ",");
}
}