Save This Page
Home » openjdk-7 » javax » swing » text » [javadoc | source]
    1   /*
    2    * Copyright 1997-2005 Sun Microsystems, Inc.  All Rights Reserved.
    3    * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
    4    *
    5    * This code is free software; you can redistribute it and/or modify it
    6    * under the terms of the GNU General Public License version 2 only, as
    7    * published by the Free Software Foundation.  Sun designates this
    8    * particular file as subject to the "Classpath" exception as provided
    9    * by Sun in the LICENSE file that accompanied this code.
   10    *
   11    * This code is distributed in the hope that it will be useful, but WITHOUT
   12    * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
   13    * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
   14    * version 2 for more details (a copy is included in the LICENSE file that
   15    * accompanied this code).
   16    *
   17    * You should have received a copy of the GNU General Public License version
   18    * 2 along with this work; if not, write to the Free Software Foundation,
   19    * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
   20    *
   21    * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
   22    * CA 95054 USA or visit www.sun.com if you need additional information or
   23    * have any questions.
   24    */
   25   package javax.swing.text;
   26   
   27   import java.util.Vector;
   28   import javax.swing.event;
   29   
   30   /**
   31    * A plain document that maintains no character attributes.  The
   32    * default element structure for this document is a map of the lines in
   33    * the text.  The Element returned by getDefaultRootElement is
   34    * a map of the lines, and each child element represents a line.
   35    * This model does not maintain any character level attributes,
   36    * but each line can be tagged with an arbitrary set of attributes.
   37    * Line to offset, and offset to line translations can be quickly
   38    * performed using the default root element.  The structure information
   39    * of the DocumentEvent's fired by edits will indicate the line
   40    * structure changes.
   41    * <p>
   42    * The default content storage management is performed by a
   43    * gapped buffer implementation (GapContent).  It supports
   44    * editing reasonably large documents with good efficiency when
   45    * the edits are contiguous or clustered, as is typical.
   46    * <p>
   47    * <strong>Warning:</strong>
   48    * Serialized objects of this class will not be compatible with
   49    * future Swing releases. The current serialization support is
   50    * appropriate for short term storage or RMI between applications running
   51    * the same version of Swing.  As of 1.4, support for long term storage
   52    * of all JavaBeans<sup><font size="-2">TM</font></sup>
   53    * has been added to the <code>java.beans</code> package.
   54    * Please see {@link java.beans.XMLEncoder}.
   55    *
   56    * @author  Timothy Prinzing
   57    * @see     Document
   58    * @see     AbstractDocument
   59    */
   60   public class PlainDocument extends AbstractDocument {
   61   
   62       /**
   63        * Name of the attribute that specifies the tab
   64        * size for tabs contained in the content.  The
   65        * type for the value is Integer.
   66        */
   67       public static final String tabSizeAttribute = "tabSize";
   68   
   69       /**
   70        * Name of the attribute that specifies the maximum
   71        * length of a line, if there is a maximum length.
   72        * The type for the value is Integer.
   73        */
   74       public static final String lineLimitAttribute = "lineLimit";
   75   
   76       /**
   77        * Constructs a plain text document.  A default model using
   78        * <code>GapContent</code> is constructed and set.
   79        */
   80       public PlainDocument() {
   81           this(new GapContent());
   82       }
   83   
   84       /**
   85        * Constructs a plain text document.  A default root element is created,
   86        * and the tab size set to 8.
   87        *
   88        * @param c  the container for the content
   89        */
   90       public PlainDocument(Content c) {
   91           super(c);
   92           putProperty(tabSizeAttribute, Integer.valueOf(8));
   93           defaultRoot = createDefaultRoot();
   94       }
   95   
   96       /**
   97        * Inserts some content into the document.
   98        * Inserting content causes a write lock to be held while the
   99        * actual changes are taking place, followed by notification
  100        * to the observers on the thread that grabbed the write lock.
  101        * <p>
  102        * This method is thread safe, although most Swing methods
  103        * are not. Please see
  104        * <A HREF="http://java.sun.com/docs/books/tutorial/uiswing/misc/threads.html">How
  105        * to Use Threads</A> for more information.
  106        *
  107        * @param offs the starting offset >= 0
  108        * @param str the string to insert; does nothing with null/empty strings
  109        * @param a the attributes for the inserted content
  110        * @exception BadLocationException  the given insert position is not a valid
  111        *   position within the document
  112        * @see Document#insertString
  113        */
  114       public void insertString(int offs, String str, AttributeSet a) throws BadLocationException {
  115           // fields don't want to have multiple lines.  We may provide a field-specific
  116           // model in the future in which case the filtering logic here will no longer
  117           // be needed.
  118           Object filterNewlines = getProperty("filterNewlines");
  119           if ((filterNewlines instanceof Boolean) && filterNewlines.equals(Boolean.TRUE)) {
  120               if ((str != null) && (str.indexOf('\n') >= 0)) {
  121                   StringBuffer filtered = new StringBuffer(str);
  122                   int n = filtered.length();
  123                   for (int i = 0; i < n; i++) {
  124                       if (filtered.charAt(i) == '\n') {
  125                           filtered.setCharAt(i, ' ');
  126                       }
  127                   }
  128                   str = filtered.toString();
  129               }
  130           }
  131           super.insertString(offs, str, a);
  132       }
  133   
  134       /**
  135        * Gets the default root element for the document model.
  136        *
  137        * @return the root
  138        * @see Document#getDefaultRootElement
  139        */
  140       public Element getDefaultRootElement() {
  141           return defaultRoot;
  142       }
  143   
  144       /**
  145        * Creates the root element to be used to represent the
  146        * default document structure.
  147        *
  148        * @return the element base
  149        */
  150       protected AbstractElement createDefaultRoot() {
  151           BranchElement map = (BranchElement) createBranchElement(null, null);
  152           Element line = createLeafElement(map, null, 0, 1);
  153           Element[] lines = new Element[1];
  154           lines[0] = line;
  155           map.replace(0, 0, lines);
  156           return map;
  157       }
  158   
  159       /**
  160        * Get the paragraph element containing the given position.  Since this
  161        * document only models lines, it returns the line instead.
  162        */
  163       public Element getParagraphElement(int pos){
  164           Element lineMap = getDefaultRootElement();
  165           return lineMap.getElement( lineMap.getElementIndex( pos ) );
  166       }
  167   
  168       /**
  169        * Updates document structure as a result of text insertion.  This
  170        * will happen within a write lock.  Since this document simply
  171        * maps out lines, we refresh the line map.
  172        *
  173        * @param chng the change event describing the dit
  174        * @param attr the set of attributes for the inserted text
  175        */
  176       protected void insertUpdate(DefaultDocumentEvent chng, AttributeSet attr) {
  177           removed.removeAllElements();
  178           added.removeAllElements();
  179           BranchElement lineMap = (BranchElement) getDefaultRootElement();
  180           int offset = chng.getOffset();
  181           int length = chng.getLength();
  182           if (offset > 0) {
  183             offset -= 1;
  184             length += 1;
  185           }
  186           int index = lineMap.getElementIndex(offset);
  187           Element rmCandidate = lineMap.getElement(index);
  188           int rmOffs0 = rmCandidate.getStartOffset();
  189           int rmOffs1 = rmCandidate.getEndOffset();
  190           int lastOffset = rmOffs0;
  191           try {
  192               if (s == null) {
  193                   s = new Segment();
  194               }
  195               getContent().getChars(offset, length, s);
  196               boolean hasBreaks = false;
  197               for (int i = 0; i < length; i++) {
  198                   char c = s.array[s.offset + i];
  199                   if (c == '\n') {
  200                       int breakOffset = offset + i + 1;
  201                       added.addElement(createLeafElement(lineMap, null, lastOffset, breakOffset));
  202                       lastOffset = breakOffset;
  203                       hasBreaks = true;
  204                   }
  205               }
  206               if (hasBreaks) {
  207                   int rmCount = 1;
  208                   removed.addElement(rmCandidate);
  209                   if ((offset + length == rmOffs1) && (lastOffset != rmOffs1) &&
  210                       ((index+1) < lineMap.getElementCount())) {
  211                       rmCount += 1;
  212                       Element e = lineMap.getElement(index+1);
  213                       removed.addElement(e);
  214                       rmOffs1 = e.getEndOffset();
  215                   }
  216                   if (lastOffset < rmOffs1) {
  217                       added.addElement(createLeafElement(lineMap, null, lastOffset, rmOffs1));
  218                   }
  219   
  220                   Element[] aelems = new Element[added.size()];
  221                   added.copyInto(aelems);
  222                   Element[] relems = new Element[removed.size()];
  223                   removed.copyInto(relems);
  224                   ElementEdit ee = new ElementEdit(lineMap, index, relems, aelems);
  225                   chng.addEdit(ee);
  226                   lineMap.replace(index, relems.length, aelems);
  227               }
  228               if (Utilities.isComposedTextAttributeDefined(attr)) {
  229                   insertComposedTextUpdate(chng, attr);
  230               }
  231           } catch (BadLocationException e) {
  232               throw new Error("Internal error: " + e.toString());
  233           }
  234           super.insertUpdate(chng, attr);
  235       }
  236   
  237       /**
  238        * Updates any document structure as a result of text removal.
  239        * This will happen within a write lock. Since the structure
  240        * represents a line map, this just checks to see if the
  241        * removal spans lines.  If it does, the two lines outside
  242        * of the removal area are joined together.
  243        *
  244        * @param chng the change event describing the edit
  245        */
  246       protected void removeUpdate(DefaultDocumentEvent chng) {
  247           removed.removeAllElements();
  248           BranchElement map = (BranchElement) getDefaultRootElement();
  249           int offset = chng.getOffset();
  250           int length = chng.getLength();
  251           int line0 = map.getElementIndex(offset);
  252           int line1 = map.getElementIndex(offset + length);
  253           if (line0 != line1) {
  254               // a line was removed
  255               for (int i = line0; i <= line1; i++) {
  256                   removed.addElement(map.getElement(i));
  257               }
  258               int p0 = map.getElement(line0).getStartOffset();
  259               int p1 = map.getElement(line1).getEndOffset();
  260               Element[] aelems = new Element[1];
  261               aelems[0] = createLeafElement(map, null, p0, p1);
  262               Element[] relems = new Element[removed.size()];
  263               removed.copyInto(relems);
  264               ElementEdit ee = new ElementEdit(map, line0, relems, aelems);
  265               chng.addEdit(ee);
  266               map.replace(line0, relems.length, aelems);
  267           } else {
  268               //Check for the composed text element
  269               Element line = map.getElement(line0);
  270               if (!line.isLeaf()) {
  271                   Element leaf = line.getElement(line.getElementIndex(offset));
  272                   if (Utilities.isComposedTextElement(leaf)) {
  273                       Element[] aelem = new Element[1];
  274                       aelem[0] = createLeafElement(map, null,
  275                           line.getStartOffset(), line.getEndOffset());
  276                       Element[] relem = new Element[1];
  277                       relem[0] = line;
  278                       ElementEdit ee = new ElementEdit(map, line0, relem, aelem);
  279                       chng.addEdit(ee);
  280                       map.replace(line0, 1, aelem);
  281                   }
  282               }
  283           }
  284           super.removeUpdate(chng);
  285       }
  286   
  287       //
  288       // Inserts the composed text of an input method. The line element
  289       // where the composed text is inserted into becomes an branch element
  290       // which contains leaf elements of the composed text and the text
  291       // backing store.
  292       //
  293       private void insertComposedTextUpdate(DefaultDocumentEvent chng, AttributeSet attr) {
  294           added.removeAllElements();
  295           BranchElement lineMap = (BranchElement) getDefaultRootElement();
  296           int offset = chng.getOffset();
  297           int length = chng.getLength();
  298           int index = lineMap.getElementIndex(offset);
  299           Element elem = lineMap.getElement(index);
  300           int elemStart = elem.getStartOffset();
  301           int elemEnd = elem.getEndOffset();
  302           BranchElement[] abelem = new BranchElement[1];
  303           abelem[0] = (BranchElement) createBranchElement(lineMap, null);
  304           Element[] relem = new Element[1];
  305           relem[0] = elem;
  306           if (elemStart != offset)
  307               added.addElement(createLeafElement(abelem[0], null, elemStart, offset));
  308           added.addElement(createLeafElement(abelem[0], attr, offset, offset+length));
  309           if (elemEnd != offset+length)
  310               added.addElement(createLeafElement(abelem[0], null, offset+length, elemEnd));
  311           Element[] alelem = new Element[added.size()];
  312           added.copyInto(alelem);
  313           ElementEdit ee = new ElementEdit(lineMap, index, relem, abelem);
  314           chng.addEdit(ee);
  315   
  316           abelem[0].replace(0, 0, alelem);
  317           lineMap.replace(index, 1, abelem);
  318       }
  319   
  320       private AbstractElement defaultRoot;
  321       private Vector added = new Vector();     // Vector<Element>
  322       private Vector removed = new Vector();   // Vector<Element>
  323       private transient Segment s;
  324   }

Save This Page
Home » openjdk-7 » javax » swing » text » [javadoc | source]