alvinalexander.com | career | drupal | java | mac | mysql | perl | scala | uml | unix  

What this is

This file is included in the DevDaily.com "Java Source Code Warehouse" project. The intent of this project is to help you "Learn Java by Example" TM.

Other links

The source code

/*
 *                 Sun Public License Notice
 * 
 * The contents of this file are subject to the Sun Public License
 * Version 1.0 (the "License"). You may not use this file except in
 * compliance with the License. A copy of the License is available at
 * http://www.sun.com/
 * 
 * The Original Code is NetBeans. The Initial Developer of the Original
 * Code is Sun Microsystems, Inc. Portions Copyright 1997-2000 Sun
 * Microsystems, Inc. All Rights Reserved.
 */

package org.openide.text;

import org.openide.util.WeakListeners;
import java.io.*;
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;


/** Implementation of a line in a {@link StyledDocument}.
* One object
* of this class represents a line in the document by holding
* a {@link PositionRef}, which can represent a position in an open or
* closed document.
*
* @author Jaroslav Tulach, David Konecny
*/
public abstract class DocumentLine extends Line {
    /** reference to one position on the line */
    protected PositionRef pos;

    /** is breakpoint there - presistent state 
     @deprecated since 1.20 */
    private boolean breakpoint;

    /** error line  - transient state
     @deprecated since 1.20 */
    private transient boolean error;

    /** current line - transient state
     @deprecated since 1.20 */
    private transient boolean current;

    /** listener for changes of state of the document */
    private transient LR listener;

    /** weak document listener assigned to the document or null */
    private transient DocumentListener docL;

    /** weak map that assignes to editor supports whether they have current or error line
    * selected. (EditorSupport, DocumentLine[2]), where Line[0] is current and Line[1] is error */
    private static WeakHashMap assigned = new WeakHashMap (5);

    /** List of Line.Part which exist for this line*/
    private List lineParts = new ArrayList(3);
    
    static final long serialVersionUID =3213776466939427487L;
    
    
    /** Constructor.
    * @param obj context we belong to
    * @param pos position on the line
     *
     * @since 4.3
    */
    public DocumentLine (org.openide.util.Lookup obj, PositionRef pos) {
        super (obj);
        this.pos = pos;
    }

    /** Init listeners
    */
    void init () {
        listener = new LR ();
        pos.getCloneableEditorSupport ().addChangeListener (org.openide.util.WeakListeners.change (listener, pos.getCloneableEditorSupport ()));
    }

    /* Get the line number.
     * The number may change if the
    * text is modified.
    *
    * @return Returns current line number.
    */
    public int getLineNumber () {
        try {
            return pos.getLine ();
        } catch (IOException ex) {
            // what else?
            return 0;
        }
    }

    /* Shows the line.
    * @param kind one of SHOW_XXX constants.
    * @column the column of this line which should be selected
    */
    public abstract void show(int kind, int column);

    /* Sets the breakpoint. */
    public void setBreakpoint(boolean b) {
        if (breakpoint != b) {
            breakpoint = b;
            refreshState ();
        }
    }

    /* Tests if the breakpoint is set. */
    public boolean isBreakpoint () {
        return breakpoint;
    }

    /* Marks the error. */
    public void markError () {
        DocumentLine previous = registerLine (1, this);
        if (previous != null) {
            previous.error = false;
            previous.refreshState ();
        }

        error = true;

        refreshState ();
    }

    /* Unmarks error at this line. */
    public void unmarkError () {
        error = false;
        registerLine (1, null);

        refreshState ();
    }

    /* Marks this line as current. */
    public void markCurrentLine () {
        DocumentLine previous = registerLine (0, this);
        if (previous != null) {
            previous.current = false;
            previous.refreshState ();
        }

        current = true;
        refreshState ();
    }

    /* Unmarks this line as current. */
    public void unmarkCurrentLine () {
        current = false;
        registerLine (0, null);

        refreshState ();
    }

