| /* |
| * 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.modules.refactoring.java.ui; |
| |
| import java.util.ArrayList; |
| import java.util.Collections; |
| import java.util.LinkedList; |
| import java.util.List; |
| import java.util.Set; |
| 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; |
| |
| /**Copied from editor/codetemplates and adjusted for the needs of the instant rename. |
| * |
| * Maintain the same text in the selected regions of the text document. |
| * |
| * @author Miloslav Metelka |
| */ |
| public final class SyncDocumentRegion { |
| |
| private Document doc; |
| private String oldVal; |
| |
| private List<? extends MutablePositionRegion> regions; |
| private List<? extends MutablePositionRegion> sortedRegions; |
| |
| /** |
| * 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 |
| boolean regionsSortPerformed = PositionRegion.isRegionsSorted(regions); |
| if (regionsSortPerformed) { |
| sortedRegions = regions; |
| } else { |
| sortedRegions = new ArrayList<>(regions); |
| Collections.sort(sortedRegions, PositionRegion.getComparator()); |
| } |
| this.oldVal = getFirstRegionText(); |
| } |
| |
| public void updateRegions(List<MutablePositionRegion> regions) { |
| boolean sorted = PositionRegion.isRegionsSorted(regions); |
| List<MutablePositionRegion> sortedRegions; |
| List<MutablePositionRegion> toRestore = new LinkedList<>(); |
| List<MutablePositionRegion> toSync = new LinkedList<>(); |
| if(sorted) { |
| sortedRegions = regions; |
| } else { |
| sortedRegions = new ArrayList<>(regions); |
| Collections.sort(sortedRegions, PositionRegion.getComparator()); |
| } |
| int j = 0; |
| for (int i = 0; i < this.regions.size();) { |
| MutablePositionRegion r1 = this.regions.get(i); |
| // if old before new, remove |
| if(j >= sortedRegions.size()) { |
| toRestore.add(r1); |
| i++; |
| } else { |
| MutablePositionRegion r2 = sortedRegions.get(j); |
| if(r1.getStartOffset() == r2.getStartOffset()) { // if same pos, skip both |
| i++; |
| j++; |
| } else if(r1.getStartOffset() < r2.getStartOffset()) { // if old before new, remove |
| toRestore.add(r1); |
| i++; |
| } else { // if old after new, skip new |
| toSync.add(r2); |
| j++; |
| } |
| } |
| } |
| while(j < sortedRegions.size()) { |
| toSync.add(sortedRegions.get(j)); |
| j++; |
| } |
| restore(toRestore); |
| sync(toSync); |
| this.regions = regions; |
| this.sortedRegions = sortedRegions; |
| } |
| |
| 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. |
| */ |
| public void sync() { |
| 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 { |
| if (!CharSequenceUtilities.textEquals(firstRegionText, DocumentUtilities.getText(doc, offset, length))) { |
| if (firstRegionText.length() > 0) { |
| doc.insertString(offset, firstRegionText, null); |
| } |
| doc.remove(offset + firstRegionText.length(), length); |
| } |
| } catch (BadLocationException e) { |
| Exceptions.printStackTrace(e); |
| } |
| |
| } |
| } |
| } |
| |
| private void sync(List<MutablePositionRegion> toSync) { |
| String firstRegionText = getFirstRegionText(); |
| for (MutablePositionRegion region : toSync) { |
| 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()); |
| } |
| } |
| } catch (BadLocationException e) { |
| Exceptions.printStackTrace(e); |
| } |
| } |
| } |
| |
| private void restore(List<MutablePositionRegion> toRestore) { |
| for (MutablePositionRegion region : toRestore) { |
| int offset = region.getStartOffset(); |
| int length = region.getEndOffset() - offset; |
| try { |
| if (!CharSequenceUtilities.textEquals(oldVal, DocumentUtilities.getText(doc, offset, length))) { |
| if (oldVal.length() > 0) { |
| doc.insertString(offset, oldVal, null); |
| } |
| doc.remove(offset + oldVal.length(), length); |
| } |
| } 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; |
| } |
| } |
| |
| } |