Source Code Cross Referenced for AbstractDocument.java in  » 6.0-JDK-Core » swing » javax » swing » text » Java Source Code / Java DocumentationJava Source Code and Java Documentation

Home
Java Source Code / Java Documentation
1.6.0 JDK Core
2.6.0 JDK Modules
3.6.0 JDK Modules com.sun
4.6.0 JDK Modules com.sun.java
5.6.0 JDK Modules sun
6.6.0 JDK Platform
7.Ajax
8.Apache Harmony Java SE
9.Aspect oriented
10.Authentication Authorization
11.Blogger System
12.Build
13.Byte Code
14.Cache
15.Chart
16.Chat
17.Code Analyzer
18.Collaboration
19.Content Management System
20.Database Client
21.Database DBMS
22.Database JDBC Connection Pool
23.Database ORM
24.Development
25.EJB Server
26.ERP CRM Financial
27.ESB
28.Forum
29.Game
30.GIS
31.Graphic 3D
32.Graphic Library
33.Groupware
34.HTML Parser
35.IDE
36.IDE Eclipse
37.IDE Netbeans
38.Installer
39.Internationalization Localization
40.Inversion of Control
41.Issue Tracking
42.J2EE
43.J2ME
44.JBoss
45.JMS
46.JMX
47.Library
48.Mail Clients
49.Music
50.Net
51.Parser
52.PDF
53.Portal
54.Profiler
55.Project Management
56.Report
57.RSS RDF
58.Rule Engine
59.Science
60.Scripting
61.Search Engine
62.Security
63.Sevlet Container
64.Source Control
65.Swing Library
66.Template Engine
67.Test Coverage
68.Testing
69.UML
70.Web Crawler
71.Web Framework
72.Web Mail
73.Web Server
74.Web Services
75.Web Services apache cxf 2.2.6
76.Web Services AXIS2
77.Wiki Engine
78.Workflow Engines
79.XML
80.XML UI
Java Source Code / Java Documentation » 6.0 JDK Core » swing » javax.swing.text 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


