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

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

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