    /** Refreshes the current line.
     *
     * @deprecated since 1.20. */
    synchronized void refreshState () {
        StyledDocument doc = pos.getCloneableEditorSupport ().getDocument ();

        if (doc != null) {
            // the document is in memory, mark the state

            if (docL != null) {
                doc.removeDocumentListener (docL);
            }

            // error line
            if (error) {
                NbDocument.markError (doc, pos.getOffset ());

                doc.addDocumentListener (docL = org.openide.util.WeakListeners.document (listener, doc));

                return;
            }

            // current line
            if (current) {
                NbDocument.markCurrent (doc, pos.getOffset ());
                return;
            }

            // breakpoint line
            if (breakpoint) {
                NbDocument.markBreakpoint (doc, pos.getOffset ());
                return;
            }

            NbDocument.markNormal (doc, pos.getOffset ());
            return;
        }
    }

    public int hashCode () {
        return pos.getCloneableEditorSupport ().hashCode ();
    }

    public boolean equals (Object o) {
        if (o instanceof DocumentLine) {
            DocumentLine dl = (DocumentLine)o;
            if (dl.pos.getCloneableEditorSupport () == pos.getCloneableEditorSupport ()) {
                return dl.getLineNumber () == getLineNumber ();
            }
        }
        return false;
    }


    //
    // Work with global hash table
    //

    /** Register this line as the one stored
    * under indx-index (0 = current, 1 = error).
    *
    * @param indx index to register
    * @param line value to add (this or null)
    * @return the previous value
    *
    * @deprecated since 1.20 */
    private DocumentLine registerLine (int indx, DocumentLine line) {
        DocumentLine prev;

        CloneableEditorSupport es = pos.getCloneableEditorSupport ();

        DocumentLine[] arr = (DocumentLine[])assigned.get (es);

        if (arr != null) {
            // remember the previous
            prev = arr[indx];
        } else {
            // create new array
            arr = new DocumentLine[2];
            assigned.put (es, arr);
            prev = null;
        }
        arr[indx] = line;
        return prev;

    }


    //
    // Serialization
    //

    /** Write fields.
    */
    private void writeObject (ObjectOutputStream oos) throws IOException {
        // do not do default read/write object
        oos.writeObject (pos);
        oos.writeBoolean (breakpoint);
    }

    /** Read important fields.
    */
    private void readObject (ObjectInputStream ois)
    throws IOException, ClassNotFoundException {
        pos = (PositionRef)ois.readObject ();
        setBreakpoint (ois.readBoolean ());
        lineParts = new ArrayList(3);
    }

