2 * (c) Copyright IBM Corp. 2000, 2001.
5 package net.sourceforge.phpdt.internal.corext.textmanipulation;
7 import java.util.ArrayList;
8 import java.util.Iterator;
11 import org.eclipse.core.runtime.CoreException;
12 import org.eclipse.core.runtime.IProgressMonitor;
13 import org.eclipse.jface.text.DocumentEvent;
14 import org.eclipse.jface.text.IDocumentListener;
17 * A helper class to arrange <code>TextEdit</code>s into a tree to optimize
20 /* package */abstract class TextEditNode {
22 /* package */TextEditNode fParent;
24 /* package */List fChildren;
26 /* package */TextEdit fEdit;
28 /* package */static class DefaultNode extends TextEditNode {
29 public DefaultNode(TextEdit edit) {
34 /* package */static class RootNode extends TextEditNode {
35 private int fUndoIndex;
37 public RootNode(int length) {
38 super(new NopTextEdit(new TextRange(0, length)));
39 fEdit.isSynthetic = true;
42 public boolean covers(TextEditNode node) {
46 public UndoMemento performDo(TextBuffer buffer, IProgressMonitor pm)
47 throws CoreException {
48 DoRangeUpdater updater = new DoRangeUpdater();
49 UndoMemento undo = new UndoMemento(TextBufferEditor.UNDO);
51 buffer.registerUpdater(updater);
52 performDo(buffer, updater, undo, pm);
54 buffer.unregisterUpdater(updater);
55 updater.setActiveNode(null);
60 public UndoMemento performUndo(TextBuffer buffer, IProgressMonitor pm)
61 throws CoreException {
62 UndoRangeUpdater updater = new UndoRangeUpdater(this);
63 UndoMemento undo = new UndoMemento(TextBufferEditor.REDO);
65 buffer.registerUpdater(updater);
66 performUndo(buffer, updater, undo, pm);
68 buffer.unregisterUpdater(updater);
69 updater.setActiveNode(null);
74 protected void setUndoIndex(int index) {
78 protected int getUndoIndex() {
83 /* package */abstract static class AbstractMoveNode extends TextEditNode {
86 private int fTargetIndex;
88 private int fSourceIndex;
90 private List fAffectedChildren;
92 public AbstractMoveNode(TextEdit edit) {
97 protected abstract TextRange getSourceRange();
99 protected abstract TextRange getTargetRange();
101 protected abstract boolean isUpMove();
103 protected boolean isDownMove() {
107 public boolean isMove() {
111 protected void checkRange(DocumentEvent event) {
112 TextRange range = getChildRange();
113 int eventOffset = event.getOffset();
114 int eventLength = event.getLength();
115 int eventEnd = eventOffset + eventLength - 1;
116 // "Edit changes text that lies outside its defined range"
117 // Assert.isTrue(range.fOffset <= eventOffset && eventEnd <=
118 // range.getInclusiveEnd());
121 protected boolean activeNodeChanged(int delta) {
122 TextRange targetRange = getTargetRange();
123 TextRange sourceRange = getSourceRange();
125 case 0: // the move delete
127 // Assert.isTrue(Math.abs(delta) == sourceRange.fLength);
129 updateOffset(fAffectedChildren, delta);
130 targetRange.fOffset += delta;
132 sourceRange.fLength = 0;
136 TextEditNode target = (TextEditNode) fParent.fChildren
138 TextEditNode source = (TextEditNode) fParent.fChildren
140 updateOffset(source.fChildren, targetRange.fOffset
141 - sourceRange.fOffset);
142 target.fChildren = source.fChildren;
143 if (target.fChildren != null) {
144 for (Iterator iter = target.fChildren.iterator(); iter
146 ((TextEditNode) iter.next()).fParent = target;
149 source.fChildren = null;
151 updateOffset(fAffectedChildren, delta);
152 sourceRange.fOffset += delta;
154 targetRange.fLength = delta;
161 private static void updateOffset(List nodes, int delta) {
164 for (int i = nodes.size() - 1; i >= 0; i--) {
165 TextEditNode node = (TextEditNode) nodes.get(i);
166 TextRange range = node.getTextRange();
167 range.fOffset += delta;
168 updateOffset(node.fChildren, delta);
172 private void init() {
173 TextRange source = getSourceRange();
174 TextRange target = getTargetRange();
175 List children = fParent.fChildren;
176 for (int i = children.size() - 1; i >= 0; i--) {
177 TextEditNode child = (TextEditNode) children.get(i);
178 TextRange range = child.fEdit.getTextRange();
181 else if (range == target)
184 int start = Math.min(fTargetIndex, fSourceIndex);
185 int end = Math.max(fTargetIndex, fSourceIndex);
186 fAffectedChildren = new ArrayList(3);
187 for (int i = start + 1; i < end; i++) {
188 fAffectedChildren.add(children.get(i));
192 private void reset() {
199 /* package */static class MoveNode extends AbstractMoveNode {
200 public MoveNode(TextEdit edit) {
204 protected TextRange getChildRange() {
205 return ((MoveTextEdit) fEdit).getChildRange();
208 protected TextRange getSourceRange() {
209 return ((MoveTextEdit) fEdit).getSourceRange();
212 protected TextRange getTargetRange() {
213 return ((MoveTextEdit) fEdit).getTargetRange();
216 protected boolean isUpMove() {
217 return ((MoveTextEdit) fEdit).isUpMove();
220 public boolean isMovePartner(TextEditNode other) {
221 if (!(other instanceof TargetMarkNode))
223 return fEdit == ((MoveTextEdit.TargetMark) other.fEdit)
227 public boolean covers(TextEditNode node) {
228 if (node instanceof TargetMarkNode) {
229 MoveTextEdit.TargetMark edit = (MoveTextEdit.TargetMark) node.fEdit;
230 if (edit.getMoveTextEdit() == fEdit)
233 return getParentRange().covers(node.getChildRange());
237 /* package */static class TargetMarkNode extends AbstractMoveNode {
238 public TargetMarkNode(TextEdit edit) {
242 protected TextRange getChildRange() {
243 return ((MoveTextEdit.TargetMark) fEdit).getMoveTextEdit()
247 protected TextRange getSourceRange() {
248 return ((MoveTextEdit.TargetMark) fEdit).getMoveTextEdit()
252 protected TextRange getTargetRange() {
253 return ((MoveTextEdit.TargetMark) fEdit).getMoveTextEdit()
257 protected boolean isUpMove() {
258 return ((MoveTextEdit.TargetMark) fEdit).getMoveTextEdit()
262 public boolean isMovePartner(TextEditNode other) {
263 return ((MoveTextEdit.TargetMark) fEdit).getMoveTextEdit() == other.fEdit;
267 // ---- Range updating
268 // ---------------------------------------------------------------------------
270 private static abstract class RangeUpdater implements IDocumentListener {
271 protected TextEditNode fActiveNode;
273 public void documentAboutToBeChanged(DocumentEvent event) {
276 public void setActiveNode(TextEditNode node) {
280 public void updateParents(int delta) {
281 TextEditNode node = fActiveNode.fParent;
282 while (node != null) {
283 node.childNodeChanged(delta);
288 public static int getDelta(DocumentEvent event) {
289 return (event.getText() == null ? 0 : event.getText().length())
294 private static class DoRangeUpdater extends RangeUpdater {
295 private List fProcessedNodes = new ArrayList(10);
297 public void setActiveNode(TextEditNode node) {
298 if (fActiveNode != null)
299 fProcessedNodes.add(fActiveNode);
300 super.setActiveNode(node);
303 public void documentChanged(DocumentEvent event) {
304 fActiveNode.checkRange(event);
305 int delta = getDelta(event);
306 if (!fActiveNode.activeNodeChanged(delta)) {
307 for (Iterator iter = fProcessedNodes.iterator(); iter.hasNext();) {
308 ((TextEditNode) iter.next()).previousNodeChanged(delta);
311 updateParents(delta);
315 private static class UndoRangeUpdater extends RangeUpdater {
316 private RootNode fRootNode;
318 public UndoRangeUpdater(RootNode root) {
322 public void setActiveNode(TextEditNode node) {
323 super.setActiveNode(node);
326 public void documentChanged(DocumentEvent event) {
327 fActiveNode.checkRange(event);
328 int delta = getDelta(event);
329 if (!fActiveNode.activeNodeChanged(delta)) {
330 int start = fRootNode.getUndoIndex() + 1;
331 List children = fRootNode.fChildren;
332 int size = children != null ? children.size() : 0;
333 for (int i = start; i < size; i++) {
334 updateUndo((TextEditNode) children.get(i), delta);
337 updateParents(delta);
340 private void updateUndo(TextEditNode node, int delta) {
341 node.previousNodeChanged(delta);
342 List children = node.fChildren;
343 int size = children != null ? children.size() : 0;
344 for (int i = 0; i < size; i++) {
345 updateUndo((TextEditNode) children.get(i), delta);
350 // ---- Creating instances
351 // ---------------------------------------------------------------------------
353 static TextEditNode create(TextEdit edit) {
354 if (edit instanceof MoveTextEdit)
355 return new MoveNode(edit);
356 if (edit instanceof MoveTextEdit.TargetMark)
357 return new TargetMarkNode(edit);
358 return new DefaultNode(edit);
361 static RootNode createRoot(int length) {
362 return new RootNode(length);
365 private TextEditNode(TextEdit edit) {
369 // ---- Adding children
370 // ---------------------------------------------------------------------------
372 protected void add(TextEditNode node) {
373 if (fChildren == null) {
374 fChildren = new ArrayList(1);
379 // Optimize using binary search
380 for (Iterator iter = fChildren.iterator(); iter.hasNext();) {
381 TextEditNode child = (TextEditNode) iter.next();
382 if (child.covers(node)) {
387 for (int i = 0; i < fChildren.size();) {
388 TextEditNode child = (TextEditNode) fChildren.get(i);
389 if (node.covers(child)) {
400 public boolean covers(TextEditNode node) {
405 // --------------------------------------------------------------------------------------
407 protected RootNode getRoot() {
408 TextEditNode candidate = this;
409 while (candidate.fParent != null)
410 candidate = candidate.fParent;
411 return (RootNode) candidate;
414 // ---- Query interface
415 // --------------------------------------------------------------------------------
417 protected boolean isSynthetic() {
418 return fEdit.isSynthetic;
421 public boolean isMove() {
425 // ---- Accessing Ranges
426 // ------------------------------------------------------------------------------
428 protected void checkRange(DocumentEvent event) {
429 TextRange range = getTextRange();
430 int eventOffset = event.getOffset();
431 int eventLength = event.getLength();
432 int eventEnd = eventOffset + eventLength - 1;
433 // "Edit changes text that lies outside its defined range"
434 // Assert.isTrue(range.fOffset <= eventOffset && eventEnd <=
435 // range.getInclusiveEnd());
438 protected TextRange getTextRange() {
439 return fEdit.getTextRange();
442 protected TextRange getChildRange() {
443 return getTextRange();
446 protected TextRange getParentRange() {
447 return getTextRange();
450 public boolean validate(int bufferLength) {
451 if (fChildren == null)
453 // Only Moves and Nops can be parents
454 if (!(fEdit instanceof MoveTextEdit || fEdit instanceof NopTextEdit))
456 TextRange lastRange = null;
457 for (Iterator iter = fChildren.iterator(); iter.hasNext();) {
458 TextEditNode node = (TextEditNode) iter.next();
459 if (!node.validate(bufferLength))
461 TextRange range = node.fEdit.getTextRange();
463 || range.fOffset + range.fLength > bufferLength)
465 if (lastRange != null
466 && !(range.isInsertionPointAt(lastRange.fOffset) || range
467 .liesBehind(lastRange)))
475 // ----------------------------------------------------------------------------------------
477 protected boolean activeNodeChanged(int delta) {
478 TextRange range = getTextRange();
479 range.fLength += delta;
480 // we didn't adjust any processed nodes.
484 protected void previousNodeChanged(int delta) {
485 TextRange range = getTextRange();
486 range.fOffset += delta;
489 protected void childNodeChanged(int delta) {
490 getTextRange().fLength += delta;
494 // ---------------------------------------------------------------------------------------------
496 protected void performDo(TextBuffer buffer, RangeUpdater updater,
497 UndoMemento undo, IProgressMonitor pm) throws CoreException {
498 int size = fChildren != null ? fChildren.size() : 0;
499 for (int i = size - 1; i >= 0; i--) {
500 TextEditNode child = (TextEditNode) fChildren.get(i);
501 child.performDo(buffer, updater, undo, pm);
503 updater.setActiveNode(this);
505 fEdit.perform(buffer);
507 undo.add(fEdit.perform(buffer));
511 public void performedDo() {
512 int size = fChildren != null ? fChildren.size() : 0;
513 for (int i = size - 1; i >= 0; i--) {
514 TextEditNode child = (TextEditNode) fChildren.get(i);
521 // -------------------------------------------------------------------------------------------
523 protected void performUndo(TextBuffer buffer, RangeUpdater updater,
524 UndoMemento undo, IProgressMonitor pm) throws CoreException {
525 int size = fChildren != null ? fChildren.size() : 0;
526 for (int i = 0; i < size; i++) {
528 TextEditNode child = (TextEditNode) fChildren.get(i);
529 child.performUndo(buffer, updater, undo, pm);
531 updater.setActiveNode(this);
533 fEdit.perform(buffer);
535 undo.add(fEdit.perform(buffer));
539 protected void setUndoIndex(int index) {
542 public void performedUndo() {
543 int size = fChildren != null ? fChildren.size() : 0;
544 for (int i = 0; i < size; i++) {
545 TextEditNode child = (TextEditNode) fChildren.get(i);
546 child.performedUndo();
551 // protected void createUndoList(List list) {
552 // int size= fChildren != null ? fChildren.size() : 0;
553 // for (int i= 0; i < size; i++) {
554 // TextEditNode child= (TextEditNode)fChildren.get(i);
555 // child.createUndoList(list);