KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > openide > text > DocumentLine


1 /*
2  * The contents of this file are subject to the terms of the Common Development
3  * and Distribution License (the License). You may not use this file except in
4  * compliance with the License.
5  *
6  * You can obtain a copy of the License at http://www.netbeans.org/cddl.html
7  * or http://www.netbeans.org/cddl.txt.
8  *
9  * When distributing Covered Code, include this CDDL Header Notice in each file
10  * and include the License file at http://www.netbeans.org/cddl.txt.
11  * If applicable, add the following below the CDDL Header, with the fields
12  * enclosed by brackets [] replaced by your own identifying information:
13  * "Portions Copyrighted [year] [name of copyright owner]"
14  *
15  * The Original Software is NetBeans. The Initial Developer of the Original
16  * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
17  * Microsystems, Inc. All Rights Reserved.
18  */

19 package org.openide.text;
20
21 import java.util.logging.Level JavaDoc;
22 import java.util.logging.Logger JavaDoc;
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 /** Implementation of a line in a {@link StyledDocument}.
34 * One object
35 * of this class represents a line in the document by holding
36 * a {@link PositionRef}, which can represent a position in an open or
37 * closed document.
38 *
39 * @author Jaroslav Tulach, David Konecny
40 */

41 public abstract class DocumentLine extends Line {
42     /** weak map that assignes to editor supports whether they have current or error line
43     * selected. (EditorSupport, DocumentLine[2]), where Line[0] is current and Line[1] is error */

44     private static Map<CloneableEditorSupport,DocumentLine[]> assigned = new WeakHashMap<CloneableEditorSupport,DocumentLine[]>(5);
45     static final long serialVersionUID = 3213776466939427487L;
46
47     /** reference to one position on the line */
48     protected PositionRef pos;
49
50     /** is breakpoint there - presistent state
51      @deprecated since 1.20 */

52     @Deprecated JavaDoc
53     private boolean breakpoint;
54
55     /** error line - transient state
56      @deprecated since 1.20 */

57     @Deprecated JavaDoc
58     private transient boolean error;
59
60     /** current line - transient state
61      @deprecated since 1.20 */

62     @Deprecated JavaDoc
63     private transient boolean current;
64
65     /** listener for changes of state of the document */
66     private transient LR listener;
67
68     /** weak document listener assigned to the document or null */
69     private transient DocumentListener docL;
70
71     /** List of Line.Part which exist for this line*/
72     private List<Part> lineParts = new ArrayList<Part>(3);
73
74     /** Constructor.
75     * @param obj context we belong to
76     * @param pos position on the line
77      *
78      * @since 4.3
79     */

80     public DocumentLine(org.openide.util.Lookup obj, PositionRef pos) {
81         super(obj);
82         this.pos = pos;
83     }
84
85     /** Init listeners
86     */

87     void init() {
88         listener = new LR();
89         pos.getCloneableEditorSupport().addChangeListener(
90             org.openide.util.WeakListeners.change(listener, pos.getCloneableEditorSupport())
91         );
92     }
93
94     /* Get the line number.
95      * The number may change if the
96     * text is modified.
97     *
98     * @return Returns current line number.
99     */

100     public int getLineNumber() {
101         try {
102             return pos.getLine();
103         } catch (IOException ex) {
104             // what else?
105
return 0;
106         }
107     }
108
109     /* Shows the line.
110     * @param kind one of SHOW_XXX constants.
111     * @column the column of this line which should be selected
112     */

113     public abstract void show(int kind, int column);
114
115     /* Sets the breakpoint. */
116     @SuppressWarnings JavaDoc("deprecation")
117     public void setBreakpoint(boolean b) {
118         if (breakpoint != b) {
119             breakpoint = b;
120             refreshState();
121         }
122     }
123
124     /* Tests if the breakpoint is set. */
125     @SuppressWarnings JavaDoc("deprecation")
126     public boolean isBreakpoint() {
127         return breakpoint;
128     }
129
130     /* Marks the error. */
131     @SuppressWarnings JavaDoc("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     /* Unmarks error at this line. */
146     @SuppressWarnings JavaDoc("deprecation")
147     public void unmarkError() {
148         error = false;
149         registerLine(1, null);
150
151         refreshState();
152     }
153
154     /* Marks this line as current. */
155     @SuppressWarnings JavaDoc("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     /* Unmarks this line as current. */
169     @SuppressWarnings JavaDoc("deprecation")
170     public void unmarkCurrentLine() {
171         current = false;
172         registerLine(0, null);
173
174         refreshState();
175     }
176
177     /** Refreshes the current line.
178      *
179      * @deprecated since 1.20. */

180     @Deprecated JavaDoc
181     synchronized void refreshState() {
182         StyledDocument doc = pos.getCloneableEditorSupport().getDocument();
183
184         if (doc != null) {
185             // the document is in memory, mark the state
186
if (docL != null) {
187                 doc.removeDocumentListener(docL);
188             }
189
190             // error line
191
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             // current line
200
if (current) {
201                 NbDocument.markCurrent(doc, pos.getOffset());
202
203                 return;
204             }
205
206             // breakpoint line
207
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 JavaDoc 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     //
236
// Work with global hash table
237
//
238

239     /** Register this line as the one stored
240     * under indx-index (0 = current, 1 = error).
241     *
242     * @param indx index to register
243     * @param line value to add (this or null)
244     * @return the previous value
245     *
246     * @deprecated since 1.20 */

247     @Deprecated JavaDoc
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             // remember the previous
257
prev = arr[indx];
258         } else {
259             // create new array
260
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     //
271
// Serialization
272
//
273

274     /** Write fields.
275     */

276     private void writeObject(ObjectOutputStream oos) throws IOException {
277         // do not do default read/write object
278
oos.writeObject(pos);
279         oos.writeBoolean(breakpoint);
280     }
281
282     /** Read important fields.
283     */

284     private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException JavaDoc {
285         pos = (PositionRef) ois.readObject();
286         setBreakpoint(ois.readBoolean());
287         lineParts = new ArrayList<Part>(3);
288     }
289
290     /** Register line.
291     */

292     Object JavaDoc readResolve() throws ObjectStreamException {
293         // return Set.registerLine (this);
294
//Set.registerPendingLine(this);
295
return this.pos.getCloneableEditorSupport().getLineSet().registerLine(this);
296     }
297
298     /** Add annotation to this Annotatable class
299      * @param anno annotation which will be attached to this class */

300     protected void addAnnotation(Annotation anno) {
301         super.addAnnotation(anno);
302
303         StyledDocument doc = pos.getCloneableEditorSupport().getDocument();
304
305         // document is not opened and so the annotation will be added to document later
306
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                 // #33165 - find position that is surely at begining of line
317
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     /** Remove annotation to this Annotatable class
327      * @param anno annotation which will be detached from this class */

328     protected void removeAnnotation(Annotation anno) {
329         super.removeAnnotation(anno);
330
331         StyledDocument doc = pos.getCloneableEditorSupport().getDocument();
332
333         // document is not opened and so no annotation is attached to it
334
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     /** When document is opened or closed the annotations must be added or
347      * removed.
348      * @since 1.27 */

349     void attachDetachAnnotations(StyledDocument doc, boolean closing) {
350         // #33165 - find position that is surely at begining of line
351
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 JavaDoc 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         // notify also all Line.Part attached to this Line
384
for (Part p : lineParts) {
385             p.attachDetachAnnotations(doc, closing);
386         }
387     }
388
389     public String JavaDoc getText() {
390         final StyledDocument doc = pos.getCloneableEditorSupport().getDocument();
391
392         // document is not opened
393
if (doc == null) {
394             return null;
395         }
396
397         final String JavaDoc[] retStringArray = new String JavaDoc[1];
398         doc.render(
399             new Runnable JavaDoc() {
400                 public void run() {
401                     // Part of #33165 - the following code is wrapped by doc.render()
402
int lineNumber = getLineNumber();
403                     int lineStart = NbDocument.findLineOffset(doc, lineNumber);
404
405                     // #24434: Check whether the next line exists
406
// (the current one could be the last one).
407
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                     // End of the code wrapped by doc.render()
423
}
424             }
425         );
426
427         return retStringArray[0];
428     }
429
430     /** Attach created Line.Part to the parent Line */
431     void addLinePart(DocumentLine.Part linePart) {
432         lineParts.add(linePart);
433     }
434
435     /** Move Line.Part from this Line to a new one*/
436     void moveLinePart(DocumentLine.Part linePart, DocumentLine newLine) {
437         lineParts.remove(linePart);
438         newLine.addLinePart(linePart);
439         linePart.changeLine(newLine);
440     }
441
442     /** Notify Line.Part(s) that content of the line was changed and that Line.Part(s) may be affected by that*/
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             // notify Line.Part about the change
450
part.handleDocumentChange(p0);
451
452             // if necessary move Line.Part to new Line
453
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         // #33165 - fix the position in the positionRef in case this line changes
462
// and reattach the annotations.
463
// The fix of #32764 in notifyMove() would only reattach
464
// the annotations in case the position does not go at a line begining
465
// after the modification but that is not enough
466
// to fix undo-related issues.
467
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             // Recreate positionRef unconditionally to avoid undo problems
481
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 JavaDoc(); // should not fail
491
}
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     /** Notify Line.Part(s) that line was moved. */
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     /** Updates <code>pos</code> the way it points at the start of line. */
520     private void updatePositionRef() {
521         // #33165 - Moved handling that follows into notifyChange()
522
// due to problems with undo operations.
523
// Rest of notifyMove() should work as the code in notifyChange()
524
// in fact includes the same work as done here and notifyChange()
525
// is called before notifyMove()
526
// (see linesChanged()/linesMoved() in LineListener).
527

528         /* CloneableEditorSupport support = pos.getCloneableEditorSupport();
529                 int startOffset = NbDocument.findLineOffset(support.getDocument(),
530                     getLineNumber());
531
532                 if(pos.getOffset() != startOffset) {
533                     pos = new PositionRef(
534                         support.getPositionManager(), startOffset, Position.Bias.Forward
535                     );
536
537                     // fix of #32764
538                     List annos = getAnnotations();
539                     for (int i=0; i<annos.size(); i++) {
540                         Annotation anno = (Annotation)annos.get(i);
541                         if (anno.isInDocument()) {
542                             anno.setInDocument(false);
543                             NbDocument.removeAnnotation(support.getDocument(), anno);
544                         }
545
546                         try {
547                             if (!anno.isInDocument()) {
548                                 anno.setInDocument(true);
549                                 NbDocument.addAnnotation (support.getDocument(), pos.getPosition(), -1, anno);
550                             }
551                         } catch (IOException ex) {
552                             ErrorManager.getDefault ().notify ( ErrorManager.EXCEPTION, ex);
553                         }
554                     }
555                 }
556          */

557     }
558
559     /**
560      * Runnable used to find a position where the annotation should be attached.
561      * It is intended to be run under document's readlock.
562      */

563     private static final class FindAnnotationPosition implements Runnable JavaDoc {
564         private StyledDocument doc;
565         private Position annoPos;
566
567         FindAnnotationPosition(StyledDocument doc, Position pos) {
568             this.doc = doc;
569             this.annoPos = pos; // by default assume the given one is correct
570
}
571
572         public void run() {
573             int offset = annoPos.getOffset();
574             int lineStartOffset = doc.getParagraphElement(offset).getStartOffset();
575
576             if (offset != lineStartOffset) { // not at line start -> correct
577

578                 try {
579                     annoPos = doc.createPosition(lineStartOffset);
580                 } catch (BadLocationException e) {
581                     throw new IllegalArgumentException JavaDoc(); // should never fail
582
}
583             }
584         }
585
586         Position getAnnotationPosition() {
587             return annoPos;
588         }
589     }
590
591     /** Implementation of Line.Part abstract class*/
592     static class Part extends Line.Part {
593         /** Reference of this part to the document*/
594         private PositionRef position;
595
596         /** Reference to Line to which this part belongs*/
597         private Line line;
598
599         /** Length of the annotated text*/
600         private int length;
601
602         /** Offset of this Part before the modification. This member is used in
603          * listener on document changes and it is updated after each change. */

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                 // Can happen when closing a document, don't know why.
617
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 JavaDoc 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 JavaDoc i = new IllegalStateException JavaDoc(ex.getMessage());
636                 i.initCause(ex);
637                 throw i;
638             }
639         }
640
641         /** Start column of annotation */
642         public int getColumn() {
643             try {
644                 return position.getColumn();
645             } catch (IOException ex) {
646                 return 0; //TODO: change this
647
}
648         }
649
650         /** Length of the annotated text. The length does not cross line end. If the annotated text is
651          * split during the editing, the annotation is shorten till the end of the line. Modules can listen on
652          * changes of this value*/

653         public int getLength() {
654             return length;
655         }
656
657         /** Line can change during editting*/
658         public Line getLine() {
659             return line;
660         }
661
662         /** Offset of the Line.Part*/
663         int getOffset() {
664             return position.getOffset();
665         }
666
667         /** Line can change during editting*/
668         void changeLine(Line line) {
669             this.line = line;
670
671             // TODO: check whether there is really some change
672
firePropertyChange(PROP_LINE_NUMBER, null, line);
673         }
674
675         /** Add annotation to this Annotatable class
676          * @param anno annotation which will be attached to this class */

677         protected void addAnnotation(Annotation anno) {
678             super.addAnnotation(anno);
679
680             StyledDocument doc = position.getCloneableEditorSupport().getDocument();
681
682             // document is not opened and so the annotation will be added to document later
683
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         /** Remove annotation to this Annotatable class
700          * @param anno annotation which will be detached from this class */

701         protected void removeAnnotation(Annotation anno) {
702             super.removeAnnotation(anno);
703
704             StyledDocument doc = position.getCloneableEditorSupport().getDocument();
705
706             // document is not opened and so no annotation is attached to it
707
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 JavaDoc getText() {
720             final StyledDocument doc = position.getCloneableEditorSupport().getDocument();
721
722             // document is not opened
723
if (doc == null) {
724                 return null;
725             }
726
727             final String JavaDoc[] retStringArray = new String JavaDoc[1];
728             doc.render(
729                 new Runnable JavaDoc() {
730                     public void run() {
731                         // Part of #33165 - the following code is wrapped by doc.render()
732
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                         // End of the code wrapped by doc.render()
746
}
747                 }
748             );
749
750             return retStringArray[0];
751         }
752
753         /** When document is opened or closed the annotations must be added or
754          * removed.*/

755         void attachDetachAnnotations(StyledDocument doc, boolean closing) {
756             java.util.List JavaDoc 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         /** Handle DocumentChange event. If the change affect this Part, fire
780          * the PROP_TEXT event. */

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     /** Definition of actions performed in Listener */
810     private final class LR implements Runnable JavaDoc, 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             // part of #33165 - done synchronously not invoking into EQ
851
//SwingUtilities.invokeLater(new LR(op));
852
new LR(op).run();
853         }
854
855         private void invoke(EnhancedChangeEvent ev) {
856             // part of #33165 - done synchronously not invoking into EQ
857
//SwingUtilities.invokeLater(new LR(ev));
858
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 JavaDoc p0) {
867             invoke(UNMARK);
868         }
869
870         public void insertUpdate(final javax.swing.event.DocumentEvent JavaDoc p0) {
871             invoke(UNMARK);
872         }
873
874         public void changedUpdate(final javax.swing.event.DocumentEvent JavaDoc p0) {
875         }
876     }
877
878     /** Abstract implementation of {@link Line.Set}.
879      * Defines
880     * ways to obtain a line set for documents following
881     * NetBeans conventions.
882     */

883     public static abstract class Set extends Line.Set {
884         /** listener on document changes, accessed from LazyLines */
885         final LineListener listener;
886
887         /** all lines in the set or null */
888         private List<Line> list;
889
890         /** Constructor.
891         * @param doc document to work on
892         */

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         /** Find the line given as parameter in list of all lines attached to this set
902          * and if the line exist in the list, notify it about being edited. */

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                 // revalidate all parts attached to this line
912
// that they are still part of the line
913
if (line instanceof DocumentLine) {
914                     ((DocumentLine) line).notifyChange(p0, this, listener.doc);
915                 }
916             }
917         }
918
919         /** Find the line given as parameter in list of all lines attached to this set
920          * and if the line exist in the list, notify it about being moved. */

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                 // notify all parts attached to this line
929
// that they were moved
930
if (line instanceof DocumentLine) {
931                     ((DocumentLine) line).notifyMove();
932                 }
933             }
934         }
935
936         /** Gets the lines with line number whitin the range inclusive.
937          * @return <code>List</code> of lines from range inclusive */

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         /* Returns an unmodifiable set of Lines sorted by their
955         * line numbers that contains all lines holded by this
956         * Line.Set.
957         *
958         * @return list of Line objects
959         */

960         public synchronized List<? extends Line> getLines() {
961             if (list == null) {
962                 list = new LazyLines(this);
963             }
964
965             return list;
966         }
967
968         /* Finder method that for the given line number finds right
969         * Line object that represent as closely as possible the line number
970         * in the time when the Line.Set has been created.
971         *
972         * @param line is a number of the line (text line) we want to acquire
973         * @exception IndexOutOfBoundsException if <code>line</code> is invalid.
974         */

975         public Line getOriginal(int line) throws IndexOutOfBoundsException JavaDoc {
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         /* Creates current line.
993         *
994         * @param line is a number of the line (text line) we want to acquire
995         * @exception IndexOutOfBoundsException if <code>line</code> is invalid.
996         */

997         public Line getCurrent(int line) throws IndexOutOfBoundsException JavaDoc {
998             int offset = NbDocument.findLineOffset(listener.doc, line);
999
1000            return safelyRegisterLine(createLine(offset));
1001        }
1002
1003        /** Creates a {@link Line} for a given offset.
1004        * @param offset the beginning offset of the line
1005        * @return line object representing the line at this offset
1006        */

1007        protected abstract Line createLine(int offset);
1008
1009        /** Registers line, but only after obtaining the lock of the document.
1010         * This is a fix to issue 37767 as this creates ordering of locks (first
1011         * of all obtain documentrenderer, then ask for any other locks like
1012         * Line.Set.lines.
1013         *
1014         * @param line line we want to register
1015         * @return the line or some line that already was registered
1016         */

1017        private Line safelyRegisterLine(final Line line) {
1018            assert line != null;
1019            class DocumentRenderer implements Runnable JavaDoc {
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