home *** CD-ROM | disk | FTP | other *** search
/ Chip 1998 November / Chip_1998-11_cd.bin / tema / Cafe / jfc.bin / AbstractDocument.java < prev    next >
Text File  |  1998-02-26  |  61KB  |  2,046 lines

  1. /*
  2.  * @(#)AbstractDocument.java    1.74 98/02/05
  3.  * 
  4.  * Copyright (c) 1997 Sun Microsystems, Inc. All Rights Reserved.
  5.  * 
  6.  * This software is the confidential and proprietary information of Sun
  7.  * Microsystems, Inc. ("Confidential Information").  You shall not
  8.  * disclose such Confidential Information and shall use it only in
  9.  * accordance with the terms of the license agreement you entered into
  10.  * with Sun.
  11.  * 
  12.  * SUN MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY OF THE
  13.  * SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
  14.  * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
  15.  * PURPOSE, OR NON-INFRINGEMENT. SUN SHALL NOT BE LIABLE FOR ANY DAMAGES
  16.  * SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR DISTRIBUTING
  17.  * THIS SOFTWARE OR ITS DERIVATIVES.
  18.  * 
  19.  */
  20. package com.sun.java.swing.text;
  21.  
  22. import java.util.*;
  23. import java.io.*;
  24.  
  25. import com.sun.java.swing.undo.*;
  26. import com.sun.java.swing.event.ChangeListener;
  27. import com.sun.java.swing.event.*;
  28.  
  29. /**
  30.  * An implementation of the document interface to serve as a 
  31.  * basis for implementing various kinds of documents.  At this
  32.  * level there is very little policy, so there is a corresponding
  33.  * increase in difficulty of use.
  34.  * <p>
  35.  * This class implements a locking mechanism for the document.  It
  36.  * allows multiple readers or one writer, and writers must wait until 
  37.  * all observers of the document have been notified of a previous 
  38.  * change before beginning another mutation to the document.  The
  39.  * read lock is aquired and released using the <code>render</code>
  40.  * method.  A write lock is aquired by the methods that mutate the
  41.  * document, and are held for the duration of the method call.
  42.  * Notification is done on the thread that produced the mutation, 
  43.  * and the thread has full read access to the document for the
  44.  * duration of the notification, but other readers are kept out
  45.  * until the notification has finished.  The notification is a
  46.  * beans event notification which does not allow any further 
  47.  * mutations until all listeners have been notified.
  48.  * <p>
  49.  * Any models subclassed from this class and used in conjunction
  50.  * with a text component that has a look and feel implementation
  51.  * that is derived from DefaultTextUI may be safely updated
  52.  * asynchronously.  The rendering in DefaultTextUI occurs under
  53.  * the protection of the <code>render</code> method, which makes
  54.  * rendering safe if it is running on the event thread (which
  55.  * happens if using repaint to schedule updates).  The code path
  56.  * for any DocumentListener implementation must not access the
  57.  * component lock if trying to be safe from deadlocks.  
  58.  * The <cod>repaint</code> and <code>revalidate</code> methods 
  59.  * on JComponent are safe.
  60.  * <p>
  61.  * Warning: serialized objects of this class will not be compatible with
  62.  * future swing releases.  The current serialization support is appropriate 
  63.  * for short term storage or RMI between Swing1.0 applications.  It will
  64.  * not be possible to load serialized Swing1.0 objects with future releases
  65.  * of Swing.  The JDK1.2 release of Swing will be the compatibility
  66.  * baseline for the serialized form of Swing objects.
  67.  *
  68.  * @author  Timothy Prinzing
  69.  * @version 1.74 02/05/98
  70.  */
  71. public abstract class AbstractDocument implements Document, Serializable {
  72.  
  73.     /**
  74.      * Constructs a new AbstractDocument, wrapped around some
  75.      * specified content storage mechanism.
  76.      *
  77.      * @param data the content
  78.      */
  79.     protected AbstractDocument(Content data) {
  80.     this(data, StyleContext.getDefaultStyleContext());
  81.     }
  82.  
  83.     /**
  84.      * Constructs a new AbstractDocument, wrapped around some
  85.      * specified content storage mechanism.
  86.      *
  87.      * @param data the content
  88.      */
  89.     protected AbstractDocument(Content data, AttributeContext context) {
  90.     this.data = data;
  91.     this.context = context;
  92.     }
  93.  
  94.     /**
  95.      * Support for managing a set of properties. Callers
  96.      * can use the documentProperties dictionary to annotate the
  97.      * document with document-wide properties.
  98.      * 
  99.      * @return a non null Dictionary 
  100.      * @see #setDocumentProperties
  101.      */
  102.     public Dictionary getDocumentProperties() {
  103.     if (documentProperties == null) {
  104.         documentProperties = new Hashtable(2);
  105.     }
  106.     return documentProperties;
  107.     }
  108.  
  109.     /**
  110.      * Replace the document properties dictionary for this document.
  111.      * 
  112.      * @see #getDocumentProperties
  113.      */
  114.     public void setDocumentProperties(Dictionary x) {
  115.     documentProperties = x;
  116.     }
  117.  
  118.     /**
  119.      * Notifies all listeners that have registered interest for
  120.      * notification on this event type.  The event instance 
  121.      * is lazily created using the parameters passed into 
  122.      * the fire method.
  123.      *
  124.      * @param e the event
  125.      * @see EventListenerList
  126.      */
  127.     protected void fireInsertUpdate(DocumentEvent e) {
  128.     // Guaranteed to return a non-null array
  129.     Object[] listeners = listenerList.getListenerList();
  130.     // Process the listeners last to first, notifying
  131.     // those that are interested in this event
  132.     for (int i = listeners.length-2; i>=0; i-=2) {
  133.         if (listeners[i]==DocumentListener.class) {
  134.         // Lazily create the event:
  135.         // if (e == null)
  136.         // e = new ListSelectionEvent(this, firstIndex, lastIndex);
  137.         ((DocumentListener)listeners[i+1]).insertUpdate(e);
  138.         }           
  139.     }
  140.     }
  141.  
  142.     /**
  143.      * Notifies all listeners that have registered interest for
  144.      * notification on this event type.  The event instance 
  145.      * is lazily created using the parameters passed into 
  146.      * the fire method.
  147.      *
  148.      * @param e the event
  149.      * @see EventListenerList
  150.      */
  151.     protected void fireChangedUpdate(DocumentEvent e) {
  152.     // Guaranteed to return a non-null array
  153.     Object[] listeners = listenerList.getListenerList();
  154.     // Process the listeners last to first, notifying
  155.     // those that are interested in this event
  156.     for (int i = listeners.length-2; i>=0; i-=2) {
  157.         if (listeners[i]==DocumentListener.class) {
  158.         // Lazily create the event:
  159.         // if (e == null)
  160.         // e = new ListSelectionEvent(this, firstIndex, lastIndex);
  161.         ((DocumentListener)listeners[i+1]).changedUpdate(e);
  162.         }           
  163.     }
  164.     }
  165.  
  166.     /**
  167.      * Notifies all listeners that have registered interest for
  168.      * notification on this event type.  The event instance 
  169.      * is lazily created using the parameters passed into 
  170.      * the fire method.
  171.      *
  172.      * @param e the event
  173.      * @see EventListenerList
  174.      */
  175.     protected void fireRemoveUpdate(DocumentEvent e) {
  176.     // Guaranteed to return a non-null array
  177.     Object[] listeners = listenerList.getListenerList();
  178.     // Process the listeners last to first, notifying
  179.     // those that are interested in this event
  180.     for (int i = listeners.length-2; i>=0; i-=2) {
  181.         if (listeners[i]==DocumentListener.class) {
  182.         // Lazily create the event:
  183.         // if (e == null)
  184.         // e = new ListSelectionEvent(this, firstIndex, lastIndex);
  185.         ((DocumentListener)listeners[i+1]).removeUpdate(e);
  186.         }           
  187.     }
  188.     }
  189.  
  190.     /**
  191.      * Notifies all listeners that have registered interest for
  192.      * notification on this event type.  The event instance 
  193.      * is lazily created using the parameters passed into 
  194.      * the fire method.
  195.      *
  196.      * @param e the event
  197.      * @see EventListenerList
  198.      */
  199.     protected void fireUndoableEditUpdate(UndoableEditEvent e) {
  200.     // Guaranteed to return a non-null array
  201.     Object[] listeners = listenerList.getListenerList();
  202.     // Process the listeners last to first, notifying
  203.     // those that are interested in this event
  204.     for (int i = listeners.length-2; i>=0; i-=2) {
  205.         if (listeners[i]==UndoableEditListener.class) {
  206.         // Lazily create the event:
  207.         // if (e == null)
  208.         // e = new ListSelectionEvent(this, firstIndex, lastIndex);
  209.         ((UndoableEditListener)listeners[i+1]).undoableEditHappened(e);
  210.         }           
  211.     }
  212.     }
  213.  
  214.     // --- Document methods -----------------------------------------
  215.  
  216.     /**
  217.      * This allows the model to be safely rendered in the presence
  218.      * of currency, if the model supports being updated asynchronously.
  219.      * The given runnable will be executed in a way that allows it
  220.      * to safely read the model with no changes while the runnable
  221.      * is being executed.  The runnable itself may <em>not</em>
  222.      * make any mutations. 
  223.      * <p>
  224.      * This is implemented to aquire a read lock for the duration
  225.      * of the runnables execution.  There may be multiple runnables
  226.      * executing at the same time, and all writers will be blocked
  227.      * while there are active rendering runnables.  If the runnable
  228.      * throws an exception, it's lock will be safely released.
  229.      * There is no protection against a runnable that never exits,
  230.      * which will effectively leave the document locked for it's
  231.      * lifetime.
  232.      * <p>
  233.      * If the given runnable attempts to make any mutations in
  234.      * this implementation, a deadlock will occur.  There is
  235.      * no tracking of individual rendering threads to enable
  236.      * detecting this situation, but a subclass could incur
  237.      * the overhead of tracking them and throwing an error.
  238.      * <p>
  239.      * This method is thread safe, although most Swing methods
  240.      * are not. Please see 
  241.      * <A HREF="http://java.sun.com/products/jfc/swingdoc/threads.html">Threads
  242.      * and Swing</A> for more information.
  243.      *
  244.      * @param r the renderer to execute.
  245.      */
  246.     public void render(Runnable r) {
  247.     try {
  248.         readLock();
  249.         r.run();
  250.     } finally {
  251.         readUnlock();
  252.     }
  253.     }
  254.  
  255.     /**
  256.      * Returns the length of the data.  This is the number of
  257.      * characters of content that represents the users data.
  258.      *
  259.      * @return the length
  260.      * @see Document#getLength
  261.      */
  262.     public int getLength() {
  263.     return data.length() - 1;
  264.     }
  265.  
  266.     /**
  267.      * Adds a document listener for notification of any changes.
  268.      *
  269.      * @param listener the listener
  270.      * @see Document#addDocumentListener
  271.      */
  272.     public void addDocumentListener(DocumentListener listener) {
  273.     listenerList.add(DocumentListener.class, listener);
  274.     }
  275.  
  276.     /**
  277.      * Removes a document listener.
  278.      *
  279.      * @param listener the listener
  280.      * @see Document#removeDocumentListener
  281.      */
  282.     public void removeDocumentListener(DocumentListener listener) {
  283.     listenerList.remove(DocumentListener.class, listener);
  284.     }
  285.  
  286.     /**
  287.      * Adds an undo listener for notification of any changes.
  288.      * Undo/Redo operations performed on the UndoableEdit will
  289.      * cause the appropriate DocumentEvent to be fired to keep
  290.      * the view(s) in sync with the model.
  291.      *
  292.      * @param listener the listener
  293.      * @see Document#addUndoableEditListener
  294.      */
  295.     public void addUndoableEditListener(UndoableEditListener listener) {
  296.     listenerList.add(UndoableEditListener.class, listener);
  297.     }
  298.  
  299.     /**
  300.      * Removes an undo listener.
  301.      *
  302.      * @param listener the listener
  303.      * @see Document#removeDocumentListener
  304.      */
  305.     public void removeUndoableEditListener(UndoableEditListener listener) {
  306.     listenerList.remove(UndoableEditListener.class, listener);
  307.     }
  308.  
  309.     /**
  310.      * A convenience method for looking up a property value. It is
  311.      * equivalent to:
  312.      * <pre>
  313.      * getDocumentProperties().get(key);
  314.      * </pre>
  315.      * 
  316.      * @return the value of this property or null
  317.      * @see #getDocumentProperties
  318.      */
  319.     public final Object getProperty(Object key) {
  320.         return getDocumentProperties().get(key);
  321.     }
  322.  
  323.  
  324.     /**
  325.      * A convenience method for storing up a property value. It is
  326.      * equivalent to:
  327.      * <pre>
  328.      * getDocumentProperties().put(key, value);
  329.      * </pre>
  330.      * If value is null this method will remove the property
  331.      * 
  332.      * @see #getDocumentProperties
  333.      */
  334.     public final void putProperty(Object key, Object value) {
  335.     if (value != null) {
  336.         getDocumentProperties().put(key, value);
  337.     } else
  338.             getDocumentProperties().remove(key);
  339.     }
  340.  
  341.     /**
  342.      * Removes some content from the document.
  343.      * Removing content causes a write lock to be held while the
  344.      * actual changes are taking place.  Observers are notified
  345.      * of the change on the thread that called this method.
  346.      * <p>
  347.      * This method is thread safe, although most Swing methods
  348.      * are not. Please see 
  349.      * <A HREF="http://java.sun.com/products/jfc/swingdoc/threads.html">Threads
  350.      * and Swing</A> for more information.
  351.      * 
  352.      * @param offs the starting offset
  353.      * @param len the number of characters to remove
  354.      * @exception BadLocationException  the given remove position is not a valid 
  355.      * position within the document
  356.      * @see Document#remove
  357.      */
  358.     public void remove(int offs, int len) throws BadLocationException {
  359.     if (len > 0) {
  360.         try {
  361.         writeLock();
  362.         DefaultDocumentEvent chng = 
  363.             new DefaultDocumentEvent(offs, len, DocumentEvent.EventType.REMOVE);
  364.         removeUpdate(chng);
  365.         UndoableEdit u = data.remove(offs, len);
  366.         if (u != null) {
  367.             chng.addEdit(u);
  368.         }
  369.         // Mark the edit as done.
  370.         chng.end();
  371.         fireRemoveUpdate(chng);
  372.         // only fire undo if Content implementation supports it
  373.         if (u != null) {
  374.             fireUndoableEditUpdate(new UndoableEditEvent(this, chng));
  375.         }
  376.         } finally {
  377.         writeUnlock();
  378.         }
  379.     }
  380.     }
  381.  
  382.     /**
  383.      * Inserts some content into the document.
  384.      * Inserting content causes a write lock to be held while the
  385.      * actual changes are taking place, followed by notification
  386.      * to the observers on the thread that grabbed the write lock.
  387.      * <p>
  388.      * This method is thread safe, although most Swing methods
  389.      * are not. Please see 
  390.      * <A HREF="http://java.sun.com/products/jfc/swingdoc/threads.html">Threads
  391.      * and Swing</A> for more information.
  392.      *
  393.      * @param offs the starting offset
  394.      * @param str the string to insert
  395.      * @param a the attributes for the inserted content
  396.      * @exception BadLocationException  the given insert position is not a valid 
  397.      * position within the document
  398.      * @see Document#insertString
  399.      */
  400.     public void insertString(int offs, String str, AttributeSet a) throws BadLocationException {
  401.     if ((str == null) || (str.length() == 0)) {
  402.         return;
  403.     }
  404.     try {
  405.         writeLock();
  406.         UndoableEdit u = data.insertString(offs, str);
  407.         DefaultDocumentEvent e = 
  408.         new DefaultDocumentEvent(offs, str.length(), DocumentEvent.EventType.INSERT);
  409.         if (u != null) {
  410.         e.addEdit(u);
  411.         }
  412.         insertUpdate(e, a);
  413.         // Mark the edit as done.
  414.         e.end();
  415.         fireInsertUpdate(e);
  416.         // only fire undo if Content implementation supports it
  417.         if (u != null) {
  418.         fireUndoableEditUpdate(new UndoableEditEvent(this, e));
  419.         }
  420.     } finally {
  421.         writeUnlock();
  422.     }
  423.     }
  424.  
  425.     /**
  426.      * Gets a sequence of text from the document.  
  427.      *
  428.      * @param offset the starting offset
  429.      * @param length the number of characters to retrieve
  430.      * @return the text
  431.      * @exception BadLocationException  the range given includes a position 
  432.      *   that is not a valid position within the document
  433.      * @see Document#getText
  434.      */
  435.     public String getText(int offset, int length) throws BadLocationException {
  436.     String str = data.getString(offset, length);
  437.     return str;
  438.     }
  439.  
  440.     /**
  441.      * Gets some text from the document potentially without
  442.      * making a copy.  The character array returned in the
  443.      * given <code>Segment</code> should never be mutated.
  444.      * This kind of access to the characters of the document
  445.      * is provided to help make the rendering potentially more
  446.      * efficient.  The caller should make no assumptions about
  447.      * the lifetime of the returned character array, which
  448.      * should be copied if needed beyond the use for rendering.
  449.      *
  450.      * @param offset the starting offset
  451.      * @param length the number of characters to retrieve
  452.      * @param txt the Segment object to retrieve the text into
  453.      * @exception BadLocationException  the range given includes a position 
  454.      *   that is not a valid position within the document
  455.      */
  456.     public void getText(int offset, int length, Segment txt) throws BadLocationException {
  457.     data.getChars(offset, length, txt);
  458.     }
  459.  
  460.     /**
  461.      * Returns a position that will track change as the document
  462.      * is altered.
  463.      * <p>
  464.      * This method is thread safe, although most Swing methods
  465.      * are not. Please see 
  466.      * <A HREF="http://java.sun.com/products/jfc/swingdoc/threads.html">Threads
  467.      * and Swing</A> for more information.
  468.      *
  469.      * @param offs the position
  470.      * @return the position
  471.      * @exception BadLocationException  if the given position does not
  472.      *   represent a valid location in the associated document
  473.      * @see Document#createPosition
  474.      */
  475.     public synchronized Position createPosition(int offs) throws BadLocationException {
  476.     return data.createPosition(offs);
  477.     }
  478.  
  479.     /**
  480.      * Returns a position that represents the start of the document.  The 
  481.      * position returned can be counted on to track change and stay 
  482.      * located at the beginning of the document.
  483.      *
  484.      * @return the position
  485.      */
  486.     public final Position getStartPosition() {
  487.     Position p;
  488.     try {
  489.         p = createPosition(0);
  490.     } catch (BadLocationException bl) {
  491.         p = null;
  492.     }
  493.     return p;
  494.     }
  495.     
  496.     /**
  497.      * Returns a position that represents the end of the document.  The
  498.      * position returned can be counted on to track change and stay 
  499.      * located at the end of the document.
  500.      *
  501.      * @return the position
  502.      */
  503.     public final Position getEndPosition() {
  504.     Position p;
  505.     try {
  506.         p = createPosition(data.length());
  507.     } catch (BadLocationException bl) {
  508.         p = null;
  509.     }
  510.     return p;
  511.     }
  512.  
  513.     /**
  514.      * Gets all root elements defined.  Typically, there
  515.      * will only be one so the default implementation
  516.      * is to return the default root element.
  517.      *
  518.      * @return the root element
  519.      */
  520.     public Element[] getRootElements() {
  521.     Element[] elems = new Element[1];
  522.     elems[0] = getDefaultRootElement();
  523.     return elems;
  524.     }
  525.  
  526.     /**
  527.      * Returns the root element that views should be based upon
  528.      * unless some other mechanism for assigning views to element
  529.      * structures is provided.
  530.      *
  531.      * @return the root element
  532.      * @see Document#getDefaultRootElement
  533.      */
  534.     public abstract Element getDefaultRootElement();
  535.  
  536.     // ---- local methods -----------------------------------------
  537.  
  538.     /**
  539.      * Fetches the context for managing attributes.  This
  540.      * method effectively establishes the strategy used 
  541.      * for compressing AttributeSet information.
  542.      *
  543.      * @return the context
  544.      */
  545.     protected final AttributeContext getAttributeContext() {
  546.     return context;
  547.     }
  548.  
  549.     /**
  550.      * Updates document structure as a result of text insertion.  This
  551.      * will happen within a write lock.  If a subclass of
  552.      * this class reimplements this method, it should delegate to the
  553.      * superclass as well.
  554.      *
  555.      * @param chng a description of the change
  556.      * @param attr the attributes for the change
  557.      */
  558.     protected void insertUpdate(DefaultDocumentEvent chng, AttributeSet attr) {
  559.     }
  560.  
  561.     /**
  562.      * Updates any document structure as a result of text removal.
  563.      * This will happen within a write lock. If a subclass
  564.      * of this class reimplements this method, it should delegate to the
  565.      * superclass as well.
  566.      *
  567.      * @param chng a description of the change
  568.      */
  569.     protected void removeUpdate(DefaultDocumentEvent chng) {
  570.     }
  571.  
  572.     /**
  573.      * Gives a diagnostic dump.
  574.      *
  575.      * @param out the output stream
  576.      */
  577.     public void dump(PrintStream out) {
  578.     Element root = getDefaultRootElement();
  579.     if (root instanceof AbstractElement) {
  580.         ((AbstractElement)root).dump(out, 0);
  581.     }
  582.     }
  583.  
  584.     /**
  585.      * Gets the content for the document.
  586.      *
  587.      * @return the content
  588.      */
  589.     protected final Content getContent() {
  590.     return data;
  591.     }
  592.  
  593.     /**
  594.      * Creates a document leaf element.
  595.      * Hook through which elements are created to represent the 
  596.      * document structure.  Because this implementation keeps 
  597.      * structure and content seperate, elements grow automatically
  598.      * when content is extended so splits of existing elements 
  599.      * follow.  The document itself gets to decide how to generate 
  600.      * elements to give flexibility in the type of elements used.
  601.      *
  602.      * @param parent the parent element
  603.      * @param a the attributes for the element
  604.      * @param p0 the beginning of the range
  605.      * @param p1 the end of the range
  606.      * @return the new element
  607.      */
  608.     protected Element createLeafElement(Element parent, AttributeSet a, int p0, int p1) {
  609.     return new LeafElement(parent, a, p0, p1);
  610.     }
  611.  
  612.     /**
  613.      * Creates a document branch element, that can contain other elements.
  614.      *
  615.      * @param parent the parent element
  616.      * @param a the attributes
  617.      * @return the element
  618.      */
  619.     protected Element createBranchElement(Element parent, AttributeSet a) {
  620.     return new BranchElement(parent, a);
  621.     }
  622.  
  623.     // --- Document locking ----------------------------------
  624.  
  625.     /**
  626.      * Fetches the current writing thread if there is one.
  627.      * This can be used to distinguish whether a method is
  628.      * being called as part of an existing modification or
  629.      * if a lock needs to be acquired and a new transaction
  630.      * started.  
  631.      *
  632.      * @returns the thread actively modifying the document
  633.      *  or null if there are no modifications in progress
  634.      */
  635.     protected synchronized final Thread getCurrentWriter() {
  636.     return currWriter;
  637.     }
  638.  
  639.     /**
  640.      * Acquires a lock to begin mutating the document this lock
  641.      * protects.  There can be no notification of changes or
  642.      * reading going on in order to gain the lock.
  643.      */
  644.     protected synchronized final void writeLock() {
  645.     try {
  646.         while ((numReaders > 0) || (currWriter != null)) {
  647.         wait();
  648.         }
  649.         currWriter = Thread.currentThread();
  650.     } catch (InterruptedException e) {
  651.         // safe to let this pass... write lock not
  652.         // held if the thread lands here.
  653.     }
  654.     }
  655.  
  656.     /**
  657.      * Releases the write lock held because the write
  658.      * operation is finished.  This allows either a new
  659.      * writer or readers to aquire a lock.
  660.      */
  661.     protected synchronized final void writeUnlock() {
  662.     if ((numReaders > 0) || (currWriter == null)) {
  663.         throw new StateInvariantError(BAD_LOCK_STATE);
  664.     }
  665.     currWriter = null;
  666.     notify();
  667.     }
  668.  
  669.     /**
  670.      * Acquires a lock to begin reading some state from the 
  671.      * document.  There can be multiple readers at the same time
  672.      * and reading can occur while notification to the listeners 
  673.      * is going on, but writing blocks the readers.  
  674.      */
  675.     protected synchronized final void readLock() {
  676.     try {
  677.         while (currWriter != null) {
  678.         wait();
  679.         }
  680.         numReaders += 1;
  681.     } catch (InterruptedException e) {
  682.         // safe to let this pass... read lock not
  683.         // held if the thread lands here.
  684.     }
  685.     }
  686.  
  687.     /**
  688.      * Does a read unlock.
  689.      * One of the readers is done.  If there are no more readers
  690.      * then writing can begin again.
  691.      */
  692.     protected synchronized final void readUnlock() {
  693.     if (numReaders <= 0) {
  694.         throw new StateInvariantError(BAD_LOCK_STATE);
  695.     }
  696.     numReaders -= 1;
  697.     notify();
  698.     }
  699.  
  700.     // --- serialization ---------------------------------------------
  701.  
  702.     private void readObject(ObjectInputStream s)
  703.       throws ClassNotFoundException, IOException 
  704.     {
  705.     s.defaultReadObject();
  706.     listenerList = new EventListenerList();
  707.     }
  708.  
  709.     // ----- member variables ------------------------------------------
  710.  
  711.     private transient int numReaders;
  712.     private transient Thread currWriter;
  713.  
  714.     /**
  715.      * Storage for document-wide properties.
  716.      */
  717.     private Dictionary documentProperties = null;
  718.  
  719.     /**
  720.      * The event listener list for the document.
  721.      */
  722.     protected EventListenerList listenerList = new EventListenerList();
  723.  
  724.     /**
  725.      * Where the text is actually stored, and a set of marks
  726.      * that track change as the document is edited are managed.
  727.      */
  728.     private Content data;
  729.  
  730.     /**
  731.      * Factory for the attributes.  This is the strategy for
  732.      * attribute compression and control of the lifetime of
  733.      * a set of attributes as a collection.  This may be shared
  734.      * with other documents.
  735.      */
  736.     private AttributeContext context;
  737.  
  738.     private static final String BAD_LOCK_STATE = "document lock failure";
  739.  
  740.     /**
  741.      * Error message to indicate a bad location.
  742.      */
  743.     protected static final String BAD_LOCATION = "document location failure";
  744.  
  745.     /**
  746.      * Name of elements used to represent paragraphs
  747.      */
  748.     public static final String ParagraphElementName = "paragraph";
  749.  
  750.     /**
  751.      * Name of elements used to represent content
  752.      */
  753.     public static final String ContentElementName = "content";
  754.  
  755.     /**
  756.      * Name of elements used to hold sections (lines/paragraphs).
  757.      */
  758.     public static final String SectionElementName = "section";
  759.  
  760.     /**
  761.      * Name of the attribute used to specify element
  762.      * names.
  763.      */
  764.     public static final String ElementNameAttribute = "$ename";
  765.  
  766.     /**
  767.      * Interface to describe a sequence of character content that
  768.      * can be edited.  Implementations may or may not support a 
  769.      * history mechanism which will be reflected by whether or not
  770.      * mutations return an UndoableEdit implementation.  
  771.      * @see AbstractDocument
  772.      */
  773.     public interface Content {
  774.  
  775.     /**
  776.      * Creates a position within the content that will
  777.      * track change as the content is mutated.
  778.          *
  779.          * @param offset the offset in the content
  780.          * @return a Position
  781.          * @exception BadLocationException for an invalid offset
  782.      */
  783.     public Position createPosition(int offset) throws BadLocationException;
  784.  
  785.     /**
  786.      * Current length of the sequence of character content.
  787.          *
  788.          * @return the length
  789.      */
  790.         public int length();
  791.  
  792.     /**
  793.      * Inserts a string of characters into the sequence.
  794.      * 
  795.      * @param where   Offset into the sequence to make the insertion.
  796.      * @param str     String to insert.
  797.      * @return  If the implementation supports a history mechansim, 
  798.      *    a reference to an Edit implementation will be returned, 
  799.      *    otherwise null.
  800.      * @exception BadLocationException  Thrown if the area covered by
  801.      *   the arguments is not contained in the character sequence.
  802.      */
  803.         public UndoableEdit insertString(int where, String str) throws BadLocationException;
  804.  
  805.     /**
  806.      * Removes some portion of the sequence.  
  807.      *
  808.      * @param where   The offset into the sequence to make the insertion.
  809.      * @param nitems  The number of items in the sequence to remove.
  810.      * @return  If the implementation supports a history mechansim, 
  811.      *    a reference to an Edit implementation will be returned, 
  812.      *    otherwise null.
  813.      * @exception BadLocationException  Thrown if the area covered by
  814.      *   the arguments is not contained in the character sequence.
  815.      */
  816.         public UndoableEdit remove(int where, int nitems) throws BadLocationException;
  817.  
  818.     /**
  819.      * Fetches a string of characters contained in the sequence.
  820.      * 
  821.      * @param where   Offset into the sequence to fetch.
  822.      * @param len     number of characters to copy.
  823.          * @return the string
  824.      * @exception BadLocationException  Thrown if the area covered by
  825.      *   the arguments is not contained in the character sequence.
  826.      */
  827.         public String getString(int where, int len) throws BadLocationException;
  828.  
  829.         /**
  830.          * Gets a sequence of characters and copies them into a Segment.
  831.          *
  832.          * @param where the starting offset
  833.          * @param len the number of characters
  834.          * @param txt the target location to copy into
  835.      * @exception BadLocationException  Thrown if the area covered by
  836.      *   the arguments is not contained in the character sequence.
  837.          */
  838.         public void getChars(int where, int len, Segment txt) throws BadLocationException;
  839.     }
  840.  
  841.     /**
  842.      * An interface that can be used to allow MutableAttributeSet 
  843.      * implementations to use pluggable attribute compression
  844.      * techniques.  Each mutation of the attribute set can be
  845.      * used to exchange a previous AttributeSet instance with
  846.      * another, preserving the possibility of the AttributeSet
  847.      * remaining immutable.  An implementation is provided by
  848.      * the StyleContext class.
  849.      *
  850.      * The Element implementations provided by this class use
  851.      * this interface to provide their MutableAttributeSet
  852.      * implementations, so that different AttributeSet compression
  853.      * techniques can be employed.  The method 
  854.      * <code>getAttributeContext</code> should be implemented to
  855.      * return the object responsible for implementing the desired
  856.      * compression technique.
  857.      * 
  858.      * @see StyleContext
  859.      */
  860.     public interface AttributeContext {
  861.  
  862.     /**
  863.      * Adds an attribute to the given set, and returns
  864.      * the new representative set.
  865.      *
  866.          * @param old the old attribute set
  867.      * @param name the attribute name
  868.      * @param value the attribute value
  869.          * @return the updated attribute set
  870.      * @see MutableAttributeSet#addAttribute
  871.      */
  872.         public AttributeSet addAttribute(AttributeSet old, Object name, Object value);
  873.  
  874.     /**
  875.      * Adds a set of attributes to the element.
  876.      *
  877.          * @param old the old attribute set
  878.      * @param attr the attributes to add
  879.          * @return the updated attribute set
  880.      * @see MutableAttributeSet#addAttribute
  881.      */
  882.         public AttributeSet addAttributes(AttributeSet old, AttributeSet attr);
  883.  
  884.     /**
  885.      * Removes an attribute from the set.
  886.      *
  887.          * @param old the old attribute set
  888.      * @param name the attribute name
  889.          * @return the updated attribute set
  890.      * @see MutableAttributeSet#removeAttribute
  891.      */
  892.         public AttributeSet removeAttribute(AttributeSet old, Object name);
  893.  
  894.     /**
  895.      * Removes a set of attributes for the element.
  896.      *
  897.          * @param old the old attribute set
  898.      * @param names the attribute names
  899.          * @return the updated attribute set
  900.      * @see MutableAttributeSet#removeAttributes
  901.      */
  902.         public AttributeSet removeAttributes(AttributeSet old, Enumeration names);
  903.  
  904.     /**
  905.      * Removes a set of attributes for the element.
  906.      *
  907.          * @param old the old attribute set
  908.      * @param attrs the attributes
  909.          * @return the updated attribute set
  910.      * @see MutableAttributeSet#removeAttributes
  911.      */
  912.         public AttributeSet removeAttributes(AttributeSet old, AttributeSet attrs);
  913.  
  914.     /**
  915.      * Fetches an empty AttributeSet.
  916.          *
  917.          * @return the attribute set
  918.      */
  919.     public AttributeSet getEmptySet();
  920.  
  921.     /**
  922.          * Reclaims an attribute set.
  923.      * This is a way for a MutableAttributeSet to mark that it no 
  924.      * longer need a particular immutable set.  This is only necessary
  925.      * in 1.1 where there are no weak references.  A 1.1 implementation
  926.      * would call this in its finalize method.
  927.          *
  928.          * @param a the attribute set to reclaim
  929.      */
  930.     public void reclaim(AttributeSet a);
  931.     }
  932.  
  933.     /**
  934.      * Implements the abstract part of an element.  By default elements
  935.      * support attributes by having a field that represents the immutable
  936.      * part of the current attribute set for the element.  The element itself
  937.      * implements MutableAttributeSet which can be used to modify the set
  938.      * by fetching a new immutable set.  The immutable sets are provided
  939.      * by the AttributeContext associated with the document.
  940.      * <p>
  941.      * Warning: serialized objects of this class will not be compatible with
  942.      * future swing releases.  The current serialization support is appropriate
  943.      * for short term storage or RMI between Swing1.0 applications.  It will
  944.      * not be possible to load serialized Swing1.0 objects with future releases
  945.      * of Swing.  The JDK1.2 release of Swing will be the compatibility
  946.      * baseline for the serialized form of Swing objects.
  947.      */
  948.     public abstract class AbstractElement implements Element, MutableAttributeSet, Serializable {
  949.  
  950.         /**
  951.          * Creates a new AbstractElement.
  952.          *
  953.          * @param parent the parent element
  954.          * @param a the attributes for the element
  955.          */
  956.         public AbstractElement(Element parent, AttributeSet a) {
  957.         attributes = (a != null) ? a.copyAttributes() : 
  958.         getAttributeContext().getEmptySet();
  959.         this.parent = parent;
  960.     }
  961.  
  962.     private final void indent(PrintStream out, int n) {
  963.         for (int i = 0; i < n; i++) {
  964.         out.print("  ");
  965.         }
  966.     }
  967.  
  968.     /**
  969.      * Dumps a debugging representation of the element hierarchy.
  970.          *
  971.          * @param out the output stream
  972.          * @param indentAmount the indentation level
  973.      */
  974.     public void dump(PrintStream out, int indentAmount) {
  975.         indent(out, indentAmount);
  976.         if (getName() == null) {
  977.         out.print("<??");
  978.         } else {
  979.         out.print("<" + getName());
  980.         }
  981.         if (getAttributeCount() > 0) {
  982.         out.println("");
  983.         // dump the attributes
  984.         Enumeration names = attributes.getAttributeNames();
  985.         while (names.hasMoreElements()) {
  986.             Object name = names.nextElement();
  987.             indent(out, indentAmount + 1);
  988.             out.println(name + "=" + getAttribute(name));
  989.         }
  990.         indent(out, indentAmount);
  991.         }
  992.         out.println(">");
  993.  
  994.         if (isLeaf()) {
  995.         indent(out, indentAmount+1);
  996.         out.print("[" + getStartOffset() + "," + getEndOffset() + "]");
  997.         Content c = getContent();
  998.         try {
  999.             String contentStr = c.getString(getStartOffset(), 
  1000.             getEndOffset() - getStartOffset()).trim();
  1001.             if (contentStr.length() > 40) {
  1002.             contentStr = contentStr.substring(0, 40) + "...";
  1003.             }
  1004.             out.println(contentStr);        
  1005.             } catch (BadLocationException e) {
  1006.             ;
  1007.         }
  1008.  
  1009.         } else {
  1010.         int n = getElementCount();
  1011.         for (int i = 0; i < n; i++) {
  1012.             AbstractElement e = (AbstractElement) getElement(i);
  1013.             e.dump(out, indentAmount+1);
  1014.         }
  1015.         }
  1016.     }
  1017.      
  1018.     // --- Object methods ---------------------------
  1019.  
  1020.         /**
  1021.          * Finalizes an AbstractElement.
  1022.          */
  1023.     protected void finalize() throws Throwable {
  1024.         AttributeContext context = getAttributeContext();
  1025.         context.reclaim(attributes);
  1026.     }
  1027.  
  1028.     // --- AttributeSet ----------------------------
  1029.     // delegated to the immutable field "attributes"
  1030.  
  1031.     /**
  1032.          * Gets the number of attributes that are defined.
  1033.          *
  1034.          * @return the number of attributes
  1035.      * @see AttributeSet#getAttributeCount
  1036.      */
  1037.         public int getAttributeCount() {
  1038.         return attributes.getAttributeCount();
  1039.     }
  1040.  
  1041.     /**
  1042.          * Checks whether a given attribute is defined.
  1043.          *
  1044.          * @param attrName the attribute name
  1045.          * @return true if the attribute is defined
  1046.      * @see AttributeSet#isDefined
  1047.      */
  1048.         public boolean isDefined(Object attrName) {
  1049.         return attributes.isDefined(attrName);
  1050.     }
  1051.  
  1052.     /**
  1053.          * Checks whether two attribute sets are equal.
  1054.          *
  1055.          * @param attr the attribute set to check against
  1056.          * @return true if the same
  1057.      * @see AttributeSet#isEqual
  1058.      */
  1059.         public boolean isEqual(AttributeSet attr) {
  1060.         return attributes.isEqual(attr);
  1061.     }
  1062.  
  1063.     /**
  1064.          * Copies a set of attributes.
  1065.          *
  1066.          * @return the copy
  1067.      * @see AttributeSet#copyAttributes
  1068.      */
  1069.         public AttributeSet copyAttributes() {
  1070.         return attributes;
  1071.     }
  1072.  
  1073.     /**
  1074.          * Gets the value of an attribute.
  1075.          *
  1076.          * @param attrName the attribute name
  1077.          * @return the attribute value
  1078.      * @see AttributeSet#getAttribute
  1079.      */
  1080.         public Object getAttribute(Object attrName) {
  1081.         Object value = attributes.getAttribute(attrName);
  1082.         if (value == null) {
  1083.         AttributeSet parent = getResolveParent();
  1084.         if (parent != null)
  1085.             value = parent.getAttribute(attrName);
  1086.         }
  1087.         return value;
  1088.     }
  1089.  
  1090.     /**
  1091.          * Gets the names of all attributes.
  1092.          *
  1093.          * @return the attribute names
  1094.      * @see AttributeSet#getAttributeNames
  1095.      */
  1096.         public Enumeration getAttributeNames() {
  1097.         return attributes.getAttributeNames();
  1098.     }
  1099.  
  1100.     /**
  1101.          * Checks whether a given attribute name/value is defined.
  1102.          *
  1103.          * @param name the attribute name
  1104.          * @param value the attribute value
  1105.          * @return true if the name/value is defined
  1106.      * @see AttributeSet#containsAttribute
  1107.      */
  1108.         public boolean containsAttribute(Object name, Object value) {
  1109.         return attributes.containsAttribute(name, value);
  1110.     }
  1111.  
  1112.  
  1113.     /**
  1114.          * Checks whether the element contains all the attributes.
  1115.          *
  1116.          * @param attrs the attributes to check
  1117.          * @return true if the element contains all the attributes
  1118.      * @see AttributeSet#containsAttributes
  1119.      */
  1120.         public boolean containsAttributes(AttributeSet attrs) {
  1121.         return attributes.containsAttributes(attrs);
  1122.     }
  1123.  
  1124.     /**
  1125.          * Gets the resolving parent.
  1126.      * If not overriden, the resolving parent defaults to 
  1127.      * the parent element.
  1128.          *
  1129.          * @return the attributes from the parent
  1130.      * @see AttributeSet#getResolveParent
  1131.      */
  1132.         public AttributeSet getResolveParent() {
  1133.         AttributeSet a = attributes.getResolveParent();
  1134.         if ((a == null) && (parent != null)) {
  1135.         a = parent.getAttributes();
  1136.         }
  1137.         return a;
  1138.     }
  1139.  
  1140.     // --- MutableAttributeSet ----------------------------------
  1141.     // should fetch a new immutable record for the field
  1142.     // "attributes".
  1143.  
  1144.     /**
  1145.          * Adds an attribute to the element.
  1146.          *
  1147.          * @param name the attribute name
  1148.          * @param value the attribute value
  1149.      * @see MutableAttributeSet#addAttribute
  1150.      */
  1151.         public void addAttribute(Object name, Object value) {
  1152.         checkForIllegalCast();
  1153.         AttributeContext context = getAttributeContext();
  1154.         attributes = context.addAttribute(attributes, name, value);
  1155.     }
  1156.  
  1157.     /**
  1158.          * Adds a set of attributes to the element.
  1159.          *
  1160.          * @param attr the attributes to add
  1161.      * @see MutableAttributeSet#addAttribute
  1162.      */
  1163.         public void addAttributes(AttributeSet attr) {
  1164.         checkForIllegalCast();
  1165.         AttributeContext context = getAttributeContext();
  1166.         attributes = context.addAttributes(attributes, attr);
  1167.     }
  1168.  
  1169.     /**
  1170.          * Removes an attribute from the set.
  1171.          *
  1172.          * @param name the attribute name
  1173.      * @see MutableAttributeSet#removeAttribute
  1174.      */
  1175.         public void removeAttribute(Object name) {
  1176.         checkForIllegalCast();
  1177.         AttributeContext context = getAttributeContext();
  1178.         attributes = context.removeAttribute(attributes, name);
  1179.     }
  1180.  
  1181.     /**
  1182.          * Removes a set of attributes for the element.
  1183.          *
  1184.          * @param names the attribute names
  1185.      * @see MutableAttributeSet#removeAttributes
  1186.      */
  1187.         public void removeAttributes(Enumeration names) {
  1188.         checkForIllegalCast();
  1189.         AttributeContext context = getAttributeContext();
  1190.         attributes = context.removeAttributes(attributes, names);
  1191.     }
  1192.  
  1193.     /**
  1194.          * Removes a set of attributes for the element.
  1195.          *
  1196.          * @param attrs the attributes
  1197.      * @see MutableAttributeSet#removeAttributes
  1198.      */
  1199.         public void removeAttributes(AttributeSet attrs) {
  1200.         checkForIllegalCast();
  1201.         AttributeContext context = getAttributeContext();
  1202.         if (attrs == this) {
  1203.         attributes = context.getEmptySet();
  1204.         } else {
  1205.         attributes = context.removeAttributes(attributes, attrs);
  1206.         }
  1207.     }
  1208.  
  1209.     /**
  1210.          * Sets the resolving parent.
  1211.          *
  1212.          * @param parent the parent
  1213.      * @see MutableAttributeSet#setResolveParent
  1214.      */
  1215.         public void setResolveParent(AttributeSet parent) {
  1216.         checkForIllegalCast();
  1217.         AttributeContext context = getAttributeContext();
  1218.         if (parent != null) {
  1219.         attributes = 
  1220.             context.addAttribute(attributes, StyleConstants.ResolveAttribute,
  1221.                      parent);
  1222.         } else {
  1223.         attributes = 
  1224.             context.removeAttribute(attributes, StyleConstants.ResolveAttribute);
  1225.         }
  1226.     }
  1227.  
  1228.     private final void checkForIllegalCast() {
  1229.         Thread t = getCurrentWriter();
  1230.         if ((t == null) || (t != Thread.currentThread())) {
  1231.         throw new StateInvariantError("Illegal cast to MutableAttributeSet");
  1232.         }
  1233.     }
  1234.  
  1235.         // --- Element methods -------------------------------------
  1236.  
  1237.         /**
  1238.          * Retrieves the underlying model.
  1239.          *
  1240.          * @return the model
  1241.          */
  1242.     public Document getDocument() {
  1243.         return AbstractDocument.this;
  1244.     }
  1245.  
  1246.         /**
  1247.          * Gets the parent of the element.
  1248.          *
  1249.          * @return the parent
  1250.          */
  1251.     public Element getParentElement() {
  1252.         return parent;
  1253.     }
  1254.  
  1255.         /**
  1256.          * Gets the attributes for the element.
  1257.          *
  1258.          * @return the attribute set
  1259.          */
  1260.     public AttributeSet getAttributes() {
  1261.         return this;
  1262.     }
  1263.  
  1264.         /**
  1265.          * Gets the name of the element.
  1266.          *
  1267.          * @return the name
  1268.          */
  1269.         public String getName() {
  1270.         if (attributes.isDefined(ElementNameAttribute)) {
  1271.         return (String) attributes.getAttribute(ElementNameAttribute);
  1272.         }
  1273.         return null;
  1274.     }
  1275.  
  1276.         /**
  1277.          * Gets the starting offset in the model for the element.
  1278.          *
  1279.          * @return the offset
  1280.          */
  1281.     public abstract int getStartOffset();
  1282.  
  1283.         /**
  1284.          * Gets the ending offset in the model for the element.
  1285.          *
  1286.          * @return the offset
  1287.          */
  1288.     public abstract int getEndOffset();
  1289.     
  1290.         /**
  1291.          * Gets a child element.
  1292.          *
  1293.          * @param index the child index
  1294.          * @return the child element
  1295.          */
  1296.     public abstract Element getElement(int index);
  1297.  
  1298.         /**
  1299.          * Gets the number of children for the element.
  1300.          *
  1301.          * @return the number of children
  1302.          */
  1303.     public abstract int getElementCount();
  1304.  
  1305.         /**
  1306.          * Gets the child element index closest to the given model offset.
  1307.          *
  1308.          * @param offset the offset
  1309.          * @return the element index
  1310.          */
  1311.     public abstract int getElementIndex(int offset);
  1312.  
  1313.         /**
  1314.          * Checks whether the element is a leaf.
  1315.          *
  1316.          * @return true if a leaf
  1317.          */
  1318.     public abstract boolean isLeaf();
  1319.  
  1320.     // --- serialization ---------------------------------------------
  1321.  
  1322.         private void writeObject(ObjectOutputStream s) throws IOException {
  1323.         s.defaultWriteObject();
  1324.         StyleContext.writeAttributeSet(s, attributes);
  1325.     }
  1326.  
  1327.         private void readObject(ObjectInputStream s)
  1328.             throws ClassNotFoundException, IOException 
  1329.         {
  1330.         s.defaultReadObject();
  1331.         MutableAttributeSet attr = new SimpleAttributeSet();
  1332.         StyleContext.readAttributeSet(s, attr);
  1333.         AttributeContext context = getAttributeContext();
  1334.         attributes = context.addAttributes(SimpleAttributeSet.EMPTY, attr);
  1335.     }
  1336.  
  1337.     // ---- variables -----------------------------------------------------
  1338.  
  1339.     private Element parent;
  1340.     private transient AttributeSet attributes;
  1341.  
  1342.     }
  1343.  
  1344.     /**
  1345.      * Implements a composite element that contains other elements.
  1346.      * <p>
  1347.      * Warning: serialized objects of this class will not be compatible with
  1348.      * future swing releases.  The current serialization support is appropriate
  1349.      * for short term storage or RMI between Swing1.0 applications.  It will
  1350.      * not be possible to load serialized Swing1.0 objects with future releases
  1351.      * of Swing.  The JDK1.2 release of Swing will be the compatibility
  1352.      * baseline for the serialized form of Swing objects.
  1353.      */
  1354.     public class BranchElement extends AbstractElement {
  1355.  
  1356.     /**
  1357.      * Constructs a composite element that initially contains
  1358.      * no children.
  1359.      *
  1360.      * @param parent  The parent element
  1361.          * @param a the attributes for the element
  1362.      */
  1363.     public BranchElement(Element parent, AttributeSet a) {
  1364.         super(parent, a);
  1365.         children = new AbstractElement[1];
  1366.         nchildren = 0;
  1367.         lastIndex = -1;
  1368.     }
  1369.  
  1370.     /**
  1371.      * Gets the child element that contains
  1372.      * the given position.
  1373.          *
  1374.          * @param pos the position
  1375.          * @return the element
  1376.      */
  1377.     public Element positionToElement(int pos) {
  1378.         int index = getElementIndex(pos);
  1379.         Element child = children[index];
  1380.         int p0 = child.getStartOffset();
  1381.         int p1 = child.getEndOffset();
  1382.         if ((pos >= p0) && (pos < p1)) {
  1383.         return child;
  1384.         }
  1385.         return null;
  1386.     }
  1387.  
  1388.         /**
  1389.          * Replaces content with a new set of elements.
  1390.          *
  1391.          * @param offset the starting offset
  1392.          * @param length the length to replace
  1393.          * @param elems the new elements
  1394.          */
  1395.         public void replace(int offset, int length, Element[] elems) {
  1396.         int delta = elems.length - length;
  1397.         int src = offset + length;
  1398.         int nmove = nchildren - src;
  1399.         int dest = src + delta;
  1400.         if ((nchildren + delta) >= children.length) {
  1401.         // need to grow the array
  1402.         int newLength = Math.max(2*children.length, nchildren + delta);
  1403.         AbstractElement[] newChildren = new AbstractElement[newLength];
  1404.         System.arraycopy(children, 0, newChildren, 0, offset);
  1405.         System.arraycopy(elems, 0, newChildren, offset, elems.length);
  1406.         System.arraycopy(children, src, newChildren, dest, nmove);
  1407.         children = newChildren;
  1408.         } else {
  1409.         // patch the existing array
  1410.         System.arraycopy(children, src, children, dest, nmove);
  1411.         System.arraycopy(elems, 0, children, offset, elems.length);
  1412.         }
  1413.         nchildren = nchildren + delta;
  1414.     }
  1415.  
  1416.         /**
  1417.          * Converts the element to a string.
  1418.          *
  1419.          * @return the string
  1420.          */
  1421.     public String toString() {
  1422.         return "BranchElement(" + getName() + ") " + getStartOffset() + "," +
  1423.         getEndOffset() + "\n";
  1424.     }
  1425.  
  1426.     // --- Element methods -----------------------------------
  1427.  
  1428.         /**
  1429.          * Gets the element name.
  1430.          *
  1431.          * @return the element name
  1432.          */
  1433.     public String getName() {
  1434.         String nm = super.getName();
  1435.         if (nm == null) {
  1436.         nm = ParagraphElementName;
  1437.         }
  1438.         return nm;
  1439.     }
  1440.  
  1441.         /**
  1442.          * Gets the starting offset in the model for the element.
  1443.          *
  1444.          * @return the offset
  1445.          */
  1446.         public int getStartOffset() {
  1447.         return children[0].getStartOffset();
  1448.     }
  1449.  
  1450.         /**
  1451.          * Gets the ending offset in the model for the element.
  1452.          *
  1453.          * @return the offset
  1454.          */
  1455.         public int getEndOffset() {
  1456.         Element child = children[nchildren - 1];
  1457.         return child.getEndOffset();
  1458.     }
  1459.     
  1460.         /**
  1461.          * Gets a child element.
  1462.          *
  1463.          * @param index the child index
  1464.          * @return the child element
  1465.          */
  1466.     public Element getElement(int index) {
  1467.         if (index < nchildren) {
  1468.         return children[index];
  1469.         }
  1470.         return null;
  1471.     }
  1472.  
  1473.         /**
  1474.          * Gets the number of children for the element.
  1475.          *
  1476.          * @return the number of children
  1477.          */
  1478.     public int getElementCount()  {
  1479.         return nchildren;
  1480.     }
  1481.  
  1482.         /**
  1483.          * Gets the child element index closest to the given model offset.
  1484.          *
  1485.          * @param offset the offset
  1486.          * @return the element index
  1487.          */
  1488.     public int getElementIndex(int offset) {
  1489.         int index;
  1490.         int lower = 0; 
  1491.         int upper = nchildren - 1;
  1492.         int mid = 0;
  1493.         int p0 = getStartOffset();
  1494.         int p1;
  1495.  
  1496.         if (nchildren == 0) {
  1497.         return 0;
  1498.         }
  1499.         if (offset >= getEndOffset()) {
  1500.         return nchildren - 1;
  1501.         }
  1502.  
  1503.         // see if the last index can be used.
  1504.         if ((lastIndex >= lower) && (lastIndex <= upper)) {
  1505.         Element lastHit = children[lastIndex];
  1506.         p0 = lastHit.getStartOffset();
  1507.         p1 = lastHit.getEndOffset();
  1508.         if ((offset >= p0) && (offset < p1)) {
  1509.             return lastIndex;
  1510.         }
  1511.  
  1512.         // last index wasn't a hit, but it does give useful info about
  1513.         // where a hit (if any) would be.
  1514.         if (offset < p0) {
  1515.             upper = lastIndex;
  1516.         } else  {
  1517.             lower = lastIndex;
  1518.         }  
  1519.         }
  1520.  
  1521.         while (lower <= upper) {
  1522.         mid = lower + ((upper - lower) / 2);
  1523.         Element elem = children[mid];
  1524.         p0 = elem.getStartOffset();
  1525.         p1 = elem.getEndOffset();
  1526.         if ((offset >= p0) && (offset < p1)) {
  1527.             // found the location
  1528.             index = mid;
  1529.             lastIndex = index;
  1530.             return index;
  1531.         } else if (offset < p0) {        
  1532.             upper = mid - 1;
  1533.         } else {
  1534.             lower = mid + 1;
  1535.         }
  1536.         }
  1537.  
  1538.         // didn't find it, but we indicate the index of where it would belong
  1539.         if (offset < p0) {
  1540.         index = mid;
  1541.         } else {
  1542.         index = mid + 1;
  1543.         }
  1544.         lastIndex = index;
  1545.         return index;   
  1546.     }
  1547.  
  1548.         /**
  1549.          * Checks whether the element is a leaf.
  1550.          *
  1551.          * @return true if a leaf
  1552.          */
  1553.     public boolean isLeaf() {
  1554.         return false;
  1555.     }
  1556.  
  1557.     // ------ members ----------------------------------------------
  1558.  
  1559.     private AbstractElement[] children;
  1560.     private int nchildren;
  1561.     private int lastIndex;
  1562.     }
  1563.     
  1564.     /**
  1565.      * Implements an element that directly represents content of
  1566.      * some kind.
  1567.      * <p>
  1568.      * Warning: serialized objects of this class will not be compatible with
  1569.      * future swing releases.  The current serialization support is appropriate
  1570.      * for short term storage or RMI between Swing1.0 applications.  It will
  1571.      * not be possible to load serialized Swing1.0 objects with future releases
  1572.      * of Swing.  The JDK1.2 release of Swing will be the compatibility
  1573.      * baseline for the serialized form of Swing objects.
  1574.      *
  1575.      * @see     Element
  1576.      */
  1577.     public class LeafElement extends AbstractElement {
  1578.  
  1579.     /**
  1580.      * Constructs an element that represents content within the
  1581.      * document (has no children).
  1582.      *
  1583.      * @param parent  The parent element
  1584.      * @param a       The element attributes
  1585.      * @param offs0   The start offset
  1586.      * @param offs1   The end offset
  1587.      */
  1588.     public LeafElement(Element parent, AttributeSet a, int offs0, int offs1) {
  1589.         super(parent, a);
  1590.         try {
  1591.         p0 = createPosition(offs0);
  1592.         p1 = createPosition(offs1);
  1593.         } catch (BadLocationException e) {
  1594.         p0 = null;
  1595.         p1 = null;
  1596.         throw new StateInvariantError("Can't create Position references");
  1597.         }
  1598.     }
  1599.  
  1600.         /**
  1601.          * Converts the element to a string.
  1602.          *
  1603.          * @return the string
  1604.          */
  1605.     public String toString() {
  1606.         return "LeafElement(" + getName() + ") " + p0 + "," + p1 + "\n";
  1607.     }
  1608.  
  1609.     // --- Element methods ---------------------------------------------
  1610.  
  1611.         /**
  1612.          * Gets the starting offset in the model for the element.
  1613.          *
  1614.          * @return the offset
  1615.          */
  1616.     public int getStartOffset() {
  1617.         return p0.getOffset();
  1618.     }
  1619.  
  1620.         /**
  1621.          * Gets the ending offset in the model for the element.
  1622.          *
  1623.          * @return the offset
  1624.          */
  1625.     public int getEndOffset() {
  1626.         return p1.getOffset();
  1627.     }
  1628.  
  1629.         /**
  1630.          * Gets the element name.
  1631.          *
  1632.          * @return the name
  1633.          */
  1634.     public String getName() {
  1635.         String nm = super.getName();
  1636.         if (nm == null) {
  1637.         nm = ContentElementName;
  1638.         }
  1639.         return nm;
  1640.     }
  1641.  
  1642.         /**
  1643.          * Gets the child element index closest to the given model offset.
  1644.          *
  1645.          * @param pos the offset
  1646.          * @return the element index
  1647.          */
  1648.     public int getElementIndex(int pos) {
  1649.         return -1;
  1650.     }
  1651.  
  1652.         /**
  1653.          * Gets a child element.
  1654.          *
  1655.          * @param index the child index
  1656.          * @return the child element
  1657.          */
  1658.     public Element getElement(int index) {
  1659.         return null;
  1660.     }
  1661.  
  1662.         /**
  1663.          * Returns the number of child elements.
  1664.          *
  1665.          * @return the number of children
  1666.          */
  1667.     public int getElementCount()  {
  1668.         return 0;
  1669.     }
  1670.  
  1671.         /**
  1672.          * Checks whether the element is a leaf.
  1673.          *
  1674.          * @return true if a leaf
  1675.          */
  1676.     public boolean isLeaf() {
  1677.         return true;
  1678.     }
  1679.  
  1680.     // --- serialization ---------------------------------------------
  1681.  
  1682.         private void writeObject(ObjectOutputStream s) throws IOException {
  1683.         s.defaultWriteObject();
  1684.         s.writeInt(p0.getOffset());
  1685.         s.writeInt(p1.getOffset());
  1686.     }
  1687.  
  1688.         private void readObject(ObjectInputStream s)
  1689.             throws ClassNotFoundException, IOException 
  1690.         {
  1691.         s.defaultReadObject();
  1692.  
  1693.         // set the range with positions that track change
  1694.         int off0 = s.readInt();
  1695.         int off1 = s.readInt();
  1696.         try {
  1697.         p0 = createPosition(off0);
  1698.         p1 = createPosition(off1);
  1699.         } catch (BadLocationException e) {
  1700.         p0 = null;
  1701.         p1 = null;
  1702.         throw new IOException("Can't restore Position references");
  1703.         }
  1704.     }
  1705.  
  1706.     // ---- members -----------------------------------------------------
  1707.  
  1708.     private transient Position p0;
  1709.     private transient Position p1;
  1710.     }
  1711.  
  1712.     /**
  1713.      * Stores document changes as the document is being
  1714.      * modified.  Can subsequently be used for change notification
  1715.      * when done with the document modification transaction.
  1716.      * This is used by the AbstractDocument class and its extensions
  1717.      * for broadcasting change information to the document listeners.
  1718.      */
  1719.     public class DefaultDocumentEvent extends CompoundEdit implements DocumentEvent {
  1720.  
  1721.     /**
  1722.      * Constructs a change record.
  1723.      *
  1724.      * @param doc  document that is being changed
  1725.      * @param offs the offset into the document of the change
  1726.      * @param len  the length of the change
  1727.      * @param type the type of event.
  1728.      */
  1729.         public DefaultDocumentEvent(int offs, int len, DocumentEvent.EventType type) {
  1730.         super();
  1731.         offset = offs;
  1732.         length = len;
  1733.         this.type = type;
  1734.     }
  1735.  
  1736.     /**
  1737.      * Returns a string description of the change event.
  1738.      *
  1739.      * @return a string
  1740.      */
  1741.         public String toString() {
  1742.         return edits.toString();
  1743.     }
  1744.     
  1745.     // --- CompoundEdit methods --------------------------
  1746.  
  1747.     /**
  1748.      * Adds a document edit.  If the number of edits crosses
  1749.      * a threshold, this switches on a hashtable lookup for
  1750.      * ElementChange implementations since access of these
  1751.      * needs to be relatively quick.
  1752.      *
  1753.      * @param anEdit a document edit record
  1754.      * @return ???
  1755.      */ 
  1756.         public boolean addEdit(UndoableEdit anEdit) {
  1757.         // if the number of changes gets too great, start using
  1758.         // a hashtable for to locate the change for a given element.
  1759.         if ((changeLookup == null) && (edits.size() > 10)) {
  1760.         changeLookup = new Hashtable();
  1761.         int n = edits.size();
  1762.         for (int i = 0; i < n; i++) {
  1763.             Object o = edits.elementAt(i);
  1764.             if (o instanceof DocumentEvent.ElementChange) {
  1765.             DocumentEvent.ElementChange ec = (DocumentEvent.ElementChange) o;
  1766.             changeLookup.put(ec.getElement(), ec);
  1767.             }
  1768.         }
  1769.         }
  1770.  
  1771.         // if we have a hashtable... add the entry if it's 
  1772.         // an ElementChange.
  1773.         if ((changeLookup != null) && (anEdit instanceof DocumentEvent.ElementChange)) {
  1774.         DocumentEvent.ElementChange ec = (DocumentEvent.ElementChange) anEdit;
  1775.         changeLookup.put(ec.getElement(), ec);
  1776.         }
  1777.         return super.addEdit(anEdit);
  1778.     }
  1779.  
  1780.     /**
  1781.      * Redoes a change.
  1782.      *
  1783.      * @exception CannotRedoException if the change cannot be redone
  1784.      */
  1785.         public void redo() throws CannotRedoException {
  1786.         writeLock();
  1787.         try {
  1788.         // change the state
  1789.         super.redo();
  1790.         // fire a DocumentEvent to notify the view(s)
  1791.         if (type == DocumentEvent.EventType.INSERT) {
  1792.             fireInsertUpdate(this);
  1793.         } else if (type == DocumentEvent.EventType.REMOVE) {
  1794.             fireRemoveUpdate(this);
  1795.         } else {
  1796.             fireChangedUpdate(this);
  1797.         }
  1798.         } finally {
  1799.         writeUnlock();
  1800.         }
  1801.     }
  1802.  
  1803.     /**
  1804.      * Undoes a change.
  1805.      *
  1806.      * @exception CannotUndoException if the change cannot be undone
  1807.      */
  1808.         public void undo() throws CannotUndoException {
  1809.         writeLock();
  1810.         try {
  1811.         // change the state
  1812.         super.undo();
  1813.         // fire a DocumentEvent to notify the view(s)
  1814.         if (type == DocumentEvent.EventType.REMOVE) {
  1815.             fireInsertUpdate(this);
  1816.         } else if (type == DocumentEvent.EventType.INSERT) {
  1817.             fireRemoveUpdate(this);
  1818.         } else {
  1819.             fireChangedUpdate(this);
  1820.         }
  1821.         } finally {
  1822.         writeUnlock();
  1823.         }
  1824.     }
  1825.  
  1826.     /**
  1827.      * DefaultDocument events are significant. If you wish to aggregate
  1828.      * DefaultDocumentEvents to present them as a single edit to the user
  1829.      * place them into a CompoundEdit.
  1830.      */
  1831.     public boolean isSignificant() {
  1832.         return true;
  1833.     }
  1834.  
  1835.  
  1836.     /**
  1837.      * Provide a localized, human readable description of this edit
  1838.      * suitable for use in, say, a change log.
  1839.      */
  1840.     public String getPresentationName() {
  1841.         DocumentEvent.EventType type = getType();
  1842.         if(type == DocumentEvent.EventType.INSERT)
  1843.         return "addition";
  1844.         if(type == DocumentEvent.EventType.REMOVE)
  1845.         return "deletion";
  1846.         return "style change";
  1847.     }
  1848.  
  1849.     /**
  1850.      * Provide a localized, human readable description of the undoable
  1851.      * form of this edit, e.g. for use as an Undo menu item. Typically
  1852.      * derived from getDescription();
  1853.      */
  1854.     public String getUndoPresentationName() {
  1855.         return "Undo " + getPresentationName();
  1856.     }
  1857.  
  1858.     /**
  1859.      * Provide a localized, human readable description of the redoable
  1860.      * form of this edit, e.g. for use as a Redo menu item. Typically
  1861.      * derived from getPresentationName();
  1862.      */
  1863.     public String getRedoPresentationName() {
  1864.         return "Redo " + getPresentationName();
  1865.     }
  1866.  
  1867.     // --- DocumentEvent methods --------------------------
  1868.  
  1869.     /**
  1870.      * Returns the type of event
  1871.      * @see DocumentEvent#getType
  1872.      */
  1873.     public DocumentEvent.EventType getType() {
  1874.         return type;
  1875.     }
  1876.  
  1877.     /**
  1878.      * Returns the offset within the document of the start of the change.
  1879.      *
  1880.      * @return the offset
  1881.      * @see DocumentEvent#getOffset
  1882.      */
  1883.         public int getOffset() {
  1884.         return offset;
  1885.     }
  1886.  
  1887.     /**
  1888.      * Returns the length of the change.
  1889.      *
  1890.      * @return the length
  1891.      * @see DocumentEvent#getLength
  1892.      */
  1893.         public int getLength() {
  1894.         return length;
  1895.     }
  1896.     
  1897.     /**
  1898.      * Gets the document that sourced the change event.
  1899.      *
  1900.      * @return the document
  1901.      * @see DocumentEvent#getDocument
  1902.      */
  1903.         public Document getDocument() {
  1904.         return AbstractDocument.this;
  1905.     }
  1906.  
  1907.     /**
  1908.      * Gets the changes for an element.
  1909.      *
  1910.      * @param elem the element
  1911.      * @return the changes
  1912.      */
  1913.         public DocumentEvent.ElementChange getChange(Element elem) {
  1914.         if (changeLookup != null) {
  1915.         return (DocumentEvent.ElementChange) changeLookup.get(elem);
  1916.         }
  1917.         int n = edits.size();
  1918.         for (int i = 0; i < n; i++) {
  1919.         Object o = edits.elementAt(i);
  1920.         if (o instanceof DocumentEvent.ElementChange) {
  1921.             DocumentEvent.ElementChange c = (DocumentEvent.ElementChange) o;
  1922.             if (c.getElement() == elem) {
  1923.             return c;
  1924.             }
  1925.         }
  1926.         }
  1927.         return null;
  1928.     }
  1929.  
  1930.     // --- member variables ------------------------------------
  1931.  
  1932.         private int offset;
  1933.         private int length;
  1934.         private Hashtable changeLookup;
  1935.     private DocumentEvent.EventType type;
  1936.  
  1937.     }
  1938.  
  1939.     /**
  1940.      * An implementation of ElementChange that can be added to the document
  1941.      * event.
  1942.      */
  1943.     public static class ElementEdit extends AbstractUndoableEdit implements DocumentEvent.ElementChange {
  1944.  
  1945.     /**
  1946.      * Constructs an edit record.  This does not modify the element
  1947.      * so it can safely be used to <em>catch up</em> a view to the
  1948.      * current model state for views that just attached to a model.
  1949.      *
  1950.      * @param e the element
  1951.      * @param index the index into the model
  1952.      * @param removed a set of elements that were removed
  1953.      * @param added a set of elements that were added
  1954.      */
  1955.         public ElementEdit(Element e, int index, Element[] removed, Element[] added) {
  1956.         super();
  1957.         this.e = e;
  1958.         this.index = index;
  1959.         this.removed = removed;
  1960.         this.added = added;
  1961.     }
  1962.         
  1963.     /**
  1964.      * Returns the underlying element.
  1965.      *
  1966.      * @return the element
  1967.      */
  1968.         public Element getElement() {
  1969.         return e;
  1970.     } 
  1971.  
  1972.     /**
  1973.      * Returns the index into the list of elements.
  1974.      *
  1975.      * @return the index
  1976.      */
  1977.         public int getIndex() {
  1978.         return index;
  1979.     }
  1980.  
  1981.     /**
  1982.      * Gets a list of children that were removed.
  1983.      *
  1984.      * @return the list
  1985.      */
  1986.         public Element[] getChildrenRemoved() {
  1987.         return removed;
  1988.     }
  1989.  
  1990.     /**
  1991.      * Gets a list of children that were added.
  1992.      *
  1993.      * @return the list
  1994.      */
  1995.         public Element[] getChildrenAdded() {
  1996.         return added;
  1997.     }
  1998.  
  1999.     /**
  2000.      * Redoes a change.
  2001.      *
  2002.      * @exception CannotRedoException if the change cannot be redone
  2003.      */
  2004.         public void redo() throws CannotRedoException {
  2005.         super.redo();
  2006.  
  2007.         // Since this event will be reused, switch around added/removed.
  2008.         Element[] tmp = removed;
  2009.         removed = added;
  2010.         added = tmp;
  2011.  
  2012.         // PENDING(prinz) need MutableElement interface, canRedo() should check
  2013.         ((AbstractDocument.BranchElement)e).replace(index, removed.length, added);
  2014.     }
  2015.  
  2016.     /**
  2017.      * Undoes a change.
  2018.      *
  2019.      * @exception CannotUndoException if the change cannot be undone
  2020.      */
  2021.         public void undo() throws CannotUndoException {
  2022.         super.undo();
  2023.         // PENDING(prinz) need MutableElement interface, canUndo() should check
  2024.         ((AbstractDocument.BranchElement)e).replace(index, added.length, removed);
  2025.  
  2026.         // Since this event will be reused, switch around added/removed.
  2027.         Element[] tmp = removed;
  2028.         removed = added;
  2029.         added = tmp;
  2030.     }
  2031.  
  2032.         private Element e;
  2033.     private int index;
  2034.     private Element[] removed;
  2035.     private Element[] added;
  2036.     }
  2037.  
  2038. }
  2039.  
  2040.  
  2041.  
  2042.  
  2043.  
  2044.  
  2045.     
  2046.