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);