1 11 package org.eclipse.jface.text.projection; 12 13 14 import java.util.ArrayList ; 15 import java.util.List ; 16 17 import org.eclipse.jface.text.AbstractDocument; 18 import org.eclipse.jface.text.BadLocationException; 19 import org.eclipse.jface.text.BadPositionCategoryException; 20 import org.eclipse.jface.text.DefaultLineTracker; 21 import org.eclipse.jface.text.DocumentEvent; 22 import org.eclipse.jface.text.IDocument; 23 import org.eclipse.jface.text.IDocumentExtension; 24 import org.eclipse.jface.text.IDocumentListener; 25 import org.eclipse.jface.text.ILineTracker; 26 import org.eclipse.jface.text.IRegion; 27 import org.eclipse.jface.text.ITextStore; 28 import org.eclipse.jface.text.Position; 29 import org.eclipse.jface.text.Region; 30 import org.eclipse.jface.text.TextUtilities; 31 32 33 60 public class ProjectionDocument extends AbstractDocument { 61 62 63 68 private final static String FRAGMENTS_CATEGORY_PREFIX= "__fragmentsCategory"; 70 75 private final static String SEGMENTS_CATEGORY= "__segmentsCategory"; 77 78 79 private IDocument fMasterDocument; 80 81 private IDocumentExtension fMasterDocumentExtension; 82 83 private String fFragmentsCategory; 84 85 private String fSegmentsCategory; 86 87 private DocumentEvent fMasterEvent; 88 89 private ProjectionDocumentEvent fSlaveEvent; 90 91 private DocumentEvent fOriginalEvent; 92 93 private boolean fIsUpdating= false; 94 95 private boolean fIsAutoExpanding= false; 96 97 private SegmentUpdater fSegmentUpdater; 98 99 private FragmentUpdater fFragmentsUpdater; 100 101 private ProjectionMapping fMapping; 102 103 108 public ProjectionDocument(IDocument masterDocument) { 109 super(); 110 111 fMasterDocument= masterDocument; 112 if (fMasterDocument instanceof IDocumentExtension) 113 fMasterDocumentExtension= (IDocumentExtension) fMasterDocument; 114 115 fSegmentsCategory= SEGMENTS_CATEGORY; 116 fFragmentsCategory= FRAGMENTS_CATEGORY_PREFIX + hashCode(); 117 fMasterDocument.addPositionCategory(fFragmentsCategory); 118 fFragmentsUpdater= new FragmentUpdater(fFragmentsCategory); 119 fMasterDocument.addPositionUpdater(fFragmentsUpdater); 120 121 fMapping= new ProjectionMapping(masterDocument, fFragmentsCategory, this, fSegmentsCategory); 122 123 ITextStore s= new ProjectionTextStore(masterDocument, fMapping); 124 ILineTracker tracker= new DefaultLineTracker(); 125 126 setTextStore(s); 127 setLineTracker(tracker); 128 129 completeInitialization(); 130 131 initializeProjection(); 132 tracker.set(s.get(0, s.getLength())); 133 } 134 135 138 public void dispose() { 139 fMasterDocument.removePositionUpdater(fFragmentsUpdater); 140 try { 141 fMasterDocument.removePositionCategory(fFragmentsCategory); 142 } catch (BadPositionCategoryException x) { 143 } 145 } 146 147 private void internalError() { 148 throw new IllegalStateException (); 149 } 150 151 156 protected final Position[] getFragments() { 157 try { 158 return fMasterDocument.getPositions(fFragmentsCategory); 159 } catch (BadPositionCategoryException e) { 160 internalError(); 161 } 162 return null; 164 } 165 166 171 protected final Position[] getSegments() { 172 try { 173 return getPositions(fSegmentsCategory); 174 } catch (BadPositionCategoryException e) { 175 internalError(); 176 } 177 return null; 179 } 180 181 186 public ProjectionMapping getProjectionMapping(){ 187 return fMapping; 188 } 189 190 195 public IDocument getMasterDocument() { 196 return fMasterDocument; 197 } 198 199 203 public String getDefaultLineDelimiter() { 204 return TextUtilities.getDefaultLineDelimiter(fMasterDocument); 205 } 206 207 211 private void initializeProjection() { 212 213 try { 214 215 addPositionCategory(fSegmentsCategory); 216 fSegmentUpdater= new SegmentUpdater(fSegmentsCategory); 217 addPositionUpdater(fSegmentUpdater); 218 219 int offset= 0; 220 Position[] fragments= getFragments(); 221 for (int i= 0; i < fragments.length; i++) { 222 Fragment fragment= (Fragment) fragments[i]; 223 Segment segment= new Segment(offset, fragment.getLength()); 224 segment.fragment= fragment; 225 addPosition(fSegmentsCategory, segment); 226 offset += fragment.length; 227 } 228 229 } catch (BadPositionCategoryException x) { 230 internalError(); 231 } catch (BadLocationException x) { 232 internalError(); 233 } 234 } 235 236 245 private Segment createSegmentFor(Fragment fragment, int index) throws BadLocationException, BadPositionCategoryException { 246 247 int offset= 0; 248 if (index > 0) { 249 Position[] segments= getSegments(); 250 Segment segment= (Segment) segments[index - 1]; 251 offset= segment.getOffset() + segment.getLength(); 252 } 253 254 Segment segment= new Segment(offset, 0); 255 segment.fragment= fragment; 256 fragment.segment= segment; 257 addPosition(fSegmentsCategory, segment); 258 return segment; 259 } 260 261 271 private void internalAddMasterDocumentRange(int offsetInMaster, int lengthInMaster, DocumentEvent masterDocumentEvent) throws BadLocationException { 272 if (lengthInMaster == 0) 273 return; 274 275 try { 276 277 Position[] fragments= getFragments(); 278 int index= fMasterDocument.computeIndexInCategory(fFragmentsCategory, offsetInMaster); 279 280 Fragment left= null; 281 Fragment right= null; 282 283 if (index < fragments.length) { 284 Fragment fragment= (Fragment) fragments[index]; 285 if (offsetInMaster == fragment.offset) 286 if (fragment.length == 0) left= fragment; 288 else 289 throw new IllegalArgumentException ("overlaps with existing fragment"); if (offsetInMaster + lengthInMaster == fragment.offset) 291 right= fragment; 292 } 293 294 if (0 < index && index <= fragments.length) { 295 Fragment fragment= (Fragment) fragments[index - 1]; 296 if (fragment.includes(offsetInMaster)) 297 throw new IllegalArgumentException ("overlaps with existing fragment"); if (fragment.getOffset() + fragment.getLength() == offsetInMaster) 299 left= fragment; 300 } 301 302 int offsetInSlave= 0; 303 if (index > 0) { 304 Fragment fragment= (Fragment) fragments[index - 1]; 305 Segment segment= fragment.segment; 306 offsetInSlave= segment.getOffset() + segment.getLength(); 307 } 308 309 ProjectionDocumentEvent event= new ProjectionDocumentEvent(this, offsetInSlave, 0, fMasterDocument.get(offsetInMaster, lengthInMaster), offsetInMaster, lengthInMaster, masterDocumentEvent); 310 super.fireDocumentAboutToBeChanged(event); 311 312 if (left != null && right != null) { 314 315 int endOffset= right.getOffset() + right.getLength(); 316 left.setLength(endOffset - left.getOffset()); 317 left.segment.setLength(left.segment.getLength() + right.segment.getLength()); 318 319 removePosition(fSegmentsCategory, right.segment); 320 fMasterDocument.removePosition(fFragmentsCategory, right); 321 322 } else if (left != null) { 323 int endOffset= offsetInMaster +lengthInMaster; 324 left.setLength(endOffset - left.getOffset()); 325 left.segment.markForStretch(); 326 327 } else if (right != null) { 328 right.setOffset(right.getOffset() - lengthInMaster); 329 right.setLength(right.getLength() + lengthInMaster); 330 right.segment.markForStretch(); 331 332 } else { 333 Fragment fragment= new Fragment(offsetInMaster, lengthInMaster); 335 fMasterDocument.addPosition(fFragmentsCategory, fragment); 336 Segment segment= createSegmentFor(fragment, index); 337 segment.markForStretch(); 338 } 339 340 getTracker().replace(event.getOffset(), event.getLength(), event.getText()); 341 super.fireDocumentChanged(event); 342 343 } catch (BadPositionCategoryException x) { 344 internalError(); 345 } 346 } 347 348 355 private Fragment findFragment(int offsetInMaster, int lengthInMaster) { 356 Position[] fragments= getFragments(); 357 for (int i= 0; i < fragments.length; i++) { 358 Fragment f= (Fragment) fragments[i]; 359 if (f.getOffset() <= offsetInMaster && offsetInMaster + lengthInMaster <= f.getOffset() + f.getLength()) 360 return f; 361 } 362 return null; 363 } 364 365 378 private void internalRemoveMasterDocumentRange(int offsetInMaster, int lengthInMaster) throws BadLocationException { 379 try { 380 381 IRegion imageRegion= fMapping.toExactImageRegion(new Region(offsetInMaster, lengthInMaster)); 382 if (imageRegion == null) 383 throw new IllegalArgumentException (); 384 385 Fragment fragment= findFragment(offsetInMaster, lengthInMaster); 386 if (fragment == null) 387 throw new IllegalArgumentException (); 388 389 ProjectionDocumentEvent event= new ProjectionDocumentEvent(this, imageRegion.getOffset(), imageRegion.getLength(), "", offsetInMaster, lengthInMaster); super.fireDocumentAboutToBeChanged(event); 391 392 if (fragment.getOffset() == offsetInMaster) { 393 fragment.setOffset(offsetInMaster + lengthInMaster); 394 fragment.setLength(fragment.getLength() - lengthInMaster); 395 } else if (fragment.getOffset() + fragment.getLength() == offsetInMaster + lengthInMaster) { 396 fragment.setLength(fragment.getLength() - lengthInMaster); 397 } else { 398 400 Fragment newFragment= new Fragment(offsetInMaster, lengthInMaster); 402 Segment segment= new Segment(imageRegion.getOffset(), imageRegion.getLength()); 403 newFragment.segment= segment; 404 segment.fragment= newFragment; 405 fMasterDocument.addPosition(fFragmentsCategory, newFragment); 406 addPosition(fSegmentsCategory, segment); 407 408 int offset= offsetInMaster + lengthInMaster; 410 newFragment= new Fragment(offset, fragment.getOffset() + fragment.getLength() - offset); 411 offset= imageRegion.getOffset() + imageRegion.getLength(); 412 segment= new Segment(offset, fragment.segment.getOffset() + fragment.segment.getLength() - offset); 413 newFragment.segment= segment; 414 segment.fragment= newFragment; 415 fMasterDocument.addPosition(fFragmentsCategory, newFragment); 416 addPosition(fSegmentsCategory, segment); 417 418 fragment.setLength(offsetInMaster - fragment.getOffset()); 420 fragment.segment.setLength(imageRegion.getOffset() - fragment.segment.getOffset()); 421 } 422 423 getTracker().replace(event.getOffset(), event.getLength(), event.getText()); 424 super.fireDocumentChanged(event); 425 426 } catch (BadPositionCategoryException x) { 427 internalError(); 428 } 429 } 430 431 443 public final IRegion[] computeUnprojectedMasterRegions(int offsetInMaster, int lengthInMaster) throws BadLocationException { 444 445 IRegion[] fragments= null; 446 IRegion imageRegion= fMapping.toImageRegion(new Region(offsetInMaster, lengthInMaster)); 447 if (imageRegion != null) 448 fragments= fMapping.toExactOriginRegions(imageRegion); 449 450 if (fragments == null || fragments.length == 0) 451 return new IRegion[] { new Region(offsetInMaster, lengthInMaster) }; 452 453 List gaps= new ArrayList (); 454 455 IRegion region= fragments[0]; 456 if (offsetInMaster < region.getOffset()) 457 gaps.add(new Region(offsetInMaster, region.getOffset() - offsetInMaster)); 458 459 for (int i= 0; i < fragments.length - 1; i++) { 460 IRegion left= fragments[i]; 461 IRegion right= fragments[i + 1]; 462 int leftEnd= left.getOffset() + left.getLength(); 463 if (leftEnd < right.getOffset()) 464 gaps.add(new Region(leftEnd, right.getOffset() - leftEnd)); 465 } 466 467 region= fragments[fragments.length - 1]; 468 int leftEnd= region.getOffset() + region.getLength(); 469 int rightEnd= offsetInMaster + lengthInMaster; 470 if (leftEnd < rightEnd) 471 gaps.add(new Region(leftEnd, rightEnd - leftEnd)); 472 473 IRegion[] result= new IRegion[gaps.size()]; 474 gaps.toArray(result); 475 return result; 476 } 477 478 490 private IRegion computeFirstUnprojectedMasterRegion(int offsetInMaster, int lengthInMaster) throws BadLocationException { 491 492 IRegion[] fragments= null; 493 IRegion imageRegion= fMapping.toImageRegion(new Region(offsetInMaster, lengthInMaster)); 494 if (imageRegion != null) 495 fragments= fMapping.toExactOriginRegions(imageRegion); 496 497 if (fragments == null || fragments.length == 0) 498 return new Region(offsetInMaster, lengthInMaster); 499 500 IRegion region= fragments[0]; 501 if (offsetInMaster < region.getOffset()) 502 return new Region(offsetInMaster, region.getOffset() - offsetInMaster); 503 504 for (int i= 0; i < fragments.length - 1; i++) { 505 IRegion left= fragments[i]; 506 IRegion right= fragments[i + 1]; 507 int leftEnd= left.getOffset() + left.getLength(); 508 if (leftEnd < right.getOffset()) 509 return new Region(leftEnd, right.getOffset() - leftEnd); 510 } 511 512 region= fragments[fragments.length - 1]; 513 int leftEnd= region.getOffset() + region.getLength(); 514 int rightEnd= offsetInMaster + lengthInMaster; 515 if (leftEnd < rightEnd) 516 return new Region(leftEnd, rightEnd - leftEnd); 517 518 return null; 519 } 520 521 529 public void addMasterDocumentRange(int offsetInMaster, int lengthInMaster) throws BadLocationException { 530 addMasterDocumentRange(offsetInMaster, lengthInMaster, null); 531 } 532 533 543 private void addMasterDocumentRange(int offsetInMaster, int lengthInMaster, DocumentEvent masterDocumentEvent) throws BadLocationException { 544 553 int limit= Math.max(getFragments().length * 2, 20); 554 while (true) { 555 if (limit-- < 0) 556 throw new IllegalArgumentException ("safety loop termination"); 558 IRegion gap= computeFirstUnprojectedMasterRegion(offsetInMaster, lengthInMaster); 559 if (gap == null) 560 return; 561 562 internalAddMasterDocumentRange(gap.getOffset(), gap.getLength(), masterDocumentEvent); 563 } 564 } 565 566 574 public void removeMasterDocumentRange(int offsetInMaster, int lengthInMaster) throws BadLocationException { 575 IRegion[] fragments= computeProjectedMasterRegions(offsetInMaster, lengthInMaster); 576 if (fragments == null || fragments.length == 0) 577 return; 578 579 for (int i= 0; i < fragments.length; i++) { 580 IRegion fragment= fragments[i]; 581 internalRemoveMasterDocumentRange(fragment.getOffset(), fragment.getLength()); 582 } 583 } 584 585 595 final public IRegion[] computeProjectedMasterRegions(int offsetInMaster, int lengthInMaster) throws BadLocationException { 596 IRegion imageRegion= fMapping.toImageRegion(new Region(offsetInMaster, lengthInMaster)); 597 return imageRegion != null ? fMapping.toExactOriginRegions(imageRegion) : null; 598 } 599 600 605 protected boolean isUpdating() { 606 return fIsUpdating; 607 } 608 609 612 public void replace(int offset, int length, String text) throws BadLocationException { 613 try { 614 fIsUpdating= true; 615 if (fMasterDocumentExtension != null) 616 fMasterDocumentExtension.stopPostNotificationProcessing(); 617 618 super.replace(offset, length, text); 619 620 } finally { 621 fIsUpdating= false; 622 if (fMasterDocumentExtension != null) 623 fMasterDocumentExtension.resumePostNotificationProcessing(); 624 } 625 } 626 627 630 public void set(String text) { 631 try { 632 fIsUpdating= true; 633 if (fMasterDocumentExtension != null) 634 fMasterDocumentExtension.stopPostNotificationProcessing(); 635 636 super.set(text); 637 638 } finally { 639 fIsUpdating= false; 640 if (fMasterDocumentExtension != null) 641 fMasterDocumentExtension.resumePostNotificationProcessing(); 642 } 643 } 644 645 653 private ProjectionDocumentEvent normalize(DocumentEvent masterEvent) throws BadLocationException { 654 if (!isUpdating()) { 655 IRegion imageRegion= fMapping.toExactImageRegion(new Region(masterEvent.getOffset(), masterEvent.getLength())); 656 if (imageRegion != null) 657 return new ProjectionDocumentEvent(this, imageRegion.getOffset(), imageRegion.getLength(), masterEvent.getText(), masterEvent); 658 return null; 659 } 660 661 ProjectionDocumentEvent event= new ProjectionDocumentEvent(this, fOriginalEvent.getOffset(), fOriginalEvent.getLength(), fOriginalEvent.getText(), masterEvent); 662 fOriginalEvent= null; 663 return event; 664 } 665 666 674 protected final boolean adaptProjectionToMasterChange(DocumentEvent masterEvent) throws BadLocationException { 675 if (!isUpdating() && fFragmentsUpdater.affectsPositions(masterEvent) || fIsAutoExpanding && masterEvent.getLength() > 0) { 676 677 addMasterDocumentRange(masterEvent.getOffset(), masterEvent.getLength(), masterEvent); 678 return true; 679 680 } else if (fMapping.getImageLength() == 0 && masterEvent.getLength() == 0) { 681 682 Position[] fragments= getFragments(); 683 if (fragments.length == 0) { 684 try { 687 Fragment fragment= new Fragment(0, 0); 688 fMasterDocument.addPosition(fFragmentsCategory, fragment); 689 createSegmentFor(fragment, 0); 690 } catch (BadPositionCategoryException x) { 691 internalError(); 692 } 693 } 694 } 695 696 return isUpdating(); 697 } 698 699 707 public void masterDocumentAboutToBeChanged(DocumentEvent masterEvent) { 708 try { 709 710 boolean assertNotNull= adaptProjectionToMasterChange(masterEvent); 711 fSlaveEvent= normalize(masterEvent); 712 if (assertNotNull && fSlaveEvent == null) 713 internalError(); 714 715 fMasterEvent= masterEvent; 716 if (fSlaveEvent != null) 717 delayedFireDocumentAboutToBeChanged(); 718 719 } catch (BadLocationException e) { 720 internalError(); 721 } 722 } 723 724 731 public void masterDocumentChanged(DocumentEvent masterEvent) { 732 if ( !isUpdating() && masterEvent == fMasterEvent) { 733 if (fSlaveEvent != null) { 734 try { 735 getTracker().replace(fSlaveEvent.getOffset(), fSlaveEvent.getLength(), fSlaveEvent.getText()); 736 fireDocumentChanged(fSlaveEvent); 737 } catch (BadLocationException e) { 738 internalError(); 739 } 740 } else if (ensureWellFormedSegmentation(masterEvent.getOffset())) 741 fMapping.projectionChanged(); 742 } 743 } 744 745 748 protected void fireDocumentAboutToBeChanged(DocumentEvent event) { 749 fOriginalEvent= event; 750 } 753 754 757 private void delayedFireDocumentAboutToBeChanged() { 758 super.fireDocumentAboutToBeChanged(fSlaveEvent); 759 } 760 761 766 protected void fireDocumentChanged(DocumentEvent event) { 767 super.fireDocumentChanged(fSlaveEvent); 768 } 769 770 773 protected void updateDocumentStructures(DocumentEvent event) { 774 super.updateDocumentStructures(event); 775 ensureWellFormedSegmentation(computeAnchor(event)); 776 fMapping.projectionChanged(); 777 } 778 779 private int computeAnchor(DocumentEvent event) { 780 if (event instanceof ProjectionDocumentEvent) { 781 ProjectionDocumentEvent slave= (ProjectionDocumentEvent) event; 782 Object changeType= slave.getChangeType(); 783 if (ProjectionDocumentEvent.CONTENT_CHANGE == changeType) { 784 DocumentEvent master= slave.getMasterEvent(); 785 if (master != null) 786 return master.getOffset(); 787 } else if (ProjectionDocumentEvent.PROJECTION_CHANGE == changeType) { 788 return slave.getMasterOffset(); 789 } 790 } 791 return -1; 792 } 793 794 private boolean ensureWellFormedSegmentation(int anchorOffset) { 795 boolean changed= false; 796 Position[] segments= getSegments(); 797 for (int i= 0; i < segments.length; i++) { 798 Segment segment= (Segment) segments[i]; 799 if (segment.isDeleted() || segment.getLength() == 0) { 800 try { 801 removePosition(fSegmentsCategory, segment); 802 fMasterDocument.removePosition(fFragmentsCategory, segment.fragment); 803 changed= true; 804 } catch (BadPositionCategoryException e) { 805 internalError(); 806 } 807 } else if (i < segments.length - 1) { 808 Segment next= (Segment) segments[i + 1]; 809 if (next.isDeleted() || next.getLength() == 0) 810 continue; 811 Fragment fragment= segment.fragment; 812 if (fragment.getOffset() + fragment.getLength() == next.fragment.getOffset()) { 813 segment.setLength(segment.getLength() + next.getLength()); 815 fragment.setLength(fragment.getLength() + next.fragment.getLength()); 816 next.delete(); 817 } 818 } 819 } 820 821 if (changed && anchorOffset != -1) { 822 Position[] changedSegments= getSegments(); 823 if (changedSegments == null || changedSegments.length == 0) { 824 Fragment fragment= new Fragment(anchorOffset, 0); 825 try { 826 fMasterDocument.addPosition(fFragmentsCategory, fragment); 827 createSegmentFor(fragment, 0); 828 } catch (BadLocationException e) { 829 internalError(); 830 } catch (BadPositionCategoryException e) { 831 internalError(); 832 } 833 } 834 } 835 836 return changed; 837 } 838 839 842 public void registerPostNotificationReplace(IDocumentListener owner, IDocumentExtension.IReplace replace) { 843 if (!isUpdating()) 844 throw new UnsupportedOperationException (); 845 super.registerPostNotificationReplace(owner, replace); 846 } 847 848 853 public void setAutoExpandMode(boolean autoExpandMode) { 854 fIsAutoExpanding= autoExpandMode; 855 } 856 857 864 public void replaceMasterDocumentRanges(int offsetInMaster, int lengthInMaster) throws BadLocationException { 865 try { 866 867 ProjectionDocumentEvent event= new ProjectionDocumentEvent(this, 0, fMapping.getImageLength(), fMasterDocument.get(offsetInMaster, lengthInMaster), offsetInMaster, lengthInMaster); 868 super.fireDocumentAboutToBeChanged(event); 869 870 Position[] fragments= getFragments(); 871 for (int i= 0; i < fragments.length; i++) { 872 Fragment fragment= (Fragment) fragments[i]; 873 fMasterDocument.removePosition(fFragmentsCategory, fragment); 874 removePosition(fSegmentsCategory, fragment.segment); 875 } 876 877 Fragment fragment= new Fragment(offsetInMaster, lengthInMaster); 878 Segment segment= new Segment(0, 0); 879 segment.fragment= fragment; 880 fragment.segment= segment; 881 fMasterDocument.addPosition(fFragmentsCategory, fragment); 882 addPosition(fSegmentsCategory, segment); 883 884 getTracker().set(fMasterDocument.get(offsetInMaster, lengthInMaster)); 885 super.fireDocumentChanged(event); 886 887 } catch (BadPositionCategoryException x) { 888 internalError(); 889 } 890 } 891 } 892 | Popular Tags |