0001        /*
0002         * Copyright 1997-2006 Sun Microsystems, Inc.  All Rights Reserved.
0003         * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
0004         *
0005         * This code is free software; you can redistribute it and/or modify it
0006         * under the terms of the GNU General Public License version 2 only, as
0007         * published by the Free Software Foundation.  Sun designates this
0008         * particular file as subject to the "Classpath" exception as provided
0009         * by Sun in the LICENSE file that accompanied this code.
0010         *
0011         * This code is distributed in the hope that it will be useful, but WITHOUT
0012         * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
0013         * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
0014         * version 2 for more details (a copy is included in the LICENSE file that
0015         * accompanied this code).
0016         *
0017         * You should have received a copy of the GNU General Public License version
0018         * 2 along with this work; if not, write to the Free Software Foundation,
0019         * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
0020         *
0021         * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
0022         * CA 95054 USA or visit www.sun.com if you need additional information or
0023         * have any questions.
0024         */
0025        package javax.swing.text;
0026
0027        import java.util.*;
0028        import java.io.*;
0029        import java.awt.font.TextAttribute;
0030        import java.text.Bidi;
0031
0032        import javax.swing.UIManager;
0033        import javax.swing.undo.*;
0034        import javax.swing.event.ChangeListener;
0035        import javax.swing.event.*;
0036        import javax.swing.tree.TreeNode;
0037
0038        import sun.font.BidiUtils;
0039        import sun.swing.SwingUtilities2;
0040
0041        /**
0042         * An implementation of the document interface to serve as a 
0043         * basis for implementing various kinds of documents.  At this
0044         * level there is very little policy, so there is a corresponding
0045         * increase in difficulty of use.
0046         * <p>
0047         * This class implements a locking mechanism for the document.  It
0048         * allows multiple readers or one writer, and writers must wait until 
0049         * all observers of the document have been notified of a previous 
0050         * change before beginning another mutation to the document.  The
0051         * read lock is acquired and released using the <code>render</code>
0052         * method.  A write lock is aquired by the methods that mutate the
0053         * document, and are held for the duration of the method call.
0054         * Notification is done on the thread that produced the mutation, 
0055         * and the thread has full read access to the document for the
0056         * duration of the notification, but other readers are kept out
0057         * until the notification has finished.  The notification is a
0058         * beans event notification which does not allow any further 
0059         * mutations until all listeners have been notified.
0060         * <p>
0061         * Any models subclassed from this class and used in conjunction
0062         * with a text component that has a look and feel implementation
0063         * that is derived from BasicTextUI may be safely updated
0064         * asynchronously, because all access to the View hierarchy
0065         * is serialized by BasicTextUI if the document is of type
0066         * <code>AbstractDocument</code>.  The locking assumes that an
0067         * independent thread will access the View hierarchy only from
0068         * the DocumentListener methods, and that there will be only
0069         * one event thread active at a time.
0070         * <p>
0071         * If concurrency support is desired, there are the following 
0072         * additional implications.  The code path for any DocumentListener 
0073         * implementation and any UndoListener implementation must be threadsafe, 
0074         * and not access the component lock if trying to be safe from deadlocks.  
0075         * The <code>repaint</code> and <code>revalidate</code> methods 
0076         * on JComponent are safe.
0077         * <p>
0078         * AbstractDocument models an implied break at the end of the document.
0079         * Among other things this allows you to position the caret after the last
0080         * character. As a result of this, <code>getLength</code> returns one less
0081         * than the length of the Content. If you create your own Content, be
0082         * sure and initialize it to have an additional character. Refer to
0083         * StringContent and GapContent for examples of this. Another implication
0084         * of this is that Elements that model the implied end character will have
0085         * an endOffset == (getLength() + 1). For example, in DefaultStyledDocument
0086         * <code>getParagraphElement(getLength()).getEndOffset() == getLength() + 1
0087         * </code>.
0088         * <p>
0089         * <strong>Warning:</strong>
0090         * Serialized objects of this class will not be compatible with
0091         * future Swing releases. The current serialization support is
0092         * appropriate for short term storage or RMI between applications running
0093         * the same version of Swing.  As of 1.4, support for long term storage
0094         * of all JavaBeans<sup><font size="-2">TM</font></sup>
0095         * has been added to the <code>java.beans</code> package.
0096         * Please see {@link java.beans.XMLEncoder}.
0097         *
0098         * @author  Timothy Prinzing
0099         * @version 1.163 05/05/07
0100         */
0101        public abstract class AbstractDocument implements  Document,
0102                Serializable {
0103
0104            /**
0105             * Constructs a new <code>AbstractDocument</code>, wrapped around some
0106             * specified content storage mechanism.
0107             *
0108             * @param data the content
0109             */
0110            protected AbstractDocument(Content data) {
0111                this (data, StyleContext.getDefaultStyleContext());
0112            }
0113
0114            /**
0115             * Constructs a new <code>AbstractDocument</code>, wrapped around some
0116             * specified content storage mechanism.
0117             *
0118             * @param data the content
0119             * @param context the attribute context
0120             */
0121            protected AbstractDocument(Content data, AttributeContext context) {
0122                this .data = data;
0123                this .context = context;
0124                bidiRoot = new BidiRootElement();
0125
0126                if (defaultI18NProperty == null) {
0127                    // determine default setting for i18n support
0128                    Object o = java.security.AccessController
0129                            .doPrivileged(new java.security.PrivilegedAction() {
0130                                public Object run() {
0131                                    return System.getProperty(I18NProperty);
0132                                }
0133                            });
0134                    if (o != null) {
0135                        defaultI18NProperty = Boolean.valueOf((String) o);
0136                    } else {
0137                        defaultI18NProperty = Boolean.FALSE;
0138                    }
0139                }
0140                putProperty(I18NProperty, defaultI18NProperty);
0141
0142                //REMIND(bcb) This creates an initial bidi element to account for
0143                //the \n that exists by default in the content.  Doing it this way
0144                //seems to expose a little too much knowledge of the content given
0145                //to us by the sub-class.  Consider having the sub-class' constructor
0146                //make an initial call to insertUpdate.
0147                writeLock();
0148                try {
0149                    Element[] p = new Element[1];
0150                    p[0] = new BidiElement(bidiRoot, 0, 1, 0);
0151                    bidiRoot.replace(0, 0, p);
0152                } finally {
0153                    writeUnlock();
0154                }
0155            }
0156
0157            /**
0158             * Supports managing a set of properties. Callers
0159             * can use the <code>documentProperties</code> dictionary
0160             * to annotate the document with document-wide properties.
0161             * 
0162             * @return a non-<code>null</code> <code>Dictionary</code>
0163             * @see #setDocumentProperties
0164             */
0165            public Dictionary<Object, Object> getDocumentProperties() {
0166                if (documentProperties == null) {
0167                    documentProperties = new Hashtable(2);
0168                }
0169                return documentProperties;
0170            }
0171
0172            /**
0173             * Replaces the document properties dictionary for this document.
0174             * 
0175             * @param x the new dictionary
0176             * @see #getDocumentProperties
0177             */
0178            public void setDocumentProperties(Dictionary<Object, Object> x) {
0179                documentProperties = x;
0180            }
0181
0182            /**
0183             * Notifies all listeners that have registered interest for
0184             * notification on this event type.  The event instance 
0185             * is lazily created using the parameters passed into 
0186             * the fire method.
0187             *
0188             * @param e the event
0189             * @see EventListenerList
0190             */
0191            protected void fireInsertUpdate(DocumentEvent e) {
0192                notifyingListeners = true;
0193                try {
0194                    // Guaranteed to return a non-null array
0195                    Object[] listeners = listenerList.getListenerList();
0196                    // Process the listeners last to first, notifying
0197                    // those that are interested in this event
0198                    for (int i = listeners.length - 2; i >= 0; i -= 2) {
0199                        if (listeners[i] == DocumentListener.class) {
0200                            // Lazily create the event:
0201                            // if (e == null)
0202                            // e = new ListSelectionEvent(this, firstIndex, lastIndex);
0203                            ((DocumentListener) listeners[i + 1])
0204                                    .insertUpdate(e);
0205                        }
0206                    }
0207                } finally {
0208                    notifyingListeners = false;
0209                }
0210            }
0211
0212            /**
0213             * Notifies all listeners that have registered interest for
0214             * notification on this event type.  The event instance 
0215             * is lazily created using the parameters passed into 
0216             * the fire method.
0217             *
0218             * @param e the event
0219             * @see EventListenerList
0220             */
0221            protected void fireChangedUpdate(DocumentEvent e) {
0222                notifyingListeners = true;
0223                try {
0224                    // Guaranteed to return a non-null array
0225                    Object[] listeners = listenerList.getListenerList();
0226                    // Process the listeners last to first, notifying
0227                    // those that are interested in this event
0228                    for (int i = listeners.length - 2; i >= 0; i -= 2) {
0229                        if (listeners[i] == DocumentListener.class) {
0230                            // Lazily create the event:
0231                            // if (e == null)
0232                            // e = new ListSelectionEvent(this, firstIndex, lastIndex);
0233                            ((DocumentListener) listeners[i + 1])
0234                                    .changedUpdate(e);
0235                        }
0236                    }
0237                } finally {
0238                    notifyingListeners = false;
0239                }
0240            }
0241
0242            /**
0243             * Notifies all listeners that have registered interest for
0244             * notification on this event type.  The event instance 
0245             * is lazily created using the parameters passed into 
0246             * the fire method.
0247             *
0248             * @param e the event
0249             * @see EventListenerList
0250             */
0251            protected void fireRemoveUpdate(DocumentEvent e) {
0252                notifyingListeners = true;
0253                try {
0254                    // Guaranteed to return a non-null array
0255                    Object[] listeners = listenerList.getListenerList();
0256                    // Process the listeners last to first, notifying
0257                    // those that are interested in this event
0258                    for (int i = listeners.length - 2; i >= 0; i -= 2) {
0259                        if (listeners[i] == DocumentListener.class) {
0260                            // Lazily create the event:
0261                            // if (e == null)
0262                            // e = new ListSelectionEvent(this, firstIndex, lastIndex);
0263                            ((DocumentListener) listeners[i + 1])
0264                                    .removeUpdate(e);
0265                        }
0266                    }
0267                } finally {
0268                    notifyingListeners = false;
0269                }
0270            }
0271
0272            /**
0273             * Notifies all listeners that have registered interest for
0274             * notification on this event type.  The event instance 
0275             * is lazily created using the parameters passed into 
0276             * the fire method.
0277             *
0278             * @param e the event
0279             * @see EventListenerList
0280             */
0281            protected void fireUndoableEditUpdate(UndoableEditEvent e) {
0282                // Guaranteed to return a non-null array
0283                Object[] listeners = listenerList.getListenerList();
0284                // Process the listeners last to first, notifying
0285                // those that are interested in this event
0286                for (int i = listeners.length - 2; i >= 0; i -= 2) {
0287                    if (listeners[i] == UndoableEditListener.class) {
0288                        // Lazily create the event:
0289                        // if (e == null)
0290                        // e = new ListSelectionEvent(this, firstIndex, lastIndex);
0291                        ((UndoableEditListener) listeners[i + 1])
0292                                .undoableEditHappened(e);
0293                    }
0294                }
0295            }
0296
0297            /**
0298             * Returns an array of all the objects currently registered
0299             * as <code><em>Foo</em>Listener</code>s
0300             * upon this document.
0301             * <code><em>Foo</em>Listener</code>s are registered using the
0302             * <code>add<em>Foo</em>Listener</code> method.
0303             *
0304             * <p>
0305             * You can specify the <code>listenerType</code> argument
0306             * with a class literal, such as
0307             * <code><em>Foo</em>Listener.class</code>.
0308             * For example, you can query a
0309             * document <code>d</code>
0310             * for its document listeners with the following code:
0311             *
0312             * <pre>DocumentListener[] mls = (DocumentListener[])(d.getListeners(DocumentListener.class));</pre>
0313             *
0314             * If no such listeners exist, this method returns an empty array.
0315             *
0316             * @param listenerType the type of listeners requested; this parameter
0317             *          should specify an interface that descends from
0318             *          <code>java.util.EventListener</code>
0319             * @return an array of all objects registered as
0320             *          <code><em>Foo</em>Listener</code>s on this component,
0321             *          or an empty array if no such
0322             *          listeners have been added
0323             * @exception ClassCastException if <code>listenerType</code>
0324             *          doesn't specify a class or interface that implements
0325             *          <code>java.util.EventListener</code>
0326             *
0327             * @see #getDocumentListeners
0328             * @see #getUndoableEditListeners
0329             *
0330             * @since 1.3
0331             */
0332            public <T extends EventListener> T[] getListeners(
0333                    Class<T> listenerType) {
0334                return listenerList.getListeners(listenerType);
0335            }
0336
0337            /**
0338             * Gets the asynchronous loading priority.  If less than zero,
0339             * the document should not be loaded asynchronously.
0340             *
0341             * @return the asynchronous loading priority, or <code>-1</code>
0342             *   if the document should not be loaded asynchronously
0343             */
0344            public int getAsynchronousLoadPriority() {
0345                Integer loadPriority = (Integer) getProperty(AbstractDocument.AsyncLoadPriority);
0346                if (loadPriority != null) {
0347                    return loadPriority.intValue();
0348                }
0349                return -1;
0350            }
0351
0352            /**
0353             * Sets the asynchronous loading priority. 
0354             * @param p the new asynchronous loading priority; a value
0355             *   less than zero indicates that the document should not be
0356             *   loaded asynchronously
0357             */
0358            public void setAsynchronousLoadPriority(int p) {
0359                Integer loadPriority = (p >= 0) ? new Integer(p) : null;
0360                putProperty(AbstractDocument.AsyncLoadPriority, loadPriority);
0361            }
0362
0363            /**
0364             * Sets the <code>DocumentFilter</code>. The <code>DocumentFilter</code>
0365             * is passed <code>insert</code> and <code>remove</code> to conditionally
0366             * allow inserting/deleting of the text.  A <code>null</code> value
0367             * indicates that no filtering will occur.
0368             *
0369             * @param filter the <code>DocumentFilter</code> used to constrain text
0370             * @see #getDocumentFilter
0371             * @since 1.4
0372             */
0373            public void setDocumentFilter(DocumentFilter filter) {
0374                documentFilter = filter;
0375            }
0376
0377            /**
0378             * Returns the <code>DocumentFilter</code> that is responsible for
0379             * filtering of insertion/removal. A <code>null</code> return value 
0380             * implies no filtering is to occur.
0381             *
0382             * @since 1.4
0383             * @see #setDocumentFilter
0384             * @return the DocumentFilter
0385             */
0386            public DocumentFilter getDocumentFilter() {
0387                return documentFilter;
0388            }
0389
0390            // --- Document methods -----------------------------------------
0391
0392            /**
0393             * This allows the model to be safely rendered in the presence
0394             * of currency, if the model supports being updated asynchronously.
0395             * The given runnable will be executed in a way that allows it
0396             * to safely read the model with no changes while the runnable
0397             * is being executed.  The runnable itself may <em>not</em>
0398             * make any mutations. 
0399             * <p>
0400             * This is implemented to aquire a read lock for the duration
0401             * of the runnables execution.  There may be multiple runnables
0402             * executing at the same time, and all writers will be blocked
0403             * while there are active rendering runnables.  If the runnable
0404             * throws an exception, its lock will be safely released.
0405             * There is no protection against a runnable that never exits,
0406             * which will effectively leave the document locked for it's
0407             * lifetime.
0408             * <p>
0409             * If the given runnable attempts to make any mutations in
0410             * this implementation, a deadlock will occur.  There is
0411             * no tracking of individual rendering threads to enable
0412             * detecting this situation, but a subclass could incur
0413             * the overhead of tracking them and throwing an error.
0414             * <p>
0415             * This method is thread safe, although most Swing methods
0416             * are not. Please see 
0417             * <A HREF="http://java.sun.com/docs/books/tutorial/uiswing/misc/threads.html">How
0418             * to Use Threads</A> for more information.
0419             *
0420             * @param r the renderer to execute
0421             */
0422            public void render(Runnable r) {
0423                readLock();
0424                try {
0425                    r.run();
0426                } finally {
0427                    readUnlock();
0428                }
0429            }
0430
0431            /**
0432             * Returns the length of the data.  This is the number of
0433             * characters of content that represents the users data.
0434             *
0435             * @return the length >= 0
0436             * @see Document#getLength
0437             */
0438            public int getLength() {
0439                return data.length() - 1;
0440            }
0441
0442            /**
0443             * Adds a document listener for notification of any changes.
0444             *
0445             * @param listener the <code>DocumentListener</code> to add
0446             * @see Document#addDocumentListener
0447             */
0448            public void addDocumentListener(DocumentListener listener) {
0449                listenerList.add(DocumentListener.class, listener);
0450            }
0451
0452            /**
0453             * Removes a document listener.
0454             *
0455             * @param listener the <code>DocumentListener</code> to remove
0456             * @see Document#removeDocumentListener
0457             */
0458            public void removeDocumentListener(DocumentListener listener) {
0459                listenerList.remove(DocumentListener.class, listener);
0460            }
0461
0462            /**
0463             * Returns an array of all the document listeners
0464             * registered on this document.
0465             *
0466             * @return all of this document's <code>DocumentListener</code>s 
0467             *         or an empty array if no document listeners are
0468             *         currently registered
0469             *
0470             * @see #addDocumentListener
0471             * @see #removeDocumentListener
0472             * @since 1.4
0473             */
0474            public DocumentListener[] getDocumentListeners() {
0475                return (DocumentListener[]) listenerList
0476                        .getListeners(DocumentListener.class);
0477            }
0478
0479            /**
0480             * Adds an undo listener for notification of any changes.
0481             * Undo/Redo operations performed on the <code>UndoableEdit</code>
0482             * will cause the appropriate DocumentEvent to be fired to keep
0483             * the view(s) in sync with the model.
0484             *
0485             * @param listener the <code>UndoableEditListener</code> to add
0486             * @see Document#addUndoableEditListener
0487             */
0488            public void addUndoableEditListener(UndoableEditListener listener) {
0489                listenerList.add(UndoableEditListener.class, listener);
0490            }
0491
0492            /**
0493             * Removes an undo listener.
0494             *
0495             * @param listener the <code>UndoableEditListener</code> to remove
0496             * @see Document#removeDocumentListener
0497             */
0498            public void removeUndoableEditListener(UndoableEditListener listener) {
0499                listenerList.remove(UndoableEditListener.class, listener);
0500            }
0501
0502            /**
0503             * Returns an array of all the undoable edit listeners
0504             * registered on this document.
0505             *
0506             * @return all of this document's <code>UndoableEditListener</code>s 
0507             *         or an empty array if no undoable edit listeners are
0508             *         currently registered
0509             *
0510             * @see #addUndoableEditListener
0511             * @see #removeUndoableEditListener
0512             *
0513             * @since 1.4
0514             */
0515            public UndoableEditListener[] getUndoableEditListeners() {
0516                return (UndoableEditListener[]) listenerList
0517                        .getListeners(UndoableEditListener.class);
0518            }
0519
0520            /**
0521             * A convenience method for looking up a property value. It is
0522             * equivalent to:
0523             * <pre>
0524             * getDocumentProperties().get(key);
0525             * </pre>
0526             * 
0527             * @param key the non-<code>null</code> property key
0528             * @return the value of this property or <code>null</code>
0529             * @see #getDocumentProperties
0530             */
0531            public final Object getProperty(Object key) {
0532                return getDocumentProperties().get(key);
0533            }
0534
0535            /**
0536             * A convenience method for storing up a property value.  It is
0537             * equivalent to:
0538             * <pre>
0539             * getDocumentProperties().put(key, value);
0540             * </pre>
0541             * If <code>value</code> is <code>null</code> this method will
0542             * remove the property.
0543             * 
0544             * @param key the non-<code>null</code> key
0545             * @param value the property value
0546             * @see #getDocumentProperties
0547             */
0548            public final void putProperty(Object key, Object value) {
0549                if (value != null) {
0550                    getDocumentProperties().put(key, value);
0551                } else {
0552                    getDocumentProperties().remove(key);
0553                }
0554                if (key == TextAttribute.RUN_DIRECTION
0555                        && Boolean.TRUE.equals(getProperty(I18NProperty))) {
0556                    //REMIND - this needs to flip on the i18n property if run dir
0557                    //is rtl and the i18n property is not already on.
0558                    writeLock();
0559                    try {
0560                        DefaultDocumentEvent e = new DefaultDocumentEvent(0,
0561                                getLength(), DocumentEvent.EventType.INSERT);
0562                        updateBidi(e);
0563                    } finally {
0564                        writeUnlock();
0565                    }
0566                }
0567            }
0568
0569            /**
0570             * Removes some content from the document.
0571             * Removing content causes a write lock to be held while the
0572             * actual changes are taking place.  Observers are notified
0573             * of the change on the thread that called this method.
0574             * <p>
0575             * This method is thread safe, although most Swing methods
0576             * are not. Please see 
0577             * <A HREF="http://java.sun.com/docs/books/tutorial/uiswing/misc/threads.html">How
0578             * to Use Threads</A> for more information.
0579             * 
0580             * @param offs the starting offset >= 0
0581             * @param len the number of characters to remove >= 0
0582             * @exception BadLocationException  the given remove position is not a valid 
0583             *   position within the document
0584             * @see Document#remove
0585             */
0586            public void remove(int offs, int len) throws BadLocationException {
0587                DocumentFilter filter = getDocumentFilter();
0588
0589                writeLock();
0590                try {
0591                    if (filter != null) {
0592                        filter.remove(getFilterBypass(), offs, len);
0593                    } else {
0594                        handleRemove(offs, len);
0595                    }
0596                } finally {
0597                    writeUnlock();
0598                }
0599            }
0600
0601            /**
0602             * Performs the actual work of the remove. It is assumed the caller
0603             * will have obtained a <code>writeLock</code> before invoking this.
0604             */
0605            void handleRemove(int offs, int len) throws BadLocationException {
0606                if (len > 0) {
0607                    if (offs < 0 || (offs + len) > getLength()) {
0608                        throw new BadLocationException("Invalid remove",
0609                                getLength() + 1);
0610                    }
0611                    DefaultDocumentEvent chng = new DefaultDocumentEvent(offs,
0612                            len, DocumentEvent.EventType.REMOVE);
0613
0614                    boolean isComposedTextElement = false;
0615                    // Check whether the position of interest is the composed text
0616                    isComposedTextElement = Utilities.isComposedTextElement(
0617                            this , offs);
0618
0619                    removeUpdate(chng);
0620                    UndoableEdit u = data.remove(offs, len);
0621                    if (u != null) {
0622                        chng.addEdit(u);
0623                    }
0624                    postRemoveUpdate(chng);
0625                    // Mark the edit as done.
0626                    chng.end();
0627                    fireRemoveUpdate(chng);
0628                    // only fire undo if Content implementation supports it
0629                    // undo for the composed text is not supported for now
0630                    if ((u != null) && !isComposedTextElement) {
0631                        fireUndoableEditUpdate(new UndoableEditEvent(this , chng));
0632                    }
0633                }
0634            }
0635
0636            /**
0637             * Deletes the region of text from <code>offset</code> to
0638             * <code>offset + length</code>, and replaces it with <code>text</code>.
0639             * It is up to the implementation as to how this is implemented, some
0640             * implementations may treat this as two distinct operations: a remove
0641             * followed by an insert, others may treat the replace as one atomic  
0642             * operation.
0643             * 
0644             * @param offset index of child element
0645             * @param length length of text to delete, may be 0 indicating don't
0646             *               delete anything
0647             * @param text text to insert, <code>null</code> indicates no text to insert
0648             * @param attrs AttributeSet indicating attributes of inserted text,
0649             *              <code>null</code>
0650             *              is legal, and typically treated as an empty attributeset,
0651             *              but exact interpretation is left to the subclass
0652             * @exception BadLocationException the given position is not a valid 
0653             *            position within the document
0654             * @since 1.4
0655             */
0656            public void replace(int offset, int length, String text,
0657                    AttributeSet attrs) throws BadLocationException {
0658                if (length == 0 && (text == null || text.length() == 0)) {
0659                    return;
0660                }
0661                DocumentFilter filter = getDocumentFilter();
0662
0663                writeLock();
0664                try {
0665                    if (filter != null) {
0666                        filter.replace(getFilterBypass(), offset, length, text,
0667                                attrs);
0668                    } else {
0669                        if (length > 0) {
0670                            remove(offset, length);
0671                        }
0672                        if (text != null && text.length() > 0) {
0673                            insertString(offset, text, attrs);
0674                        }
0675                    }
0676                } finally {
0677                    writeUnlock();
0678                }
0679            }
0680
0681            /**
0682             * Inserts some content into the document.
0683             * Inserting content causes a write lock to be held while the
0684             * actual changes are taking place, followed by notification
0685             * to the observers on the thread that grabbed the write lock.
0686             * <p>
0687             * This method is thread safe, although most Swing methods
0688             * are not. Please see 
0689             * <A HREF="http://java.sun.com/docs/books/tutorial/uiswing/misc/threads.html">How
0690             * to Use Threads</A> for more information.
0691             *
0692             * @param offs the starting offset >= 0
0693             * @param str the string to insert; does nothing with null/empty strings
0694             * @param a the attributes for the inserted content
0695             * @exception BadLocationException  the given insert position is not a valid 
0696             *   position within the document
0697             * @see Document#insertString
0698             */
0699            public void insertString(int offs, String str, AttributeSet a)
0700                    throws BadLocationException {
0701                if ((str == null) || (str.length() == 0)) {
0702                    return;
0703                }
0704                DocumentFilter filter = getDocumentFilter();
0705
0706                writeLock();
0707                try {
0708                    if (filter != null) {
0709                        filter.insertString(getFilterBypass(), offs, str, a);
0710                    } else {
0711                        handleInsertString(offs, str, a);
0712                    }
0713                } finally {
0714                    writeUnlock();
0715                }
0716            }
0717
0718            /**
0719             * Performs the actual work of inserting the text; it is assumed the
0720             * caller has obtained a write lock before invoking this.
0721             */
0722            void handleInsertString(int offs, String str, AttributeSet a)
0723                    throws BadLocationException {
0724                if ((str == null) || (str.length() == 0)) {
0725                    return;
0726                }
0727                UndoableEdit u = data.insertString(offs, str);
0728                DefaultDocumentEvent e = new DefaultDocumentEvent(offs, str
0729                        .length(), DocumentEvent.EventType.INSERT);
0730                if (u != null) {
0731                    e.addEdit(u);
0732                }
0733
0734                // see if complex glyph layout support is needed
0735                if (getProperty(I18NProperty).equals(Boolean.FALSE)) {
0736                    // if a default direction of right-to-left has been specified,
0737                    // we want complex layout even if the text is all left to right.
0738                    Object d = getProperty(TextAttribute.RUN_DIRECTION);
0739                    if ((d != null)
0740                            && (d.equals(TextAttribute.RUN_DIRECTION_RTL))) {
0741                        putProperty(I18NProperty, Boolean.TRUE);
0742                    } else {
0743                        char[] chars = str.toCharArray();
0744                        if (SwingUtilities2.isComplexLayout(chars, 0,
0745                                chars.length)) {
0746                            putProperty(I18NProperty, Boolean.TRUE);
0747                        }
0748                    }
0749                }
0750
0751                insertUpdate(e, a);
0752                // Mark the edit as done.
0753                e.end();
0754                fireInsertUpdate(e);
0755                // only fire undo if Content implementation supports it
0756                // undo for the composed text is not supported for now
0757                if (u != null
0758                        && (a == null || !a
0759                                .isDefined(StyleConstants.ComposedTextAttribute))) {
0760                    fireUndoableEditUpdate(new UndoableEditEvent(this , e));
0761                }
0762            }
0763
0764            /**
0765             * Gets a sequence of text from the document.  
0766             *
0767             * @param offset the starting offset >= 0
0768             * @param length the number of characters to retrieve >= 0
0769             * @return the text
0770             * @exception BadLocationException  the range given includes a position 
0771             *   that is not a valid position within the document
0772             * @see Document#getText
0773             */
0774            public String getText(int offset, int length)
0775                    throws BadLocationException {
0776                if (length < 0) {
0777                    throw new BadLocationException("Length must be positive",
0778                            length);
0779                }
0780                String str = data.getString(offset, length);
0781                return str;
0782            }
0783
0784            /**
0785             * Fetches the text contained within the given portion 
0786             * of the document.
0787             * <p>
0788             * If the partialReturn property on the txt parameter is false, the
0789             * data returned in the Segment will be the entire length requested and
0790             * may or may not be a copy depending upon how the data was stored.
0791             * If the partialReturn property is true, only the amount of text that
0792             * can be returned without creating a copy is returned.  Using partial
0793             * returns will give better performance for situations where large 
0794             * parts of the document are being scanned.  The following is an example
0795             * of using the partial return to access the entire document:
0796             * <p>
0797             * <pre>
0798             * &nbsp; int nleft = doc.getDocumentLength();
0799             * &nbsp; Segment text = new Segment();
0800             * &nbsp; int offs = 0;
0801             * &nbsp; text.setPartialReturn(true);   
0802             * &nbsp; while (nleft > 0) {
0803             * &nbsp;     doc.getText(offs, nleft, text);
0804             * &nbsp;     // do something with text
0805             * &nbsp;     nleft -= text.count;
0806             * &nbsp;     offs += text.count;
0807             * &nbsp; }
0808             * </pre>
0809             *
0810             * @param offset the starting offset >= 0
0811             * @param length the number of characters to retrieve >= 0
0812             * @param txt the Segment object to retrieve the text into
0813             * @exception BadLocationException  the range given includes a position 
0814             *   that is not a valid position within the document
0815             */
0816            public void getText(int offset, int length, Segment txt)
0817                    throws BadLocationException {
0818                if (length < 0) {
0819                    throw new BadLocationException("Length must be positive",
0820                            length);
0821                }
0822                data.getChars(offset, length, txt);
0823            }
0824
0825            /**
0826             * Returns a position that will track change as the document
0827             * is altered.
0828             * <p>
0829             * This method is thread safe, although most Swing methods
0830             * are not. Please see 
0831             * <A HREF="http://java.sun.com/docs/books/tutorial/uiswing/misc/threads.html">How
0832             * to Use Threads</A> for more information.
0833             *
0834             * @param offs the position in the model >= 0
0835             * @return the position
0836             * @exception BadLocationException  if the given position does not
0837             *   represent a valid location in the associated document
0838             * @see Document#createPosition
0839             */
0840            public synchronized Position createPosition(int offs)
0841                    throws BadLocationException {
0842                return data.createPosition(offs);
0843            }
0844
0845            /**
0846             * Returns a position that represents the start of the document.  The 
0847             * position returned can be counted on to track change and stay 
0848             * located at the beginning of the document.
0849             *
0850             * @return the position
0851             */
0852            public final Position getStartPosition() {
0853                Position p;
0854                try {
0855                    p = createPosition(0);
0856                } catch (BadLocationException bl) {
0857                    p = null;
0858                }
0859                return p;
0860            }
0861
0862            /**
0863             * Returns a position that represents the end of the document.  The
0864             * position returned can be counted on to track change and stay 
0865             * located at the end of the document.
0866             *
0867             * @return the position
0868             */
0869            public final Position getEndPosition() {
0870                Position p;
0871                try {
0872                    p = createPosition(data.length());
0873                } catch (BadLocationException bl) {
0874                    p = null;
0875                }
0876                return p;
0877            }
0878
0879            /**
0880             * Gets all root elements defined.  Typically, there
0881             * will only be one so the default implementation
0882             * is to return the default root element.
0883             *
0884             * @return the root element
0885             */
0886            public Element[] getRootElements() {
0887                Element[] elems = new Element[2];
0888                elems[0] = getDefaultRootElement();
0889                elems[1] = getBidiRootElement();
0890                return elems;
0891            }
0892
0893            /**
0894             * Returns the root element that views should be based upon
0895             * unless some other mechanism for assigning views to element
0896             * structures is provided.
0897             *
0898             * @return the root element
0899             * @see Document#getDefaultRootElement
0900             */
0901            public abstract Element getDefaultRootElement();
0902
0903            // ---- local methods -----------------------------------------
0904
0905            /**
0906             * Returns the <code>FilterBypass</code>. This will create one if one
0907             * does not yet exist.
0908             */
0909            private DocumentFilter.FilterBypass getFilterBypass() {
0910                if (filterBypass == null) {
0911                    filterBypass = new DefaultFilterBypass();
0912                }
0913                return filterBypass;
0914            }
0915
0916            /**
0917             * Returns the root element of the bidirectional structure for this
0918             * document.  Its children represent character runs with a given
0919             * Unicode bidi level.
0920             */
0921            public Element getBidiRootElement() {
0922                return bidiRoot;
0923            }
0924
0925            /**
0926             * Returns true if the text in the range <code>p0</code> to
0927             * <code>p1</code> is left to right.
0928             */
0929            boolean isLeftToRight(int p0, int p1) {
0930                if (!getProperty(I18NProperty).equals(Boolean.TRUE)) {
0931                    return true;
0932                }
0933                Element bidiRoot = getBidiRootElement();
0934                int index = bidiRoot.getElementIndex(p0);
0935                Element bidiElem = bidiRoot.getElement(index);
0936                if (bidiElem.getEndOffset() >= p1) {
0937                    AttributeSet bidiAttrs = bidiElem.getAttributes();
0938                    return ((StyleConstants.getBidiLevel(bidiAttrs) % 2) == 0);
0939                }
0940                return true;
0941            }
0942
0943            /**
0944             * Get the paragraph element containing the given position.  Sub-classes
0945             * must define for themselves what exactly constitutes a paragraph.  They
0946             * should keep in mind however that a paragraph should at least be the
0947             * unit of text over which to run the Unicode bidirectional algorithm.
0948             *
0949             * @param pos the starting offset >= 0
0950             * @return the element */
0951            public abstract Element getParagraphElement(int pos);
0952
0953            /**
0954             * Fetches the context for managing attributes.  This
0955             * method effectively establishes the strategy used 
0956             * for compressing AttributeSet information.
0957             *
0958             * @return the context
0959             */
0960            protected final AttributeContext getAttributeContext() {
0961                return context;
0962            }
0963
0964            /**
0965             * Updates document structure as a result of text insertion.  This
0966             * will happen within a write lock.  If a subclass of
0967             * this class reimplements this method, it should delegate to the
0968             * superclass as well.
0969             *
0970             * @param chng a description of the change
0971             * @param attr the attributes for the change
0972             */
0973            protected void insertUpdate(DefaultDocumentEvent chng,
0974                    AttributeSet attr) {
0975                if (getProperty(I18NProperty).equals(Boolean.TRUE))
0976                    updateBidi(chng);
0977
0978                // Check if a multi byte is encountered in the inserted text.
0979                if (chng.type == DocumentEvent.EventType.INSERT
0980                        && chng.getLength() > 0
0981                        && !Boolean.TRUE.equals(getProperty(MultiByteProperty))) {
0982                    Segment segment = SegmentCache.getSharedSegment();
0983                    try {
0984                        getText(chng.getOffset(), chng.getLength(), segment);
0985                        segment.first();
0986                        do {
0987                            if ((int) segment.current() > 255) {
0988                                putProperty(MultiByteProperty, Boolean.TRUE);
0989                                break;
0990                            }
0991                        } while (segment.next() != Segment.DONE);
0992                    } catch (BadLocationException ble) {
0993                        // Should never happen
0994                    }
0995                    SegmentCache.releaseSharedSegment(segment);
0996                }
0997            }
0998
0999            /**
1000             * Updates any document structure as a result of text removal.  This
1001             * method is called before the text is actually removed from the Content.
1002             * This will happen within a write lock. If a subclass
1003             * of this class reimplements this method, it should delegate to the
1004             * superclass as well.
1005             *
1006             * @param chng a description of the change
1007             */
1008            protected void removeUpdate(DefaultDocumentEvent chng) {
1009            }
1010
1011            /**
1012             * Updates any document structure as a result of text removal.  This
1013             * method is called after the text has been removed from the Content.
1014             * This will happen within a write lock. If a subclass
1015             * of this class reimplements this method, it should delegate to the
1016             * superclass as well.
1017             *
1018             * @param chng a description of the change
1019             */
1020            protected void postRemoveUpdate(DefaultDocumentEvent chng) {
1021                if (getProperty(I18NProperty).equals(Boolean.TRUE))
1022                    updateBidi(chng);
1023            }
1024
1025            /**
1026             * Update the bidi element structure as a result of the given change
1027             * to the document.  The given change will be updated to reflect the
1028             * changes made to the bidi structure.
1029             *
1030             * This method assumes that every offset in the model is contained in
1031             * exactly one paragraph.  This method also assumes that it is called
1032             * after the change is made to the default element structure.
1033             */
1034            void updateBidi(DefaultDocumentEvent chng) {
1035
1036                // Calculate the range of paragraphs affected by the change.
1037                int firstPStart;
1038                int lastPEnd;
1039                if (chng.type == DocumentEvent.EventType.INSERT
1040                        || chng.type == DocumentEvent.EventType.CHANGE) {
1041                    int chngStart = chng.getOffset();
1042                    int chngEnd = chngStart + chng.getLength();
1043                    firstPStart = getParagraphElement(chngStart)
1044                            .getStartOffset();
1045                    lastPEnd = getParagraphElement(chngEnd).getEndOffset();
1046                } else if (chng.type == DocumentEvent.EventType.REMOVE) {
1047                    Element paragraph = getParagraphElement(chng.getOffset());
1048                    firstPStart = paragraph.getStartOffset();
1049                    lastPEnd = paragraph.getEndOffset();
1050                } else {
1051                    throw new Error("Internal error: unknown event type.");
1052                }
1053                //System.out.println("updateBidi: firstPStart = " + firstPStart + " lastPEnd = " + lastPEnd );
1054
1055                // Calculate the bidi levels for the affected range of paragraphs.  The
1056                // levels array will contain a bidi level for each character in the
1057                // affected text.
1058                byte levels[] = calculateBidiLevels(firstPStart, lastPEnd);
1059
1060                Vector newElements = new Vector();
1061
1062                // Calculate the first span of characters in the affected range with
1063                // the same bidi level.  If this level is the same as the level of the
1064                // previous bidi element (the existing bidi element containing
1065                // firstPStart-1), then merge in the previous element.  If not, but
1066                // the previous element overlaps the affected range, truncate the
1067                // previous element at firstPStart.
1068                int firstSpanStart = firstPStart;
1069                int removeFromIndex = 0;
1070                if (firstSpanStart > 0) {
1071                    int prevElemIndex = bidiRoot
1072                            .getElementIndex(firstPStart - 1);
1073                    removeFromIndex = prevElemIndex;
1074                    Element prevElem = bidiRoot.getElement(prevElemIndex);
1075                    int prevLevel = StyleConstants.getBidiLevel(prevElem
1076                            .getAttributes());
1077                    //System.out.println("createbidiElements: prevElem= " + prevElem  + " prevLevel= " + prevLevel + "level[0] = " + levels[0]);
1078                    if (prevLevel == levels[0]) {
1079                        firstSpanStart = prevElem.getStartOffset();
1080                    } else if (prevElem.getEndOffset() > firstPStart) {
1081                        newElements.addElement(new BidiElement(bidiRoot,
1082                                prevElem.getStartOffset(), firstPStart,
1083                                prevLevel));
1084                    } else {
1085                        removeFromIndex++;
1086                    }
1087                }
1088
1089                int firstSpanEnd = 0;
1090                while ((firstSpanEnd < levels.length)
1091                        && (levels[firstSpanEnd] == levels[0]))
1092                    firstSpanEnd++;
1093
1094                // Calculate the last span of characters in the affected range with
1095                // the same bidi level.  If this level is the same as the level of the
1096                // next bidi element (the existing bidi element containing lastPEnd),
1097                // then merge in the next element.  If not, but the next element
1098                // overlaps the affected range, adjust the next element to start at
1099                // lastPEnd.
1100                int lastSpanEnd = lastPEnd;
1101                Element newNextElem = null;
1102                int removeToIndex = bidiRoot.getElementCount() - 1;
1103                if (lastSpanEnd <= getLength()) {
1104                    int nextElemIndex = bidiRoot.getElementIndex(lastPEnd);
1105                    removeToIndex = nextElemIndex;
1106                    Element nextElem = bidiRoot.getElement(nextElemIndex);
1107                    int nextLevel = StyleConstants.getBidiLevel(nextElem
1108                            .getAttributes());
1109                    if (nextLevel == levels[levels.length - 1]) {
1110                        lastSpanEnd = nextElem.getEndOffset();
1111                    } else if (nextElem.getStartOffset() < lastPEnd) {
1112                        newNextElem = new BidiElement(bidiRoot, lastPEnd,
1113                                nextElem.getEndOffset(), nextLevel);
1114                    } else {
1115                        removeToIndex--;
1116                    }
1117                }
1118
1119                int lastSpanStart = levels.length;
1120                while ((lastSpanStart > firstSpanEnd)
1121                        && (levels[lastSpanStart - 1] == levels[levels.length - 1]))
1122                    lastSpanStart--;
1123
1124                // If the first and last spans are contiguous and have the same level,
1125                // merge them and create a single new element for the entire span.
1126                // Otherwise, create elements for the first and last spans as well as
1127                // any spans in between.
1128                if ((firstSpanEnd == lastSpanStart)
1129                        && (levels[0] == levels[levels.length - 1])) {
1130                    newElements.addElement(new BidiElement(bidiRoot,
1131                            firstSpanStart, lastSpanEnd, levels[0]));
1132                } else {
1133                    // Create an element for the first span.
1134                    newElements.addElement(new BidiElement(bidiRoot,
1135                            firstSpanStart, firstSpanEnd + firstPStart,
1136                            levels[0]));
1137                    // Create elements for the spans in between the first and last
1138                    for (int i = firstSpanEnd; i < lastSpanStart;) {
1139                        //System.out.println("executed line 872");
1140                        int j;
1141                        for (j = i; (j < levels.length)
1142                                && (levels[j] == levels[i]); j++)
1143                            ;
1144                        newElements.addElement(new BidiElement(bidiRoot,
1145                                firstPStart + i, firstPStart + j,
1146                                (int) levels[i]));
1147                        i = j;
1148                    }
1149                    // Create an element for the last span.
1150                    newElements.addElement(new BidiElement(bidiRoot,
1151                            lastSpanStart + firstPStart, lastSpanEnd,
1152                            levels[levels.length - 1]));
1153                }
1154
1155                if (newNextElem != null)
1156                    newElements.addElement(newNextElem);
1157
1158                // Calculate the set of existing bidi elements which must be
1159                // removed.
1160                int removedElemCount = 0;
1161                if (bidiRoot.getElementCount() > 0) {
1162                    removedElemCount = removeToIndex - removeFromIndex + 1;
1163                }
1164                Element[] removedElems = new Element[removedElemCount];
1165                for (int i = 0; i < removedElemCount; i++) {
1166                    removedElems[i] = bidiRoot.getElement(removeFromIndex + i);
1167                }
1168
1169                Element[] addedElems = new Element[newElements.size()];
1170                newElements.copyInto(addedElems);
1171
1172                // Update the change record.
1173                ElementEdit ee = new ElementEdit(bidiRoot, removeFromIndex,
1174                        removedElems, addedElems);
1175                chng.addEdit(ee);
1176
1177                // Update the bidi element structure.
1178                bidiRoot.replace(removeFromIndex, removedElems.length,
1179                        addedElems);
1180            }
1181
1182            /**
1183             * Calculate the levels array for a range of paragraphs.  
1184             */
1185            private byte[] calculateBidiLevels(int firstPStart, int lastPEnd) {
1186
1187                byte levels[] = new byte[lastPEnd - firstPStart];
1188                int levelsEnd = 0;
1189                Boolean defaultDirection = null;
1190                Object d = getProperty(TextAttribute.RUN_DIRECTION);
1191                if (d instanceof  Boolean) {
1192                    defaultDirection = (Boolean) d;
1193                }
1194
1195                // For each paragraph in the given range of paragraphs, get its
1196                // levels array and add it to the levels array for the entire span.
1197                for (int o = firstPStart; o < lastPEnd;) {
1198                    Element p = getParagraphElement(o);
1199                    int pStart = p.getStartOffset();
1200                    int pEnd = p.getEndOffset();
1201
1202                    // default run direction for the paragraph.  This will be
1203                    // null if there is no direction override specified (i.e. 
1204                    // the direction will be determined from the content).
1205                    Boolean direction = defaultDirection;
1206                    d = p.getAttributes().getAttribute(
1207                            TextAttribute.RUN_DIRECTION);
1208                    if (d instanceof  Boolean) {
1209                        direction = (Boolean) d;
1210                    }
1211
1212                    //System.out.println("updateBidi: paragraph start = " + pStart + " paragraph end = " + pEnd);
1213
1214                    // Create a Bidi over this paragraph then get the level
1215                    // array.
1216                    Segment seg = SegmentCache.getSharedSegment();
1217                    try {
1218                        getText(pStart, pEnd - pStart, seg);
1219                    } catch (BadLocationException e) {
1220                        throw new Error("Internal error: " + e.toString());
1221                    }
1222                    // REMIND(bcb) we should really be using a Segment here.
1223                    Bidi bidiAnalyzer;
1224                    int bidiflag = Bidi.DIRECTION_DEFAULT_LEFT_TO_RIGHT;
1225                    if (direction != null) {
1226                        if (TextAttribute.RUN_DIRECTION_LTR.equals(direction)) {
1227                            bidiflag = Bidi.DIRECTION_LEFT_TO_RIGHT;
1228                        } else {
1229                            bidiflag = Bidi.DIRECTION_RIGHT_TO_LEFT;
1230                        }
1231                    }
1232                    bidiAnalyzer = new Bidi(seg.array, seg.offset, null, 0,
1233                            seg.count, bidiflag);
1234                    BidiUtils.getLevels(bidiAnalyzer, levels, levelsEnd);
1235                    levelsEnd += bidiAnalyzer.getLength();
1236
1237                    o = p.getEndOffset();
1238                    SegmentCache.releaseSharedSegment(seg);
1239                }
1240
1241                // REMIND(bcb) remove this code when debugging is done.
1242                if (levelsEnd != levels.length)
1243                    throw new Error("levelsEnd assertion failed.");
1244
1245                return levels;
1246            }
1247
1248            /**
1249             * Gives a diagnostic dump.
1250             *
1251             * @param out the output stream
1252             */
1253            public void dump(PrintStream out) {
1254                Element root = getDefaultRootElement();
1255                if (root instanceof  AbstractElement) {
1256                    ((AbstractElement) root).dump(out, 0);
1257                }
1258                bidiRoot.dump(out, 0);
1259            }
1260
1261            /**
1262             * Gets the content for the document.
1263             *
1264             * @return the content
1265             */
1266            protected final Content getContent() {
1267                return data;
1268            }
1269
1270            /**
1271             * Creates a document leaf element.
1272             * Hook through which elements are created to represent the 
1273             * document structure.  Because this implementation keeps 
1274             * structure and content separate, elements grow automatically
1275             * when content is extended so splits of existing elements 
1276             * follow.  The document itself gets to decide how to generate 
1277             * elements to give flexibility in the type of elements used.
1278             *
1279             * @param parent the parent element
1280             * @param a the attributes for the element
1281             * @param p0 the beginning of the range >= 0
1282             * @param p1 the end of the range >= p0
1283             * @return the new element
1284             */
1285            protected Element createLeafElement(Element parent, AttributeSet a,
1286                    int p0, int p1) {
1287                return new LeafElement(parent, a, p0, p1);
1288            }
1289
1290            /**
1291             * Creates a document branch element, that can contain other elements.
1292             *
1293             * @param parent the parent element
1294             * @param a the attributes
1295             * @return the element
1296             */
1297            protected Element createBranchElement(Element parent, AttributeSet a) {
1298                return new BranchElement(parent, a);
1299            }
1300
1301            // --- Document locking ----------------------------------
1302
1303            /**
1304             * Fetches the current writing thread if there is one.
1305             * This can be used to distinguish whether a method is
1306             * being called as part of an existing modification or
1307             * if a lock needs to be acquired and a new transaction
1308             * started.  
1309             *
1310             * @return the thread actively modifying the document
1311             *  or <code>null</code> if there are no modifications in progress
1312             */
1313            protected synchronized final Thread getCurrentWriter() {
1314                return currWriter;
1315            }
1316
1317            /**
1318             * Acquires a lock to begin mutating the document this lock
1319             * protects.  There can be no writing, notification of changes, or
1320             * reading going on in order to gain the lock.  Additionally a thread is
1321             * allowed to gain more than one <code>writeLock</code>,
1322             * as long as it doesn't attempt to gain additional <code>writeLock</code>s
1323             * from within document notification.  Attempting to gain a 
1324             * <code>writeLock</code> from within a DocumentListener notification will
1325             * result in an <code>IllegalStateException</code>.  The ability
1326             * to obtain more than one <code>writeLock</code> per thread allows
1327             * subclasses to gain a writeLock, perform a number of operations, then
1328             * release the lock.
1329             * <p>
1330             * Calls to <code>writeLock</code>
1331             * must be balanced with calls to <code>writeUnlock</code>, else the
1332             * <code>Document</code> will be left in a locked state so that no
1333             * reading or writing can be done.
1334             *
1335             * @exception IllegalStateException thrown on illegal lock
1336             *  attempt.  If the document is implemented properly, this can
1337             *  only happen if a document listener attempts to mutate the 
1338             *  document.  This situation violates the bean event model
1339             *  where order of delivery is not guaranteed and all listeners
1340             *  should be notified before further mutations are allowed.
1341             */
1342            protected synchronized final void writeLock() {
1343                try {
1344                    while ((numReaders > 0) || (currWriter != null)) {
1345                        if (Thread.currentThread() == currWriter) {
1346                            if (notifyingListeners) {
1347                                // Assuming one doesn't do something wrong in a
1348                                // subclass this should only happen if a
1349                                // DocumentListener tries to mutate the document.
1350                                throw new IllegalStateException(
1351                                        "Attempt to mutate in notification");
1352                            }
1353                            numWriters++;
1354                            return;
1355                        }
1356                        wait();
1357                    }
1358                    currWriter = Thread.currentThread();
1359                    numWriters = 1;
1360                } catch (InterruptedException e) {
1361                    throw new Error("Interrupted attempt to aquire write lock");
1362                }
1363            }
1364
1365            /**
1366             * Releases a write lock previously obtained via <code>writeLock</code>.
1367             * After decrementing the lock count if there are no oustanding locks
1368             * this will allow a new writer, or readers.
1369             *
1370             * @see #writeLock
1371             */
1372            protected synchronized final void writeUnlock() {
1373                if (--numWriters <= 0) {
1374                    numWriters = 0;
1375                    currWriter = null;
1376                    notifyAll();
1377                }
1378            }
1379
1380            /**
1381             * Acquires a lock to begin reading some state from the 
1382             * document.  There can be multiple readers at the same time.
1383             * Writing blocks the readers until notification of the change
1384             * to the listeners has been completed.  This method should
1385             * be used very carefully to avoid unintended compromise
1386             * of the document.  It should always be balanced with a
1387             * <code>readUnlock</code>.
1388             *
1389             * @see #readUnlock
1390             */
1391            public synchronized final void readLock() {
1392                try {
1393                    while (currWriter != null) {
1394                        if (currWriter == Thread.currentThread()) {
1395                            // writer has full read access.... may try to acquire
1396                            // lock in notification
1397                            return;
1398                        }
1399                        wait();
1400                    }
1401                    numReaders += 1;
1402                } catch (InterruptedException e) {
1403                    throw new Error("Interrupted attempt to aquire read lock");
1404                }
1405            }
1406
1407            /**
1408             * Does a read unlock.  This signals that one
1409             * of the readers is done.  If there are no more readers
1410             * then writing can begin again.  This should be balanced
1411             * with a readLock, and should occur in a finally statement
1412             * so that the balance is guaranteed.  The following is an
1413             * example.
1414             * <pre><code>
1415             * &nbsp;   readLock();
1416             * &nbsp;   try {
1417             * &nbsp;       // do something
1418             * &nbsp;   } finally {
1419             * &nbsp;       readUnlock();
1420             * &nbsp;   }
1421             * </code></pre>
1422             *
1423             * @see #readLock
1424             */
1425            public synchronized final void readUnlock() {
1426                if (currWriter == Thread.currentThread()) {
1427                    // writer has full read access.... may try to acquire
1428                    // lock in notification
1429                    return;
1430                }
1431                if (numReaders <= 0) {
1432                    throw new StateInvariantError(BAD_LOCK_STATE);
1433                }
1434                numReaders -= 1;
1435                notify();
1436            }
1437
1438            // --- serialization ---------------------------------------------
1439
1440            private void readObject(ObjectInputStream s)
1441                    throws ClassNotFoundException, IOException {
1442                s.defaultReadObject();
1443                listenerList = new EventListenerList();
1444
1445                // Restore bidi structure
1446                //REMIND(bcb) This creates an initial bidi element to account for
1447                //the \n that exists by default in the content.  
1448                bidiRoot = new BidiRootElement();
1449                try {
1450                    writeLock();
1451                    Element[] p = new Element[1];
1452                    p[0] = new BidiElement(bidiRoot, 0, 1, 0);
1453                    bidiRoot.replace(0, 0, p);
1454                } finally {
1455                    writeUnlock();
1456                }
1457                // At this point bidi root is only partially correct. To fully
1458                // restore it we need access to getDefaultRootElement. But, this
1459                // is created by the subclass and at this point will be null. We
1460                // thus use registerValidation.
1461                s.registerValidation(new ObjectInputValidation() {
1462                    public void validateObject() {
1463                        try {
1464                            writeLock();
1465                            DefaultDocumentEvent e = new DefaultDocumentEvent(
1466                                    0, getLength(),
1467                                    DocumentEvent.EventType.INSERT);
1468                            updateBidi(e);
1469                        } finally {
1470                            writeUnlock();
1471                        }
1472                    }
1473                }, 0);
1474            }
1475
1476            // ----- member variables ------------------------------------------
1477
1478            private transient int numReaders;
1479            private transient Thread currWriter;
1480            /**
1481             * The number of writers, all obtained from <code>currWriter</code>.
1482             */
1483            private transient int numWriters;
1484            /**
1485             * True will notifying listeners.
1486             */
1487            private transient boolean notifyingListeners;
1488
1489            private static Boolean defaultI18NProperty;
1490
1491            /**
1492             * Storage for document-wide properties.
1493             */
1494            private Dictionary<Object, Object> documentProperties = null;
1495
1496            /**
1497             * The event listener list for the document.
1498             */
1499            protected EventListenerList listenerList = new EventListenerList();
1500
1501            /**
1502             * Where the text is actually stored, and a set of marks
1503             * that track change as the document is edited are managed.
1504             */
1505            private Content data;
1506
1507            /**
1508             * Factory for the attributes.  This is the strategy for
1509             * attribute compression and control of the lifetime of
1510             * a set of attributes as a collection.  This may be shared
1511             * with other documents.
1512             */
1513            private AttributeContext context;
1514
1515            /**
1516             * The root of the bidirectional structure for this document.  Its children
1517             * represent character runs with the same Unicode bidi level.
1518             */
1519            private transient BranchElement bidiRoot;
1520
1521            /**
1522             * Filter for inserting/removing of text.
1523             */
1524            private DocumentFilter documentFilter;
1525
1526            /**
1527             * Used by DocumentFilter to do actual insert/remove.
1528             */
1529            private transient DocumentFilter.FilterBypass filterBypass;
1530
1531            private static final String BAD_LOCK_STATE = "document lock failure";
1532
1533            /**
1534             * Error message to indicate a bad location.
1535             */
1536            protected static final String BAD_LOCATION = "document location failure";
1537
1538            /**
1539             * Name of elements used to represent paragraphs
1540             */
1541            public static final String ParagraphElementName = "paragraph";
1542
1543            /**
1544             * Name of elements used to represent content
1545             */
1546            public static final String ContentElementName = "content";
1547
1548            /**
1549             * Name of elements used to hold sections (lines/paragraphs).
1550             */
1551            public static final String SectionElementName = "section";
1552
1553            /**
1554             * Name of elements used to hold a unidirectional run
1555             */
1556            public static final String BidiElementName = "bidi level";
1557
1558            /**
1559             * Name of the attribute used to specify element
1560             * names.
1561             */
1562            public static final String ElementNameAttribute = "$ename";
1563
1564            /**
1565             * Document property that indicates whether internationalization
1566             * functions such as text reordering or reshaping should be
1567             * performed. This property should not be publicly exposed,
1568             * since it is used for implementation convenience only.  As a 
1569             * side effect, copies of this property may be in its subclasses
1570             * that live in different packages (e.g. HTMLDocument as of now),
1571             * so those copies should also be taken care of when this property
1572             * needs to be modified.
1573             */
1574            static final String I18NProperty = "i18n";
1575
1576            /**
1577             * Document property that indicates if a character has been inserted
1578             * into the document that is more than one byte long.  GlyphView uses
1579             * this to determine if it should use BreakIterator.
1580             */
1581            static final Object MultiByteProperty = "multiByte";
1582
1583            /**
1584             * Document property that indicates asynchronous loading is
1585             * desired, with the thread priority given as the value.
1586             */
1587            static final String AsyncLoadPriority = "load priority";
1588
1589            /**
1590             * Interface to describe a sequence of character content that
1591             * can be edited.  Implementations may or may not support a 
1592             * history mechanism which will be reflected by whether or not
1593             * mutations return an UndoableEdit implementation.  
1594             * @see AbstractDocument
1595             */
1596            public interface Content {
1597
1598                /**
1599                 * Creates a position within the content that will
1600                 * track change as the content is mutated.
1601                 *
1602                 * @param offset the offset in the content >= 0
1603                 * @return a Position
1604                 * @exception BadLocationException for an invalid offset
1605                 */
1606                public Position createPosition(int offset)
1607                        throws BadLocationException;
1608
1609                /**
1610                 * Current length of the sequence of character content.
1611                 *
1612                 * @return the length >= 0
1613                 */
1614                public int length();
1615
1616                /**
1617                 * Inserts a string of characters into the sequence.
1618                 * 
1619                 * @param where   offset into the sequence to make the insertion >= 0
1620                 * @param str     string to insert
1621                 * @return  if the implementation supports a history mechanism, 
1622                 *    a reference to an <code>Edit</code> implementation will be returned, 
1623                 *    otherwise returns <code>null</code>
1624                 * @exception BadLocationException  thrown if the area covered by
1625                 *   the arguments is not contained in the character sequence
1626                 */
1627                public UndoableEdit insertString(int where, String str)
1628                        throws BadLocationException;
1629
1630                /**
1631                 * Removes some portion of the sequence.  
1632                 *
1633                 * @param where   The offset into the sequence to make the
1634                 *   insertion >= 0.
1635                 * @param nitems  The number of items in the sequence to remove >= 0.
1636                 * @return  If the implementation supports a history mechansim, 
1637                 *    a reference to an Edit implementation will be returned, 
1638                 *    otherwise null.
1639                 * @exception BadLocationException  Thrown if the area covered by
1640                 *   the arguments is not contained in the character sequence.
1641                 */
1642                public UndoableEdit remove(int where, int nitems)
1643                        throws BadLocationException;
1644
1645                /**
1646                 * Fetches a string of characters contained in the sequence.
1647                 * 
1648                 * @param where   Offset into the sequence to fetch >= 0.
1649                 * @param len     number of characters to copy >= 0.
1650                 * @return the string
1651                 * @exception BadLocationException  Thrown if the area covered by
1652                 *   the arguments is not contained in the character sequence.
1653                 */
1654                public String getString(int where, int len)
1655                        throws BadLocationException;
1656
1657                /**
1658                 * Gets a sequence of characters and copies them into a Segment.
1659                 *
1660                 * @param where the starting offset >= 0
1661                 * @param len the number of characters >= 0
1662                 * @param txt the target location to copy into
1663                 * @exception BadLocationException  Thrown if the area covered by
1664                 *   the arguments is not contained in the character sequence.
1665                 */
1666                public void getChars(int where, int len, Segment txt)
1667                        throws BadLocationException;
1668            }
1669
1670            /**
1671             * An interface that can be used to allow MutableAttributeSet 
1672             * implementations to use pluggable attribute compression
1673             * techniques.  Each mutation of the attribute set can be
1674             * used to exchange a previous AttributeSet instance with
1675             * another, preserving the possibility of the AttributeSet
1676             * remaining immutable.  An implementation is provided by
1677             * the StyleContext class.
1678             *
1679             * The Element implementations provided by this class use
1680             * this interface to provide their MutableAttributeSet
1681             * implementations, so that different AttributeSet compression
1682             * techniques can be employed.  The method 
1683             * <code>getAttributeContext</code> should be implemented to
1684             * return the object responsible for implementing the desired
1685             * compression technique.
1686             * 
1687             * @see StyleContext
1688             */
1689            public interface AttributeContext {
1690
1691                /**
1692                 * Adds an attribute to the given set, and returns
1693                 * the new representative set.
1694                 *
1695                 * @param old the old attribute set
1696                 * @param name the non-null attribute name
1697                 * @param value the attribute value
1698                 * @return the updated attribute set
1699                 * @see MutableAttributeSet#addAttribute
1700                 */
1701                public AttributeSet addAttribute(AttributeSet old, Object name,
1702                        Object value);
1703
1704                /**
1705                 * Adds a set of attributes to the element.
1706                 *
1707                 * @param old the old attribute set
1708                 * @param attr the attributes to add
1709                 * @return the updated attribute set
1710                 * @see MutableAttributeSet#addAttribute
1711                 */
1712                public AttributeSet addAttributes(AttributeSet old,
1713                        AttributeSet attr);
1714
1715                /**
1716                 * Removes an attribute from the set.
1717                 *
1718                 * @param old the old attribute set
1719                 * @param name the non-null attribute name
1720                 * @return the updated attribute set
1721                 * @see MutableAttributeSet#removeAttribute
1722                 */
1723                public AttributeSet removeAttribute(AttributeSet old,
1724                        Object name);
1725
1726                /**
1727                 * Removes a set of attributes for the element.
1728                 *
1729                 * @param old the old attribute set
1730                 * @param names the attribute names
1731                 * @return the updated attribute set
1732                 * @see MutableAttributeSet#removeAttributes
1733                 */
1734                public AttributeSet removeAttributes(AttributeSet old,
1735                        Enumeration<?> names);
1736
1737                /**
1738                 * Removes a set of attributes for the element.
1739                 *
1740                 * @param old the old attribute set
1741                 * @param attrs the attributes
1742                 * @return the updated attribute set
1743                 * @see MutableAttributeSet#removeAttributes
1744                 */
1745                public AttributeSet removeAttributes(AttributeSet old,
1746                        AttributeSet attrs);
1747
1748                /**
1749                 * Fetches an empty AttributeSet.
1750                 *
1751                 * @return the attribute set
1752                 */
1753                public AttributeSet getEmptySet();
1754
1755                /**
1756                 * Reclaims an attribute set.
1757                 * This is a way for a MutableAttributeSet to mark that it no 
1758                 * longer need a particular immutable set.  This is only necessary
1759                 * in 1.1 where there are no weak references.  A 1.1 implementation
1760                 * would call this in its finalize method.
1761                 *
1762                 * @param a the attribute set to reclaim
1763                 */
1764                public void reclaim(AttributeSet a);
1765            }
1766
1767            /**
1768             * Implements the abstract part of an element.  By default elements
1769             * support attributes by having a field that represents the immutable
1770             * part of the current attribute set for the element.  The element itself
1771             * implements MutableAttributeSet which can be used to modify the set
1772             * by fetching a new immutable set.  The immutable sets are provided
1773             * by the AttributeContext associated with the document.
1774             * <p>
1775             * <strong>Warning:</strong>
1776             * Serialized objects of this class will not be compatible with
1777             * future Swing releases. The current serialization support is
1778             * appropriate for short term storage or RMI between applications running
1779             * the same version of Swing.  As of 1.4, support for long term storage
1780             * of all JavaBeans<sup><font size="-2">TM</font></sup>
1781             * has been added to the <code>java.beans</code> package.
1782             * Please see {@link java.beans.XMLEncoder}.
1783             */
1784            public abstract class AbstractElement implements  Element,
1785                    MutableAttributeSet, Serializable, TreeNode {
1786
1787                /**
1788                 * Creates a new AbstractElement.
1789                 *
1790                 * @param parent the parent element
1791                 * @param a the attributes for the element
1792                 * @since 1.4
1793                 */
1794                public AbstractElement(Element parent, AttributeSet a) {
1795                    this .parent = parent;
1796                    attributes = getAttributeContext().getEmptySet();
1797                    if (a != null) {
1798                        addAttributes(a);
1799                    }
1800                }
1801
1802                private final void indent(PrintWriter out, int n) {
1803                    for (int i = 0; i < n; i++) {
1804                        out.print("  ");
1805                    }
1806                }
1807
1808                /**
1809                 * Dumps a debugging representation of the element hierarchy.
1810                 *
1811                 * @param psOut the output stream
1812                 * @param indentAmount the indentation level >= 0
1813                 */
1814                public void dump(PrintStream psOut, int indentAmount) {
1815                    PrintWriter out;
1816                    try {
1817                        out = new PrintWriter(new OutputStreamWriter(psOut,
1818                                "JavaEsc"), true);
1819                    } catch (UnsupportedEncodingException e) {
1820                        out = new PrintWriter(psOut, true);
1821                    }
1822                    indent(out, indentAmount);
1823                    if (getName() == null) {
1824                        out.print("<??");
1825                    } else {
1826                        out.print("<" + getName());
1827                    }
1828                    if (getAttributeCount() > 0) {
1829                        out.println("");
1830                        // dump the attributes
1831                        Enumeration names = attributes.getAttributeNames();
1832                        while (names.hasMoreElements()) {
1833                            Object name = names.nextElement();
1834                            indent(out, indentAmount + 1);
1835                            out.println(name + "=" + getAttribute(name));
1836                        }
1837                        indent(out, indentAmount);
1838                    }
1839                    out.println(">");
1840
1841                    if (isLeaf()) {
1842                        indent(out, indentAmount + 1);
1843                        out.print("[" + getStartOffset() + "," + getEndOffset()
1844                                + "]");
1845                        Content c = getContent();
1846                        try {
1847                            String contentStr = c.getString(getStartOffset(),
1848                                    getEndOffset() - getStartOffset())/*.trim()*/;
1849                            if (contentStr.length() > 40) {
1850                                contentStr = contentStr.substring(0, 40)
1851                                        + "...";
1852                            }
1853                            out.println("[" + contentStr + "]");
1854                        } catch (BadLocationException e) {
1855                            ;
1856                        }
1857
1858                    } else {
1859                        int n = getElementCount();
1860                        for (int i = 0; i < n; i++) {
1861                            AbstractElement e = (AbstractElement) getElement(i);
1862                            e.dump(psOut, indentAmount + 1);
1863                        }
1864                    }
1865                }
1866
1867                // --- AttributeSet ----------------------------
1868                // delegated to the immutable field "attributes"
1869
1870                /**
1871                 * Gets the number of attributes that are defined.
1872                 *
1873                 * @return the number of attributes >= 0
1874                 * @see AttributeSet#getAttributeCount
1875                 */
1876                public int getAttributeCount() {
1877                    return attributes.getAttributeCount();
1878                }
1879
1880                /**
1881                 * Checks whether a given attribute is defined.
1882                 *
1883                 * @param attrName the non-null attribute name
1884                 * @return true if the attribute is defined
1885                 * @see AttributeSet#isDefined
1886                 */
1887                public boolean isDefined(Object attrName) {
1888                    return attributes.isDefined(attrName);
1889                }
1890
1891                /**
1892                 * Checks whether two attribute sets are equal.
1893                 *
1894                 * @param attr the attribute set to check against
1895                 * @return true if the same
1896                 * @see AttributeSet#isEqual
1897                 */
1898                public boolean isEqual(AttributeSet attr) {
1899                    return attributes.isEqual(attr);
1900                }
1901
1902                /**
1903                 * Copies a set of attributes.
1904                 *
1905                 * @return the copy
1906                 * @see AttributeSet#copyAttributes
1907                 */
1908                public AttributeSet copyAttributes() {
1909                    return attributes.copyAttributes();
1910                }
1911
1912                /**
1913                 * Gets the value of an attribute.
1914                 *
1915                 * @param attrName the non-null attribute name
1916                 * @return the attribute value
1917                 * @see AttributeSet#getAttribute
1918                 */
1919                public Object getAttribute(Object attrName) {
1920                    Object value = attributes.getAttribute(attrName);
1921                    if (value == null) {
1922                        // The delegate nor it's resolvers had a match,
1923                        // so we'll try to resolve through the parent
1924                        // element.
1925                        AttributeSet a = (parent != null) ? parent
1926                                .getAttributes() : null;
1927                        if (a != null) {
1928                            value = a.getAttribute(attrName);
1929                        }
1930                    }
1931                    return value;
1932                }
1933
1934                /**
1935                 * Gets the names of all attributes.
1936                 *
1937                 * @return the attribute names as an enumeration
1938                 * @see AttributeSet#getAttributeNames
1939                 */
1940                public Enumeration<?> getAttributeNames() {
1941                    return attributes.getAttributeNames();
1942                }
1943
1944                /**
1945                 * Checks whether a given attribute name/value is defined.
1946                 *
1947                 * @param name the non-null attribute name
1948                 * @param value the attribute value
1949                 * @return true if the name/value is defined
1950                 * @see AttributeSet#containsAttribute
1951                 */
1952                public boolean containsAttribute(Object name, Object value) {
1953                    return attributes.containsAttribute(name, value);
1954                }
1955
1956                /**
1957                 * Checks whether the element contains all the attributes.
1958                 *
1959                 * @param attrs the attributes to check
1960                 * @return true if the element contains all the attributes
1961                 * @see AttributeSet#containsAttributes
1962                 */
1963                public boolean containsAttributes(AttributeSet attrs) {
1964                    return attributes.containsAttributes(attrs);
1965                }
1966
1967                /**
1968                 * Gets the resolving parent.
1969                 * If not overridden, the resolving parent defaults to 
1970                 * the parent element.
1971                 *
1972                 * @return the attributes from the parent, <code>null</code> if none
1973                 * @see AttributeSet#getResolveParent
1974                 */
1975                public AttributeSet getResolveParent() {
1976                    AttributeSet a = attributes.getResolveParent();
1977                    if ((a == null) && (parent != null)) {
1978                        a = parent.getAttributes();
1979                    }
1980                    return a;
1981                }
1982
1983                // --- MutableAttributeSet ----------------------------------
1984                // should fetch a new immutable record for the field
1985                // "attributes".
1986
1987                /**
1988                 * Adds an attribute to the element.
1989                 *
1990                 * @param name the non-null attribute name
1991                 * @param value the attribute value
1992                 * @see MutableAttributeSet#addAttribute
1993                 */
1994                public void addAttribute(Object name, Object value) {
1995                    checkForIllegalCast();
1996                    AttributeContext context = getAttributeContext();
1997                    attributes = context.addAttribute(attributes, name, value);
1998                }
1999
2000                /**
2001                 * Adds a set of attributes to the element.
2002                 *
2003                 * @param attr the attributes to add
2004                 * @see MutableAttributeSet#addAttribute
2005                 */
2006                public void addAttributes(AttributeSet attr) {
2007                    checkForIllegalCast();
2008                    AttributeContext context = getAttributeContext();
2009                    attributes = context.addAttributes(attributes, attr);
2010                }
2011
2012                /**
2013                 * Removes an attribute from the set.
2014                 *
2015                 * @param name the non-null attribute name
2016                 * @see MutableAttributeSet#removeAttribute
2017                 */
2018                public void removeAttribute(Object name) {
2019                    checkForIllegalCast();
2020                    AttributeContext context = getAttributeContext();
2021                    attributes = context.removeAttribute(attributes, name);
2022                }
2023
2024                /**
2025                 * Removes a set of attributes for the element.
2026                 *
2027                 * @param names the attribute names
2028                 * @see MutableAttributeSet#removeAttributes
2029                 */
2030                public void removeAttributes(Enumeration<?> names) {
2031                    checkForIllegalCast();
2032                    AttributeContext context = getAttributeContext();
2033                    attributes = context.removeAttributes(attributes, names);
2034                }
2035
2036                /**
2037                 * Removes a set of attributes for the element.
2038                 *
2039                 * @param attrs the attributes
2040                 * @see MutableAttributeSet#removeAttributes
2041                 */
2042                public void removeAttributes(AttributeSet attrs) {
2043                    checkForIllegalCast();
2044                    AttributeContext context = getAttributeContext();
2045                    if (attrs == this ) {
2046                        attributes = context.getEmptySet();
2047                    } else {
2048                        attributes = context
2049                                .removeAttributes(attributes, attrs);
2050                    }
2051                }
2052
2053                /**
2054                 * Sets the resolving parent.
2055                 *
2056                 * @param parent the parent, null if none
2057                 * @see MutableAttributeSet#setResolveParent
2058                 */
2059                public void setResolveParent(AttributeSet parent) {
2060                    checkForIllegalCast();
2061                    AttributeContext context = getAttributeContext();
2062                    if (parent != null) {
2063                        attributes = context.addAttribute(attributes,
2064                                StyleConstants.ResolveAttribute, parent);
2065                    } else {
2066                        attributes = context.removeAttribute(attributes,
2067                                StyleConstants.ResolveAttribute);
2068                    }
2069                }
2070
2071                private final void checkForIllegalCast() {
2072                    Thread t = getCurrentWriter();
2073                    if ((t == null) || (t != Thread.currentThread())) {
2074                        throw new StateInvariantError(
2075                                "Illegal cast to MutableAttributeSet");
2076                    }
2077                }
2078
2079                // --- Element methods -------------------------------------
2080
2081                /**
2082                 * Retrieves the underlying model.
2083                 *
2084                 * @return the model
2085                 */
2086                public Document getDocument() {
2087                    return AbstractDocument.this ;
2088                }
2089
2090                /**
2091                 * Gets the parent of the element.
2092                 *
2093                 * @return the parent
2094                 */
2095                public Element getParentElement() {
2096                    return parent;
2097                }
2098
2099                /**
2100                 * Gets the attributes for the element.
2101                 *
2102                 * @return the attribute set
2103                 */
2104                public AttributeSet getAttributes() {
2105                    return this ;
2106                }
2107
2108                /**
2109                 * Gets the name of the element.
2110                 *
2111                 * @return the name, null if none
2112                 */
2113                public String getName() {
2114                    if (attributes.isDefined(ElementNameAttribute)) {
2115                        return (String) attributes
2116                                .getAttribute(ElementNameAttribute);
2117                    }
2118                    return null;
2119                }
2120
2121                /**
2122                 * Gets the starting offset in the model for the element.
2123                 *
2124                 * @return the offset >= 0
2125                 */
2126                public abstract int getStartOffset();
2127
2128                /**
2129                 * Gets the ending offset in the model for the element.
2130                 *
2131                 * @return the offset >= 0
2132                 */
2133                public abstract int getEndOffset();
2134
2135                /**
2136                 * Gets a child element.
2137                 *
2138                 * @param index the child index, >= 0 && < getElementCount()
2139                 * @return the child element
2140                 */
2141                public abstract Element getElement(int index);
2142
2143                /**
2144                 * Gets the number of children for the element.
2145                 *
2146                 * @return the number of children >= 0
2147                 */
2148                public abstract int getElementCount();
2149
2150                /**
2151                 * Gets the child element index closest to the given model offset.
2152                 *
2153                 * @param offset the offset >= 0
2154                 * @return the element index >= 0
2155                 */
2156                public abstract int getElementIndex(int offset);
2157
2158                /**
2159                 * Checks whether the element is a leaf.
2160                 *
2161                 * @return true if a leaf
2162                 */
2163                public abstract boolean isLeaf();
2164
2165                // --- TreeNode methods -------------------------------------
2166
2167                /**
2168                 * Returns the child <code>TreeNode</code> at index 
2169                 * <code>childIndex</code>.
2170                 */
2171                public TreeNode getChildAt(int childIndex) {
2172                    return (TreeNode) getElement(childIndex);
2173                }
2174
2175                /**
2176                 * Returns the number of children <code>TreeNode</code>'s 
2177                 * receiver contains.
2178                 * @return the number of children <code>TreeNodews</code>'s
2179                 * receiver contains
2180                 */
2181                public int getChildCount() {
2182                    return getElementCount();
2183                }
2184
2185                /**
2186                 * Returns the parent <code>TreeNode</code> of the receiver.
2187                 * @return the parent <code>TreeNode</code> of the receiver
2188                 */
2189                public TreeNode getParent() {
2190                    return (TreeNode) getParentElement();
2191                }
2192
2193                /**
2194                 * Returns the index of <code>node</code> in the receivers children.
2195                 * If the receiver does not contain <code>node</code>, -1 will be
2196                 * returned.
2197                 * @param node the location of interest
2198                 * @return the index of <code>node</code> in the receiver's
2199                 * children, or -1 if absent
2200                 */
2201                public int getIndex(TreeNode node) {
2202                    for (int counter = getChildCount() - 1; counter >= 0; counter--)
2203                        if (getChildAt(counter) == node)
2204                            return counter;
2205                    return -1;
2206                }
2207
2208                /**
2209                 * Returns true if the receiver allows children.
2210                 * @return true if the receiver allows children, otherwise false
2211                 */
2212                public abstract boolean getAllowsChildren();
2213
2214                /**
2215                 * Returns the children of the receiver as an
2216                 * <code>Enumeration</code>.
2217                 * @return the children of the receiver as an <code>Enumeration</code>
2218                 */
2219                public abstract Enumeration children();
2220
2221                // --- serialization ---------------------------------------------
2222
2223                private void writeObject(ObjectOutputStream s)
2224                        throws IOException {
2225                    s.defaultWriteObject();
2226                    StyleContext.writeAttributeSet(s, attributes);
2227                }
2228
2229                private void readObject(ObjectInputStream s)
2230                        throws ClassNotFoundException, IOException {
2231                    s.defaultReadObject();
2232                    MutableAttributeSet attr = new SimpleAttributeSet();
2233                    StyleContext.readAttributeSet(s, attr);
2234                    AttributeContext context = getAttributeContext();
2235                    attributes = context.addAttributes(
2236                            SimpleAttributeSet.EMPTY, attr);
2237                }
2238
2239                // ---- variables -----------------------------------------------------
2240
2241                private Element parent;
2242                private transient AttributeSet attributes;
2243
2244            }
2245
2246            /**
2247             * Implements a composite element that contains other elements.
2248             * <p>
2249             * <strong>Warning:</strong>
2250             * Serialized objects of this class will not be compatible with
2251             * future Swing releases. The current serialization support is
2252             * appropriate for short term storage or RMI between applications running
2253             * the same version of Swing.  As of 1.4, support for long term storage
2254             * of all JavaBeans<sup><font size="-2">TM</font></sup>
2255             * has been added to the <code>java.beans</code> package.
2256             * Please see {@link java.beans.XMLEncoder}.
2257             */
2258            public class BranchElement extends AbstractElement {
2259
2260                /**
2261                 * Constructs a composite element that initially contains
2262                 * no children.
2263                 *
2264                 * @param parent  The parent element
2265                 * @param a the attributes for the element
2266                 * @since 1.4
2267                 */
2268                public BranchElement(Element parent, AttributeSet a) {
2269                    super (parent, a);
2270                    children = new AbstractElement[1];
2271                    nchildren = 0;
2272                    lastIndex = -1;
2273                }
2274
2275                /**
2276                 * Gets the child element that contains
2277                 * the given model position.
2278                 *
2279                 * @param pos the position >= 0
2280                 * @return the element, null if none
2281                 */
2282                public Element positionToElement(int pos) {
2283                    int index = getElementIndex(pos);
2284                    Element child = children[index];
2285                    int p0 = child.getStartOffset();
2286                    int p1 = child.getEndOffset();
2287                    if ((pos >= p0) && (pos < p1)) {
2288                        return child;
2289                    }
2290                    return null;
2291                }
2292
2293                /**
2294                 * Replaces content with a new set of elements.
2295                 *
2296                 * @param offset the starting offset >= 0
2297                 * @param length the length to replace >= 0
2298                 * @param elems the new elements
2299                 */
2300                public void replace(int offset, int length, Element[] elems) {
2301                    int delta = elems.length - length;
2302                    int src = offset + length;
2303                    int nmove = nchildren - src;
2304                    int dest = src + delta;
2305                    if ((nchildren + delta) >= children.length) {
2306                        // need to grow the array
2307                        int newLength = Math.max(2 * children.length, nchildren
2308                                + delta);
2309                        AbstractElement[] newChildren = new AbstractElement[newLength];
2310                        System.arraycopy(children, 0, newChildren, 0, offset);
2311                        System.arraycopy(elems, 0, newChildren, offset,
2312                                elems.length);
2313                        System.arraycopy(children, src, newChildren, dest,
2314                                nmove);
2315                        children = newChildren;
2316                    } else {
2317                        // patch the existing array
2318                        System.arraycopy(children, src, children, dest, nmove);
2319                        System.arraycopy(elems, 0, children, offset,
2320                                elems.length);
2321                    }
2322                    nchildren = nchildren + delta;
2323                }
2324
2325                /**
2326                 * Converts the element to a string.
2327                 *
2328                 * @return the string
2329                 */
2330                public String toString() {
2331                    return "BranchElement(" + getName() + ") "
2332                            + getStartOffset() + "," + getEndOffset() + "\n";
2333                }
2334
2335                // --- Element methods -----------------------------------
2336
2337                /**
2338                 * Gets the element name.
2339                 *
2340                 * @return the element name
2341                 */
2342                public String getName() {
2343                    String nm = super .getName();
2344                    if (nm == null) {
2345                        nm = ParagraphElementName;
2346                    }
2347                    return nm;
2348                }
2349
2350                /**
2351                 * Gets the starting offset in the model for the element.
2352                 *
2353                 * @return the offset >= 0
2354                 */
2355                public int getStartOffset() {
2356                    return children[0].getStartOffset();
2357                }
2358
2359                /**
2360                 * Gets the ending offset in the model for the element.
2361                 * @throws NullPointerException if this element has no children
2362                 *
2363                 * @return the offset >= 0
2364                 */
2365                public int getEndOffset() {
2366                    Element child = (nchildren > 0) ? children[nchildren - 1]
2367                            : children[0];
2368                    return child.getEndOffset();
2369                }
2370
2371                /**
2372                 * Gets a child element.
2373                 *
2374                 * @param index the child index, >= 0 && < getElementCount()
2375                 * @return the child element, null if none
2376                 */
2377                public Element getElement(int index) {
2378                    if (index < nchildren) {
2379                        return children[index];
2380                    }
2381                    return null;
2382                }
2383
2384                /**
2385                 * Gets the number of children for the element.
2386                 *
2387                 * @return the number of children >= 0
2388                 */
2389                public int getElementCount() {
2390                    return nchildren;
2391                }
2392
2393                /**
2394                 * Gets the child element index closest to the given model offset.
2395                 *
2396                 * @param offset the offset >= 0
2397                 * @return the element index >= 0
2398                 */
2399                public int getElementIndex(int offset) {
2400                    int index;
2401                    int lower = 0;
2402                    int upper = nchildren - 1;
2403                    int mid = 0;
2404                    int p0 = getStartOffset();
2405                    int p1;
2406
2407                    if (nchildren == 0) {
2408                        return 0;
2409                    }
2410                    if (offset >= getEndOffset()) {
2411                        return nchildren - 1;
2412                    }
2413
2414                    // see if the last index can be used.
2415                    if ((lastIndex >= lower) && (lastIndex <= upper)) {
2416                        Element lastHit = children[lastIndex];
2417                        p0 = lastHit.getStartOffset();
2418                        p1 = lastHit.getEndOffset();
2419                        if ((offset >= p0) && (offset < p1)) {
2420                            return lastIndex;
2421                        }
2422
2423                        // last index wasn't a hit, but it does give useful info about
2424                        // where a hit (if any) would be.
2425                        if (offset < p0) {
2426                            upper = lastIndex;
2427                        } else {
2428                            lower = lastIndex;
2429                        }
2430                    }
2431
2432                    while (lower <= upper) {
2433                        mid = lower + ((upper - lower) / 2);
2434                        Element elem = children[mid];
2435                        p0 = elem.getStartOffset();
2436                        p1 = elem.getEndOffset();
2437                        if ((offset >= p0) && (offset < p1)) {
2438                            // found the location
2439                            index = mid;
2440                            lastIndex = index;
2441                            return index;
2442                        } else if (offset < p0) {
2443                            upper = mid - 1;
2444                        } else {
2445                            lower = mid + 1;
2446                        }
2447                    }
2448
2449                    // didn't find it, but we indicate the index of where it would belong
2450                    if (offset < p0) {
2451                        index = mid;
2452                    } else {
2453                        index = mid + 1;
2454                    }
2455                    lastIndex = index;
2456                    return index;
2457                }
2458
2459                /**
2460                 * Checks whether the element is a leaf.
2461                 *
2462                 * @return true if a leaf
2463                 */
2464                public boolean isLeaf() {
2465                    return false;
2466                }
2467
2468                // ------ TreeNode ----------------------------------------------
2469
2470                /**
2471                 * Returns true if the receiver allows children.
2472                 * @return true if the receiver allows children, otherwise false
2473                 */
2474                public boolean getAllowsChildren() {
2475                    return true;
2476                }
2477
2478                /**
2479                 * Returns the children of the receiver as an
2480                 * <code>Enumeration</code>.
2481                 * @return the children of the receiver
2482                 */
2483                public Enumeration children() {
2484                    if (nchildren == 0)
2485                        return null;
2486
2487                    Vector tempVector = new Vector(nchildren);
2488
2489                    for (int counter = 0; counter < nchildren; counter++)
2490                        tempVector.addElement(children[counter]);
2491                    return tempVector.elements();
2492                }
2493
2494                // ------ members ----------------------------------------------
2495
2496                private AbstractElement[] children;
2497                private int nchildren;
2498                private int lastIndex;
2499            }
2500
2501            /**
2502             * Implements an element that directly represents content of
2503             * some kind.
2504             * <p>
2505             * <strong>Warning:</strong>
2506             * Serialized objects of this class will not be compatible with
2507             * future Swing releases. The current serialization support is
2508             * appropriate for short term storage or RMI between applications running
2509             * the same version of Swing.  As of 1.4, support for long term storage
2510             * of all JavaBeans<sup><font size="-2">TM</font></sup>
2511             * has been added to the <code>java.beans</code> package.
2512             * Please see {@link java.beans.XMLEncoder}.
2513             *
2514             * @see     Element
2515             */
2516            public class LeafElement extends AbstractElement {
2517
2518                /**
2519                 * Constructs an element that represents content within the
2520                 * document (has no children).
2521                 *
2522                 * @param parent  The parent element
2523                 * @param a       The element attributes
2524                 * @param offs0   The start offset >= 0
2525                 * @param offs1   The end offset >= offs0
2526                 * @since 1.4
2527                 */
2528                public LeafElement(Element parent, AttributeSet a, int offs0,
2529                        int offs1) {
2530                    super (parent, a);
2531                    try {
2532                        p0 = createPosition(offs0);
2533                        p1 = createPosition(offs1);
2534                    } catch (BadLocationException e) {
2535                        p0 = null;
2536                        p1 = null;
2537                        throw new StateInvariantError(
2538                                "Can't create Position references");
2539                    }
2540                }
2541
2542                /**
2543                 * Converts the element to a string.
2544                 *
2545                 * @return the string
2546                 */
2547                public String toString() {
2548                    return "LeafElement(" + getName() + ") " + p0 + "," + p1
2549                            + "\n";
2550                }
2551
2552                // --- Element methods ---------------------------------------------
2553
2554                /**
2555                 * Gets the starting offset in the model for the element.
2556                 *
2557                 * @return the offset >= 0
2558                 */
2559                public int getStartOffset() {
2560                    return p0.getOffset();
2561                }
2562
2563                /**
2564                 * Gets the ending offset in the model for the element.
2565                 *
2566                 * @return the offset >= 0
2567                 */
2568                public int getEndOffset() {
2569                    return p1.getOffset();
2570                }
2571
2572                /**
2573                 * Gets the element name.
2574                 *
2575                 * @return the name
2576                 */
2577                public String getName() {
2578                    String nm = super .getName();
2579                    if (nm == null) {
2580                        nm = ContentElementName;
2581                    }
2582                    return nm;
2583                }
2584
2585                /**
2586                 * Gets the child element index closest to the given model offset.
2587                 *
2588                 * @param pos the offset >= 0
2589                 * @return the element index >= 0
2590                 */
2591                public int getElementIndex(int pos) {
2592                    return -1;
2593                }
2594
2595                /**
2596                 * Gets a child element.
2597                 *
2598                 * @param index the child index, >= 0 && < getElementCount()
2599                 * @return the child element
2600                 */
2601                public Element getElement(int index) {
2602                    return null;
2603                }
2604
2605                /**
2606                 * Returns the number of child elements.
2607                 *
2608                 * @return the number of children >= 0
2609                 */
2610                public int getElementCount() {
2611                    return 0;
2612                }
2613
2614                /**
2615                 * Checks whether the element is a leaf.
2616                 *
2617                 * @return true if a leaf
2618                 */
2619                public boolean isLeaf() {
2620                    return true;
2621                }
2622
2623                // ------ TreeNode ----------------------------------------------
2624
2625                /**
2626                 * Returns true if the receiver allows children.
2627                 * @return true if the receiver allows children, otherwise false
2628                 */
2629                public boolean getAllowsChildren() {
2630                    return false;
2631                }
2632
2633                /**
2634                 * Returns the children of the receiver as an
2635                 * <code>Enumeration</code>.
2636                 * @return the children of the receiver
2637                 */
2638                public Enumeration children() {
2639                    return null;
2640                }
2641
2642                // --- serialization ---------------------------------------------
2643
2644                private void writeObject(ObjectOutputStream s)
2645                        throws IOException {
2646                    s.defaultWriteObject();
2647                    s.writeInt(p0.getOffset());
2648                    s.writeInt(p1.getOffset());
2649                }
2650
2651                private void readObject(ObjectInputStream s)
2652                        throws ClassNotFoundException, IOException {
2653                    s.defaultReadObject();
2654
2655                    // set the range with positions that track change
2656                    int off0 = s.readInt();
2657                    int off1 = s.readInt();
2658                    try {
2659                        p0 = createPosition(off0);
2660                        p1 = createPosition(off1);
2661                    } catch (BadLocationException e) {
2662                        p0 = null;
2663                        p1 = null;
2664                        throw new IOException(
2665                                "Can't restore Position references");
2666                    }
2667                }
2668
2669                // ---- members -----------------------------------------------------
2670
2671                private transient Position p0;
2672                private transient Position p1;
2673            }
2674
2675            /**
2676             * Represents the root element of the bidirectional element structure.
2677             * The root element is the only element in the bidi element structure
2678             * which contains children.  
2679             */
2680            class BidiRootElement extends BranchElement {
2681
2682                BidiRootElement() {
2683                    super (null, null);
2684                }
2685
2686                /**
2687                 * Gets the name of the element.
2688                 * @return the name
2689                 */
2690                public String getName() {
2691                    return "bidi root";
2692                }
2693            }
2694
2695            /**
2696             * Represents an element of the bidirectional element structure.
2697             */
2698            class BidiElement extends LeafElement {
2699
2700                /**
2701                 * Creates a new BidiElement.
2702                 */
2703                BidiElement(Element parent, int start, int end, int level) {
2704                    super (parent, new SimpleAttributeSet(), start, end);
2705                    addAttribute(StyleConstants.BidiLevel, new Integer(level));
2706                    //System.out.println("BidiElement: start = " + start
2707                    //                   + " end = " + end + " level = " + level );
2708                }
2709
2710                /**
2711                 * Gets the name of the element.
2712                 * @return the name
2713                 */
2714                public String getName() {
2715                    return BidiElementName;
2716                }
2717
2718                int getLevel() {
2719                    Integer o = (Integer) getAttribute(StyleConstants.BidiLevel);
2720                    if (o != null) {
2721                        return o.intValue();
2722                    }
2723                    return 0; // Level 0 is base level (non-embedded) left-to-right
2724                }
2725
2726                boolean isLeftToRight() {
2727                    return ((getLevel() % 2) == 0);
2728                }
2729            }
2730
2731            /**
2732             * Stores document changes as the document is being
2733             * modified.  Can subsequently be used for change notification
2734             * when done with the document modification transaction.
2735             * This is used by the AbstractDocument class and its extensions
2736             * for broadcasting change information to the document listeners.
2737             */
2738            public class DefaultDocumentEvent extends CompoundEdit implements 
2739                    DocumentEvent {
2740
2741                /**
2742                 * Constructs a change record.
2743                 *
2744                 * @param offs the offset into the document of the change >= 0
2745                 * @param len  the length of the change >= 0
2746                 * @param type the type of event (DocumentEvent.EventType)
2747                 * @since 1.4
2748                 */
2749                public DefaultDocumentEvent(int offs, int len,
2750                        DocumentEvent.EventType type) {
2751                    super ();
2752                    offset = offs;
2753                    length = len;
2754                    this .type = type;
2755                }
2756
2757                /**
2758                 * Returns a string description of the change event.
2759                 *
2760                 * @return a string
2761                 */
2762                public String toString() {
2763                    return edits.toString();
2764                }
2765
2766                // --- CompoundEdit methods --------------------------
2767
2768                /**
2769                 * Adds a document edit.  If the number of edits crosses
2770                 * a threshold, this switches on a hashtable lookup for
2771                 * ElementChange implementations since access of these
2772                 * needs to be relatively quick.
2773                 *
2774                 * @param anEdit a document edit record
2775                 * @return true if the edit was added
2776                 */
2777                public boolean addEdit(UndoableEdit anEdit) {
2778                    // if the number of changes gets too great, start using
2779                    // a hashtable for to locate the change for a given element.
2780                    if ((changeLookup == null) && (edits.size() > 10)) {
2781                        changeLookup = new Hashtable();
2782                        int n = edits.size();
2783                        for (int i = 0; i < n; i++) {
2784                            Object o = edits.elementAt(i);
2785                            if (o instanceof  DocumentEvent.ElementChange) {
2786                                DocumentEvent.ElementChange ec = (DocumentEvent.ElementChange) o;
2787                                changeLookup.put(ec.getElement(), ec);
2788                            }
2789                        }
2790                    }
2791
2792                    // if we have a hashtable... add the entry if it's 
2793                    // an ElementChange.
2794                    if ((changeLookup != null)
2795                            && (anEdit instanceof  DocumentEvent.ElementChange)) {
2796                        DocumentEvent.ElementChange ec = (DocumentEvent.ElementChange) anEdit;
2797                        changeLookup.put(ec.getElement(), ec);
2798                    }
2799                    return super .addEdit(anEdit);
2800                }
2801
2802                /**
2803                 * Redoes a change.
2804                 *
2805                 * @exception CannotRedoException if the change cannot be redone
2806                 */
2807                public void redo() throws CannotRedoException {
2808                    writeLock();
2809                    try {
2810                        // change the state
2811                        super .redo();
2812                        // fire a DocumentEvent to notify the view(s)
2813                        UndoRedoDocumentEvent ev = new UndoRedoDocumentEvent(
2814                                this , false);
2815                        if (type == DocumentEvent.EventType.INSERT) {
2816                            fireInsertUpdate(ev);
2817                        } else if (type == DocumentEvent.EventType.REMOVE) {
2818                            fireRemoveUpdate(ev);
2819                        } else {
2820                            fireChangedUpdate(ev);
2821                        }
2822                    } finally {
2823                        writeUnlock();
2824                    }
2825                }
2826
2827                /**
2828                 * Undoes a change.
2829                 *
2830                 * @exception CannotUndoException if the change cannot be undone
2831                 */
2832                public void undo() throws CannotUndoException {
2833                    writeLock();
2834                    try {
2835                        // change the state
2836                        super .undo();
2837                        // fire a DocumentEvent to notify the view(s)
2838                        UndoRedoDocumentEvent ev = new UndoRedoDocumentEvent(
2839                                this , true);
2840                        if (type == DocumentEvent.EventType.REMOVE) {
2841                            fireInsertUpdate(ev);
2842                        } else if (type == DocumentEvent.EventType.INSERT) {
2843                            fireRemoveUpdate(ev);
2844                        } else {
2845                            fireChangedUpdate(ev);
2846                        }
2847                    } finally {
2848                        writeUnlock();
2849                    }
2850                }
2851
2852                /**
2853                 * DefaultDocument events are significant.  If you wish to aggregate
2854                 * DefaultDocumentEvents to present them as a single edit to the user
2855                 * place them into a CompoundEdit.
2856                 *
2857                 * @return whether the event is significant for edit undo purposes
2858                 */
2859                public boolean isSignificant() {
2860                    return true;
2861                }
2862
2863                /**
2864                 * Provides a localized, human readable description of this edit
2865                 * suitable for use in, say, a change log.
2866                 *
2867                 * @return the description
2868                 */
2869                public String getPresentationName() {
2870                    DocumentEvent.EventType type = getType();
2871                    if (type == DocumentEvent.EventType.INSERT)
2872                        return UIManager
2873                                .getString("AbstractDocument.additionText");
2874                    if (type == DocumentEvent.EventType.REMOVE)
2875                        return UIManager
2876                                .getString("AbstractDocument.deletionText");
2877                    return UIManager
2878                            .getString("AbstractDocument.styleChangeText");
2879                }
2880
2881                /**
2882                 * Provides a localized, human readable description of the undoable
2883                 * form of this edit, e.g. for use as an Undo menu item. Typically
2884                 * derived from getDescription();
2885                 *
2886                 * @return the description
2887                 */
2888                public String getUndoPresentationName() {
2889                    return UIManager.getString("AbstractDocument.undoText")
2890                            + " " + getPresentationName();
2891                }
2892
2893                /**
2894                 * Provides a localized, human readable description of the redoable
2895                 * form of this edit, e.g. for use as a Redo menu item. Typically
2896                 * derived from getPresentationName();
2897                 *
2898                 * @return the description
2899                 */
2900                public String getRedoPresentationName() {
2901                    return UIManager.getString("AbstractDocument.redoText")
2902                            + " " + getPresentationName();
2903                }
2904
2905                // --- DocumentEvent methods --------------------------
2906
2907                /**
2908                 * Returns the type of event.
2909                 *
2910                 * @return the event type as a DocumentEvent.EventType
2911                 * @see DocumentEvent#getType
2912                 */
2913                public DocumentEvent.EventType getType() {
2914                    return type;
2915                }
2916
2917                /**
2918                 * Returns the offset within the document of the start of the change.
2919                 *
2920                 * @return the offset >= 0
2921                 * @see DocumentEvent#getOffset
2922                 */
2923                public int getOffset() {
2924                    return offset;
2925                }
2926
2927                /**
2928                 * Returns the length of the change.
2929                 *
2930                 * @return the length >= 0
2931                 * @see DocumentEvent#getLength
2932                 */
2933                public int getLength() {
2934                    return length;
2935                }
2936
2937                /**
2938                 * Gets the document that sourced the change event.
2939                 *
2940                 * @return the document
2941                 * @see DocumentEvent#getDocument
2942                 */
2943                public Document getDocument() {
2944                    return AbstractDocument.this ;
2945                }
2946
2947                /**
2948                 * Gets the changes for an element.
2949                 *
2950                 * @param elem the element
2951                 * @return the changes
2952                 */
2953                public DocumentEvent.ElementChange getChange(Element elem) {
2954                    if (changeLookup != null) {
2955                        return (DocumentEvent.ElementChange) changeLookup
2956                                .get(elem);
2957                    }
2958                    int n = edits.size();
2959                    for (int i = 0; i < n; i++) {
2960                        Object o = edits.elementAt(i);
2961                        if (o instanceof  DocumentEvent.ElementChange) {
2962                            DocumentEvent.ElementChange c = (DocumentEvent.ElementChange) o;
2963                            if (elem.equals(c.getElement())) {
2964                                return c;
2965                            }
2966                        }
2967                    }
2968                    return null;
2969                }
2970
2971                // --- member variables ------------------------------------
2972
2973                private int offset;
2974                private int length;
2975                private Hashtable changeLookup;
2976                private DocumentEvent.EventType type;
2977
2978            }
2979
2980            /**
2981             * This event used when firing document changes while Undo/Redo
2982             * operations. It just wraps DefaultDocumentEvent and delegates
2983             * all calls to it except getType() which depends on operation
2984             * (Undo or Redo).
2985             */
2986            class UndoRedoDocumentEvent implements  DocumentEvent {
2987                private DefaultDocumentEvent src = null;
2988                private boolean isUndo;
2989                private EventType type = null;
2990
2991                public UndoRedoDocumentEvent(DefaultDocumentEvent src,
2992                        boolean isUndo) {
2993                    this .src = src;
2994                    this .isUndo = isUndo;
2995                    if (isUndo) {
2996                        if (src.getType().equals(EventType.INSERT)) {
2997                            type = EventType.REMOVE;
2998                        } else if (src.getType().equals(EventType.REMOVE)) {
2999                            type = EventType.INSERT;
3000                        } else {
3001                            type = src.getType();
3002                        }
3003                    } else {
3004                        type = src.getType();
3005                    }
3006                }
3007
3008                public DefaultDocumentEvent getSource() {
3009                    return src;
3010                }
3011
3012                // DocumentEvent methods delegated to DefaultDocumentEvent source
3013                // except getType() which depends on operation (Undo or Redo).
3014                public int getOffset() {
3015                    return src.getOffset();
3016                }
3017
3018                public int getLength() {
3019                    return src.getLength();
3020                }
3021
3022                public Document getDocument() {
3023                    return src.getDocument();
3024                }
3025
3026                public DocumentEvent.EventType getType() {
3027                    return type;
3028                }
3029
3030                public DocumentEvent.ElementChange getChange(Element elem) {
3031                    return src.getChange(elem);
3032                }
3033            }
3034
3035            /**
3036             * An implementation of ElementChange that can be added to the document
3037             * event.
3038             */
3039            public static class ElementEdit extends AbstractUndoableEdit
3040                    implements  DocumentEvent.ElementChange {
3041
3042                /**
3043                 * Constructs an edit record.  This does not modify the element
3044                 * so it can safely be used to <em>catch up</em> a view to the
3045                 * current model state for views that just attached to a model.
3046                 *
3047                 * @param e the element
3048                 * @param index the index into the model >= 0
3049                 * @param removed a set of elements that were removed
3050                 * @param added a set of elements that were added
3051                 */
3052                public ElementEdit(Element e, int index, Element[] removed,
3053                        Element[] added) {
3054                    super ();
3055                    this .e = e;
3056                    this .index = index;
3057                    this .removed = removed;
3058                    this .added = added;
3059                }
3060
3061                /**
3062                 * Returns the underlying element.
3063                 *
3064                 * @return the element
3065                 */
3066                public Element getElement() {
3067                    return e;
3068                }
3069
3070                /**
3071                 * Returns the index into the list of elements.
3072                 *
3073                 * @return the index >= 0
3074                 */
3075                public int getIndex() {
3076                    return index;
3077                }
3078
3079                /**
3080                 * Gets a list of children that were removed.
3081                 *
3082                 * @return the list
3083                 */
3084                public Element[] getChildrenRemoved() {
3085                    return removed;
3086                }
3087
3088                /**
3089                 * Gets a list of children that were added.
3090                 *
3091                 * @return the list
3092                 */
3093                public Element[] getChildrenAdded() {
3094                    return added;
3095                }
3096
3097                /**
3098                 * Redoes a change.
3099                 *
3100                 * @exception CannotRedoException if the change cannot be redone
3101                 */
3102                public void redo() throws CannotRedoException {
3103                    super .redo();
3104
3105                    // Since this event will be reused, switch around added/removed.
3106                    Element[] tmp = removed;
3107                    removed = added;
3108                    added = tmp;
3109
3110                    // PENDING(prinz) need MutableElement interface, canRedo() should check
3111                    ((AbstractDocument.BranchElement) e).replace(index,
3112                            removed.length, added);
3113                }
3114
3115                /**
3116                 * Undoes a change.
3117                 *
3118                 * @exception CannotUndoException if the change cannot be undone
3119                 */
3120                public void undo() throws CannotUndoException {
3121                    super .undo();
3122                    // PENDING(prinz) need MutableElement interface, canUndo() should check
3123                    ((AbstractDocument.BranchElement) e).replace(index,
3124                            added.length, removed);
3125
3126                    // Since this event will be reused, switch around added/removed.
3127                    Element[] tmp = removed;
3128                    removed = added;
3129                    added = tmp;
3130                }
3131
3132                private Element e;
3133                private int index;
3134                private Element[] removed;
3135                private Element[] added;
3136            }
3137
3138            private class DefaultFilterBypass extends
3139                    DocumentFilter.FilterBypass {
3140                public Document getDocument() {
3141                    return AbstractDocument.this ;
3142                }
3143
3144                public void remove(int offset, int length)
3145                        throws BadLocationException {
3146                    handleRemove(offset, length);
3147                }
3148
3149                public void insertString(int offset, String string,
3150                        AttributeSet attr) throws BadLocationException {
3151                    handleInsertString(offset, string, attr);
3152                }
3153
3154                public void replace(int offset, int length, String text,
3155                        AttributeSet attrs) throws BadLocationException {
3156                    handleRemove(offset, length);
3157                    handleInsertString(offset, text, attrs);
3158                }
3159            }
3160        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.