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

    1   /*
    2    * Copyright (c) 1998, 2006, 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   
   26   package javax.swing.text;
   27   
   28   import java.io.Writer;
   29   import java.io.IOException;
   30   import java.util.Enumeration;
   31   
   32   /**
   33    * AbstractWriter is an abstract class that actually
   34    * does the work of writing out the element tree
   35    * including the attributes.  In terms of how much is
   36    * written out per line, the writer defaults to 100.
   37    * But this value can be set by subclasses.
   38    *
   39    * @author Sunita Mani
   40    */
   41   
   42   public abstract class AbstractWriter {
   43   
   44       private ElementIterator it;
   45       private Writer out;
   46       private int indentLevel = 0;
   47       private int indentSpace = 2;
   48       private Document doc = null;
   49       private int maxLineLength = 100;
   50       private int currLength = 0;
   51       private int startOffset = 0;
   52       private int endOffset = 0;
   53       // If (indentLevel * indentSpace) becomes >= maxLineLength, this will
   54       // get incremened instead of indentLevel to avoid indenting going greater
   55       // than line length.
   56       private int offsetIndent = 0;
   57   
   58       /**
   59        * String used for end of line. If the Document has the property
   60        * EndOfLineStringProperty, it will be used for newlines. Otherwise
   61        * the System property line.separator will be used. The line separator
   62        * can also be set.
   63        */
   64       private String lineSeparator;
   65   
   66       /**
   67        * True indicates that when writing, the line can be split, false
   68        * indicates that even if the line is > than max line length it should
   69        * not be split.
   70        */
   71       private boolean canWrapLines;
   72   
   73       /**
   74        * True while the current line is empty. This will remain true after
   75        * indenting.
   76        */
   77       private boolean isLineEmpty;
   78   
   79       /**
   80        * Used when indenting. Will contain the spaces.
   81        */
   82       private char[] indentChars;
   83   
   84       /**
   85        * Used when writing out a string.
   86        */
   87       private char[] tempChars;
   88   
   89       /**
   90        * This is used in <code>writeLineSeparator</code> instead of
   91        * tempChars. If tempChars were used it would mean write couldn't invoke
   92        * <code>writeLineSeparator</code> as it might have been passed
   93        * tempChars.
   94        */
   95       private char[] newlineChars;
   96   
   97       /**
   98        * Used for writing text.
   99        */
  100       private Segment segment;
  101   
  102       /**
  103        * How the text packages models newlines.
  104        * @see #getLineSeparator
  105        */
  106       protected static final char NEWLINE = '\n';
  107   
  108   
  109       /**
  110        * Creates a new AbstractWriter.
  111        * Initializes the ElementIterator with the default
  112        * root of the document.
  113        *
  114        * @param w a Writer.
  115        * @param doc a Document
  116        */
  117       protected AbstractWriter(Writer w, Document doc) {
  118           this(w, doc, 0, doc.getLength());
  119       }
  120   
  121       /**
  122        * Creates a new AbstractWriter.
  123        * Initializes the ElementIterator with the
  124        * element passed in.
  125        *
  126        * @param w a Writer
  127        * @param doc an Element
  128        * @param pos The location in the document to fetch the
  129        *   content.
  130        * @param len The amount to write out.
  131        */
  132       protected AbstractWriter(Writer w, Document doc, int pos, int len) {
  133           this.doc = doc;
  134           it = new ElementIterator(doc.getDefaultRootElement());
  135           out = w;
  136           startOffset = pos;
  137           endOffset = pos + len;
  138           Object docNewline = doc.getProperty(DefaultEditorKit.
  139                                          EndOfLineStringProperty);
  140           if (docNewline instanceof String) {
  141               setLineSeparator((String)docNewline);
  142           }
  143           else {
  144               String newline = null;
  145               try {
  146                   newline = System.getProperty("line.separator");
  147               } catch (SecurityException se) {}
  148               if (newline == null) {
  149                   // Should not get here, but if we do it means we could not
  150                   // find a newline string, use \n in this case.
  151                   newline = "\n";
  152               }
  153               setLineSeparator(newline);
  154           }
  155           canWrapLines = true;
  156       }
  157   
  158       /**
  159        * Creates a new AbstractWriter.
  160        * Initializes the ElementIterator with the
  161        * element passed in.
  162        *
  163        * @param w a Writer
  164        * @param root an Element
  165        */
  166       protected AbstractWriter(Writer w, Element root) {
  167           this(w, root, 0, root.getEndOffset());
  168       }
  169   
  170       /**
  171        * Creates a new AbstractWriter.
  172        * Initializes the ElementIterator with the
  173        * element passed in.
  174        *
  175        * @param w a Writer
  176        * @param root an Element
  177        * @param pos The location in the document to fetch the
  178        *   content.
  179        * @param len The amount to write out.
  180        */
  181       protected AbstractWriter(Writer w, Element root, int pos, int len) {
  182           this.doc = root.getDocument();
  183           it = new ElementIterator(root);
  184           out = w;
  185           startOffset = pos;
  186           endOffset = pos + len;
  187           canWrapLines = true;
  188       }
  189   
  190       /**
  191        * Returns the first offset to be output.
  192        *
  193        * @since 1.3
  194        */
  195       public int getStartOffset() {
  196           return startOffset;
  197       }
  198   
  199       /**
  200        * Returns the last offset to be output.
  201        *
  202        * @since 1.3
  203        */
  204       public int getEndOffset() {
  205           return endOffset;
  206       }
  207   
  208       /**
  209        * Fetches the ElementIterator.
  210        *
  211        * @return the ElementIterator.
  212        */
  213       protected ElementIterator getElementIterator() {
  214           return it;
  215       }
  216   
  217       /**
  218        * Returns the Writer that is used to output the content.
  219        *
  220        * @since 1.3
  221        */
  222       protected Writer getWriter() {
  223           return out;
  224       }
  225   
  226       /**
  227        * Fetches the document.
  228        *
  229        * @return the Document.
  230        */
  231       protected Document getDocument() {
  232           return doc;
  233       }
  234   
  235       /**
  236        * This method determines whether the current element
  237        * is in the range specified.  When no range is specified,
  238        * the range is initialized to be the entire document.
  239        * inRange() returns true if the range specified intersects
  240        * with the element's range.
  241        *
  242        * @param  next an Element.
  243        * @return boolean that indicates whether the element
  244        *         is in the range.
  245        */
  246       protected boolean inRange(Element next) {
  247           int startOffset = getStartOffset();
  248           int endOffset = getEndOffset();
  249           if ((next.getStartOffset() >= startOffset &&
  250                next.getStartOffset()  < endOffset) ||
  251               (startOffset >= next.getStartOffset() &&
  252                startOffset < next.getEndOffset())) {
  253               return true;
  254           }
  255           return false;
  256       }
  257   
  258       /**
  259        * This abstract method needs to be implemented
  260        * by subclasses.  Its responsibility is to
  261        * iterate over the elements and use the write()
  262        * methods to generate output in the desired format.
  263        */
  264       abstract protected void write() throws IOException, BadLocationException;
  265   
  266       /**
  267        * Returns the text associated with the element.
  268        * The assumption here is that the element is a
  269        * leaf element.  Throws a BadLocationException
  270        * when encountered.
  271        *
  272        * @param     elem an <code>Element</code>
  273        * @exception BadLocationException if pos represents an invalid
  274        *            location within the document
  275        * @return    the text as a <code>String</code>
  276        */
  277       protected String getText(Element elem) throws BadLocationException {
  278           return doc.getText(elem.getStartOffset(),
  279                              elem.getEndOffset() - elem.getStartOffset());
  280       }
  281   
  282   
  283       /**
  284        * Writes out text.  If a range is specified when the constructor
  285        * is invoked, then only the appropriate range of text is written
  286        * out.
  287        *
  288        * @param     elem an Element.
  289        * @exception IOException on any I/O error
  290        * @exception BadLocationException if pos represents an invalid
  291        *            location within the document.
  292        */
  293       protected void text(Element elem) throws BadLocationException,
  294                                                IOException {
  295           int start = Math.max(getStartOffset(), elem.getStartOffset());
  296           int end = Math.min(getEndOffset(), elem.getEndOffset());
  297           if (start < end) {
  298               if (segment == null) {
  299                   segment = new Segment();
  300               }
  301               getDocument().getText(start, end - start, segment);
  302               if (segment.count > 0) {
  303                   write(segment.array, segment.offset, segment.count);
  304               }
  305           }
  306       }
  307   
  308       /**
  309        * Enables subclasses to set the number of characters they
  310        * want written per line.   The default is 100.
  311        *
  312        * @param l the maximum line length.
  313        */
  314       protected void setLineLength(int l) {
  315           maxLineLength = l;
  316       }
  317   
  318       /**
  319        * Returns the maximum line length.
  320        *
  321        * @since 1.3
  322        */
  323       protected int getLineLength() {
  324           return maxLineLength;
  325       }
  326   
  327       /**
  328        * Sets the current line length.
  329        *
  330        * @since 1.3
  331        */
  332       protected void setCurrentLineLength(int length) {
  333           currLength = length;
  334           isLineEmpty = (currLength == 0);
  335       }
  336   
  337       /**
  338        * Returns the current line length.
  339        *
  340        * @since 1.3
  341        */
  342       protected int getCurrentLineLength() {
  343           return currLength;
  344       }
  345   
  346       /**
  347        * Returns true if the current line should be considered empty. This
  348        * is true when <code>getCurrentLineLength</code> == 0 ||
  349        * <code>indent</code> has been invoked on an empty line.
  350        *
  351        * @since 1.3
  352        */
  353       protected boolean isLineEmpty() {
  354           return isLineEmpty;
  355       }
  356   
  357       /**
  358        * Sets whether or not lines can be wrapped. This can be toggled
  359        * during the writing of lines. For example, outputting HTML might
  360        * set this to false when outputting a quoted string.
  361        *
  362        * @since 1.3
  363        */
  364       protected void setCanWrapLines(boolean newValue) {
  365           canWrapLines = newValue;
  366       }
  367   
  368       /**
  369        * Returns whether or not the lines can be wrapped. If this is false
  370        * no lineSeparator's will be output.
  371        *
  372        * @since 1.3
  373        */
  374       protected boolean getCanWrapLines() {
  375           return canWrapLines;
  376       }
  377   
  378       /**
  379        * Enables subclasses to specify how many spaces an indent
  380        * maps to. When indentation takes place, the indent level
  381        * is multiplied by this mapping.  The default is 2.
  382        *
  383        * @param space an int representing the space to indent mapping.
  384        */
  385       protected void setIndentSpace(int space) {
  386           indentSpace = space;
  387       }
  388   
  389       /**
  390        * Returns the amount of space to indent.
  391        *
  392        * @since 1.3
  393        */
  394       protected int getIndentSpace() {
  395           return indentSpace;
  396       }
  397   
  398       /**
  399        * Sets the String used to reprsent newlines. This is initialized
  400        * in the constructor from either the Document, or the System property
  401        * line.separator.
  402        *
  403        * @since 1.3
  404        */
  405       public void setLineSeparator(String value) {
  406           lineSeparator = value;
  407       }
  408   
  409       /**
  410        * Returns the string used to represent newlines.
  411        *
  412        * @since 1.3
  413        */
  414       public String getLineSeparator() {
  415           return lineSeparator;
  416       }
  417   
  418       /**
  419        * Increments the indent level. If indenting would cause
  420        * <code>getIndentSpace()</code> *<code>getIndentLevel()</code> to be >
  421        * than <code>getLineLength()</code> this will not cause an indent.
  422        */
  423       protected void incrIndent() {
  424           // Only increment to a certain point.
  425           if (offsetIndent > 0) {
  426               offsetIndent++;
  427           }
  428           else {
  429               if (++indentLevel * getIndentSpace() >= getLineLength()) {
  430                   offsetIndent++;
  431                   --indentLevel;
  432               }
  433           }
  434       }
  435   
  436       /**
  437        * Decrements the indent level.
  438        */
  439       protected void decrIndent() {
  440           if (offsetIndent > 0) {
  441               --offsetIndent;
  442           }
  443           else {
  444               indentLevel--;
  445           }
  446       }
  447   
  448       /**
  449        * Returns the current indentation level. That is, the number of times
  450        * <code>incrIndent</code> has been invoked minus the number of times
  451        * <code>decrIndent</code> has been invoked.
  452        *
  453        * @since 1.3
  454        */
  455       protected int getIndentLevel() {
  456           return indentLevel;
  457       }
  458   
  459       /**
  460        * Does indentation. The number of spaces written
  461        * out is indent level times the space to map mapping. If the current
  462        * line is empty, this will not make it so that the current line is
  463        * still considered empty.
  464        *
  465        * @exception IOException on any I/O error
  466        */
  467       protected void indent() throws IOException {
  468           int max = getIndentLevel() * getIndentSpace();
  469           if (indentChars == null || max > indentChars.length) {
  470               indentChars = new char[max];
  471               for (int counter = 0; counter < max; counter++) {
  472                   indentChars[counter] = ' ';
  473               }
  474           }
  475           int length = getCurrentLineLength();
  476           boolean wasEmpty = isLineEmpty();
  477           output(indentChars, 0, max);
  478           if (wasEmpty && length == 0) {
  479               isLineEmpty = true;
  480           }
  481       }
  482   
  483       /**
  484        * Writes out a character. This is implemented to invoke
  485        * the <code>write</code> method that takes a char[].
  486        *
  487        * @param     ch a char.
  488        * @exception IOException on any I/O error
  489        */
  490       protected void write(char ch) throws IOException {
  491           if (tempChars == null) {
  492               tempChars = new char[128];
  493           }
  494           tempChars[0] = ch;
  495           write(tempChars, 0, 1);
  496       }
  497   
  498       /**
  499        * Writes out a string. This is implemented to invoke the
  500        * <code>write</code> method that takes a char[].
  501        *
  502        * @param     content a String.
  503        * @exception IOException on any I/O error
  504        */
  505       protected void write(String content) throws IOException {
  506           if (content == null) {
  507               return;
  508           }
  509           int size = content.length();
  510           if (tempChars == null || tempChars.length < size) {
  511               tempChars = new char[size];
  512           }
  513           content.getChars(0, size, tempChars, 0);
  514           write(tempChars, 0, size);
  515       }
  516   
  517       /**
  518        * Writes the line separator. This invokes <code>output</code> directly
  519        * as well as setting the <code>lineLength</code> to 0.
  520        *
  521        * @since 1.3
  522        */
  523       protected void writeLineSeparator() throws IOException {
  524           String newline = getLineSeparator();
  525           int length = newline.length();
  526           if (newlineChars == null || newlineChars.length < length) {
  527               newlineChars = new char[length];
  528           }
  529           newline.getChars(0, length, newlineChars, 0);
  530           output(newlineChars, 0, length);
  531           setCurrentLineLength(0);
  532       }
  533   
  534       /**
  535        * All write methods call into this one. If <code>getCanWrapLines()</code>
  536        * returns false, this will call <code>output</code> with each sequence
  537        * of <code>chars</code> that doesn't contain a NEWLINE, followed
  538        * by a call to <code>writeLineSeparator</code>. On the other hand,
  539        * if <code>getCanWrapLines()</code> returns true, this will split the
  540        * string, as necessary, so <code>getLineLength</code> is honored.
  541        * The only exception is if the current string contains no whitespace,
  542        * and won't fit in which case the line length will exceed
  543        * <code>getLineLength</code>.
  544        *
  545        * @since 1.3
  546        */
  547       protected void write(char[] chars, int startIndex, int length)
  548                      throws IOException {
  549           if (!getCanWrapLines()) {
  550               // We can not break string, just track if a newline
  551               // is in it.
  552               int lastIndex = startIndex;
  553               int endIndex = startIndex + length;
  554               int newlineIndex = indexOf(chars, NEWLINE, startIndex, endIndex);
  555               while (newlineIndex != -1) {
  556                   if (newlineIndex > lastIndex) {
  557                       output(chars, lastIndex, newlineIndex - lastIndex);
  558                   }
  559                   writeLineSeparator();
  560                   lastIndex = newlineIndex + 1;
  561                   newlineIndex = indexOf(chars, '\n', lastIndex, endIndex);
  562               }
  563               if (lastIndex < endIndex) {
  564                   output(chars, lastIndex, endIndex - lastIndex);
  565               }
  566           }
  567           else {
  568               // We can break chars if the length exceeds maxLength.
  569               int lastIndex = startIndex;
  570               int endIndex = startIndex + length;
  571               int lineLength = getCurrentLineLength();
  572               int maxLength = getLineLength();
  573   
  574               while (lastIndex < endIndex) {
  575                   int newlineIndex = indexOf(chars, NEWLINE, lastIndex,
  576                                              endIndex);
  577                   boolean needsNewline = false;
  578                   boolean forceNewLine = false;
  579   
  580                   lineLength = getCurrentLineLength();
  581                   if (newlineIndex != -1 && (lineLength +
  582                                 (newlineIndex - lastIndex)) < maxLength) {
  583                       if (newlineIndex > lastIndex) {
  584                           output(chars, lastIndex, newlineIndex - lastIndex);
  585                       }
  586                       lastIndex = newlineIndex + 1;
  587                       forceNewLine = true;
  588                   }
  589                   else if (newlineIndex == -1 && (lineLength +
  590                                   (endIndex - lastIndex)) < maxLength) {
  591                       if (endIndex > lastIndex) {
  592                           output(chars, lastIndex, endIndex - lastIndex);
  593                       }
  594                       lastIndex = endIndex;
  595                   }
  596                   else {
  597                       // Need to break chars, find a place to split chars at,
  598                       // from lastIndex to endIndex,
  599                       // or maxLength - lineLength whichever is smaller
  600                       int breakPoint = -1;
  601                       int maxBreak = Math.min(endIndex - lastIndex,
  602                                               maxLength - lineLength - 1);
  603                       int counter = 0;
  604                       while (counter < maxBreak) {
  605                           if (Character.isWhitespace(chars[counter +
  606                                                           lastIndex])) {
  607                               breakPoint = counter;
  608                           }
  609                           counter++;
  610                       }
  611                       if (breakPoint != -1) {
  612                           // Found a place to break at.
  613                           breakPoint += lastIndex + 1;
  614                           output(chars, lastIndex, breakPoint - lastIndex);
  615                           lastIndex = breakPoint;
  616                           needsNewline = true;
  617                       }
  618                       else {
  619                           // No where good to break.
  620   
  621                           // find the next whitespace, or write out the
  622                           // whole string.
  623                               // maxBreak will be negative if current line too
  624                               // long.
  625                               counter = Math.max(0, maxBreak);
  626                               maxBreak = endIndex - lastIndex;
  627                               while (counter < maxBreak) {
  628                                   if (Character.isWhitespace(chars[counter +
  629                                                                   lastIndex])) {
  630                                       breakPoint = counter;
  631                                       break;
  632                                   }
  633                                   counter++;
  634                               }
  635                               if (breakPoint == -1) {
  636                                   output(chars, lastIndex, endIndex - lastIndex);
  637                                   breakPoint = endIndex;
  638                               }
  639                               else {
  640                                   breakPoint += lastIndex;
  641                                   if (chars[breakPoint] == NEWLINE) {
  642                                       output(chars, lastIndex, breakPoint++ -
  643                                              lastIndex);
  644                                   forceNewLine = true;
  645                                   }
  646                                   else {
  647                                       output(chars, lastIndex, ++breakPoint -
  648                                                 lastIndex);
  649                                   needsNewline = true;
  650                                   }
  651                               }
  652                               lastIndex = breakPoint;
  653                           }
  654                       }
  655                   if (forceNewLine || needsNewline || lastIndex < endIndex) {
  656                       writeLineSeparator();
  657                       if (lastIndex < endIndex || !forceNewLine) {
  658                           indent();
  659                       }
  660                   }
  661               }
  662           }
  663       }
  664   
  665       /**
  666        * Writes out the set of attributes as " <name>=<value>"
  667        * pairs. It throws an IOException when encountered.
  668        *
  669        * @param     attr an AttributeSet.
  670        * @exception IOException on any I/O error
  671        */
  672       protected void writeAttributes(AttributeSet attr) throws IOException {
  673   
  674           Enumeration names = attr.getAttributeNames();
  675           while (names.hasMoreElements()) {
  676               Object name = names.nextElement();
  677               write(" " + name + "=" + attr.getAttribute(name));
  678           }
  679       }
  680   
  681       /**
  682        * The last stop in writing out content. All the write methods eventually
  683        * make it to this method, which invokes <code>write</code> on the
  684        * Writer.
  685        * <p>This method also updates the line length based on
  686        * <code>length</code>. If this is invoked to output a newline, the
  687        * current line length will need to be reset as will no longer be
  688        * valid. If it is up to the caller to do this. Use
  689        * <code>writeLineSeparator</code> to write out a newline, which will
  690        * property update the current line length.
  691        *
  692        * @since 1.3
  693        */
  694       protected void output(char[] content, int start, int length)
  695                      throws IOException {
  696           getWriter().write(content, start, length);
  697           setCurrentLineLength(getCurrentLineLength() + length);
  698       }
  699   
  700       /**
  701        * Support method to locate an occurence of a particular character.
  702        */
  703       private int indexOf(char[] chars, char sChar, int startIndex,
  704                           int endIndex) {
  705           while(startIndex < endIndex) {
  706               if (chars[startIndex] == sChar) {
  707                   return startIndex;
  708               }
  709               startIndex++;
  710           }
  711           return -1;
  712       }
  713   }

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