1 19 package org.openide.text; 20 21 import org.openide.util.RequestProcessor; 22 23 import java.io.*; 24 25 import java.lang.ref.ReferenceQueue ; 26 import java.lang.ref.WeakReference ; 27 28 import java.util.*; 29 30 import javax.swing.text.BadLocationException ; 31 import javax.swing.text.Element ; 32 import javax.swing.text.Position ; 33 import javax.swing.text.StyledDocument ; 34 35 36 42 public final class PositionRef extends Object implements Serializable { 43 static final long serialVersionUID = -4931337398907426948L; 44 45 46 transient private Manager.Kind kind; 47 48 49 private Manager manager; 50 51 52 private boolean insertAfter; 53 54 60 PositionRef(Manager manager, int offset, Position.Bias bias) { 61 this(manager, new Manager.OffsetKind(offset, manager), bias); 62 } 63 64 71 PositionRef(Manager manager, int line, int column, Position.Bias bias) { 72 this(manager, new Manager.LineKind(line, column, manager), bias); 73 } 74 75 80 private PositionRef(Manager manager, Manager.Kind kind, Position.Bias bias) { 81 this.manager = manager; 82 this.kind = kind; 83 insertAfter = (bias == Position.Bias.Backward); 84 init(); 85 } 86 87 88 private void init() { 89 kind = manager.addPosition(this); 90 } 91 92 93 private void writeObject(ObjectOutputStream out) throws IOException { 94 out.writeBoolean(insertAfter); 95 out.writeObject(manager); 96 kind.write(out); 97 } 98 99 100 private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { 101 insertAfter = in.readBoolean(); 102 manager = (Manager) in.readObject(); 103 kind = manager.readKind(in); 104 init(); 105 } 106 107 109 public CloneableEditorSupport getCloneableEditorSupport() { 110 return manager.getCloneableEditorSupport(); 111 } 112 113 115 public Position.Bias getPositionBias() { 116 return insertAfter ? Position.Bias.Backward : Position.Bias.Forward; 117 } 118 119 122 public Position getPosition() throws IOException { 123 if (manager.getCloneableEditorSupport().getDocument() == null) { 124 manager.getCloneableEditorSupport().openDocument(); 125 } 126 127 synchronized (manager.getLock()) { 128 Manager.PositionKind p = (Manager.PositionKind) kind; 129 130 return p.pos; 131 } 132 } 133 134 136 public int getOffset() { 137 return kind.getOffset(); 138 } 139 140 144 public int getLine() throws IOException { 145 return kind.getLine(); 146 } 147 148 152 public int getColumn() throws IOException { 153 return kind.getColumn(); 154 } 155 156 public String toString() { 157 return "Pos[" + getOffset() + "]" + ", kind=" + kind; } 159 160 165 static final class Manager extends Object implements Runnable , Serializable { 166 167 private static ThreadLocal <Object > DOCUMENT = new ThreadLocal <Object >(); 169 static final long serialVersionUID = -4374030124265110801L; 170 171 173 private transient ChainItem head; 174 175 176 private transient ReferenceQueue <PositionRef> queue; 177 178 180 private transient int counter; 181 182 185 private transient RequestProcessor.Task sweepTask; 186 187 188 transient private CloneableEditorSupport support; 189 190 191 transient private StyledDocument doc; 192 193 196 public Manager(CloneableEditorSupport supp) { 197 support = supp; 198 init(); 199 } 200 201 202 protected void init() { 203 queue = new ReferenceQueue <PositionRef>(); 204 205 head = new ChainItem(null, queue, null); 207 } 208 209 private Object getLock() { 210 return support.getLock(); 211 } 212 213 214 private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { 215 Object firstObject = in.readObject(); 216 217 223 { 224 CloneableEditorSupport.Env env = (CloneableEditorSupport.Env) firstObject; 226 support = (CloneableEditorSupport) env.findCloneableOpenSupport(); 227 } 228 229 if (support == null) { 230 throw new IOException(); 232 } 233 } 234 235 final Object readResolve() { 236 return support.getPositionManager(); 237 } 238 239 private void writeObject(ObjectOutputStream out) 240 throws IOException { 241 out.writeObject(support.cesEnv()); 243 } 244 245 247 public CloneableEditorSupport getCloneableEditorSupport() { 248 return support; 249 } 250 251 253 void documentOpened(StyledDocument doc) { 254 this.doc = doc; 255 256 processPositions(true); 257 } 258 259 262 void documentClosed() { 263 processPositions(false); 264 265 doc = null; 266 } 267 268 271 private StyledDocument getDoc() { 272 Object d = DOCUMENT.get(); 273 274 if (d instanceof StyledDocument ) { 275 return (StyledDocument ) d; 276 } 277 278 if (d == this) { 279 return null; 280 } 281 282 return this.doc; 283 } 284 285 289 private void processPositions(final boolean toMemory) { 290 while (queue.poll() != null) 292 ; 293 294 counter = 0; 295 296 321 new DocumentRenderer(this, DocumentRenderer.PROCESS_POSITIONS, toMemory).render(); 322 } 323 324 326 private void checkQueue() { 327 while (queue.poll() != null) { 328 counter++; 329 } 330 331 if (counter > 100) { 332 counter = 0; 333 334 if (sweepTask == null) { 335 sweepTask = RequestProcessor.getDefault().post(this); 336 } else if (sweepTask.isFinished()) { 337 sweepTask.schedule(0); 338 } 339 } 340 } 341 342 344 public synchronized void run() { 345 synchronized (getLock()) { 346 ChainItem previous = head; 347 ChainItem ref = previous.next; 348 349 while (ref != null) { 350 if (ref.get() == null) { 351 previous.next = ref.next; 353 } else { 354 previous = ref; 355 } 356 357 ref = ref.next; 358 } 359 } 360 } 361 362 363 Kind addPosition(final PositionRef pos) { 364 Kind kind; 365 366 375 kind = (Kind) new DocumentRenderer(this, DocumentRenderer.ADD_POSITION, pos).renderToObject(); 376 checkQueue(); 377 378 return kind; 379 } 380 381 385 386 Kind readKind(DataInput is) throws IOException { 387 int offset = is.readInt(); 388 int line = is.readInt(); 389 int column = is.readInt(); 390 391 if (offset == -1) { 392 return new LineKind(line, column, this); 394 } 395 396 if ((line == -1) || (column == -1)) { 397 return new OffsetKind(offset, this); 399 } 400 401 return new OutKind(offset, line, column, this); 403 } 404 405 408 410 private static class ChainItem extends WeakReference <PositionRef> { 411 412 ChainItem next; 413 414 419 public ChainItem(PositionRef position, ReferenceQueue <PositionRef> queue, ChainItem next) { 420 super(position, queue); 421 422 this.next = next; 423 } 424 } 425 427 428 private static abstract class Kind extends Object { 429 protected final PositionRef.Manager mgr; 430 431 Kind(PositionRef.Manager mgr) { 432 this.mgr = mgr; 433 } 434 435 436 public abstract int getOffset(); 437 438 439 public abstract int getLine() throws IOException; 440 441 442 public abstract int getColumn() throws IOException; 443 444 445 public abstract void write(DataOutput os) throws IOException; 446 447 448 public PositionKind toMemory(boolean insertAfter) { 449 459 return (PositionKind) new DocumentRenderer(mgr, DocumentRenderer.KIND_TO_MEMORY, this, insertAfter).renderToObject(); 460 } 461 462 463 public Kind fromMemory() { 464 return this; 465 } 466 } 467 468 471 private static final class PositionKind extends Kind { 472 473 private Position pos; 474 475 476 public PositionKind(Position pos, PositionRef.Manager mgr) { 477 super(mgr); 478 this.pos = pos; 479 } 480 481 482 public int getOffset() { 483 return pos.getOffset(); 484 } 485 486 487 public int getLine() { 488 return new DocumentRenderer(mgr, DocumentRenderer.POSITION_KIND_GET_LINE, this).renderToInt(); 490 } 491 492 493 public int getColumn() { 494 return new DocumentRenderer(mgr, DocumentRenderer.POSITION_KIND_GET_COLUMN, this).renderToInt(); 496 } 497 498 499 public void write(DataOutput os) throws IOException { 500 505 DocumentRenderer renderer = new DocumentRenderer(mgr, DocumentRenderer.POSITION_KIND_WRITE, this); 506 507 int offset = renderer.renderToIntIOE(); 508 int line = renderer.getLine(); 509 int column = renderer.getColumn(); 510 511 if ((offset < 0) || (line < 0) || (column < 0)) { 512 throw new IOException( 513 "Illegal PositionKind: " + pos + "[offset=" +offset + ",line=" +line + ",column=" + column + "] in " +mgr.getDoc() + " used by " + mgr.support + "." 518 ); 519 } 520 521 os.writeInt(offset); 522 os.writeInt(line); 523 os.writeInt(column); 524 } 525 526 527 public PositionKind toMemory(boolean insertAfter) { 528 return this; 529 } 530 531 532 public Kind fromMemory() { 533 return new OutKind(this, mgr); 534 } 535 } 536 537 541 private static final class OutKind extends Kind { 542 private int offset; 543 private int line; 544 private int column; 545 546 548 public OutKind(PositionKind kind, PositionRef.Manager mgr) { 549 super(mgr); 550 551 556 DocumentRenderer renderer = new DocumentRenderer(mgr, DocumentRenderer.OUT_KIND_CONSTRUCTOR, kind); 557 558 int offset = renderer.renderToInt(); 559 int line = renderer.getLine(); 560 int column = renderer.getColumn(); 561 562 if ((offset < 0) || (line < 0) || (column < 0)) { 563 throw new IndexOutOfBoundsException ( 564 "Illegal OutKind[offset=" +offset + ",line=" +line + ",column=" + column + "] in " +mgr.getDoc() + " used by " + mgr.support + "." 569 ); 570 } 571 572 this.offset = offset; 573 this.line = line; 574 this.column = column; 575 } 576 577 579 OutKind(int offset, int line, int column, PositionRef.Manager mgr) { 580 super(mgr); 581 this.offset = offset; 582 this.line = line; 583 this.column = column; 584 } 585 586 587 public int getOffset() { 588 return offset; 589 } 590 591 592 public int getLine() { 593 return line; 594 } 595 596 597 public int getColumn() { 598 return column; 599 } 600 601 602 public void write(DataOutput os) throws IOException { 603 if ((offset < 0) || (line < 0) || (column < 0)) { 604 throw new IOException( 605 "Illegal OutKind[offset=" +offset + ",line=" +line + ",column=" + column + "] in " +mgr.getDoc() + " used by " + mgr.support + "." 610 ); 611 } 612 613 os.writeInt(offset); 614 os.writeInt(line); 615 os.writeInt(column); 616 } 617 } 618 620 623 private static final class OffsetKind extends Kind { 624 private int offset; 625 626 628 public OffsetKind(int offset, PositionRef.Manager mgr) { 629 super(mgr); 630 631 if (offset < 0) { 632 throw new IndexOutOfBoundsException ( 633 "Illegal OffsetKind[offset=" +offset + "] in " + mgr.getDoc() + " used by " +mgr.support + "." 637 ); 638 } 639 640 this.offset = offset; 641 } 642 643 644 public int getOffset() { 645 return offset; 646 } 647 648 649 public int getLine() throws IOException { 650 mgr.getCloneableEditorSupport().openDocument(); 653 return new DocumentRenderer(mgr, DocumentRenderer.OFFSET_KIND_GET_LINE, this, offset).renderToIntIOE(); 654 } 655 656 657 public int getColumn() throws IOException { 658 mgr.getCloneableEditorSupport().openDocument(); 661 return new DocumentRenderer(mgr, DocumentRenderer.OFFSET_KIND_GET_COLUMN, this, offset).renderToIntIOE(); 662 } 663 664 665 public void write(DataOutput os) throws IOException { 666 if (offset < 0) { 667 throw new IOException( 668 "Illegal OffsetKind[offset=" +offset + "] in " + mgr.getDoc() + " used by " +mgr.support + "." 672 ); 673 } 674 675 os.writeInt(offset); 676 os.writeInt(-1); 677 os.writeInt(-1); 678 } 679 } 680 681 684 private static final class LineKind extends Kind { 685 private int line; 686 private int column; 687 688 690 public LineKind(int line, int column, PositionRef.Manager mgr) { 691 super(mgr); 692 693 if ((line < 0) || (column < 0)) { 694 throw new IndexOutOfBoundsException ( 695 "Illegal LineKind[line=" +line + ",column=" + column + "] in " +mgr.getDoc() + " used by " + mgr.support + "." 699 ); 700 } 701 702 this.line = line; 703 this.column = column; 704 } 705 706 707 public int getOffset() { 708 720 try { 721 StyledDocument doc = mgr.getCloneableEditorSupport().getDocument(); 722 723 if (doc == null) { 724 doc = mgr.getCloneableEditorSupport().openDocument(); 725 } 726 727 int retOffset = new DocumentRenderer( 728 mgr, DocumentRenderer.LINE_KIND_GET_OFFSET, this, line, column, doc 729 ).renderToInt(); 730 731 return retOffset; 732 } catch (IOException e) { 733 return 0; 735 } 736 } 737 738 739 public int getLine() throws IOException { 740 return line; 741 } 742 743 744 public int getColumn() throws IOException { 745 return column; 746 } 747 748 749 public void write(DataOutput os) throws IOException { 750 if ((line < 0) || (column < 0)) { 751 throw new IOException( 752 "Illegal LineKind[line=" +line + ",column=" + column + "] in " +mgr.getDoc() + " used by " + mgr.support + "." 756 ); 757 } 758 759 os.writeInt(-1); 760 os.writeInt(line); 761 os.writeInt(column); 762 } 763 764 765 public PositionKind toMemory(boolean insertAfter) { 766 775 Position p = (Position ) new DocumentRenderer( 776 mgr, DocumentRenderer.LINE_KIND_TO_MEMORY, this, line, column, insertAfter 777 ).renderToObject(); 778 779 return new PositionKind(p, mgr); 780 } 781 } 782 783 787 private static final class DocumentRenderer implements Runnable { 788 private static final int KIND_TO_MEMORY = 0; 789 private static final int POSITION_KIND_GET_LINE = KIND_TO_MEMORY + 1; 790 private static final int POSITION_KIND_GET_COLUMN = POSITION_KIND_GET_LINE + 1; 791 private static final int POSITION_KIND_WRITE = POSITION_KIND_GET_COLUMN + 1; 792 private static final int OUT_KIND_CONSTRUCTOR = POSITION_KIND_WRITE + 1; 793 private static final int OFFSET_KIND_GET_LINE = OUT_KIND_CONSTRUCTOR + 1; 794 private static final int OFFSET_KIND_GET_COLUMN = OFFSET_KIND_GET_LINE + 1; 795 private static final int LINE_KIND_GET_OFFSET = OFFSET_KIND_GET_COLUMN + 1; 796 private static final int LINE_KIND_TO_MEMORY = LINE_KIND_GET_OFFSET + 1; 797 private static final int PROCESS_POSITIONS = LINE_KIND_TO_MEMORY + 1; 798 private static final int ADD_POSITION = PROCESS_POSITIONS + 1; 799 private final Manager mgr; 800 private final int opCode; 801 private Kind argKind; 802 private boolean argInsertAfter; 803 private boolean argToMemory; 804 private int argInt; 805 private Object retObject; 806 private int retInt; 807 private int argLine; 808 private int argColumn; 809 private PositionRef argPos; 810 private StyledDocument argDoc; 811 private IOException ioException; 812 813 DocumentRenderer(Manager mgr, int opCode, Kind argKind) { 814 this.mgr = mgr; 815 this.opCode = opCode; 816 this.argKind = argKind; 817 } 818 819 DocumentRenderer(Manager mgr, int opCode, Kind argKind, boolean argInsertAfter) { 820 this(mgr, opCode, argKind); 821 this.argInsertAfter = argInsertAfter; 822 } 823 824 DocumentRenderer(Manager mgr, int opCode, Kind argKind, int argInt) { 825 this(mgr, opCode, argKind); 826 this.argInt = argInt; 827 } 828 829 DocumentRenderer(Manager mgr, int opCode, Kind argKind, int argLine, int argColumn) { 830 this(mgr, opCode, argKind); 831 this.argLine = argLine; 832 this.argColumn = argColumn; 833 } 834 835 DocumentRenderer(Manager mgr, int opCode, Kind argKind, int argLine, int argColumn, StyledDocument argDoc) { 836 this(mgr, opCode, argKind, argLine, argColumn); 837 this.argDoc = argDoc; 838 } 839 840 DocumentRenderer(Manager mgr, int opCode, Kind argKind, int argLine, int argColumn, boolean argInsertAfter) { 841 this(mgr, opCode, argKind, argLine, argColumn); 842 this.argInsertAfter = argInsertAfter; 843 } 844 845 DocumentRenderer(Manager mgr, int opCode, boolean toMemory) { 846 this.mgr = mgr; 847 this.opCode = opCode; 848 this.argToMemory = toMemory; 849 } 850 851 DocumentRenderer(Manager mgr, int opCode, PositionRef argPos) { 852 this.mgr = mgr; 853 this.opCode = opCode; 854 this.argPos = argPos; 855 } 856 857 void render() { 858 StyledDocument d = mgr.getDoc(); 859 Object prev = DOCUMENT.get(); 860 861 try { 862 if (d != null) { 863 DOCUMENT.set(d); 864 d.render(this); 865 } else { 866 DOCUMENT.set(mgr); 867 this.run(); 868 } 869 } finally { 870 DOCUMENT.set(prev); 871 } 872 } 873 874 Object renderToObjectIOE() throws IOException { 875 Object o = renderToObject(); 876 877 if (ioException != null) { 878 throw ioException; 879 } 880 881 return o; 882 } 883 884 Object renderToObject() { 885 render(); 886 887 return retObject; 888 } 889 890 int renderToIntIOE() throws IOException { 891 int i = renderToInt(); 892 893 if (ioException != null) { 894 throw ioException; 895 } 896 897 return i; 898 } 899 900 int renderToInt() { 901 render(); 902 903 return retInt; 904 } 905 906 int getLine() { 907 return argLine; 908 } 909 910 int getColumn() { 911 return argColumn; 912 } 913 914 public void run() { 915 try { 916 switch (opCode) { 917 case KIND_TO_MEMORY: { 918 Position p; 920 921 int offset = argKind.getOffset(); 922 923 if (argKind.getClass() == OutKind.class) { 927 try { 928 int line = argKind.getLine(); 929 int col = argKind.getColumn(); 930 Element lineRoot = NbDocument.findLineRootElement(mgr.getDoc()); 931 932 if (line < lineRoot.getElementCount()) { 933 Element lineElem = lineRoot.getElement(line); 934 int lineStartOffset = lineElem.getStartOffset(); 935 int lineLen = lineElem.getEndOffset() - lineStartOffset; 936 937 if (lineLen >= 1) { col = Math.min(col, lineLen - 1); 939 offset = lineStartOffset + col; 940 } 941 } 942 } catch (IOException e) { 943 } 945 } 946 947 try { 948 p = NbDocument.createPosition( 949 mgr.getDoc(), offset, 950 argInsertAfter ? Position.Bias.Backward : Position.Bias.Forward 951 ); 952 } catch (BadLocationException e) { 953 p = mgr.getDoc().getEndPosition(); 954 } 955 956 retObject = new PositionKind(p, mgr); 957 958 break; 959 } 960 961 case POSITION_KIND_GET_LINE: { 962 retInt = NbDocument.findLineNumber(mgr.getDoc(), argKind.getOffset()); 963 964 break; 965 } 966 967 case POSITION_KIND_GET_COLUMN: { 968 retInt = NbDocument.findLineColumn(mgr.getDoc(), argKind.getOffset()); 969 970 break; 971 } 972 973 case POSITION_KIND_WRITE: 974 case OUT_KIND_CONSTRUCTOR: { 975 retInt = argKind.getOffset(); 976 argLine = argKind.getLine(); 977 argColumn = argKind.getColumn(); 978 979 break; 980 } 981 982 case OFFSET_KIND_GET_LINE: { 983 retInt = NbDocument.findLineNumber(mgr.getCloneableEditorSupport().openDocument(), argInt); 984 985 break; 986 } 987 988 case OFFSET_KIND_GET_COLUMN: { 989 retInt = NbDocument.findLineColumn(mgr.getCloneableEditorSupport().openDocument(), argInt); 990 991 break; 992 } 993 994 case LINE_KIND_GET_OFFSET: { 995 retInt = NbDocument.findLineOffset(argDoc, argLine) + argColumn; 996 997 break; 998 } 999 1000 case LINE_KIND_TO_MEMORY: { 1001 try { 1003 retObject = NbDocument.createPosition( 1004 mgr.getDoc(), NbDocument.findLineOffset(mgr.getDoc(), argLine) + argColumn, 1005 argInsertAfter ? Position.Bias.Backward : Position.Bias.Forward 1006 ); 1007 } catch (BadLocationException e) { 1008 retObject = mgr.getDoc().getEndPosition(); 1009 } catch (IndexOutOfBoundsException e) { 1010 retObject = mgr.getDoc().getEndPosition(); 1011 } 1012 1013 break; 1014 } 1015 1016 case PROCESS_POSITIONS: { 1017 synchronized (mgr.getLock()) { 1018 ChainItem previous = mgr.head; 1019 ChainItem ref = previous.next; 1020 1021 while (ref != null) { 1022 PositionRef pos = ref.get(); 1023 1024 if (pos == null) { 1025 previous.next = ref.next; 1027 } else { 1028 if (argToMemory) { 1030 pos.kind = pos.kind.toMemory(pos.insertAfter); 1031 } else { 1032 pos.kind = pos.kind.fromMemory(); 1033 } 1034 1035 previous = ref; 1036 } 1037 1038 ref = ref.next; 1039 } 1040 } 1041 1042 break; 1043 } 1044 1045 case ADD_POSITION: { 1046 mgr.support.howToReproduceDeadlock40766(true); 1048 1049 synchronized (mgr.getLock()) { 1050 mgr.support.howToReproduceDeadlock40766(false); 1051 mgr.head.next = new ChainItem(argPos, mgr.queue, mgr.head.next); 1052 1053 retObject = ((mgr.getDoc() == null) ? argPos.kind : argPos.kind.toMemory( 1054 argPos.insertAfter 1055 )); 1056 } 1057 1058 break; 1059 } 1060 1061 default: 1062 throw new IllegalStateException (); } 1064 } catch (IOException e) { 1065 ioException = e; 1066 } 1067 } 1068 } 1069 } 1070} 1071 | Popular Tags |