Index: editor/libsrc/org/netbeans/editor/Annotations.java =================================================================== RCS file: /cvs/editor/libsrc/org/netbeans/editor/Annotations.java,v retrieving revision 1.12 diff -c -r1.12 Annotations.java *** editor/libsrc/org/netbeans/editor/Annotations.java 27 Feb 2003 23:36:14 -0000 1.12 --- editor/libsrc/org/netbeans/editor/Annotations.java 30 Sep 2003 15:03:03 -0000 *************** *** 45,50 **** --- 45,51 ---- import javax.swing.JCheckBoxMenuItem; import java.awt.event.ItemListener; import java.awt.event.ItemEvent; + import javax.swing.SwingUtilities; import org.netbeans.editor.LocaleSupport; import org.netbeans.editor.Settings; import org.netbeans.editor.BaseKit; *************** *** 165,208 **** /** Add annotation */ public void addAnnotation(AnnotationDesc anno) { // create mark for this annotation. One mark can be shared by more annotations MarkChain chain = getLayer().getMarkChain(); try { ! chain.addMark(anno.getOffset()); } catch (BadLocationException e) { ! return; } // attach created mark to annotation ! anno.setMark(chain.getAddedMark()); ! // fine LineAnnotations instance corresponding to the line of this annotation ! // or create new LineAnnotations if this is first annotation on this line ! LineAnnotations lineAnnos = getLineAnnotations(anno.getLine()); if (lineAnnos == null) { ! lineAnnos = new LineAnnotations(); ! lineAnnos.addAnnotation(anno); ! lineAnnotationsByMark.put(anno.getMark(), lineAnnos); ! ! // insert newly created LineAnnotations into sorted array ! boolean inserted = false; ! for (int i=0; i < lineAnnotationsArray.size(); i++) { ! if (((LineAnnotations)lineAnnotationsArray.get(i)).getLine() > lineAnnos.getLine()) { ! lineAnnotationsArray.add(i, lineAnnos); ! inserted = true; ! break; } - } - if (!inserted) - lineAnnotationsArray.add(lineAnnos); ! } ! else { lineAnnos.addAnnotation(anno); - // check whether this mark is in lineAnnotationsByMark Map - // it is possible that Line.Part annotations will have more marks - // for one line - if (lineAnnotationsByMark.get(anno.getMark()) == null) - lineAnnotationsByMark.put(anno.getMark(), lineAnnos); } // add listener on changes of annotation type --- 166,268 ---- /** Add annotation */ public void addAnnotation(AnnotationDesc anno) { + // Should always be run in EQ + if (!SwingUtilities.isEventDispatchThread()) { + throw new IllegalStateException("Must be run in EQ"); // NOI18N + } + // create mark for this annotation. One mark can be shared by more annotations MarkChain chain = getLayer().getMarkChain(); try { ! /* Always adds a fresh mark. It helps to behave well ! * in the following scenario: ! * 1. add annotaion at line e.g. 3. ! * 2. add annotation at line 4. ! * 3. goto begining of line 2. ! * 4. select lines 2,3,4,5 by pressing down arrow. ! * 5. remove selected lines. ! * 6. Undo by ctrl-z. ! * If reusing marks this will result into line selection ! * being at line 2 but gutter marking of annotations ! * being at line 4. ! * It happens because although DocumentLine.updatePositionRef() ! * removes and adds the annotations on modified lines, it does that ! * sequentially for each line so it removes ! * first of the two DocumentLine's annotations ! * and tries to readd it to begining of removed block ! * but it finds there an existing mark ! * from second DocumentLine's annotation. ! * It reuses the mark but as the mark was inside ! * the removed block originally the undo restores its position ! * inside the block. ! * Creation of fresh marks fixes that problem. ! */ ! chain.addMark(anno.getOffset(), true); ! } catch (BadLocationException e) { ! /* ! * This is problematic point. ! * Once this place is reached the annotation desc ! * will have null mark and will ! * not in fact be actively present ! * in the document. ! * Such annotation will then throw NPE ! * in removeAnnotation(). ! * Hopefully causes of getting to this place ! * were eliminated. ! */ ! throw new IllegalStateException("offset=" + anno.getOffset() // NOI18N ! + ", docLen=" + doc.getLength()); // NOI18N } // attach created mark to annotation ! MarkFactory.ChainDrawMark annoMark = chain.getAddedMark(); ! if (annoMark == null) { ! throw new NullPointerException(); ! } ! anno.setMark(annoMark); ! ! // #33165 - different strategy - first trying to search ! // by the mark in the map [mark, lineAnnos]. That should ! // eliminate problems when a mark is tried to be removed ! // from the mark chain a second time. ! // Hopefully this will not cause any other sorts of problems. ! LineAnnotations lineAnnos = (LineAnnotations)lineAnnotationsByMark.get(annoMark); if (lineAnnos == null) { ! // fine LineAnnotations instance corresponding to the line of this annotation ! // or create new LineAnnotations if this is first annotation on this line ! lineAnnos = getLineAnnotations(anno.getLine()); ! if (lineAnnos == null) { ! lineAnnos = new LineAnnotations(); ! lineAnnos.addAnnotation(anno); ! if (lineAnnotationsByMark.put(anno.getMark(), lineAnnos) != null) { ! throw new IllegalStateException("Mark already in the map."); } ! // insert newly created LineAnnotations into sorted array ! boolean inserted = false; ! for (int i=0; i < lineAnnotationsArray.size(); i++) { ! if (((LineAnnotations)lineAnnotationsArray.get(i)).getLine() > lineAnnos.getLine()) { ! lineAnnotationsArray.add(i, lineAnnos); ! inserted = true; ! break; ! } ! } ! if (!inserted) ! lineAnnotationsArray.add(lineAnnos); ! ! } ! else { ! lineAnnos.addAnnotation(anno); ! // check whether this mark is in lineAnnotationsByMark Map ! // it is possible that Line.Part annotations will have more marks ! // for one line ! if (lineAnnotationsByMark.get(anno.getMark()) == null) ! lineAnnotationsByMark.put(anno.getMark(), lineAnnos); ! } ! ! } else { // mark was already in lineAnnotationsByMark map lineAnnos.addAnnotation(anno); } // add listener on changes of annotation type *************** *** 218,239 **** // notify view that it must be redrawn refreshLine(lineAnnos.getLine()); } /** Remove annotation */ public void removeAnnotation(AnnotationDesc anno) { // find LineAnnotations for the mark ! LineAnnotations lineAnnos = (LineAnnotations)lineAnnotationsByMark.get(anno.getMark()); int line = lineAnnos.getLine(); // remove annotation from the line lineAnnos.removeAnnotation(anno); // check if this mark is referenced or not. If not, remove it ! if (!lineAnnos.isMarkStillReferenced(anno.getMark())) { ! lineAnnotationsByMark.remove(anno.getMark()); MarkChain chain = getLayer().getMarkChain(); ! chain.removeMark(anno.getOffset()); } // if there is no more annotations on the line, remove LineAnnotations --- 278,319 ---- // notify view that it must be redrawn refreshLine(lineAnnos.getLine()); + + // System.out.println("AFTER ADD:\n" + dumpLineAnnotationsArray()); } /** Remove annotation */ public void removeAnnotation(AnnotationDesc anno) { + // Should always be run in EQ + if (!SwingUtilities.isEventDispatchThread()) { + throw new IllegalStateException("Must be run in EQ"); // NOI18N + } + // find LineAnnotations for the mark ! MarkFactory.ChainDrawMark annoMark = (MarkFactory.ChainDrawMark)anno.getMark(); ! if (annoMark == null) { ! throw new NullPointerException(); ! } ! ! LineAnnotations lineAnnos = (LineAnnotations)lineAnnotationsByMark.get(annoMark); ! int line = lineAnnos.getLine(); // remove annotation from the line lineAnnos.removeAnnotation(anno); // check if this mark is referenced or not. If not, remove it ! if (!lineAnnos.isMarkStillReferenced(annoMark)) { ! if (lineAnnotationsByMark.remove(annoMark) != lineAnnos) { ! throw new IllegalStateException(); ! } ! MarkChain chain = getLayer().getMarkChain(); ! // Partial fix of #33165 - rather remove mark explicitly than through its offset ! //chain.removeMark(anno.getOffset()); ! if (!chain.removeMark(annoMark)) { ! throw new IllegalStateException("Mark not removed"); // NOI18N ! } } // if there is no more annotations on the line, remove LineAnnotations *************** *** 249,254 **** --- 329,335 ---- // notify view that must be redrawn refreshLine(line); + // System.out.println("AFTER REMOVE:\n" + dumpLineAnnotationsArray()); } /** Finds active annotation for the Mark. It is called from DrawLayer *************** *** 378,383 **** --- 459,465 ---- int changedLine = be.getLine(); + /* #33165 - line in line-annotations handled in a different way LineAnnotations annos; for (int i=0; i changedLine) annos.setLine(annos.getLine()-countOfDeletedLines); } + */ // fire event to AnnotationsListeners that everything should be redraw fireChangedAll(); } *************** *** 398,403 **** --- 481,487 ---- if (countOfInsertedLines == 0) return; + /* #33165 - line in line-annotations handled in a different way int changedLine = be.getLine(); LineAnnotations annos; *************** *** 411,416 **** --- 495,501 ---- } if (current != null) current.setLine(current.getLine()+countOfInsertedLines); + */ // fire event to AnnotationsListeners that everything should be redraw fireChangedAll(); *************** *** 628,633 **** --- 713,741 ---- return createMenu(kit, line, !bkgInit); } + private String dumpAnnotaionDesc(AnnotationDesc ad) { + return "offset=" + ad.getOffset() // NOI18N + + "(ls=" + doc.getParagraphElement(ad.getOffset()).getStartOffset() + + "), line=" + ad.getLine() // NOI18N + + ", type=" + ad.getAnnotationType(); // NOI18N + } + + private String dumpLineAnnotationsArray() { + StringBuffer sb = new StringBuffer(); + for (int i = 0; i < lineAnnotationsArray.size(); i++) { + LineAnnotations la = (LineAnnotations)lineAnnotationsArray.get(i); + LinkedList annos = la.annos; + sb.append("[" + i + "]: line=" + la.getLine() // NOI18N + + ", anos:"); // NOI18N + for (int j = 0; j < annos.size(); j++) { + sb.append("\n [" + j + "]: " + dumpAnnotaionDesc((AnnotationDesc)annos.get(j))); // NOI18N + } + sb.append('\n'); + } + return sb.toString(); + } + + /** Manager of all annotations attached to one line. Class stores * the references to all annotations from one line in List and also * stores which annotation is active, count of visible annotations *************** *** 645,662 **** private AnnotationDesc active; /** Line number */ ! private int lineNumber; protected LineAnnotations() { annos = new LinkedList(); annosVisible = new LinkedList(); ! lineNumber = -1; } /** Add annotation to this line and activate it. */ public void addAnnotation(AnnotationDesc anno) { ! if (lineNumber == -1) ! lineNumber = anno.getLine(); annos.add(anno); if (anno.isVisible()) { active = anno; --- 753,770 ---- private AnnotationDesc active; /** Line number */ ! // private int lineNumber; protected LineAnnotations() { annos = new LinkedList(); annosVisible = new LinkedList(); ! // lineNumber = -1; } /** Add annotation to this line and activate it. */ public void addAnnotation(AnnotationDesc anno) { ! // if (lineNumber == -1) ! // lineNumber = anno.getLine(); annos.add(anno); if (anno.isVisible()) { active = anno; *************** *** 682,693 **** /** Getter for the line number property */ public int getLine() { ! return lineNumber; } /** Setter for the line number property */ public void setLine(int line) { ! lineNumber = line; } /** Gets the array of all pasive and visible annotations */ --- 790,806 ---- /** Getter for the line number property */ public int getLine() { ! // #33165 - delegating of getting of the line number to first anno ! return (annos.size() > 0) ! ? ((AnnotationDesc)annos.get(0)).getLine() ! : 0; ! // return lineNumber; } /** Setter for the line number property */ public void setLine(int line) { ! // lineNumber = line; ! throw new IllegalStateException("Setting of line number not allowed"); // NOI18N } /** Gets the array of all pasive and visible annotations */ Index: editor/libsrc/org/netbeans/editor/DrawEngine.java =================================================================== RCS file: /cvs/editor/libsrc/org/netbeans/editor/DrawEngine.java,v retrieving revision 1.27 diff -c -r1.27 DrawEngine.java *** editor/libsrc/org/netbeans/editor/DrawEngine.java 23 Jul 2003 16:31:26 -0000 1.27 --- editor/libsrc/org/netbeans/editor/DrawEngine.java 30 Sep 2003 15:03:03 -0000 *************** *** 1006,1013 **** } if (debug) { System.err.println("DrawEngine:---------- DRAWING startOffset=" ! + startOffset + ", endOffset=" + endOffset + " ------------------"); // NOI18N } --- 1006,1023 ---- } if (debug) { + BaseDocument doc = editorUI.getDocument(); + javax.swing.text.Element lineRoot = doc.getParagraphElement(0).getParentElement(); + int startLine = lineRoot.getElementIndex(startOffset); + int startLineOffset = lineRoot.getElement(startLine).getStartOffset(); + int endLine = lineRoot.getElementIndex(endOffset); + int endLineOffset = lineRoot.getElement(endLine).getStartOffset(); + Graphics g = drawGraphics.getGraphics(); System.err.println("DrawEngine:---------- DRAWING startOffset=" ! + startOffset + ", startLine=" + startLine ! + "(o=" + startLineOffset + "), endOffset=" + endOffset ! + ", endLine=" + endLine + "(o=" + endLineOffset ! + "), clip=" + ((g != null) ? g.getClipBounds().toString() : "null") + " ------------------"); // NOI18N } Index: editor/libsrc/org/netbeans/editor/DrawGraphics.java =================================================================== RCS file: /cvs/editor/libsrc/org/netbeans/editor/DrawGraphics.java,v retrieving revision 1.13 diff -c -r1.13 DrawGraphics.java *** editor/libsrc/org/netbeans/editor/DrawGraphics.java 27 Feb 2003 23:36:19 -0000 1.13 --- editor/libsrc/org/netbeans/editor/DrawGraphics.java 30 Sep 2003 15:03:03 -0000 *************** *** 325,330 **** --- 325,337 ---- private Color underlineColor; private Color waveUnderlineColor; + + /** Whether annotations were drawn on the current line already */ + private int lastDrawnAnnosY; + private int lastDrawnAnnosX; + + /** Annotation description cached for the lastDrawnAnnosY */ + private AnnotationDesc[] passiveAnnosAtY; /** Alpha used for drawing the glyphs on the background */ private AlphaComposite alpha = null; *************** *** 336,341 **** --- 343,350 ---- GraphicsDG(Graphics graphics) { this.graphics = graphics; + // #33165 - set invalid y initially + this.y = -1; } public void setForeColor(Color foreColor) { *************** *** 416,467 **** } private void flush() { ! if (startOffset < 0) { ! return; ! } ! if (startOffset == endOffset) { ! startOffset = -1; ! return; } ! // First possibly fill the rectangle ! fillRectImpl(startX, startY, x - startX); ! ! if (AnnotationTypes.getTypes().isBackgroundDrawing().booleanValue()) { ! ! if (alpha == null) ! alpha = AlphaComposite.getInstance(AlphaComposite.SRC_OVER, AnnotationTypes.getTypes().getBackgroundGlyphAlpha().intValue() / 100f); ! ! AnnotationDesc[] annosArray = annos.getPasiveAnnotations( (int)( (float)startY / (float)lineHeight )); ! int glyphX=2; ! if (annosArray != null) { ! Graphics2D g2d = (Graphics2D) graphics; ! ! Shape shape = graphics.getClip(); ! ! // set alpha composite ! Composite origin = g2d.getComposite(); ! g2d.setComposite(alpha); ! ! // clip the drawing area ! Rectangle r = new Rectangle(startX, startY, x - startX, lineHeight); ! r = r.intersection(shape.getBounds()); ! graphics.setClip(r); ! ! for (int i=0; i < annosArray.length; i++) { ! g2d.drawImage(annosArray[i].getGlyph(), glyphX, startY, null); ! glyphX += annosArray[i].getGlyph().getWidth(null)+1; ! } ! // restore original clip region ! graphics.setClip(shape); ! // restore original ocmposite ! g2d.setComposite(origin); ! } } ! // Check whether the graphics uses right color if (foreColor != gColor) { graphics.setColor(foreColor); --- 425,494 ---- } private void flush() { ! flush(false); ! } ! private void flush(boolean atEOL) { ! if (y < 0) { // not yet initialized ! return ; } + + if (startOffset >= 0 && startOffset != endOffset) { // some text on the line + // First possibly fill the rectangle + fillRectImpl(startX, startY, x - startX); + } + + // #33165 - for each fragment getPasiveAnnotations() was called + // but it can done just once per line. + if (lastDrawnAnnosY != y) { + lastDrawnAnnosY = y; + lastDrawnAnnosX = 0; + if (AnnotationTypes.getTypes().isBackgroundDrawing().booleanValue()) { + if (alpha == null) + alpha = AlphaComposite.getInstance(AlphaComposite.SRC_OVER, AnnotationTypes.getTypes().getBackgroundGlyphAlpha().intValue() / 100f); + + passiveAnnosAtY = annos.getPasiveAnnotations( (int)( (float)y / (float)lineHeight )); + } else { + passiveAnnosAtY = null; + } + } + + int glyphX=2; + if (passiveAnnosAtY != null) { + Graphics2D g2d = (Graphics2D) graphics; + + Shape shape = graphics.getClip(); + + // set alpha composite + Composite origin = g2d.getComposite(); + g2d.setComposite(alpha); + + // clip the drawing area + int endX = atEOL ? Integer.MAX_VALUE : x; + int startX = Math.min(lastDrawnAnnosX, this.startX); + Rectangle r = new Rectangle(startX, y, endX - startX, lineHeight); + lastDrawnAnnosX = endX; + r = r.intersection(shape.getBounds()); + graphics.setClip(r); + + for (int i=0; i < passiveAnnosAtY.length; i++) { + g2d.drawImage(passiveAnnosAtY[i].getGlyph(), glyphX, y, null); + glyphX += passiveAnnosAtY[i].getGlyph().getWidth(null)+1; + } ! // restore original clip region ! graphics.setClip(shape); ! // restore original ocmposite ! g2d.setComposite(origin); ! } ! // If no text on the line then return ! if (startOffset < 0 || startOffset == endOffset) { ! startOffset = -1; ! return; } ! // Check whether the graphics uses right color if (foreColor != gColor) { graphics.setColor(foreColor); *************** *** 586,592 **** } public void eol() { ! flush(); } } --- 613,619 ---- } public void eol() { ! flush(true); } } Index: editor/libsrc/org/netbeans/editor/Mark.java =================================================================== RCS file: /cvs/editor/libsrc/org/netbeans/editor/Mark.java,v retrieving revision 1.10 diff -c -r1.10 Mark.java *** editor/libsrc/org/netbeans/editor/Mark.java 27 Feb 2003 23:36:22 -0000 1.10 --- editor/libsrc/org/netbeans/editor/Mark.java 30 Sep 2003 15:03:03 -0000 *************** *** 83,89 **** + ", class=" + this.getClass()); } ! if (offset < 0 || offset > ldoc.getLength()) { throw new BadLocationException("Invalid offset", offset); } --- 83,89 ---- + ", class=" + this.getClass()); } ! if (offset < 0 || offset > ldoc.getLength() + 1) { // doc.getEndPosition() is valid throw new BadLocationException("Invalid offset", offset); } Index: editor/libsrc/org/netbeans/editor/MarkChain.java =================================================================== RCS file: /cvs/editor/libsrc/org/netbeans/editor/MarkChain.java,v retrieving revision 1.18 diff -c -r1.18 MarkChain.java *** editor/libsrc/org/netbeans/editor/MarkChain.java 27 Feb 2003 23:36:23 -0000 1.18 --- editor/libsrc/org/netbeans/editor/MarkChain.java 30 Sep 2003 15:03:03 -0000 *************** *** 127,141 **** return mark; } - /** Add mark to the chain - * @return true if the mark was added - * false if there's already mark at that pos - */ public boolean addMark(int pos) throws BadLocationException { int rel = compareMark(pos); if (rel == 0) { ! recentlyAddedMark = curMark; ! return false; // already exists } else if (rel > 0) { // curMark after pos MarkFactory.ChainDrawMark mark = createAndInsertNewMark(pos); recentlyAddedMark = mark; --- 127,160 ---- return mark; } public boolean addMark(int pos) throws BadLocationException { + return addMark(pos, false); + } + + /** Add mark to the chain + * @param pos position at which the mark should be added + * @param forceAdd force adding of a fresh mark + * even if the mark at the same position already exists + * @return true if the mark was added + * false if there's already mark at that pos + */ + public boolean addMark(int pos, boolean forceAdd) throws BadLocationException { int rel = compareMark(pos); if (rel == 0) { ! if (forceAdd) { // create fresh ! MarkFactory.ChainDrawMark mark = createAndInsertNewMark(pos); ! recentlyAddedMark = mark; ! if (curMark == chain) { // curMark is first mark ! chain = curMark.insertChain(mark); ! } else { // curMark is not first mark ! curMark.insertChain(mark); ! } ! ! } else { // return existing ! recentlyAddedMark = curMark; ! return false; // already exists ! } ! } else if (rel > 0) { // curMark after pos MarkFactory.ChainDrawMark mark = createAndInsertNewMark(pos); recentlyAddedMark = mark; *************** *** 182,187 **** --- 201,234 ---- } return true; } else { // not found + return false; + } + } + + public boolean removeMark(MarkFactory.ChainDrawMark mark) { + if (mark == null) { + throw new NullPointerException(); + } + + if (curMark != mark) { + // dumb impl + curMark = chain; + while (curMark != null) { + if (curMark == mark) { + break; + } + curMark = curMark.next; + } + } + + if (curMark != null) { + boolean first = (curMark == chain); + curMark = curMark.removeChain(); + if (first) { + chain = curMark; + } + return true; + } else { return false; } } Index: editor/src/org/netbeans/modules/editor/NbEditorDocument.java =================================================================== RCS file: /cvs/editor/src/org/netbeans/modules/editor/NbEditorDocument.java,v retrieving revision 1.33 diff -c -r1.33 NbEditorDocument.java *** editor/src/org/netbeans/modules/editor/NbEditorDocument.java 27 Feb 2003 23:36:38 -0000 1.33 --- editor/src/org/netbeans/modules/editor/NbEditorDocument.java 30 Sep 2003 15:03:04 -0000 *************** *** 41,46 **** --- 41,47 ---- import org.netbeans.editor.Annotations; import java.beans.PropertyChangeListener; import java.beans.PropertyChangeEvent; + import java.util.WeakHashMap; import org.netbeans.editor.BaseDocument; import javax.swing.text.BadLocationException; import org.netbeans.editor.AnnotationDesc; *************** *** 73,79 **** /** Map of [Annotation, AnnotationDesc] */ private HashMap annoMap; ! public NbEditorDocument(Class kitClass) { super(kitClass); addStyleToLayerMapping(NbDocument.BREAKPOINT_STYLE_NAME, --- 74,80 ---- /** Map of [Annotation, AnnotationDesc] */ private HashMap annoMap; ! public NbEditorDocument(Class kitClass) { super(kitClass); addStyleToLayerMapping(NbDocument.BREAKPOINT_STYLE_NAME, *************** *** 154,160 **** return (f != null) ? f : super.getFormatter(); } - /** Add annotation to the document. For annotation of whole line * the length parameter can be ignored (specify value -1). * @param startPos position which represent begining --- 155,160 ---- *************** *** 163,172 **** * the whole line will be annotated * @param annotation annotation which is attached to this text */ public void addAnnotation(Position startPos, int length, Annotation annotation) { ! if (annotation.getAnnotationType() != null) { ! AnnotationDesc a = new AnnotationDescDelegate(this, startPos, length, annotation); ! annoMap.put(annotation, a); ! getAnnotations().addAnnotation(a); } } --- 163,193 ---- * the whole line will be annotated * @param annotation annotation which is attached to this text */ public void addAnnotation(Position startPos, int length, Annotation annotation) { ! // partial fix of #33165 - read-locking of the document added ! // BTW should only be invoked in EQ - see NbDocument.addAnnotation() ! readLock(); ! try { ! // Recreate annotation's position to make sure it's in this doc at a valid offset ! int docLen = getLength(); ! int offset = startPos.getOffset(); ! offset = Math.min(offset, docLen); ! try { ! startPos = createPosition(offset); ! } catch (BadLocationException e) { ! startPos = null; // should never happen ! } ! ! if (annoMap.get(annotation) != null) { // already added before ! throw new IllegalStateException("Annotation " + annotation // NOI18N ! + " already added"); // NOI18N ! } ! if (annotation.getAnnotationType() != null) { ! AnnotationDesc a = new AnnotationDescDelegate(this, startPos, length, annotation); ! annoMap.put(annotation, a); ! getAnnotations().addAnnotation(a); ! } ! } finally { ! readUnlock(); } } *************** *** 177,196 **** return; // can't do more as the rest of stacktrace is in openide and ant } ! if (annotation.getAnnotationType() != null) { ! AnnotationDescDelegate a = (AnnotationDescDelegate)annoMap.get(annotation); ! try { ! a.detachListeners(); ! } catch (NullPointerException ex) { ! ErrorManager.getDefault().annotate(ex, ErrorManager.INFORMATIONAL, ! "Editor module received request to remove annotation which does not exist in the document. "+ ! "Cause of this error is either in OpenIDE module or in module which originated the annotation. "+ ! "Annotation class = "+annotation, null, null, null); ! ErrorManager.getDefault().notify(ErrorManager.INFORMATIONAL, ex); ! return; } ! getAnnotations().removeAnnotation(a); ! annoMap.remove(annotation); } } --- 198,224 ---- return; // can't do more as the rest of stacktrace is in openide and ant } ! // partial fix of #33165 - read-locking of the document added ! // BTW should only be invoked in EQ - see NbDocument.removeAnnotation() ! readLock(); ! try { ! if (annotation.getAnnotationType() != null) { ! AnnotationDescDelegate a = (AnnotationDescDelegate)annoMap.get(annotation); ! try { ! a.detachListeners(); ! } catch (NullPointerException ex) { ! ErrorManager.getDefault().annotate(ex, ErrorManager.INFORMATIONAL, ! "Editor module received request to remove annotation which does not exist in the document. "+ ! "Cause of this error is either in OpenIDE module or in module which originated the annotation. "+ ! "Annotation class = "+annotation, null, null, null); ! ErrorManager.getDefault().notify(ErrorManager.INFORMATIONAL, ex); ! return; ! } ! getAnnotations().removeAnnotation(a); ! annoMap.remove(annotation); } ! } finally { ! readUnlock(); } } Index: editor/src/org/netbeans/modules/editor/NbToolTip.java =================================================================== RCS file: /cvs/editor/src/org/netbeans/modules/editor/NbToolTip.java,v retrieving revision 1.7 diff -c -r1.7 NbToolTip.java *** editor/src/org/netbeans/modules/editor/NbToolTip.java 27 Feb 2003 23:36:38 -0000 1.7 --- editor/src/org/netbeans/modules/editor/NbToolTip.java 30 Sep 2003 15:03:04 -0000 *************** *** 14,19 **** --- 14,20 ---- package org.netbeans.modules.editor; import javax.swing.text.JTextComponent; + import javax.swing.text.StyledDocument; import javax.swing.text.BadLocationException; import org.openide.filesystems.FileChangeAdapter; import org.openide.filesystems.FileObject; *************** *** 156,184 **** if (tts != null) { BaseDocument doc = Utilities.getDocument(target); if (doc != null) { ! int offset = target.viewToModel(tts.getLastMouseEvent().getPoint()); ! if (offset >= 0) { ! try { ! int line = Utilities.getLineOffset(doc, offset); ! int col = offset - Utilities.getRowStart(target, offset); ! ! DataObject dob = NbEditorUtilities.getDataObject(doc); ! if (dob != null) { ! EditorCookie ec = (EditorCookie)dob.getCookie(EditorCookie.class); ! if (ec != null) { ! Line.Set ls = ec.getLineSet(); ! if (ls != null) { ! Line l = ls.getCurrent(line); ! if (l != null) { ! Line.Part lp = l.createPart(col, 0); ! if (lp != null) { ! new Request(annos, lp, tts).run(); } } } } } - } catch (BadLocationException e) { } } } --- 157,201 ---- if (tts != null) { BaseDocument doc = Utilities.getDocument(target); if (doc != null) { ! DataObject dob = NbEditorUtilities.getDataObject(doc); ! if (dob != null) { ! EditorCookie ec = (EditorCookie)dob.getCookie(EditorCookie.class); ! if (ec != null) { ! StyledDocument openedDoc; ! try { ! openedDoc = ec.openDocument(); ! } catch (IOException e) { ! openedDoc = null; // should return in next if stmt ! } ! ! if (openedDoc != doc) { // doc has changed in meantime ! return; ! } ! ! // partial fix of #33165 - read-locking of the document added ! doc.readLock(); ! try { ! int offset = target.viewToModel(tts.getLastMouseEvent().getPoint()); ! if (offset >= 0) { ! try { ! int line = Utilities.getLineOffset(doc, offset); ! int col = offset - Utilities.getRowStart(target, offset); ! Line.Set ls = ec.getLineSet(); ! if (ls != null) { ! Line l = ls.getCurrent(line); ! if (l != null) { ! Line.Part lp = l.createPart(col, 0); ! if (lp != null) { ! new Request(annos, lp, tts).run(); ! } } } + } catch (BadLocationException e) { } } + } finally { + doc.readUnlock(); } } } } Index: java/src/org/netbeans/modules/java/JavaEditor.java =================================================================== RCS file: /cvs/java/src/org/netbeans/modules/java/JavaEditor.java,v retrieving revision 1.142 diff -c -r1.142 JavaEditor.java *** java/src/org/netbeans/modules/java/JavaEditor.java 22 Aug 2003 10:23:48 -0000 1.142 --- java/src/org/netbeans/modules/java/JavaEditor.java 30 Sep 2003 15:03:16 -0000 *************** *** 424,430 **** private void processAnnotations(ParserMessage[] errors) { ArrayList added,removed,unchanged; - Iterator i; Collection newAnnotations; if (errors==null) { // request processed by someone else ErrorManager.getDefault().log("request for "+findDataObject().getPrimaryFile().getPath()+" processed by someone else"); --- 424,429 ---- *************** *** 440,454 **** removed.removeAll(newAnnotations); detachAnnotations(removed); if (!added.isEmpty() && isDocumentLoaded()) { ! LineCookie cookie = (LineCookie)findDataObject().getCookie(LineCookie.class); ! Line.Set lines = cookie.getLineSet(); ! for (i=added.iterator();i.hasNext();) { ! ParserAnnotation ann=(ParserAnnotation)i.next(); ! ann.attachToLineSet(lines); } } annotations=unchanged; annotations.addAll(added); } --- 439,468 ---- removed.removeAll(newAnnotations); detachAnnotations(removed); if (!added.isEmpty() && isDocumentLoaded()) { ! ! // Partial fix of #33165 - document read-locking ! final ArrayList finalAdded = added; ! StyledDocument doc = getDocument(); ! Runnable docRenderer = new Runnable() { ! public void run() { ! LineCookie cookie = (LineCookie)findDataObject().getCookie(LineCookie.class); ! Line.Set lines = cookie.getLineSet(); ! for (Iterator i=finalAdded.iterator();i.hasNext();) { ! ParserAnnotation ann=(ParserAnnotation)i.next(); ! ann.attachToLineSet(lines); ! } ! } ! }; ! ! if (doc != null) { ! doc.render(docRenderer); ! } else { ! docRenderer.run(); } } + annotations=unchanged; annotations.addAll(added); } cvs server: [08:05:32] waiting for tigrisc's lock in /cvs/java/test/qa-functional/src/org/netbeans/test/oo/gui/jelly cvs server: [08:06:02] obtained lock in /cvs/java/test/qa-functional/src/org/netbeans/test/oo/gui/jelly Index: openide/src/org/openide/text/CloneableEditorSupport.java =================================================================== RCS file: /cvs/openide/src/org/openide/text/CloneableEditorSupport.java,v retrieving revision 1.95 diff -c -r1.95 CloneableEditorSupport.java *** openide/src/org/openide/text/CloneableEditorSupport.java 27 Aug 2003 20:40:32 -0000 1.95 --- openide/src/org/openide/text/CloneableEditorSupport.java 30 Sep 2003 15:03:23 -0000 *************** *** 80,85 **** --- 80,90 ---- /** Common name for editor mode. */ public static final String EDITOR_MODE = "editor"; // NOI18N + private static final int DOCUMENT_NO = 0; + private static final int DOCUMENT_LOADING = 1; + private static final int DOCUMENT_READY = 2; + private static final int DOCUMENT_RELOADING = 3; + /** Flag saying if the CloneableEditorSupport handles already the UserQuestionException*/ private boolean inUserQuestionExceptionHandler; *************** *** 135,141 **** /** position manager */ private PositionRef.Manager positionManager; ! /** The string which will be appended to the name of top component * when top component becomes modified */ // protected String modifiedAppendix = " *"; // NOI18N --- 140,146 ---- /** position manager */ private PositionRef.Manager positionManager; ! /** The string which will be appended to the name of top component * when top component becomes modified */ // protected String modifiedAppendix = " *"; // NOI18N *************** *** 165,170 **** --- 170,179 ---- /** Flag whether the document is already modified or not.*/ // #34728 performance optimization private boolean alreadyModified = false; + + private int documentStatus = DOCUMENT_NO; + + private RuntimeException prepareDocumentRuntimeException; /** Creates new CloneableEditorSupport attached to given environment. * *************** *** 270,282 **** return positionManager; } ! ! /** Overrides superclass method, first processes document preparation. * @see #prepareDocument */ public void open() { ! prepareDocument().waitFinished(); ! super.open(); } // --- 279,295 ---- return positionManager; } ! /** Overrides superclass method, first processes document preparation. * @see #prepareDocument */ public void open() { ! try { ! openDocument(); ! super.open(); ! } catch (IOException e) { ! ErrorManager.getDefault().notify( ! ErrorManager.INFORMATIONAL, e); ! } } // *************** *** 302,308 **** } /** Report a bound property update to any registered listeners. ! * @param propertyName the programmatic name of the property that was changed. * @param oldValue rhe old value of the property. * @param newValue the new value of the property. * @since 3.40 --- 315,321 ---- } /** Report a bound property update to any registered listeners. ! * @param propertyName the programmatic name of the property that was changed. * @param oldValue rhe old value of the property. * @param newValue the new value of the property. * @since 3.40 *************** *** 334,399 **** * @return task for control over loading */ public Task prepareDocument() { ! return prepareDocument(false); } /** @param clearDocument indicates whether the document is needed * to clear before (used for reloading) */ private Task prepareDocument(final boolean clearDocument) { ! // first test is done outside of getLock block because the getLock ! // can be held for a long time ! Task t = prepareTask; ! if (t != null) { ! return t; } - - synchronized (getLock ()) { - if (prepareTask != null) - return prepareTask; - - // listen to modifications on env, but remove - // previous instance first - env.removePropertyChangeListener(getListener()); - env.addPropertyChangeListener(getListener()); - - // after call to this method the originalDoc and kit are initialized - // in spite of that the document is not yet fully read in - - kit = createEditorKit (); - if (doc == null) { - doc = createStyledDocument (kit); - } - - // The thread nume should be: "Loading document " + env; // NOI18N - prepareTask = RequestProcessor.getDefault().post(new Runnable () { - public void run () { - try { - synchronized (getLock ()) { - if(clearDocument) { - // #24676. Reloading: Put positions into memory - // and fire document is closing (little trick - // to detach annotations). - getPositionManager().documentClosed(); - fireDocumentChange(doc, true); ! clearDocument(); ! } ! // uses the listener's run method to initialize whole document ! loadTask = new Task(getListener()); ! loadTask.run (); } ! fireDocumentChange(doc, false); ! } catch (RuntimeException t) { ! t.printStackTrace(); ! throw t; } } ! }); ! return prepareTask; ! } } /** Clears the doc document. Helper method. */ --- 347,459 ---- * @return task for control over loading */ public Task prepareDocument() { ! synchronized (getLock()) { ! switch (documentStatus) { ! case DOCUMENT_NO: ! documentStatus = DOCUMENT_LOADING; ! return prepareDocument(false); ! ! default: ! if (prepareTask == null) { // should never happen ! throw new IllegalStateException(); ! } ! return prepareTask; ! } ! } } + /** @param clearDocument indicates whether the document is needed * to clear before (used for reloading) */ private Task prepareDocument(final boolean clearDocument) { ! if (prepareTask != null) ! return prepareTask; ! ! // listen to modifications on env, but remove ! // previous instance first ! env.removePropertyChangeListener(getListener()); ! env.addPropertyChangeListener(getListener()); ! ! // after call to this method the originalDoc and kit are initialized ! // in spite of that the document is not yet fully read in ! ! kit = createEditorKit (); ! if (doc == null) { ! doc = createStyledDocument (kit); } ! // The thread nume should be: "Loading document " + env; // NOI18N ! prepareTask = RequestProcessor.getDefault().post(new Runnable () { ! ! private boolean runningInAtomicLock; ! ! public void run () { ! ! // Run the operations under atomic lock primarily due ! // to reload which occurs in a widely published document instance ! // where another threads may operate already ! if (!runningInAtomicLock) { ! runningInAtomicLock = true; ! NbDocument.runAtomic(doc, this); ! ! // attach undo/redo manager after atomic change has finished ! doc.addUndoableEditListener (getUndoRedo ()); ! ! runningInAtomicLock = false; // is not strictly necessary ! return; ! } ! ! prepareDocumentRuntimeException = null; ! int targetStatus = DOCUMENT_NO; // be pesimistic initially ! try { ! // synchronized (getLock ()) { ! if(clearDocument) { ! // document no longer valid here ! documentStatus = DOCUMENT_RELOADING; ! ! // #24676. Reloading: Put positions into memory ! // and fire document is closing (little trick ! // to detach annotations). ! getPositionManager().documentClosed(); ! updateLineSet(true); ! ! fireDocumentChange(doc, true); ! ! clearDocument(); } ! // uses the listener's run method to initialize whole document ! loadTask = new Task(getListener()); ! loadTask.run (); ! // } ! ! // assign before fireDocumentChange() as listener should be able to access getDocument() ! documentStatus = DOCUMENT_READY; ! ! fireDocumentChange(doc, false); ! ! // Confirm that whole loading succeeded ! targetStatus = DOCUMENT_READY; ! ! } catch (RuntimeException t) { ! prepareDocumentRuntimeException = t; ! t.printStackTrace(); ! throw t; ! ! } finally { ! ! synchronized (getLock()) { ! documentStatus = targetStatus; ! ! getLock().notifyAll(); } } ! ! } ! }); ! return prepareTask; } /** Clears the doc document. Helper method. */ *************** *** 425,441 **** * @exception IOException if the document could not be loaded */ public StyledDocument openDocument () throws IOException { ! for (;;) { ! // load the document ! prepareDocument ().waitFinished (); ! IOException loadExc = getListener().checkLoadException(); ! if (loadExc != null) { ! throw loadExc; ! } ! ! StyledDocument d = doc; ! if (d != null) ! return d; } } --- 485,533 ---- * @exception IOException if the document could not be loaded */ public StyledDocument openDocument () throws IOException { ! synchronized (getLock()) { ! return openDocumentCheckIOE(); ! } ! } ! ! private StyledDocument openDocumentCheckIOE() throws IOException { ! StyledDocument doc = openDocumentImpl(); ! ! IOException ioe = getListener().checkLoadException(); ! if (ioe != null) { ! throw ioe; ! } ! ! return doc; ! } ! ! /** ! * Must be called under getLock(). ! */ ! private StyledDocument openDocumentImpl() { ! switch (documentStatus) { ! case DOCUMENT_NO: ! documentStatus = DOCUMENT_LOADING; ! prepareDocument(false); ! return openDocumentImpl(); ! ! case DOCUMENT_RELOADING: // proceed to DOCUMENT_READY ! case DOCUMENT_READY: ! return doc; ! ! default: // loading ! try { ! getLock().wait(); ! } catch (InterruptedException e) { ! ErrorManager.getDefault().notify( ! ErrorManager.INFORMATIONAL, e); ! } ! ! if (prepareDocumentRuntimeException != null) { ! throw prepareDocumentRuntimeException; ! } ! ! return openDocumentImpl(); } } *************** *** 445,468 **** * @return document or null if it is not yet loaded */ public StyledDocument getDocument () { ! // XXX #16048. In case there is called this method from loadTask ! // (possible only via LineListener->DocumentLine..). ! // PENDING Needs to be tried to redesign DocumentLine to avoid this. ! if(LOCAL_LOAD_TASK.get() != null) { ! return doc; ! } ! Task t = loadTask; ! if (t != null) { ! // if an task exists ! t.waitFinished (); ! return doc; ! } else { ! return null; } } ! ! /** Test whether the document is modified. * @return true if the document is in memory and is modified; * otherwise false --- 537,566 ---- * @return document or null if it is not yet loaded */ public StyledDocument getDocument () { ! synchronized (getLock()) { ! while (true) { ! switch (documentStatus) { ! case DOCUMENT_NO: ! return null; ! ! default: // ready, loading or reloading ! // XXX #16048. In case there is called this method from loadTask ! // (possible only via LineListener->DocumentLine..). ! // PENDING Needs to be tried to redesign DocumentLine to avoid this. ! if (LOCAL_LOAD_TASK.get() != null) { ! return doc; ! } ! try { ! return openDocumentCheckIOE(); ! } catch (IOException e) { ! return null; ! } ! } ! } } } ! /** Test whether the document is modified. * @return true if the document is in memory and is modified; * otherwise false *************** *** 911,974 **** * by calling prepareDocument(). */ protected Task reloadDocument() { ! synchronized (getLock ()) { ! if (doc != null) { ! // UndoManager must be detached from document here because it will be attached in loadDocument() ! doc.removeUndoableEditListener (getUndoRedo ()); ! // Remember caret positions in all opened panes ! final JEditorPane[] panes = getOpenedPanes(); ! final int[] carets; ! if (panes != null) { ! carets = new int[panes.length]; ! for(int i = 0; i < panes.length; i++) { ! carets[i] = panes[i].getCaretPosition(); ! } ! } else { ! carets = new int[0]; } ! prepareTask = null; // make sure new loading will occur ! final Task docLoadTask = prepareDocument(true); ! docLoadTask.addTaskListener( ! new TaskListener() { ! public void taskFinished (Task task) { ! //Bugfix #12338: This Swing code replanned to AWT thread ! SwingUtilities.invokeLater(new Runnable() { ! public void run () { ! if (panes != null) { ! for (int i = 0; i < panes.length; i++) { ! // #26407 Adjusts caret position, ! // (reloaded doc could be shorter). ! int textLength = panes[i].getText().length(); ! if(carets[i] >= textLength) { ! carets[i] = textLength-1; ! } ! ! panes[i].setCaretPosition(carets[i]); } } - getUndoRedo().discardAllEdits(); // reset undo manager - // Insert before-save undo event to enable unmodifying undo - getUndoRedo().undoableEditHappened( - new UndoableEditEvent( - CloneableEditorSupport.this, - new BeforeSaveEdit(lastSaveTime) - ) - ); - - notifyUnmodified (); - updateLineSet(true); } ! }); ! docLoadTask.removeTaskListener(this); ! } ! } ! ); ! return docLoadTask; ! } } return prepareDocument(); --- 1009,1072 ---- * by calling prepareDocument(). */ protected Task reloadDocument() { ! if (doc != null) { ! // UndoManager must be detached from document here because it will be attached in loadDocument() ! doc.removeUndoableEditListener (getUndoRedo ()); ! // Remember caret positions in all opened panes ! final JEditorPane[] panes = getOpenedPanes(); ! final int[] carets; ! if (panes != null) { ! carets = new int[panes.length]; ! for(int i = 0; i < panes.length; i++) { ! carets[i] = panes[i].getCaretPosition(); } + } else { + carets = new int[0]; + } ! documentStatus = DOCUMENT_RELOADING; ! ! prepareTask = null; // make sure new loading will occur ! final Task docLoadTask = prepareDocument(true); ! docLoadTask.addTaskListener( ! new TaskListener() { ! public void taskFinished (Task task) { ! //Bugfix #12338: This Swing code replanned to AWT thread ! SwingUtilities.invokeLater(new Runnable() { ! public void run () { ! if (panes != null) { ! for (int i = 0; i < panes.length; i++) { ! // #26407 Adjusts caret position, ! // (reloaded doc could be shorter). ! int textLength = panes[i].getText().length(); ! if(carets[i] >= textLength) { ! carets[i] = textLength-1; } + + panes[i].setCaretPosition(carets[i]); } } ! getUndoRedo().discardAllEdits(); // reset undo manager ! // Insert before-save undo event to enable unmodifying undo ! getUndoRedo().undoableEditHappened( ! new UndoableEditEvent( ! CloneableEditorSupport.this, ! new BeforeSaveEdit(lastSaveTime) ! ) ! ); + notifyUnmodified (); + updateLineSet(true); + } + }); + docLoadTask.removeTaskListener(this); + } + } + ); ! ! return docLoadTask; } return prepareDocument(); *************** *** 1165,1178 **** * @return the set */ Line.Set updateLineSet (boolean clear) { ! synchronized(LOCK_LINE_SET) { if(lineSet != null && !clear) { return lineSet; } Line.Set oldSet = lineSet; ! if (doc == null) { lineSet = new EditorSupportLineSet.Closed(CloneableEditorSupport.this); } else { lineSet = new EditorSupportLineSet(CloneableEditorSupport.this, doc); --- 1263,1276 ---- * @return the set */ Line.Set updateLineSet (boolean clear) { ! synchronized(getLock()) { if(lineSet != null && !clear) { return lineSet; } Line.Set oldSet = lineSet; ! if (doc == null || documentStatus == DOCUMENT_RELOADING) { lineSet = new EditorSupportLineSet.Closed(CloneableEditorSupport.this); } else { lineSet = new EditorSupportLineSet(CloneableEditorSupport.this, doc); *************** *** 1236,1243 **** } finally { is.close (); } - // attach undo/redo manager - doc.addUndoableEditListener (getUndoRedo ()); } catch (IOException ex) { aProblem = ex; throw ex; --- 1334,1339 ---- *************** *** 1275,1308 **** /** Clears all data from memory. */ private void closeDocument () { ! for (;;) { ! Task prep; ! ! synchronized (getLock()) { ! if (doc == null) { ! return; ! } ! ! if (loadTask == null) { ! return; ! } ! ! prep = prepareTask; ! if (prep == null) { ! return; ! } ! ! if (prep.isFinished ()) { ! doCloseDocument (); ! return; } } - - /* Wait for loading task to be finished - * so that the document etc. stays valid - * during the load operation. - */ - prep.waitFinished(); } } --- 1371,1392 ---- /** Clears all data from memory. */ private void closeDocument () { ! synchronized (getLock()) { ! while (true) { ! switch (documentStatus) { ! case DOCUMENT_NO: ! return; ! ! case DOCUMENT_LOADING: ! case DOCUMENT_RELOADING: ! openDocumentImpl(); ! break; // try to close again ! ! default: ! doCloseDocument(); ! return; } } } } *************** *** 1324,1352 **** if (positionManager != null) { positionManager.documentClosed (); fireDocumentChange(doc, true); } doc = null; kit = null; updateLineSet (true); } /** Handles the actual reload of document. * @param doReload false if we should first ask the user */ private void checkReload(boolean doReload) { ! StyledDocument doc = this.doc; ! ! if (doc == null) { ! return; } ! if (!doReload && !reloadDialogOpened) { String msg = NbBundle.getMessage (CloneableEditorSupport.class, "FMT_External_change", // NOI18N ! doc.getProperty (javax.swing.text.Document.TitleProperty) ); NotifyDescriptor nd = new NotifyDescriptor.Confirmation(msg, NotifyDescriptor.YES_NO_OPTION); --- 1408,1445 ---- if (positionManager != null) { positionManager.documentClosed (); + + documentStatus = DOCUMENT_NO; fireDocumentChange(doc, true); } + + documentStatus = DOCUMENT_NO; doc = null; kit = null; updateLineSet (true); + } /** Handles the actual reload of document. * @param doReload false if we should first ask the user */ private void checkReload(boolean doReload) { ! StyledDocument d; ! synchronized (getLock()) { ! switch (documentStatus) { ! case DOCUMENT_NO: ! return; // return if no document loaded ! } ! ! d = doc; // used with reload dialog - should not be null } ! if (!doReload && !reloadDialogOpened) { String msg = NbBundle.getMessage (CloneableEditorSupport.class, "FMT_External_change", // NOI18N ! d.getProperty (javax.swing.text.Document.TitleProperty) ); NotifyDescriptor nd = new NotifyDescriptor.Confirmation(msg, NotifyDescriptor.YES_NO_OPTION); *************** *** 1362,1380 **** } } ! if (doReload) { ! //Bugfix #9612: Call of reloadDocument() is now posted to ! //RequestProcessor ! RequestProcessor.getDefault().post(new Runnable() { ! public void run () { ! reloadDocument(); ! } ! }); } } - - /** Creates netbeans document for a given document. * @param d document to use as underlaying one * @return styled document that could support Guarded.ATTRIBUTE --- 1455,1490 ---- } } ! synchronized (getLock()) { ! switch (documentStatus) { ! case DOCUMENT_NO: ! return; // return if no document loaded ! ! case DOCUMENT_LOADING: ! case DOCUMENT_RELOADING: ! openDocumentImpl(); // finish opening first ! break; ! } ! ! if (doReload) { ! // #33165 ! // reloadDocument() itself should be fast and the task ! // that it returns is scheduled to RP automatically ! reloadDocument(); ! ! /* #33165 - not posting to RP, reason is above ! //Bugfix #9612: Call of reloadDocument() is now posted to ! //RequestProcessor ! RequestProcessor.getDefault().post(new Runnable() { ! public void run () { ! reloadDocument().waitFinished(); ! } ! }); ! */ ! } } } /** Creates netbeans document for a given document. * @param d document to use as underlaying one * @return styled document that could support Guarded.ATTRIBUTE *************** *** 1535,1541 **** /** Access to lock on operations on the support */ ! Object getLock () { return allEditors; } --- 1645,1651 ---- /** Access to lock on operations on the support */ ! final Object getLock () { return allEditors; } *************** *** 1612,1618 **** * It resets loadExc to null. */ public IOException checkLoadException() { IOException ret = loadExc; ! loadExc = null; return ret; } --- 1722,1728 ---- * It resets loadExc to null. */ public IOException checkLoadException() { IOException ret = loadExc; ! // loadExc = null; return ret; } *************** *** 1683,1689 **** /** Initialization of the document. */ public void run () { ! synchronized (getLock ()) { /* Remove existing listener before running the loading task * This should prevent firing of insertUpdate() during load (or reload) * which can prevent dedloks that sometimes occured during file reload. --- 1793,1799 ---- /** Initialization of the document. */ public void run () { ! // synchronized (getLock ()) { /* Remove existing listener before running the loading task * This should prevent firing of insertUpdate() during load (or reload) * which can prevent dedloks that sometimes occured during file reload. *************** *** 1714,1720 **** // Start listening on changes in document doc.addDocumentListener(getListener()); } ! } } --- 1824,1830 ---- // Start listening on changes in document doc.addDocumentListener(getListener()); } ! // } } Index: openide/src/org/openide/text/DocumentLine.java =================================================================== RCS file: /cvs/openide/src/org/openide/text/DocumentLine.java,v retrieving revision 1.50 diff -c -r1.50 DocumentLine.java *** openide/src/org/openide/text/DocumentLine.java 12 Aug 2003 09:38:11 -0000 1.50 --- openide/src/org/openide/text/DocumentLine.java 30 Sep 2003 15:03:23 -0000 *************** *** 18,24 **** import java.util.*; import javax.swing.text.*; import javax.swing.event.*; ! import javax.swing.SwingUtilities; import org.openide.ErrorManager; import org.openide.text.EnhancedChangeEvent; --- 18,24 ---- import java.util.*; import javax.swing.text.*; import javax.swing.event.*; ! import javax.swing.text.Element; import org.openide.ErrorManager; import org.openide.text.EnhancedChangeEvent; *************** *** 286,292 **** return this.pos.getCloneableEditorSupport().getLineSet().registerLine(this); } - /** Add annotation to this Annotatable class * @param anno annotation which will be attached to this class */ protected void addAnnotation(Annotation anno) { --- 286,291 ---- *************** *** 302,308 **** try { if (!anno.isInDocument()) { anno.setInDocument(true); ! NbDocument.addAnnotation (doc, pos.getPosition(), -1, anno); } } catch (IOException ex) { ErrorManager.getDefault ().notify ( ErrorManager.EXCEPTION, ex); --- 301,310 ---- try { if (!anno.isInDocument()) { anno.setInDocument(true); ! // #33165 - find position that is surely at begining of line ! FindAnnotationPosition fap = new FindAnnotationPosition(doc, pos.getPosition()); ! doc.render(fap); ! NbDocument.addAnnotation (doc, fap.getAnnotationPosition(), -1, anno); } } catch (IOException ex) { ErrorManager.getDefault ().notify ( ErrorManager.EXCEPTION, ex); *************** *** 331,347 **** * removed. * @since 1.27 */ void attachDetachAnnotations(StyledDocument doc, boolean closing) { java.util.List list = getAnnotations(); for (int i=0; i correct + try { + annoPos = doc.createPosition(lineStartOffset); + } catch (BadLocationException e) { + throw new IllegalArgumentException(); // should never fail + } + } + } + + Position getAnnotationPosition() { + return annoPos; + } + + } public String getText() { ! final StyledDocument doc = pos.getCloneableEditorSupport ().getDocument (); // document is not opened if (doc == null) return null; + final String[] retStringArray = new String[1]; + doc.render(new Runnable() { public void run() { + // Part of #33165 - the following code is wrapped by doc.render() int lineNumber = getLineNumber(); int lineStart = NbDocument.findLineOffset(doc, lineNumber); // #24434: Check whether the next line exists *************** *** 377,387 **** } try { ! return doc.getText(lineStart, lineEnd - lineStart); } catch (BadLocationException ex) { ErrorManager.getDefault ().notify ( ErrorManager.EXCEPTION, ex); ! return null; } } /** Attach created Line.Part to the parent Line */ --- 424,437 ---- } try { ! retStringArray[0] = doc.getText(lineStart, lineEnd - lineStart); } catch (BadLocationException ex) { ErrorManager.getDefault ().notify ( ErrorManager.EXCEPTION, ex); ! retStringArray[0] = null; } + // End of the code wrapped by doc.render() + }}); + return retStringArray[0]; } /** Attach created Line.Part to the parent Line */ *************** *** 411,416 **** --- 461,512 ---- i++; } } + + // #33165 - fix the position in the positionRef in case this line changes + // and reattach the annotations. + // The fix of #32764 in notifyMove() would only reattach + // the annotations in case the position does not go at a line begining + // after the modification but that is not enough + // to fix undo-related issues. + Position p; + try { + p = pos.getPosition(); + } catch (IOException ex) { + ErrorManager.getDefault ().notify ( ErrorManager.EXCEPTION, ex); + p = null; + } + + if (p != null) { + int lineStartOffset = doc.getParagraphElement(p.getOffset()).getStartOffset(); + CloneableEditorSupport support = pos.getCloneableEditorSupport(); + // Recreate positionRef unconditionally to avoid undo problems + pos = new PositionRef( + support.getPositionManager(), lineStartOffset, Position.Bias.Forward + ); + + List annos = getAnnotations(); + int annosSize = annos.size(); + if (annosSize > 0) { + try { + p = pos.getPosition(); + } catch (IOException e) { + throw new IllegalArgumentException(); // should not fail + } + + for (int i=0; ipos the way it points at the start of line. */ private void updatePositionRef() { ! CloneableEditorSupport support = pos.getCloneableEditorSupport(); int startOffset = NbDocument.findLineOffset(support.getDocument(), getLineNumber()); if(pos.getOffset() != startOffset) { pos = new PositionRef( support.getPositionManager(), startOffset, Position.Bias.Forward --- 520,536 ---- /** Updates pos the way it points at the start of line. */ private void updatePositionRef() { ! // #33165 - Moved handling that follows into notifyChange() ! // due to problems with undo operations. ! // Rest of notifyMove() should work as the code in notifyChange() ! // in fact includes the same work as done here and notifyChange() ! // is called before notifyMove() ! // (see linesChanged()/linesMoved() in LineListener). ! ! /* CloneableEditorSupport support = pos.getCloneableEditorSupport(); int startOffset = NbDocument.findLineOffset(support.getDocument(), getLineNumber()); + if(pos.getOffset() != startOffset) { pos = new PositionRef( support.getPositionManager(), startOffset, Position.Bias.Forward *************** *** 451,456 **** --- 555,561 ---- } } } + */ } *************** *** 552,569 **** } public String getText() { ! StyledDocument doc = position.getCloneableEditorSupport ().getDocument (); // document is not opened if (doc == null) return null; try { ! return doc.getText(position.getOffset(), getLength()); } catch (BadLocationException ex) { ErrorManager.getDefault ().notify ( ErrorManager.EXCEPTION, ex); ! return null; } } /** When document is opened or closed the annotations must be added or --- 657,681 ---- } public String getText() { ! final StyledDocument doc = position.getCloneableEditorSupport ().getDocument (); // document is not opened if (doc == null) return null; + final String[] retStringArray = new String[1]; + doc.render(new Runnable() { public void run() { + // Part of #33165 - the following code is wrapped by doc.render() try { ! retStringArray[0] = doc.getText(position.getOffset(), getLength()); } catch (BadLocationException ex) { ErrorManager.getDefault ().notify ( ErrorManager.EXCEPTION, ex); ! retStringArray[0] = null; } + // End of the code wrapped by doc.render() + }}); + return retStringArray[0]; + } /** When document is opened or closed the annotations must be added or *************** *** 645,655 **** } private void invoke(int op) { ! SwingUtilities.invokeLater(new LR(op)); } private void invoke(EnhancedChangeEvent ev) { ! SwingUtilities.invokeLater(new LR(ev)); } public void stateChanged (ChangeEvent ev) { --- 757,771 ---- } private void invoke(int op) { ! // part of #33165 - done synchronously not invoking into EQ ! //SwingUtilities.invokeLater(new LR(op)); ! new LR(op).run(); } private void invoke(EnhancedChangeEvent ev) { ! // part of #33165 - done synchronously not invoking into EQ ! //SwingUtilities.invokeLater(new LR(ev)); ! new LR(ev).run(); } public void stateChanged (ChangeEvent ev) { Index: openide/src/org/openide/text/NbDocument.java =================================================================== RCS file: /cvs/openide/src/org/openide/text/NbDocument.java,v retrieving revision 1.51 diff -c -r1.51 NbDocument.java *** openide/src/org/openide/text/NbDocument.java 27 Feb 2003 23:41:05 -0000 1.51 --- openide/src/org/openide/text/NbDocument.java 30 Sep 2003 15:03:24 -0000 *************** *** 93,100 **** --- 93,104 ---- * is null. */ public static int findLineNumber (StyledDocument doc, int offset) { + /* pre-33165 Element paragraphsParent = findLineRootElement (doc); return paragraphsParent.getElementIndex (offset); + */ + + return new DocumentRenderer(DocumentRenderer.FIND_LINE_NUMBER, doc, offset).renderToInt(); } /** Finds column number given an offset. *************** *** 105,113 **** --- 109,120 ---- * is null. */ public static int findLineColumn (StyledDocument doc, int offset) { + /* Element paragraphsParent = findLineRootElement (doc); int indx = paragraphsParent.getElementIndex (offset); return offset - paragraphsParent.getElement (indx).getStartOffset (); + */ + return new DocumentRenderer(DocumentRenderer.FIND_LINE_COLUMN, doc, offset).renderToInt(); } /** Finds offset of the beginning of a line. *************** *** 120,125 **** --- 127,133 ---- * lineNumber value is inserted */ public static int findLineOffset (StyledDocument doc, int lineNumber) { + /* Element paragraphsParent = findLineRootElement (doc); Element line = paragraphsParent.getElement (lineNumber); *************** *** 129,134 **** --- 137,145 ---- } return line.getStartOffset (); + */ + + return new DocumentRenderer(DocumentRenderer.FIND_LINE_OFFSET, doc, lineNumber).renderToInt(); } /** Creates position with a bias. If the bias is {@link javax.swing.text.Position.Bias#Backward} *************** *** 573,577 **** * @param annotation annotation which is going to be removed */ public void removeAnnotation(Annotation annotation); } ! } --- 584,648 ---- * @param annotation annotation which is going to be removed */ public void removeAnnotation(Annotation annotation); } ! ! private static final class DocumentRenderer implements Runnable { ! ! private static final int FIND_LINE_NUMBER = 0; ! private static final int FIND_LINE_COLUMN = 1; ! private static final int FIND_LINE_OFFSET = 2; ! ! private StyledDocument doc; ! ! private int opCode; ! ! private int argInt; ! ! private int retInt; ! ! DocumentRenderer(int opCode, StyledDocument doc, int argInt) { ! this.opCode = opCode; ! this.doc = doc; ! this.argInt = argInt; ! } ! ! int renderToInt() { ! doc.render(this); ! return retInt; ! } ! ! ! public void run() { ! switch (opCode) { ! case FIND_LINE_NUMBER: { ! Element paragraphsParent = findLineRootElement (doc); ! retInt = paragraphsParent.getElementIndex (argInt); // argInt is offset ! break; } ! ! ! case FIND_LINE_COLUMN: { ! Element paragraphsParent = findLineRootElement (doc); ! int indx = paragraphsParent.getElementIndex (argInt); // argInt is offset ! retInt = argInt - paragraphsParent.getElement (indx).getStartOffset (); ! break; } ! ! ! case FIND_LINE_OFFSET: { ! Element paragraphsParent = findLineRootElement (doc); ! Element line = paragraphsParent.getElement (argInt); // argInt is lineNumber ! ! if(line == null) { ! throw new IndexOutOfBoundsException( ! "Index=" + argInt + " is out of bounds."); // NOI18N ! } ! ! retInt = line.getStartOffset (); ! break; } ! ! default: ! throw new IllegalStateException(); ! ! } ! } ! } ! } Index: openide/src/org/openide/text/PositionRef.java =================================================================== RCS file: /cvs/openide/src/org/openide/text/PositionRef.java,v retrieving revision 1.49 diff -c -r1.49 PositionRef.java *** openide/src/org/openide/text/PositionRef.java 2 Apr 2003 09:46:48 -0000 1.49 --- openide/src/org/openide/text/PositionRef.java 30 Sep 2003 15:03:24 -0000 *************** *** 20,25 **** --- 20,26 ---- import javax.swing.text.Position; import javax.swing.text.StyledDocument; import javax.swing.text.BadLocationException; + import javax.swing.text.Element; import org.openide.util.RequestProcessor; *************** *** 41,47 **** /** insert after? */ private boolean insertAfter; ! /** Creates new PositionRef using the given manager at the specified * position offset. * @param manager manager for the position --- 42,48 ---- /** insert after? */ private boolean insertAfter; ! /** Creates new PositionRef using the given manager at the specified * position offset. * @param manager manager for the position *************** *** 165,171 **** } public String toString() { ! return "Pos[" + getOffset () + "]"; // NOI18N } /** This class is responsible for the holding the Document object --- 166,172 ---- } public String toString() { ! return "Pos[" + getOffset () + "]" + ", kind=" + kind; // NOI18N } /** This class is responsible for the holding the Document object *************** *** 271,281 **** * pass sweep of the data structure (inlined in the code). * @param toMemory puts positions to memory if true, * from memory if false */ ! private void processPositions(boolean toMemory) { // clear the queue, we'll do the sweep inline anyway while(queue.poll() != null); counter = 0; ! synchronized(this) { ChainItem previous = head; ChainItem ref = previous.next; --- 272,283 ---- * pass sweep of the data structure (inlined in the code). * @param toMemory puts positions to memory if true, * from memory if false */ ! private void processPositions(final boolean toMemory) { // clear the queue, we'll do the sweep inline anyway while(queue.poll() != null); counter = 0; ! ! /* pre-33165 synchronized(this) { ChainItem previous = head; ChainItem ref = previous.next; *************** *** 298,304 **** ref = ref.next; } ! } } /** Polls queue and increases the counter accordingly. --- 300,309 ---- ref = ref.next; } ! } ! */ ! ! new DocumentRenderer(DocumentRenderer.PROCESS_POSITIONS, toMemory).render(); } /** Polls queue and increases the counter accordingly. *************** *** 339,346 **** } /** Adds the position to this manager. */ ! Kind addPosition(PositionRef pos) { Kind kind; synchronized(this) { head.next = new ChainItem(pos, queue, head.next); --- 344,353 ---- } /** Adds the position to this manager. */ ! Kind addPosition(final PositionRef pos) { Kind kind; + + /* pre-33165 synchronized(this) { head.next = new ChainItem(pos, queue, head.next); *************** *** 348,353 **** --- 355,363 ---- pos.kind : pos.kind.toMemory(pos.insertAfter)); } + */ + + kind = (Kind)new DocumentRenderer(DocumentRenderer.ADD_POSITION, pos).renderToObject(); checkQueue(); *************** *** 418,423 **** --- 428,435 ---- /** Converts the kind to representation in memory */ public PositionKind toMemory (boolean insertAfter) { + + /* pre-33165 // try to find the right position Position p; try { *************** *** 426,431 **** --- 438,447 ---- p = doc.getEndPosition (); } return new PositionKind (p); + */ + + return (PositionKind)new DocumentRenderer( + DocumentRenderer.KIND_TO_MEMORY, this, insertAfter).renderToObject(); } /** Converts the kind to representation out from memory */ *************** *** 453,472 **** /** Get the line number */ public int getLine() { ! return NbDocument.findLineNumber(doc, getOffset()); } /** Get the column number */ public int getColumn() { ! return NbDocument.findLineColumn(doc, getOffset()); } /** Writes the kind to stream */ public void write (DataOutput os) throws IOException { int offset = getOffset(); int line = getLine(); int column = getColumn(); if(offset < 0 || line < 0 || column < 0) { throw new IOException( "Illegal PositionKind: " + pos + "[offset=" // NOI18N --- 469,502 ---- /** Get the line number */ public int getLine() { ! // pre-33165 return NbDocument.findLineNumber(doc, getOffset()); ! return new DocumentRenderer( ! DocumentRenderer.POSITION_KIND_GET_LINE, this).renderToInt(); } /** Get the column number */ public int getColumn() { ! // pre-33165 return NbDocument.findLineColumn(doc, getOffset()); ! return new DocumentRenderer( ! DocumentRenderer.POSITION_KIND_GET_COLUMN, this).renderToInt(); } /** Writes the kind to stream */ public void write (DataOutput os) throws IOException { + + /* pre-33165 int offset = getOffset(); int line = getLine(); int column = getColumn(); + */ + + DocumentRenderer renderer = new DocumentRenderer( + DocumentRenderer.POSITION_KIND_WRITE, this); + int offset = renderer.renderToIntIOE(); + int line = renderer.getLine(); + int column = renderer.getColumn(); + if(offset < 0 || line < 0 || column < 0) { throw new IOException( "Illegal PositionKind: " + pos + "[offset=" // NOI18N *************** *** 505,513 **** --- 535,554 ---- /** Constructs the out kind from the position kind. */ public OutKind (PositionKind kind) { + + /* pre-33165 int offset = kind.getOffset(); int line = kind.getLine(); int column = kind.getColumn(); + */ + + + DocumentRenderer renderer = new DocumentRenderer( + DocumentRenderer.OUT_KIND_CONSTRUCTOR, kind); + + int offset = renderer.renderToInt(); + int line = renderer.getLine(); + int column = renderer.getColumn(); if(offset < 0 || line < 0 || column < 0) { throw new IndexOutOfBoundsException( *************** *** 590,601 **** /** Get the line number */ public int getLine() throws IOException { ! return NbDocument.findLineNumber(getCloneableEditorSupport().openDocument(), offset); } /** Get the column number */ public int getColumn() throws IOException { ! return NbDocument.findLineColumn (getCloneableEditorSupport().openDocument(), offset); } /** Writes the kind to stream */ --- 631,648 ---- /** Get the line number */ public int getLine() throws IOException { ! // pre-33165 return NbDocument.findLineNumber(getCloneableEditorSupport().openDocument(), offset); ! getCloneableEditorSupport().openDocument(); // make sure document is fully read ! return new DocumentRenderer(DocumentRenderer.OFFSET_KIND_GET_LINE, ! this, offset).renderToIntIOE(); } /** Get the column number */ public int getColumn() throws IOException { ! // pre-33165 return NbDocument.findLineColumn (getCloneableEditorSupport().openDocument(), offset); ! getCloneableEditorSupport().openDocument(); // make sure document fully read ! return new DocumentRenderer(DocumentRenderer.OFFSET_KIND_GET_COLUMN, ! this, offset).renderToIntIOE(); } /** Writes the kind to stream */ *************** *** 638,643 **** --- 685,692 ---- /** Offset */ public int getOffset () { + + /* pre-33165 try { StyledDocument doc = getCloneableEditorSupport().getDocument(); if (doc == null) { *************** *** 648,653 **** --- 697,718 ---- // what to do? hopefully unlikelly return 0; } + */ + + + try { + StyledDocument doc = getCloneableEditorSupport().getDocument(); + if (doc == null) { + doc = getCloneableEditorSupport().openDocument(); + } + int retOffset = new DocumentRenderer(DocumentRenderer.LINE_KIND_GET_OFFSET, + this, line, column, doc).renderToInt(); + return retOffset; + + } catch (IOException e) { + // what to do? hopefully unlikelly + return 0; + } } /** Get the line number */ *************** *** 677,682 **** --- 742,749 ---- /** Converts the kind to representation in memory */ public PositionKind toMemory (boolean insertAfter) { + + /* pre-33165 // try to find the right position Position p; try { *************** *** 684,695 **** } catch (BadLocationException e) { p = doc.getEndPosition (); } return new PositionKind (p); } } ! } } --- 751,1033 ---- } catch (BadLocationException e) { p = doc.getEndPosition (); } + */ + + Position p = (Position)new DocumentRenderer( + DocumentRenderer.LINE_KIND_TO_MEMORY, this, + line, column, insertAfter).renderToObject(); + return new PositionKind (p); } } + + /** + * Helper class ensuring that critical parts will run under document's read lock + * by using {@link javax.swing.text.Document#render(Runnable)}. + */ + private final class DocumentRenderer implements Runnable { + + private static final int KIND_TO_MEMORY = 0; + + private static final int POSITION_KIND_GET_LINE = KIND_TO_MEMORY + 1; + + private static final int POSITION_KIND_GET_COLUMN = POSITION_KIND_GET_LINE + 1; + + private static final int POSITION_KIND_WRITE = POSITION_KIND_GET_COLUMN + 1; ! private static final int OUT_KIND_CONSTRUCTOR = POSITION_KIND_WRITE + 1; ! ! private static final int OFFSET_KIND_GET_LINE = OUT_KIND_CONSTRUCTOR + 1; ! ! private static final int OFFSET_KIND_GET_COLUMN = OFFSET_KIND_GET_LINE + 1; ! ! private static final int LINE_KIND_GET_OFFSET = OFFSET_KIND_GET_COLUMN + 1; ! ! private static final int LINE_KIND_TO_MEMORY = LINE_KIND_GET_OFFSET + 1; ! ! private static final int PROCESS_POSITIONS = LINE_KIND_TO_MEMORY + 1; ! ! private static final int ADD_POSITION = PROCESS_POSITIONS + 1; ! ! private final int opCode; ! ! private Kind argKind; ! ! private boolean argInsertAfter; ! ! private boolean argToMemory; ! ! private int argInt; ! ! private Object retObject; ! ! private int retInt; ! ! private int argLine; ! ! private int argColumn; ! ! private PositionRef argPos; ! ! private StyledDocument argDoc; ! ! private IOException ioException; ! ! DocumentRenderer(int opCode, Kind argKind) { ! this.opCode = opCode; ! this.argKind = argKind; ! } ! ! DocumentRenderer(int opCode, Kind argKind, boolean argInsertAfter) { ! this(opCode, argKind); ! this.argInsertAfter = argInsertAfter; ! } ! ! DocumentRenderer(int opCode, Kind argKind, int argInt) { ! this(opCode, argKind); ! this.argInt = argInt; ! } ! ! DocumentRenderer(int opCode, Kind argKind, int argLine, int argColumn) { ! this(opCode, argKind); ! this.argLine = argLine; ! this.argColumn = argColumn; ! } ! ! DocumentRenderer(int opCode, Kind argKind, int argLine, int argColumn, StyledDocument argDoc) { ! this(opCode, argKind, argLine, argColumn); ! this.argDoc = argDoc; ! } ! ! DocumentRenderer(int opCode, Kind argKind, int argLine, int argColumn, boolean argInsertAfter) { ! this(opCode, argKind, argLine, argColumn); ! this.argInsertAfter = argInsertAfter; ! } ! ! DocumentRenderer(int opCode, boolean toMemory) { ! this.opCode = opCode; ! this.argToMemory = toMemory; ! } ! ! DocumentRenderer(int opCode, PositionRef argPos) { ! this.opCode = opCode; ! this.argPos = argPos; ! } ! ! void render() { ! if (doc != null) { ! doc.render(this); ! } else { ! this.run(); ! } ! } ! ! Object renderToObjectIOE() throws IOException { ! Object o = renderToObject(); ! ! if (ioException != null) { ! throw ioException; ! } ! ! return o; ! } ! ! Object renderToObject() { ! render(); ! return retObject; ! } ! ! int renderToIntIOE() throws IOException { ! int i = renderToInt(); ! ! if (ioException != null) { ! throw ioException; ! } ! ! return i; ! } ! ! int renderToInt() { ! render(); ! return retInt; ! } ! ! int getLine() { ! return argLine; ! } ! ! int getColumn() { ! return argColumn; ! } ! ! public void run() { ! try { ! switch (opCode) { ! case KIND_TO_MEMORY: { ! // try to find the right position ! Position p; ! ! int offset = argKind.getOffset(); ! ! // #33165 ! // Try to use line:column instead ! // Following code can be commented out to retain old behavior ! if (argKind.getClass() == OutKind.class) { ! try { ! int line = argKind.getLine(); ! int col = argKind.getColumn(); ! Element lineRoot = NbDocument.findLineRootElement(doc); ! if (line < lineRoot.getElementCount()) { ! Element lineElem = lineRoot.getElement(line); ! int lineStartOffset = lineElem.getStartOffset(); ! int lineLen = lineElem.getEndOffset() ! - lineStartOffset; ! if (lineLen >= 1) { // should always be at least '\n' ! col = Math.min(col, lineLen - 1); ! offset = lineStartOffset + col; ! } ! } ! } catch (IOException e) { ! // use offset in that case ! } ! } ! ! try { ! p = NbDocument.createPosition (doc, offset, ! argInsertAfter ? Position.Bias.Forward : Position.Bias.Backward); ! } catch (BadLocationException e) { ! p = doc.getEndPosition (); ! } ! retObject = (PositionKind)new PositionKind (p); ! break; } ! ! case POSITION_KIND_GET_LINE: { ! retInt = NbDocument.findLineNumber(doc, argKind.getOffset()); ! break; } ! ! case POSITION_KIND_GET_COLUMN: { ! retInt = NbDocument.findLineColumn(doc, argKind.getOffset()); ! break; } ! ! case POSITION_KIND_WRITE: ! case OUT_KIND_CONSTRUCTOR: { ! retInt = argKind.getOffset(); ! argLine = argKind.getLine(); ! argColumn = argKind.getColumn(); ! break; } ! ! case OFFSET_KIND_GET_LINE: { ! retInt = NbDocument.findLineNumber(getCloneableEditorSupport().openDocument(), argInt); ! break; } ! ! case OFFSET_KIND_GET_COLUMN: { ! retInt = NbDocument.findLineColumn (getCloneableEditorSupport().openDocument(), argInt); ! break; } ! ! case LINE_KIND_GET_OFFSET: { ! retInt = NbDocument.findLineOffset (argDoc, argLine) + argColumn; ! break; } ! ! case LINE_KIND_TO_MEMORY: { ! // try to find the right position ! try { ! retObject = NbDocument.createPosition (doc, ! NbDocument.findLineOffset (doc, argLine) + argColumn, ! argInsertAfter ? Position.Bias.Forward : Position.Bias.Backward); ! } catch (BadLocationException e) { ! retObject = doc.getEndPosition (); ! } catch (IndexOutOfBoundsException e) { ! retObject = doc.getEndPosition(); ! } ! break; } ! ! case PROCESS_POSITIONS: { ! synchronized(Manager.this) { ! ChainItem previous = head; ! ChainItem ref = previous.next; ! ! while(ref != null) { ! PositionRef pos = (PositionRef)ref.get(); ! if(pos == null) { ! // Remove the item from data structure. ! previous.next = ref.next; ! } else { ! // Process the PostionRef. ! if(argToMemory) { ! pos.kind = pos.kind.toMemory(pos.insertAfter); ! } else { ! pos.kind = pos.kind.fromMemory(); ! } ! ! previous = ref; ! } ! ! ref = ref.next; ! } ! } ! break; } ! ! case ADD_POSITION: { ! synchronized(Manager.this) { ! head.next = new ChainItem(argPos, queue, head.next); ! ! retObject = (doc == null ? ! argPos.kind : ! argPos.kind.toMemory(argPos.insertAfter)); ! } ! break; } + default: + throw new IllegalStateException(); // Unknown opcode + } + } catch (IOException e) { + ioException = e; + } + } + + } + + } }