3118 lines · 108.4 KB · Java
RawDownload
/* * Copyright (c) 1997, 2008, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation.  Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */package javax.swing.text; import java.util.*;import java.io.*;import java.awt.font.TextAttribute;import java.text.Bidi; import javax.swing.UIManager;import javax.swing.undo.*;import javax.swing.event.ChangeListener;import javax.swing.event.*;import javax.swing.tree.TreeNode; import sun.font.BidiUtils;import sun.swing.SwingUtilities2; /** * An implementation of the document interface to serve as a * basis for implementing various kinds of documents.  At this * level there is very little policy, so there is a corresponding * increase in difficulty of use. * <p> * This class implements a locking mechanism for the document.  It * allows multiple readers or one writer, and writers must wait until * all observers of the document have been notified of a previous * change before beginning another mutation to the document.  The * read lock is acquired and released using the <code>render</code> * method.  A write lock is aquired by the methods that mutate the * document, and are held for the duration of the method call. * Notification is done on the thread that produced the mutation, * and the thread has full read access to the document for the * duration of the notification, but other readers are kept out * until the notification has finished.  The notification is a * beans event notification which does not allow any further * mutations until all listeners have been notified. * <p> * Any models subclassed from this class and used in conjunction * with a text component that has a look and feel implementation * that is derived from BasicTextUI may be safely updated * asynchronously, because all access to the View hierarchy * is serialized by BasicTextUI if the document is of type * <code>AbstractDocument</code>.  The locking assumes that an * independent thread will access the View hierarchy only from * the DocumentListener methods, and that there will be only * one event thread active at a time. * <p> * If concurrency support is desired, there are the following * additional implications.  The code path for any DocumentListener * implementation and any UndoListener implementation must be threadsafe, * and not access the component lock if trying to be safe from deadlocks. * The <code>repaint</code> and <code>revalidate</code> methods * on JComponent are safe. * <p> * AbstractDocument models an implied break at the end of the document. * Among other things this allows you to position the caret after the last * character. As a result of this, <code>getLength</code> returns one less * than the length of the Content. If you create your own Content, be * sure and initialize it to have an additional character. Refer to * StringContent and GapContent for examples of this. Another implication * of this is that Elements that model the implied end character will have * an endOffset == (getLength() + 1). For example, in DefaultStyledDocument * <code>getParagraphElement(getLength()).getEndOffset() == getLength() + 1 * </code>. * <p> * <strong>Warning:</strong> * Serialized objects of this class will not be compatible with * future Swing releases. The current serialization support is * appropriate for short term storage or RMI between applications running * the same version of Swing.  As of 1.4, support for long term storage * of all JavaBeans<sup><font size="-2">TM</font></sup> * has been added to the <code>java.beans</code> package. * Please see {@link java.beans.XMLEncoder}. * * @author  Timothy Prinzing */public abstract class AbstractDocument implements Document, Serializable {     /**     * Constructs a new <code>AbstractDocument</code>, wrapped around some     * specified content storage mechanism.     *     * @param data the content     */    protected AbstractDocument(Content data) {        this(data, StyleContext.getDefaultStyleContext());    }     /**     * Constructs a new <code>AbstractDocument</code>, wrapped around some     * specified content storage mechanism.     *     * @param data the content     * @param context the attribute context     */    protected AbstractDocument(Content data, AttributeContext context) {        this.data = data;        this.context = context;        bidiRoot = new BidiRootElement();         if (defaultI18NProperty == null) {            // determine default setting for i18n support            String o = java.security.AccessController.doPrivileged(                new java.security.PrivilegedAction<String>() {                    public String run() {                        return System.getProperty(I18NProperty);                    }                }            );            if (o != null) {                defaultI18NProperty = Boolean.valueOf(o);            } else {                defaultI18NProperty = Boolean.FALSE;            }        }        putProperty( I18NProperty, defaultI18NProperty);         //REMIND(bcb) This creates an initial bidi element to account for        //the \n that exists by default in the content.  Doing it this way        //seems to expose a little too much knowledge of the content given        //to us by the sub-class.  Consider having the sub-class' constructor        //make an initial call to insertUpdate.        writeLock();        try {            Element[] p = new Element[1];            p[0] = new BidiElement( bidiRoot, 0, 1, 0 );            bidiRoot.replace(0,0,p);        } finally {            writeUnlock();        }    }     /**     * Supports managing a set of properties. Callers     * can use the <code>documentProperties</code> dictionary     * to annotate the document with document-wide properties.     *     * @return a non-<code>null</code> <code>Dictionary</code>     * @see #setDocumentProperties     */    public Dictionary<Object,Object> getDocumentProperties() {        if (documentProperties == null) {            documentProperties = new Hashtable<Object, Object>(2);        }        return documentProperties;    }     /**     * Replaces the document properties dictionary for this document.     *     * @param x the new dictionary     * @see #getDocumentProperties     */    public void setDocumentProperties(Dictionary<Object,Object> x) {        documentProperties = x;    }     /**     * Notifies all listeners that have registered interest for     * notification on this event type.  The event instance     * is lazily created using the parameters passed into     * the fire method.     *     * @param e the event     * @see EventListenerList     */    protected void fireInsertUpdate(DocumentEvent e) {        notifyingListeners = true;        try {            // Guaranteed to return a non-null array            Object[] listeners = listenerList.getListenerList();            // Process the listeners last to first, notifying            // those that are interested in this event            for (int i = listeners.length-2; i>=0; i-=2) {                if (listeners[i]==DocumentListener.class) {                    // Lazily create the event:                    // if (e == null)                    // e = new ListSelectionEvent(this, firstIndex, lastIndex);                    ((DocumentListener)listeners[i+1]).insertUpdate(e);                }            }        } finally {            notifyingListeners = false;        }    }     /**     * Notifies all listeners that have registered interest for     * notification on this event type.  The event instance     * is lazily created using the parameters passed into     * the fire method.     *     * @param e the event     * @see EventListenerList     */    protected void fireChangedUpdate(DocumentEvent e) {        notifyingListeners = true;        try {            // Guaranteed to return a non-null array            Object[] listeners = listenerList.getListenerList();            // Process the listeners last to first, notifying            // those that are interested in this event            for (int i = listeners.length-2; i>=0; i-=2) {                if (listeners[i]==DocumentListener.class) {                    // Lazily create the event:                    // if (e == null)                    // e = new ListSelectionEvent(this, firstIndex, lastIndex);                    ((DocumentListener)listeners[i+1]).changedUpdate(e);                }            }        } finally {            notifyingListeners = false;        }    }     /**     * Notifies all listeners that have registered interest for     * notification on this event type.  The event instance     * is lazily created using the parameters passed into     * the fire method.     *     * @param e the event     * @see EventListenerList     */    protected void fireRemoveUpdate(DocumentEvent e) {        notifyingListeners = true;        try {            // Guaranteed to return a non-null array            Object[] listeners = listenerList.getListenerList();            // Process the listeners last to first, notifying            // those that are interested in this event            for (int i = listeners.length-2; i>=0; i-=2) {                if (listeners[i]==DocumentListener.class) {                    // Lazily create the event:                    // if (e == null)                    // e = new ListSelectionEvent(this, firstIndex, lastIndex);                    ((DocumentListener)listeners[i+1]).removeUpdate(e);                }            }        } finally {            notifyingListeners = false;        }    }     /**     * Notifies all listeners that have registered interest for     * notification on this event type.  The event instance     * is lazily created using the parameters passed into     * the fire method.     *     * @param e the event     * @see EventListenerList     */    protected void fireUndoableEditUpdate(UndoableEditEvent e) {        // Guaranteed to return a non-null array        Object[] listeners = listenerList.getListenerList();        // Process the listeners last to first, notifying        // those that are interested in this event        for (int i = listeners.length-2; i>=0; i-=2) {            if (listeners[i]==UndoableEditListener.class) {                // Lazily create the event:                // if (e == null)                // e = new ListSelectionEvent(this, firstIndex, lastIndex);                ((UndoableEditListener)listeners[i+1]).undoableEditHappened(e);            }        }    }     /**     * Returns an array of all the objects currently registered     * as <code><em>Foo</em>Listener</code>s     * upon this document.     * <code><em>Foo</em>Listener</code>s are registered using the     * <code>add<em>Foo</em>Listener</code> method.     *     * <p>     * You can specify the <code>listenerType</code> argument     * with a class literal, such as     * <code><em>Foo</em>Listener.class</code>.     * For example, you can query a     * document <code>d</code>     * for its document listeners with the following code:     *     * <pre>DocumentListener[] mls = (DocumentListener[])(d.getListeners(DocumentListener.class));</pre>     *     * If no such listeners exist, this method returns an empty array.     *     * @param listenerType the type of listeners requested; this parameter     *          should specify an interface that descends from     *          <code>java.util.EventListener</code>     * @return an array of all objects registered as     *          <code><em>Foo</em>Listener</code>s on this component,     *          or an empty array if no such     *          listeners have been added     * @exception ClassCastException if <code>listenerType</code>     *          doesn't specify a class or interface that implements     *          <code>java.util.EventListener</code>     *     * @see #getDocumentListeners     * @see #getUndoableEditListeners     *     * @since 1.3     */    public <T extends EventListener> T[] getListeners(Class<T> listenerType) {        return listenerList.getListeners(listenerType);    }     /**     * Gets the asynchronous loading priority.  If less than zero,     * the document should not be loaded asynchronously.     *     * @return the asynchronous loading priority, or <code>-1</code>     *   if the document should not be loaded asynchronously     */    public int getAsynchronousLoadPriority() {        Integer loadPriority = (Integer)            getProperty(AbstractDocument.AsyncLoadPriority);        if (loadPriority != null) {            return loadPriority.intValue();        }        return -1;    }     /**     * Sets the asynchronous loading priority.     * @param p the new asynchronous loading priority; a value     *   less than zero indicates that the document should not be     *   loaded asynchronously     */    public void setAsynchronousLoadPriority(int p) {        Integer loadPriority = (p >= 0) ? Integer.valueOf(p) : null;        putProperty(AbstractDocument.AsyncLoadPriority, loadPriority);    }     /**     * Sets the <code>DocumentFilter</code>. The <code>DocumentFilter</code>     * is passed <code>insert</code> and <code>remove</code> to conditionally     * allow inserting/deleting of the text.  A <code>null</code> value     * indicates that no filtering will occur.     *     * @param filter the <code>DocumentFilter</code> used to constrain text     * @see #getDocumentFilter     * @since 1.4     */    public void setDocumentFilter(DocumentFilter filter) {        documentFilter = filter;    }     /**     * Returns the <code>DocumentFilter</code> that is responsible for     * filtering of insertion/removal. A <code>null</code> return value     * implies no filtering is to occur.     *     * @since 1.4     * @see #setDocumentFilter     * @return the DocumentFilter     */    public DocumentFilter getDocumentFilter() {        return documentFilter;    }     // --- Document methods -----------------------------------------     /**     * This allows the model to be safely rendered in the presence     * of currency, if the model supports being updated asynchronously.     * The given runnable will be executed in a way that allows it     * to safely read the model with no changes while the runnable     * is being executed.  The runnable itself may <em>not</em>     * make any mutations.     * <p>     * This is implemented to aquire a read lock for the duration     * of the runnables execution.  There may be multiple runnables     * executing at the same time, and all writers will be blocked     * while there are active rendering runnables.  If the runnable     * throws an exception, its lock will be safely released.     * There is no protection against a runnable that never exits,     * which will effectively leave the document locked for it's     * lifetime.     * <p>     * If the given runnable attempts to make any mutations in     * this implementation, a deadlock will occur.  There is     * no tracking of individual rendering threads to enable     * detecting this situation, but a subclass could incur     * the overhead of tracking them and throwing an error.     * <p>     * This method is thread safe, although most Swing methods     * are not. Please see     * <A HREF="http://java.sun.com/docs/books/tutorial/uiswing/misc/threads.html">How     * to Use Threads</A> for more information.     *     * @param r the renderer to execute     */    public void render(Runnable r) {        readLock();        try {            r.run();        } finally {            readUnlock();        }    }     /**     * Returns the length of the data.  This is the number of     * characters of content that represents the users data.     *     * @return the length >= 0     * @see Document#getLength     */    public int getLength() {        return data.length() - 1;    }     /**     * Adds a document listener for notification of any changes.     *     * @param listener the <code>DocumentListener</code> to add     * @see Document#addDocumentListener     */    public void addDocumentListener(DocumentListener listener) {        listenerList.add(DocumentListener.class, listener);    }     /**     * Removes a document listener.     *     * @param listener the <code>DocumentListener</code> to remove     * @see Document#removeDocumentListener     */    public void removeDocumentListener(DocumentListener listener) {        listenerList.remove(DocumentListener.class, listener);    }     /**     * Returns an array of all the document listeners     * registered on this document.     *     * @return all of this document's <code>DocumentListener</code>s     *         or an empty array if no document listeners are     *         currently registered     *     * @see #addDocumentListener     * @see #removeDocumentListener     * @since 1.4     */    public DocumentListener[] getDocumentListeners() {        return listenerList.getListeners(DocumentListener.class);    }     /**     * Adds an undo listener for notification of any changes.     * Undo/Redo operations performed on the <code>UndoableEdit</code>     * will cause the appropriate DocumentEvent to be fired to keep     * the view(s) in sync with the model.     *     * @param listener the <code>UndoableEditListener</code> to add     * @see Document#addUndoableEditListener     */    public void addUndoableEditListener(UndoableEditListener listener) {        listenerList.add(UndoableEditListener.class, listener);    }     /**     * Removes an undo listener.     *     * @param listener the <code>UndoableEditListener</code> to remove     * @see Document#removeDocumentListener     */    public void removeUndoableEditListener(UndoableEditListener listener) {        listenerList.remove(UndoableEditListener.class, listener);    }     /**     * Returns an array of all the undoable edit listeners     * registered on this document.     *     * @return all of this document's <code>UndoableEditListener</code>s

Lines 1–500 of 3118.