    /** Register line.
    */
    Object readResolve() throws ObjectStreamException {
//        return Set.registerLine (this);
        //Set.registerPendingLine(this);
        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) {
        super.addAnnotation(anno);
        StyledDocument doc = pos.getCloneableEditorSupport ().getDocument ();
        
        // document is not opened and so the annotation will be added to document later
        if (doc == null)
            return;

        pos.getCloneableEditorSupport().prepareDocument().waitFinished();
        
        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);
        }
    }
    
    /** Remove annotation to this Annotatable class
     * @param anno annotation which will be detached from this class  */
    protected void removeAnnotation(Annotation anno) {
        super.removeAnnotation(anno);
        StyledDocument doc = pos.getCloneableEditorSupport ().getDocument ();
        
        // document is not opened and so no annotation is attached to it
        if (doc == null)
            return;

        pos.getCloneableEditorSupport().prepareDocument().waitFinished();

        if (anno.isInDocument()) {
            anno.setInDocument(false);
            NbDocument.removeAnnotation(doc, anno);
        }
    }

    /** When document is opened or closed the annotations must be added or
     * removed.
     * @since 1.27 */
    void attachDetachAnnotations(StyledDocument doc, boolean closing) {
        // #33165 - find position that is surely at begining of line
        Position annoPos = null;
        if (!closing) {
            try {
                annoPos = pos.getPosition();
                FindAnnotationPosition fap = new FindAnnotationPosition(doc, annoPos);
                doc.render(fap);
                annoPos = fap.getAnnotationPosition();
            }  catch (IOException ex) {
                ErrorManager.getDefault ().notify ( ErrorManager.EXCEPTION, ex);
            }
        }

        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
        // (the current one could be the last one).
        int lineEnd;
        if((lineNumber + 1) 
        >= NbDocument.findLineRootElement(doc).getElementCount()) {
            lineEnd = doc.getLength();
        } else {
            lineEnd = NbDocument.findLineOffset(doc, lineNumber + 1);
        }
        
        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 */
    void addLinePart(DocumentLine.Part linePart) {
        lineParts.add(linePart);
    }

    /** Move Line.Part from this Line to a new one*/
    void moveLinePart(DocumentLine.Part linePart, DocumentLine newLine) {
        lineParts.remove(linePart);
        newLine.addLinePart(linePart);
        linePart.changeLine(newLine);
    }

    /** Notify Line.Part(s) that content of the line was changed and that Line.Part(s) may be affected by that*/
    void notifyChange(DocumentEvent p0, DocumentLine.Set set, StyledDocument doc) {
        DocumentLine.Part part;
        for (int i=0; i 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() {
        // #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
            );
            
            // fix of #32764
            List annos = getAnnotations();
            for (int i=0; i= previousOffset &&
                    p0.getOffset() < (previousOffset+getLength()) ) {
                    firePropertyChange(Annotatable.PROP_TEXT, null, null);
                }
            }
            if (p0.getType().equals(DocumentEvent.EventType.REMOVE)) {
                if ( (p0.getOffset() >= previousOffset && p0.getOffset() < previousOffset+getLength()) ||
                    (p0.getOffset() < previousOffset && p0.getOffset()+p0.getLength() > previousOffset) ) {
                    firePropertyChange(Annotatable.PROP_TEXT, null, null);
                }
            }
            if ((p0.getType().equals(DocumentEvent.EventType.INSERT) ||
                p0.getType().equals(DocumentEvent.EventType.REMOVE)) &&
                p0.getOffset() < previousOffset) {
                firePropertyChange(Line.Part.PROP_COLUMN, null, null);
            }
            previousOffset = position.getOffset();
        }

    }    
      

    /** Definition of actions performed in Listener */
    private final class LR implements Runnable, ChangeListener, DocumentListener {
        private static final int REFRESH = 0;
        private static final int UNMARK = 1;
        private static final int ATTACH_DETACH = 2;

        private int actionId;
        private EnhancedChangeEvent ev;
        
	public LR() {}

        public LR (int actionId) {
            this.actionId = actionId;
        }
        
        public LR (EnhancedChangeEvent ev) {
            this.actionId = ATTACH_DETACH;
            this.ev = ev;
        }

        public void run () {
            switch (actionId) {
                case REFRESH: refreshState (); break;
                case UNMARK:  unmarkError (); break;
                case ATTACH_DETACH: attachDetachAnnotations(ev.getDocument(), ev.isClosingDocument()); ev = null; break;
            }
        }

	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) {
            invoke(REFRESH);
            invoke((EnhancedChangeEvent)ev);
        }

        public void removeUpdate(final javax.swing.event.DocumentEvent p0) {
            invoke(UNMARK);
        }

        public void insertUpdate(final javax.swing.event.DocumentEvent p0) {
            invoke(UNMARK);
        }

        public void changedUpdate(final javax.swing.event.DocumentEvent p0) {
        }
    }
    
    /** Abstract implementation of {@link Line.Set}.
     *  Defines
    * ways to obtain a line set for documents following
    * NetBeans conventions.
    */
    public static abstract class Set extends Line.Set {
        /** listener on document changes, accessed from LazyLines */
        final LineListener listener;
        /** all lines in the set or null */
        private java.util.List list;

        
        /** Constructor.
        * @param doc document to work on
        */
        public Set (StyledDocument doc) {
            this(doc, null);
        }

        Set (StyledDocument doc, CloneableEditorSupport support) {
            listener = new LineListener (doc, support);
        }

        
        /** Find the line given as parameter in list of all lines attached to this set
         * and if the line exist in the list, notify it about being edited. */
        void linesChanged(int startLineNumber, int endLineNumber, DocumentEvent p0) {
            List changedLines = getLinesFromRange(startLineNumber, endLineNumber);
            
            for(Iterator it = changedLines.iterator(); it.hasNext(); ) {
                Line line = (Line)it.next();
                
                line.firePropertyChange(Annotatable.PROP_TEXT, null, null);

                // revalidate all parts attached to this line
                // that they are still part of the line
                if(line instanceof DocumentLine) {
                    ((DocumentLine)line).notifyChange(p0, this, listener.doc);
                }
            }
        }
        
        /** Find the line given as parameter in list of all lines attached to this set
         * and if the line exist in the list, notify it about being moved. */
        void linesMoved(int startLineNumber, int endLineNumber) {
            List movedLines = getLinesFromRange(startLineNumber, endLineNumber);
            
            for(Iterator it = movedLines.iterator(); it.hasNext(); ) {
                Line line = (Line)it.next();
                line.firePropertyChange(Line.PROP_LINE_NUMBER, null, null);

                // notify all parts attached to this line
                // that they were moved
                if (line instanceof DocumentLine) {
                    ((DocumentLine)line).notifyMove();
                } 
            }
        }
        
        /** Gets the lines with line number whitin the range inclusive.
         * @return List of lines from range inclusive */
        private List getLinesFromRange(int startLineNumber, int endLineNumber) {
            List linesInRange = new ArrayList(10);

            synchronized(findWeakHashMap ()) {
                for(Iterator it = findWeakHashMap ().keySet().iterator(); it.hasNext(); ) {
                    Line line = (Line)it.next();
                    int lineNumber = line.getLineNumber();
                    if(startLineNumber <= lineNumber
                    && lineNumber <= endLineNumber) {
                        linesInRange.add(line);
                    }
                }
            }
            
            return linesInRange;
        }

        /* Returns an unmodifiable set of Lines sorted by their
        * line numbers that contains all lines holded by this
        * Line.Set.
        *
        * @return list of Line objects
        */
        public synchronized java.util.List getLines () {
            if (list == null) {
                list = new LazyLines (this);
            }
            return list;
        }

        /* Finder method that for the given line number finds right
        * Line object that represent as closely as possible the line number
        * in the time when the Line.Set has been created.
        *
        * @param line is a number of the line (text line) we want to acquire
        * @exception IndexOutOfBoundsException if line is invalid.
        */
        public Line getOriginal (int line) throws IndexOutOfBoundsException {
            int newLine = listener.getLine (line);
            int offset = NbDocument.findLineOffset (listener.doc, newLine);

            return safelyRegisterLine(createLine(offset));
        }

        
        public int getOriginalLineNumber (Line line) {
            Line find = findLine (line);
            if (find != null) {
                return listener.getOld (find.getLineNumber ());
            } else {
                return -1;
            }
        }
        
        /* Creates current line.
        *
        * @param line is a number of the line (text line) we want to acquire
        * @exception IndexOutOfBoundsException if line is invalid.
        */
        public Line getCurrent (int line) throws IndexOutOfBoundsException {
            int offset = NbDocument.findLineOffset (listener.doc, line);

            return safelyRegisterLine(createLine(offset));
        }

        /** Creates a {@link Line} for a given offset.
        * @param offset the beginning offset of the line
        * @return line object representing the line at this offset
        */
        protected abstract Line createLine (int offset);

        /** Registers line, but only after obtaining the lock of the document.
         * This is a fix to issue 37767 as this creates ordering of locks (first 
         * of all obtain documentrenderer, then ask for any other locks like
         * Line.Set.lines.
         *
         * @param line line we want to register
         * @return the line or some line that already was registered
         */
        private Line safelyRegisterLine (final Line line) {
            class DocumentRenderer implements Runnable {
                public Line result;
                
                public void run () {
                    result = DocumentLine.Set.super.registerLine (line);
                }
            }
            DocumentRenderer renderer = new DocumentRenderer ();
            listener.doc.render (renderer);
            return renderer.result;
        }
    }


}
... this post is sponsored by my books ...

#1 New Release!

FP Best Seller

 

new blog posts

 

Copyright 1998-2021 Alvin Alexander, alvinalexander.com
All Rights Reserved.

A percentage of advertising revenue from
pages under the /java/jwarehouse URI on this website is
paid back to open source projects.