1 19 package org.openide.text; 20 21 import java.util.logging.Level ; 22 import java.util.logging.Logger ; 23 import org.openide.text.EnhancedChangeEvent; 24 25 import java.io.*; 26 27 import java.util.*; 28 29 import javax.swing.event.*; 30 import javax.swing.text.*; 31 32 33 41 public abstract class DocumentLine extends Line { 42 44 private static Map<CloneableEditorSupport,DocumentLine[]> assigned = new WeakHashMap<CloneableEditorSupport,DocumentLine[]>(5); 45 static final long serialVersionUID = 3213776466939427487L; 46 47 48 protected PositionRef pos; 49 50 52 @Deprecated 53 private boolean breakpoint; 54 55 57 @Deprecated 58 private transient boolean error; 59 60 62 @Deprecated 63 private transient boolean current; 64 65 66 private transient LR listener; 67 68 69 private transient DocumentListener docL; 70 71 72 private List<Part> lineParts = new ArrayList<Part>(3); 73 74 80 public DocumentLine(org.openide.util.Lookup obj, PositionRef pos) { 81 super(obj); 82 this.pos = pos; 83 } 84 85 87 void init() { 88 listener = new LR(); 89 pos.getCloneableEditorSupport().addChangeListener( 90 org.openide.util.WeakListeners.change(listener, pos.getCloneableEditorSupport()) 91 ); 92 } 93 94 100 public int getLineNumber() { 101 try { 102 return pos.getLine(); 103 } catch (IOException ex) { 104 return 0; 106 } 107 } 108 109 113 public abstract void show(int kind, int column); 114 115 116 @SuppressWarnings ("deprecation") 117 public void setBreakpoint(boolean b) { 118 if (breakpoint != b) { 119 breakpoint = b; 120 refreshState(); 121 } 122 } 123 124 125 @SuppressWarnings ("deprecation") 126 public boolean isBreakpoint() { 127 return breakpoint; 128 } 129 130 131 @SuppressWarnings ("deprecation") 132 public void markError() { 133 DocumentLine previous = registerLine(1, this); 134 135 if (previous != null) { 136 previous.error = false; 137 previous.refreshState(); 138 } 139 140 error = true; 141 142 refreshState(); 143 } 144 145 146 @SuppressWarnings ("deprecation") 147 public void unmarkError() { 148 error = false; 149 registerLine(1, null); 150 151 refreshState(); 152 } 153 154 155 @SuppressWarnings ("deprecation") 156 public void markCurrentLine() { 157 DocumentLine previous = registerLine(0, this); 158 159 if (previous != null) { 160 previous.current = false; 161 previous.refreshState(); 162 } 163 164 current = true; 165 refreshState(); 166 } 167 168 169 @SuppressWarnings ("deprecation") 170 public void unmarkCurrentLine() { 171 current = false; 172 registerLine(0, null); 173 174 refreshState(); 175 } 176 177 180 @Deprecated 181 synchronized void refreshState() { 182 StyledDocument doc = pos.getCloneableEditorSupport().getDocument(); 183 184 if (doc != null) { 185 if (docL != null) { 187 doc.removeDocumentListener(docL); 188 } 189 190 if (error) { 192 NbDocument.markError(doc, pos.getOffset()); 193 194 doc.addDocumentListener(docL = org.openide.util.WeakListeners.document(listener, doc)); 195 196 return; 197 } 198 199 if (current) { 201 NbDocument.markCurrent(doc, pos.getOffset()); 202 203 return; 204 } 205 206 if (breakpoint) { 208 NbDocument.markBreakpoint(doc, pos.getOffset()); 209 210 return; 211 } 212 213 NbDocument.markNormal(doc, pos.getOffset()); 214 215 return; 216 } 217 } 218 219 public int hashCode() { 220 return pos.getCloneableEditorSupport().hashCode(); 221 } 222 223 public boolean equals(Object o) { 224 if (o instanceof DocumentLine) { 225 DocumentLine dl = (DocumentLine) o; 226 227 if (dl.pos.getCloneableEditorSupport() == pos.getCloneableEditorSupport()) { 228 return dl.getLineNumber() == getLineNumber(); 229 } 230 } 231 232 return false; 233 } 234 235 239 247 @Deprecated 248 private DocumentLine registerLine(int indx, DocumentLine line) { 249 DocumentLine prev; 250 251 CloneableEditorSupport es = pos.getCloneableEditorSupport(); 252 253 DocumentLine[] arr = assigned.get(es); 254 255 if (arr != null) { 256 prev = arr[indx]; 258 } else { 259 arr = new DocumentLine[2]; 261 assigned.put(es, arr); 262 prev = null; 263 } 264 265 arr[indx] = line; 266 267 return prev; 268 } 269 270 274 276 private void writeObject(ObjectOutputStream oos) throws IOException { 277 oos.writeObject(pos); 279 oos.writeBoolean(breakpoint); 280 } 281 282 284 private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException { 285 pos = (PositionRef) ois.readObject(); 286 setBreakpoint(ois.readBoolean()); 287 lineParts = new ArrayList<Part>(3); 288 } 289 290 292 Object readResolve() throws ObjectStreamException { 293 return this.pos.getCloneableEditorSupport().getLineSet().registerLine(this); 296 } 297 298 300 protected void addAnnotation(Annotation anno) { 301 super.addAnnotation(anno); 302 303 StyledDocument doc = pos.getCloneableEditorSupport().getDocument(); 304 305 if (doc == null) { 307 return; 308 } 309 310 pos.getCloneableEditorSupport().prepareDocument().waitFinished(); 311 312 try { 313 if (!anno.isInDocument()) { 314 anno.setInDocument(true); 315 316 FindAnnotationPosition fap = new FindAnnotationPosition(doc, pos.getPosition()); 318 doc.render(fap); 319 NbDocument.addAnnotation(doc, fap.getAnnotationPosition(), -1, anno); 320 } 321 } catch (IOException ex) { 322 Logger.getLogger(DocumentLine.class.getName()).log(Level.WARNING, null, ex); 323 } 324 } 325 326 328 protected void removeAnnotation(Annotation anno) { 329 super.removeAnnotation(anno); 330 331 StyledDocument doc = pos.getCloneableEditorSupport().getDocument(); 332 333 if (doc == null) { 335 return; 336 } 337 338 pos.getCloneableEditorSupport().prepareDocument().waitFinished(); 339 340 if (anno.isInDocument()) { 341 anno.setInDocument(false); 342 NbDocument.removeAnnotation(doc, anno); 343 } 344 } 345 346 349 void attachDetachAnnotations(StyledDocument doc, boolean closing) { 350 Position annoPos = null; 352 353 if (!closing) { 354 try { 355 annoPos = pos.getPosition(); 356 357 FindAnnotationPosition fap = new FindAnnotationPosition(doc, annoPos); 358 doc.render(fap); 359 annoPos = fap.getAnnotationPosition(); 360 } catch (IOException ex) { 361 Logger.getLogger(DocumentLine.class.getName()).log(Level.WARNING, null, ex); 362 } 363 } 364 365 java.util.List list = getAnnotations(); 366 367 for (int i = 0; i < list.size(); i++) { 368 Annotation anno = (Annotation) list.get(i); 369 370 if (!closing) { 371 if (!anno.isInDocument()) { 372 anno.setInDocument(true); 373 NbDocument.addAnnotation(doc, annoPos, -1, anno); 374 } 375 } else { 376 if (anno.isInDocument()) { 377 anno.setInDocument(false); 378 NbDocument.removeAnnotation(doc, anno); 379 } 380 } 381 } 382 383 for (Part p : lineParts) { 385 p.attachDetachAnnotations(doc, closing); 386 } 387 } 388 389 public String getText() { 390 final StyledDocument doc = pos.getCloneableEditorSupport().getDocument(); 391 392 if (doc == null) { 394 return null; 395 } 396 397 final String [] retStringArray = new String [1]; 398 doc.render( 399 new Runnable () { 400 public void run() { 401 int lineNumber = getLineNumber(); 403 int lineStart = NbDocument.findLineOffset(doc, lineNumber); 404 405 int lineEnd; 408 409 if ((lineNumber + 1) >= NbDocument.findLineRootElement(doc).getElementCount()) { 410 lineEnd = doc.getLength(); 411 } else { 412 lineEnd = NbDocument.findLineOffset(doc, lineNumber + 1); 413 } 414 415 try { 416 retStringArray[0] = doc.getText(lineStart, lineEnd - lineStart); 417 } catch (BadLocationException ex) { 418 Logger.getLogger(DocumentLine.class.getName()).log(Level.WARNING, null, ex); 419 retStringArray[0] = null; 420 } 421 422 } 424 } 425 ); 426 427 return retStringArray[0]; 428 } 429 430 431 void addLinePart(DocumentLine.Part linePart) { 432 lineParts.add(linePart); 433 } 434 435 436 void moveLinePart(DocumentLine.Part linePart, DocumentLine newLine) { 437 lineParts.remove(linePart); 438 newLine.addLinePart(linePart); 439 linePart.changeLine(newLine); 440 } 441 442 443 void notifyChange(DocumentEvent p0, DocumentLine.Set set, StyledDocument doc) { 444 DocumentLine.Part part; 445 446 for (int i = 0; i < lineParts.size();) { 447 part = lineParts.get(i); 448 449 part.handleDocumentChange(p0); 451 452 if (NbDocument.findLineNumber(doc, part.getOffset()) != part.getLine().getLineNumber()) { 454 DocumentLine line = (DocumentLine) set.getCurrent(NbDocument.findLineNumber(doc, part.getOffset())); 455 moveLinePart(part, line); 456 } else { 457 i++; 458 } 459 } 460 461 Position p; 468 469 try { 470 p = pos.getPosition(); 471 } catch (IOException ex) { 472 Logger.getLogger(DocumentLine.class.getName()).log(Level.WARNING, null, ex); 473 p = null; 474 } 475 476 if (p != null) { 477 int lineStartOffset = NbDocument.findLineOffset(doc, NbDocument.findLineNumber(doc, p.getOffset())); 478 CloneableEditorSupport support = pos.getCloneableEditorSupport(); 479 480 pos = new PositionRef(support.getPositionManager(), lineStartOffset, Position.Bias.Forward); 482 483 List annos = getAnnotations(); 484 int annosSize = annos.size(); 485 486 if (annosSize > 0) { 487 try { 488 p = pos.getPosition(); 489 } catch (IOException e) { 490 throw new IllegalArgumentException (); } 492 493 for (int i = 0; i < annosSize; i++) { 494 Annotation anno = (Annotation) annos.get(i); 495 496 if (anno.isInDocument()) { 497 anno.setInDocument(false); 498 NbDocument.removeAnnotation(support.getDocument(), anno); 499 } 500 501 if (!anno.isInDocument()) { 502 anno.setInDocument(true); 503 NbDocument.addAnnotation(support.getDocument(), p, -1, anno); 504 } 505 } 506 } 507 } 508 } 509 510 511 void notifyMove() { 512 updatePositionRef(); 513 514 for (int i = 0; i < lineParts.size(); i++) { 515 ((DocumentLine.Part) lineParts.get(i)).firePropertyChange(Line.Part.PROP_LINE, null, null); 516 } 517 } 518 519 520 private void updatePositionRef() { 521 528 557 } 558 559 563 private static final class FindAnnotationPosition implements Runnable { 564 private StyledDocument doc; 565 private Position annoPos; 566 567 FindAnnotationPosition(StyledDocument doc, Position pos) { 568 this.doc = doc; 569 this.annoPos = pos; } 571 572 public void run() { 573 int offset = annoPos.getOffset(); 574 int lineStartOffset = doc.getParagraphElement(offset).getStartOffset(); 575 576 if (offset != lineStartOffset) { 578 try { 579 annoPos = doc.createPosition(lineStartOffset); 580 } catch (BadLocationException e) { 581 throw new IllegalArgumentException (); } 583 } 584 } 585 586 Position getAnnotationPosition() { 587 return annoPos; 588 } 589 } 590 591 592 static class Part extends Line.Part { 593 594 private PositionRef position; 595 596 597 private Line line; 598 599 600 private int length; 601 602 604 private int previousOffset; 605 606 public Part(Line line, PositionRef position, int length) { 607 this.position = position; 608 this.line = line; 609 previousOffset = position.getOffset(); 610 this.length = limitLength(length); 611 } 612 613 private int limitLength(int suggestedLength) { 614 Document d = position.getCloneableEditorSupport().getDocument(); 615 if (d == null) { 616 return suggestedLength; 618 } 619 int end = position.getOffset() + suggestedLength; 620 621 if (end > d.getLength()) { 622 end = d.getLength(); 623 } 624 625 if (end < position.getOffset()) { 626 return 0; 627 } 628 629 try { 630 String text = d.getText(position.getOffset(), end - position.getOffset()); 631 int newLine = text.indexOf('\n'); 632 633 return (newLine == -1) ? text.length() : (newLine + 1); 634 } catch (BadLocationException ex) { 635 IllegalStateException i = new IllegalStateException (ex.getMessage()); 636 i.initCause(ex); 637 throw i; 638 } 639 } 640 641 642 public int getColumn() { 643 try { 644 return position.getColumn(); 645 } catch (IOException ex) { 646 return 0; } 648 } 649 650 653 public int getLength() { 654 return length; 655 } 656 657 658 public Line getLine() { 659 return line; 660 } 661 662 663 int getOffset() { 664 return position.getOffset(); 665 } 666 667 668 void changeLine(Line line) { 669 this.line = line; 670 671 firePropertyChange(PROP_LINE_NUMBER, null, line); 673 } 674 675 677 protected void addAnnotation(Annotation anno) { 678 super.addAnnotation(anno); 679 680 StyledDocument doc = position.getCloneableEditorSupport().getDocument(); 681 682 if (doc == null) { 684 return; 685 } 686 687 position.getCloneableEditorSupport().prepareDocument().waitFinished(); 688 689 try { 690 if (!anno.isInDocument()) { 691 anno.setInDocument(true); 692 NbDocument.addAnnotation(doc, position.getPosition(), length, anno); 693 } 694 } catch (IOException ex) { 695 Logger.getLogger(DocumentLine.class.getName()).log(Level.WARNING, null, ex); 696 } 697 } 698 699 701 protected void removeAnnotation(Annotation anno) { 702 super.removeAnnotation(anno); 703 704 StyledDocument doc = position.getCloneableEditorSupport().getDocument(); 705 706 if (doc == null) { 708 return; 709 } 710 711 position.getCloneableEditorSupport().prepareDocument().waitFinished(); 712 713 if (anno.isInDocument()) { 714 anno.setInDocument(false); 715 NbDocument.removeAnnotation(doc, anno); 716 } 717 } 718 719 public String getText() { 720 final StyledDocument doc = position.getCloneableEditorSupport().getDocument(); 721 722 if (doc == null) { 724 return null; 725 } 726 727 final String [] retStringArray = new String [1]; 728 doc.render( 729 new Runnable () { 730 public void run() { 731 try { 733 int p = position.getOffset(); 734 735 if (p >= doc.getLength()) { 736 retStringArray[0] = ""; 737 } else { 738 retStringArray[0] = doc.getText(position.getOffset(), getLength()); 739 } 740 } catch (BadLocationException ex) { 741 Logger.getLogger(DocumentLine.class.getName()).log(Level.WARNING, null, ex); 742 retStringArray[0] = null; 743 } 744 745 } 747 } 748 ); 749 750 return retStringArray[0]; 751 } 752 753 755 void attachDetachAnnotations(StyledDocument doc, boolean closing) { 756 java.util.List list = getAnnotations(); 757 758 for (int i = 0; i < list.size(); i++) { 759 Annotation anno = (Annotation) list.get(i); 760 761 if (!closing) { 762 try { 763 if (!anno.isInDocument()) { 764 anno.setInDocument(true); 765 NbDocument.addAnnotation(doc, position.getPosition(), getLength(), anno); 766 } 767 } catch (IOException ex) { 768 Logger.getLogger(DocumentLine.class.getName()).log(Level.WARNING, null, ex); 769 } 770 } else { 771 if (anno.isInDocument()) { 772 anno.setInDocument(false); 773 NbDocument.removeAnnotation(doc, anno); 774 } 775 } 776 } 777 } 778 779 781 void handleDocumentChange(DocumentEvent p0) { 782 if (p0.getType().equals(DocumentEvent.EventType.INSERT)) { 783 if ((p0.getOffset() >= previousOffset) && (p0.getOffset() < (previousOffset + getLength()))) { 784 firePropertyChange(Annotatable.PROP_TEXT, null, null); 785 } 786 } 787 788 if (p0.getType().equals(DocumentEvent.EventType.REMOVE)) { 789 if ( 790 ((p0.getOffset() >= previousOffset) && (p0.getOffset() < (previousOffset + getLength()))) || 791 ((p0.getOffset() < previousOffset) && ((p0.getOffset() + p0.getLength()) > previousOffset)) 792 ) { 793 length = limitLength(length); 794 firePropertyChange(Annotatable.PROP_TEXT, null, null); 795 } 796 } 797 798 if ( 799 (p0.getType().equals(DocumentEvent.EventType.INSERT) || 800 p0.getType().equals(DocumentEvent.EventType.REMOVE)) && (p0.getOffset() < previousOffset) 801 ) { 802 firePropertyChange(Line.Part.PROP_COLUMN, null, null); 803 } 804 805 previousOffset = position.getOffset(); 806 } 807 } 808 809 810 private final class LR implements Runnable , ChangeListener, DocumentListener { 811 private static final int REFRESH = 0; 812 private static final int UNMARK = 1; 813 private static final int ATTACH_DETACH = 2; 814 private int actionId; 815 private EnhancedChangeEvent ev; 816 817 public LR() { 818 } 819 820 public LR(int actionId) { 821 this.actionId = actionId; 822 } 823 824 public LR(EnhancedChangeEvent ev) { 825 this.actionId = ATTACH_DETACH; 826 this.ev = ev; 827 } 828 829 public void run() { 830 switch (actionId) { 831 case REFRESH: 832 refreshState(); 833 834 break; 835 836 case UNMARK: 837 unmarkError(); 838 839 break; 840 841 case ATTACH_DETACH: 842 attachDetachAnnotations(ev.getDocument(), ev.isClosingDocument()); 843 ev = null; 844 845 break; 846 } 847 } 848 849 private void invoke(int op) { 850 new LR(op).run(); 853 } 854 855 private void invoke(EnhancedChangeEvent ev) { 856 new LR(ev).run(); 859 } 860 861 public void stateChanged(ChangeEvent ev) { 862 invoke(REFRESH); 863 invoke((EnhancedChangeEvent) ev); 864 } 865 866 public void removeUpdate(final javax.swing.event.DocumentEvent p0) { 867 invoke(UNMARK); 868 } 869 870 public void insertUpdate(final javax.swing.event.DocumentEvent p0) { 871 invoke(UNMARK); 872 } 873 874 public void changedUpdate(final javax.swing.event.DocumentEvent p0) { 875 } 876 } 877 878 883 public static abstract class Set extends Line.Set { 884 885 final LineListener listener; 886 887 888 private List<Line> list; 889 890 893 public Set(StyledDocument doc) { 894 this(doc, null); 895 } 896 897 Set(StyledDocument doc, CloneableEditorSupport support) { 898 listener = new LineListener(doc, support); 899 } 900 901 903 void linesChanged(int startLineNumber, int endLineNumber, DocumentEvent p0) { 904 List changedLines = getLinesFromRange(startLineNumber, endLineNumber); 905 906 for (Iterator it = changedLines.iterator(); it.hasNext();) { 907 Line line = (Line) it.next(); 908 909 line.firePropertyChange(Annotatable.PROP_TEXT, null, null); 910 911 if (line instanceof DocumentLine) { 914 ((DocumentLine) line).notifyChange(p0, this, listener.doc); 915 } 916 } 917 } 918 919 921 void linesMoved(int startLineNumber, int endLineNumber) { 922 List movedLines = getLinesFromRange(startLineNumber, endLineNumber); 923 924 for (Iterator it = movedLines.iterator(); it.hasNext();) { 925 Line line = (Line) it.next(); 926 line.firePropertyChange(Line.PROP_LINE_NUMBER, null, null); 927 928 if (line instanceof DocumentLine) { 931 ((DocumentLine) line).notifyMove(); 932 } 933 } 934 } 935 936 938 private List<Line> getLinesFromRange(int startLineNumber, int endLineNumber) { 939 List<Line> linesInRange = new ArrayList<Line>(10); 940 941 synchronized (findWeakHashMap()) { 942 for (Line line : findWeakHashMap().keySet()) { 943 int lineNumber = line.getLineNumber(); 944 945 if ((startLineNumber <= lineNumber) && (lineNumber <= endLineNumber)) { 946 linesInRange.add(line); 947 } 948 } 949 } 950 951 return linesInRange; 952 } 953 954 960 public synchronized List<? extends Line> getLines() { 961 if (list == null) { 962 list = new LazyLines(this); 963 } 964 965 return list; 966 } 967 968 975 public Line getOriginal(int line) throws IndexOutOfBoundsException { 976 int newLine = listener.getLine(line); 977 int offset = NbDocument.findLineOffset(listener.doc, newLine); 978 979 return safelyRegisterLine(createLine(offset)); 980 } 981 982 public int getOriginalLineNumber(Line line) { 983 Line find = findLine(line); 984 985 if (find != null) { 986 return listener.getOld(find.getLineNumber()); 987 } else { 988 return -1; 989 } 990 } 991 992 997 public Line getCurrent(int line) throws IndexOutOfBoundsException { 998 int offset = NbDocument.findLineOffset(listener.doc, line); 999 1000 return safelyRegisterLine(createLine(offset)); 1001 } 1002 1003 1007 protected abstract Line createLine(int offset); 1008 1009 1017 private Line safelyRegisterLine(final Line line) { 1018 assert line != null; 1019 class DocumentRenderer implements Runnable { 1020 public Line result; 1021 1022 public void run() { 1023 result = DocumentLine.Set.super.registerLine(line); 1024 } 1025 } 1026 1027 DocumentRenderer renderer = new DocumentRenderer(); 1028 listener.doc.render(renderer); 1029 1030 return renderer.result; 1031 } 1032 } 1033} 1034 | Popular Tags |