blob: 4abd9b310ad7554ee3cd9ad8b5fd8139d6798936 [file] [log] [blame]
/*
* 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.
*/
package org.netbeans.lib.editor.codetemplates;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import javax.swing.text.BadLocationException;
import javax.swing.text.Document;
import javax.swing.text.Position;
import org.netbeans.lib.editor.util.CharSequenceUtilities;
import org.netbeans.lib.editor.util.swing.DocumentUtilities;
import org.netbeans.lib.editor.util.swing.MutablePositionRegion;
import org.netbeans.lib.editor.util.swing.PositionRegion;
import org.openide.util.Exceptions;
/**
* Maintain the same text in the selected regions of the text document.
*
* @author Miloslav Metelka
*/
public final class SyncDocumentRegion {
private final Document doc;
private final List<? extends MutablePositionRegion> regions;
private final List<? extends MutablePositionRegion> sortedRegions;
private final boolean regionsSortPerformed;
/**
* Construct synchronized document regions.
*
* @param doc document on which to operate.
* @param regions regions that should be kept synchronized.
* The first region is the master. All the regions need to have
* the initial position to have the backward bias.
*/
public SyncDocumentRegion(Document doc, List<? extends MutablePositionRegion> regions) {
this.doc = doc;
this.regions = regions;
// Check bounds correctness and whether they are sorted
regionsSortPerformed = PositionRegion.isRegionsSorted(regions);
if (regionsSortPerformed) {
sortedRegions = regions;
} else {
sortedRegions = new ArrayList<MutablePositionRegion>(regions);
Collections.sort(sortedRegions, PositionRegion.getComparator());
}
}
public int getRegionCount() {
return regions.size();
}
public MutablePositionRegion getRegion(int regionIndex) {
return regions.get(regionIndex);
}
public int getFirstRegionStartOffset() {
return getRegion(0).getStartOffset();
}
public int getFirstRegionEndOffset() {
return getRegion(0).getEndOffset();
}
public int getFirstRegionLength() {
return getFirstRegionEndOffset() - getFirstRegionStartOffset();
}
/**
* Get region in a sorted list of the regions.
*
* @param regionIndex of the region.
* @return region in a sorted list of the regions.
*/
public MutablePositionRegion getSortedRegion(int regionIndex) {
return sortedRegions.get(regionIndex);
}
/**
* Propagate text of the first region into all other regions.
*
* @param moveStartDownLength how much to move starting position
* down. It may be 0 to signal that the startng position should
* stay as is.
*/
public void sync(int moveStartDownLength) {
if (moveStartDownLength != 0) {
// Move first region's start offset down
MutablePositionRegion firstRegion = getRegion(0);
try {
Position newStartPos = doc.createPosition(
firstRegion.getStartOffset() - moveStartDownLength);
firstRegion.setStartPosition(newStartPos);
} catch (BadLocationException e) {
Exceptions.printStackTrace(e);
}
}
String firstRegionText = getFirstRegionText();
if (firstRegionText != null) {
int regionCount = getRegionCount();
for (int i = 1; i < regionCount; i++) {
MutablePositionRegion region = getRegion(i);
int offset = region.getStartOffset();
int length = region.getEndOffset() - offset;
try {
final CharSequence old = DocumentUtilities.getText(doc, offset, length);
if (!CharSequenceUtilities.textEquals(firstRegionText, old)) {
int res = -1;
for(int k = 0; k < Math.min(old.length(), firstRegionText.length()); k++) {
if (old.charAt(k) == firstRegionText.charAt(k)) {
res = k;
} else {
break;
}
}
String insert = firstRegionText.substring(res+1);
CharSequence remove = old.subSequence(res + 1, old.length());
if (insert.length() > 0) {
doc.insertString(offset + res + 1, insert, null);
}
if (remove.length() > 0) {
doc.remove(offset + res + 1 + insert.length(), remove.length());
}
}
// Recreate the start position as the position are put together
Position newStartPos = doc.createPosition(offset);
region.setStartPosition(newStartPos);
} catch (BadLocationException e) {
Exceptions.printStackTrace(e);
}
}
}
}
private String getFirstRegionText() {
return getRegionText(0);
}
private String getRegionText(int regionIndex) {
try {
MutablePositionRegion region = getRegion(regionIndex);
int offset = region.getStartOffset();
int length = region.getEndOffset() - offset;
return doc.getText(offset, length);
} catch (BadLocationException e) {
Exceptions.printStackTrace(e);
return null;
}
}
}