Save This Page
Home » openjdk-7 » javax » swing » text » html » [javadoc | source]
    1   /*
    2    * Copyright (c) 1998, 2008, 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.html;
   26   
   27   import javax.swing.text;
   28   import java.io.Writer;
   29   import java.util.Stack;
   30   import java.util.Enumeration;
   31   import java.util.Vector;
   32   import java.io.IOException;
   33   import java.util.StringTokenizer;
   34   import java.util.NoSuchElementException;
   35   import java.net.URL;
   36   
   37   /**
   38    * This is a writer for HTMLDocuments.
   39    *
   40    * @author  Sunita Mani
   41    */
   42   
   43   
   44   public class HTMLWriter extends AbstractWriter {
   45       /*
   46        * Stores all elements for which end tags have to
   47        * be emitted.
   48        */
   49       private Stack<Element> blockElementStack = new Stack<Element>();
   50       private boolean inContent = false;
   51       private boolean inPre = false;
   52       /** When inPre is true, this will indicate the end offset of the pre
   53        * element. */
   54       private int preEndOffset;
   55       private boolean inTextArea = false;
   56       private boolean newlineOutputed = false;
   57       private boolean completeDoc;
   58   
   59       /*
   60        * Stores all embedded tags. Embedded tags are tags that are
   61        * stored as attributes in other tags. Generally they're
   62        * character level attributes.  Examples include
   63        * &lt;b&gt;, &lt;i&gt;, &lt;font&gt;, and &lt;a&gt;.
   64        */
   65       private Vector<HTML.Tag> tags = new Vector<HTML.Tag>(10);
   66   
   67       /**
   68        * Values for the tags.
   69        */
   70       private Vector<Object> tagValues = new Vector<Object>(10);
   71   
   72       /**
   73        * Used when writing out content.
   74        */
   75       private Segment segment;
   76   
   77       /*
   78        * This is used in closeOutUnwantedEmbeddedTags.
   79        */
   80       private Vector<HTML.Tag> tagsToRemove = new Vector<HTML.Tag>(10);
   81   
   82       /**
   83        * Set to true after the head has been output.
   84        */
   85       private boolean wroteHead;
   86   
   87       /**
   88        * Set to true when entities (such as &lt;) should be replaced.
   89        */
   90       private boolean replaceEntities;
   91   
   92       /**
   93        * Temporary buffer.
   94        */
   95       private char[] tempChars;
   96   
   97   
   98       /**
   99        * Creates a new HTMLWriter.
  100        *
  101        * @param w   a Writer
  102        * @param doc  an HTMLDocument
  103        *
  104        */
  105       public HTMLWriter(Writer w, HTMLDocument doc) {
  106           this(w, doc, 0, doc.getLength());
  107       }
  108   
  109       /**
  110        * Creates a new HTMLWriter.
  111        *
  112        * @param w  a Writer
  113        * @param doc an HTMLDocument
  114        * @param pos the document location from which to fetch the content
  115        * @param len the amount to write out
  116        */
  117       public HTMLWriter(Writer w, HTMLDocument doc, int pos, int len) {
  118           super(w, doc, pos, len);
  119           completeDoc = (pos == 0 && len == doc.getLength());
  120           setLineLength(80);
  121       }
  122   
  123       /**
  124        * Iterates over the
  125        * Element tree and controls the writing out of
  126        * all the tags and its attributes.
  127        *
  128        * @exception IOException on any I/O error
  129        * @exception BadLocationException if pos represents an invalid
  130        *            location within the document.
  131        *
  132        */
  133       public void write() throws IOException, BadLocationException {
  134           ElementIterator it = getElementIterator();
  135           Element current = null;
  136           Element next;
  137   
  138           wroteHead = false;
  139           setCurrentLineLength(0);
  140           replaceEntities = false;
  141           setCanWrapLines(false);
  142           if (segment == null) {
  143               segment = new Segment();
  144           }
  145           inPre = false;
  146           boolean forcedBody = false;
  147           while ((next = it.next()) != null) {
  148               if (!inRange(next)) {
  149                   if (completeDoc && next.getAttributes().getAttribute(
  150                           StyleConstants.NameAttribute) == HTML.Tag.BODY) {
  151                       forcedBody = true;
  152                   }
  153                   else {
  154                       continue;
  155                   }
  156               }
  157               if (current != null) {
  158   
  159                   /*
  160                     if next is child of current increment indent
  161                   */
  162   
  163                   if (indentNeedsIncrementing(current, next)) {
  164                       incrIndent();
  165                   } else if (current.getParentElement() != next.getParentElement()) {
  166                       /*
  167                          next and current are not siblings
  168                          so emit end tags for items on the stack until the
  169                          item on top of the stack, is the parent of the
  170                          next.
  171                       */
  172                       Element top = blockElementStack.peek();
  173                       while (top != next.getParentElement()) {
  174                           /*
  175                              pop() will return top.
  176                           */
  177                           blockElementStack.pop();
  178                           if (!synthesizedElement(top)) {
  179                               AttributeSet attrs = top.getAttributes();
  180                               if (!matchNameAttribute(attrs, HTML.Tag.PRE) &&
  181                                   !isFormElementWithContent(attrs)) {
  182                                   decrIndent();
  183                               }
  184                               endTag(top);
  185                           }
  186                           top = blockElementStack.peek();
  187                       }
  188                   } else if (current.getParentElement() == next.getParentElement()) {
  189                       /*
  190                          if next and current are siblings the indent level
  191                          is correct.  But, we need to make sure that if current is
  192                          on the stack, we pop it off, and put out its end tag.
  193                       */
  194                       Element top = blockElementStack.peek();
  195                       if (top == current) {
  196                           blockElementStack.pop();
  197                           endTag(top);
  198                       }
  199                   }
  200               }
  201               if (!next.isLeaf() || isFormElementWithContent(next.getAttributes())) {
  202                   blockElementStack.push(next);
  203                   startTag(next);
  204               } else {
  205                   emptyTag(next);
  206               }
  207               current = next;
  208           }
  209           /* Emit all remaining end tags */
  210   
  211           /* A null parameter ensures that all embedded tags
  212              currently in the tags vector have their
  213              corresponding end tags written out.
  214           */
  215           closeOutUnwantedEmbeddedTags(null);
  216   
  217           if (forcedBody) {
  218               blockElementStack.pop();
  219               endTag(current);
  220           }
  221           while (!blockElementStack.empty()) {
  222               current = blockElementStack.pop();
  223               if (!synthesizedElement(current)) {
  224                   AttributeSet attrs = current.getAttributes();
  225                   if (!matchNameAttribute(attrs, HTML.Tag.PRE) &&
  226                                 !isFormElementWithContent(attrs)) {
  227                       decrIndent();
  228                   }
  229                   endTag(current);
  230               }
  231           }
  232   
  233           if (completeDoc) {
  234               writeAdditionalComments();
  235           }
  236   
  237           segment.array = null;
  238       }
  239   
  240   
  241       /**
  242        * Writes out the attribute set.  Ignores all
  243        * attributes with a key of type HTML.Tag,
  244        * attributes with a key of type StyleConstants,
  245        * and attributes with a key of type
  246        * HTML.Attribute.ENDTAG.
  247        *
  248        * @param attr   an AttributeSet
  249        * @exception IOException on any I/O error
  250        *
  251        */
  252       protected void writeAttributes(AttributeSet attr) throws IOException {
  253           // translate css attributes to html
  254           convAttr.removeAttributes(convAttr);
  255           convertToHTML32(attr, convAttr);
  256   
  257           Enumeration names = convAttr.getAttributeNames();
  258           while (names.hasMoreElements()) {
  259               Object name = names.nextElement();
  260               if (name instanceof HTML.Tag ||
  261                   name instanceof StyleConstants ||
  262                   name == HTML.Attribute.ENDTAG) {
  263                   continue;
  264               }
  265               write(" " + name + "=\"" + convAttr.getAttribute(name) + "\"");
  266           }
  267       }
  268   
  269       /**
  270        * Writes out all empty elements (all tags that have no
  271        * corresponding end tag).
  272        *
  273        * @param elem   an Element
  274        * @exception IOException on any I/O error
  275        * @exception BadLocationException if pos represents an invalid
  276        *            location within the document.
  277        */
  278       protected void emptyTag(Element elem) throws BadLocationException, IOException {
  279   
  280           if (!inContent && !inPre) {
  281               indentSmart();
  282           }
  283   
  284           AttributeSet attr = elem.getAttributes();
  285           closeOutUnwantedEmbeddedTags(attr);
  286           writeEmbeddedTags(attr);
  287   
  288           if (matchNameAttribute(attr, HTML.Tag.CONTENT)) {
  289               inContent = true;
  290               text(elem);
  291           } else if (matchNameAttribute(attr, HTML.Tag.COMMENT)) {
  292               comment(elem);
  293           }  else {
  294               boolean isBlock = isBlockTag(elem.getAttributes());
  295               if (inContent && isBlock ) {
  296                   writeLineSeparator();
  297                   indentSmart();
  298               }
  299   
  300               Object nameTag = (attr != null) ? attr.getAttribute
  301                                 (StyleConstants.NameAttribute) : null;
  302               Object endTag = (attr != null) ? attr.getAttribute
  303                                 (HTML.Attribute.ENDTAG) : null;
  304   
  305               boolean outputEndTag = false;
  306               // If an instance of an UNKNOWN Tag, or an instance of a
  307               // tag that is only visible during editing
  308               //
  309               if (nameTag != null && endTag != null &&
  310                   (endTag instanceof String) &&
  311                   endTag.equals("true")) {
  312                   outputEndTag = true;
  313               }
  314   
  315               if (completeDoc && matchNameAttribute(attr, HTML.Tag.HEAD)) {
  316                   if (outputEndTag) {
  317                       // Write out any styles.
  318                       writeStyles(((HTMLDocument)getDocument()).getStyleSheet());
  319                   }
  320                   wroteHead = true;
  321               }
  322   
  323               write('<');
  324               if (outputEndTag) {
  325                   write('/');
  326               }
  327               write(elem.getName());
  328               writeAttributes(attr);
  329               write('>');
  330               if (matchNameAttribute(attr, HTML.Tag.TITLE) && !outputEndTag) {
  331                   Document doc = elem.getDocument();
  332                   String title = (String)doc.getProperty(Document.TitleProperty);
  333                   write(title);
  334               } else if (!inContent || isBlock) {
  335                   writeLineSeparator();
  336                   if (isBlock && inContent) {
  337                       indentSmart();
  338                   }
  339               }
  340           }
  341       }
  342   
  343       /**
  344        * Determines if the HTML.Tag associated with the
  345        * element is a block tag.
  346        *
  347        * @param attr  an AttributeSet
  348        * @return  true if tag is block tag, false otherwise.
  349        */
  350       protected boolean isBlockTag(AttributeSet attr) {
  351           Object o = attr.getAttribute(StyleConstants.NameAttribute);
  352           if (o instanceof HTML.Tag) {
  353               HTML.Tag name = (HTML.Tag) o;
  354               return name.isBlock();
  355           }
  356           return false;
  357       }
  358   
  359   
  360       /**
  361        * Writes out a start tag for the element.
  362        * Ignores all synthesized elements.
  363        *
  364        * @param elem   an Element
  365        * @exception IOException on any I/O error
  366        */
  367       protected void startTag(Element elem) throws IOException, BadLocationException {
  368   
  369           if (synthesizedElement(elem)) {
  370               return;
  371           }
  372   
  373           // Determine the name, as an HTML.Tag.
  374           AttributeSet attr = elem.getAttributes();
  375           Object nameAttribute = attr.getAttribute(StyleConstants.NameAttribute);
  376           HTML.Tag name;
  377           if (nameAttribute instanceof HTML.Tag) {
  378               name = (HTML.Tag)nameAttribute;
  379           }
  380           else {
  381               name = null;
  382           }
  383   
  384           if (name == HTML.Tag.PRE) {
  385               inPre = true;
  386               preEndOffset = elem.getEndOffset();
  387           }
  388   
  389           // write out end tags for item on stack
  390           closeOutUnwantedEmbeddedTags(attr);
  391   
  392           if (inContent) {
  393               writeLineSeparator();
  394               inContent = false;
  395               newlineOutputed = false;
  396           }
  397   
  398           if (completeDoc && name == HTML.Tag.BODY && !wroteHead) {
  399               // If the head has not been output, output it and the styles.
  400               wroteHead = true;
  401               indentSmart();
  402               write("<head>");
  403               writeLineSeparator();
  404               incrIndent();
  405               writeStyles(((HTMLDocument)getDocument()).getStyleSheet());
  406               decrIndent();
  407               writeLineSeparator();
  408               indentSmart();
  409               write("</head>");
  410               writeLineSeparator();
  411           }
  412   
  413           indentSmart();
  414           write('<');
  415           write(elem.getName());
  416           writeAttributes(attr);
  417           write('>');
  418           if (name != HTML.Tag.PRE) {
  419               writeLineSeparator();
  420           }
  421   
  422           if (name == HTML.Tag.TEXTAREA) {
  423               textAreaContent(elem.getAttributes());
  424           } else if (name == HTML.Tag.SELECT) {
  425               selectContent(elem.getAttributes());
  426           } else if (completeDoc && name == HTML.Tag.BODY) {
  427               // Write out the maps, which is not stored as Elements in
  428               // the Document.
  429               writeMaps(((HTMLDocument)getDocument()).getMaps());
  430           }
  431           else if (name == HTML.Tag.HEAD) {
  432               HTMLDocument document = (HTMLDocument)getDocument();
  433               wroteHead = true;
  434               incrIndent();
  435               writeStyles(document.getStyleSheet());
  436               if (document.hasBaseTag()) {
  437                   indentSmart();
  438                   write("<base href=\"" + document.getBase() + "\">");
  439                   writeLineSeparator();
  440               }
  441               decrIndent();
  442           }
  443   
  444       }
  445   
  446   
  447       /**
  448        * Writes out text that is contained in a TEXTAREA form
  449        * element.
  450        *
  451        * @param attr  an AttributeSet
  452        * @exception IOException on any I/O error
  453        * @exception BadLocationException if pos represents an invalid
  454        *            location within the document.
  455        */
  456       protected void textAreaContent(AttributeSet attr) throws BadLocationException, IOException {
  457           Document doc = (Document)attr.getAttribute(StyleConstants.ModelAttribute);
  458           if (doc != null && doc.getLength() > 0) {
  459               if (segment == null) {
  460                   segment = new Segment();
  461               }
  462               doc.getText(0, doc.getLength(), segment);
  463               if (segment.count > 0) {
  464                   inTextArea = true;
  465                   incrIndent();
  466                   indentSmart();
  467                   setCanWrapLines(true);
  468                   replaceEntities = true;
  469                   write(segment.array, segment.offset, segment.count);
  470                   replaceEntities = false;
  471                   setCanWrapLines(false);
  472                   writeLineSeparator();
  473                   inTextArea = false;
  474                   decrIndent();
  475               }
  476           }
  477       }
  478   
  479   
  480       /**
  481        * Writes out text.  If a range is specified when the constructor
  482        * is invoked, then only the appropriate range of text is written
  483        * out.
  484        *
  485        * @param elem   an Element
  486        * @exception IOException on any I/O error
  487        * @exception BadLocationException if pos represents an invalid
  488        *            location within the document.
  489        */
  490       protected void text(Element elem) throws BadLocationException, IOException {
  491           int start = Math.max(getStartOffset(), elem.getStartOffset());
  492           int end = Math.min(getEndOffset(), elem.getEndOffset());
  493           if (start < end) {
  494               if (segment == null) {
  495                   segment = new Segment();
  496               }
  497               getDocument().getText(start, end - start, segment);
  498               newlineOutputed = false;
  499               if (segment.count > 0) {
  500                   if (segment.array[segment.offset + segment.count - 1] == '\n'){
  501                       newlineOutputed = true;
  502                   }
  503                   if (inPre && end == preEndOffset) {
  504                       if (segment.count > 1) {
  505                           segment.count--;
  506                       }
  507                       else {
  508                           return;
  509                       }
  510                   }
  511                   replaceEntities = true;
  512                   setCanWrapLines(!inPre);
  513                   write(segment.array, segment.offset, segment.count);
  514                   setCanWrapLines(false);
  515                   replaceEntities = false;
  516               }
  517           }
  518       }
  519   
  520       /**
  521        * Writes out the content of the SELECT form element.
  522        *
  523        * @param attr the AttributeSet associated with the form element
  524        * @exception IOException on any I/O error
  525        */
  526       protected void selectContent(AttributeSet attr) throws IOException {
  527           Object model = attr.getAttribute(StyleConstants.ModelAttribute);
  528           incrIndent();
  529           if (model instanceof OptionListModel) {
  530               OptionListModel listModel = (OptionListModel)model;
  531               int size = listModel.getSize();
  532               for (int i = 0; i < size; i++) {
  533                   Option option = (Option)listModel.getElementAt(i);
  534                   writeOption(option);
  535               }
  536           } else if (model instanceof OptionComboBoxModel) {
  537               OptionComboBoxModel comboBoxModel = (OptionComboBoxModel)model;
  538               int size = comboBoxModel.getSize();
  539               for (int i = 0; i < size; i++) {
  540                   Option option = (Option)comboBoxModel.getElementAt(i);
  541                   writeOption(option);
  542               }
  543           }
  544           decrIndent();
  545       }
  546   
  547   
  548       /**
  549        * Writes out the content of the Option form element.
  550        * @param option  an Option
  551        * @exception IOException on any I/O error
  552        *
  553        */
  554       protected void writeOption(Option option) throws IOException {
  555   
  556           indentSmart();
  557           write('<');
  558           write("option");
  559           // PENDING: should this be changed to check for null first?
  560           Object value = option.getAttributes().getAttribute
  561                                 (HTML.Attribute.VALUE);
  562           if (value != null) {
  563               write(" value="+ value);
  564           }
  565           if (option.isSelected()) {
  566               write(" selected");
  567           }
  568           write('>');
  569           if (option.getLabel() != null) {
  570               write(option.getLabel());
  571           }
  572           writeLineSeparator();
  573       }
  574   
  575       /**
  576        * Writes out an end tag for the element.
  577        *
  578        * @param elem    an Element
  579        * @exception IOException on any I/O error
  580        */
  581       protected void endTag(Element elem) throws IOException {
  582           if (synthesizedElement(elem)) {
  583               return;
  584           }
  585   
  586           // write out end tags for item on stack
  587           closeOutUnwantedEmbeddedTags(elem.getAttributes());
  588           if (inContent) {
  589               if (!newlineOutputed && !inPre) {
  590                   writeLineSeparator();
  591               }
  592               newlineOutputed = false;
  593               inContent = false;
  594           }
  595           if (!inPre) {
  596               indentSmart();
  597           }
  598           if (matchNameAttribute(elem.getAttributes(), HTML.Tag.PRE)) {
  599               inPre = false;
  600           }
  601           write('<');
  602           write('/');
  603           write(elem.getName());
  604           write('>');
  605           writeLineSeparator();
  606       }
  607   
  608   
  609   
  610       /**
  611        * Writes out comments.
  612        *
  613        * @param elem    an Element
  614        * @exception IOException on any I/O error
  615        * @exception BadLocationException if pos represents an invalid
  616        *            location within the document.
  617        */
  618       protected void comment(Element elem) throws BadLocationException, IOException {
  619           AttributeSet as = elem.getAttributes();
  620           if (matchNameAttribute(as, HTML.Tag.COMMENT)) {
  621               Object comment = as.getAttribute(HTML.Attribute.COMMENT);
  622               if (comment instanceof String) {
  623                   writeComment((String)comment);
  624               }
  625               else {
  626                   writeComment(null);
  627               }
  628           }
  629       }
  630   
  631   
  632       /**
  633        * Writes out comment string.
  634        *
  635        * @param string   the comment
  636        * @exception IOException on any I/O error
  637        * @exception BadLocationException if pos represents an invalid
  638        *            location within the document.
  639        */
  640       void writeComment(String string) throws IOException {
  641           write("<!--");
  642           if (string != null) {
  643               write(string);
  644           }
  645           write("-->");
  646           writeLineSeparator();
  647           indentSmart();
  648       }
  649   
  650   
  651       /**
  652        * Writes out any additional comments (comments outside of the body)
  653        * stored under the property HTMLDocument.AdditionalComments.
  654        */
  655       void writeAdditionalComments() throws IOException {
  656           Object comments = getDocument().getProperty
  657                                           (HTMLDocument.AdditionalComments);
  658   
  659           if (comments instanceof Vector) {
  660               Vector v = (Vector)comments;
  661               for (int counter = 0, maxCounter = v.size(); counter < maxCounter;
  662                    counter++) {
  663                   writeComment(v.elementAt(counter).toString());
  664               }
  665           }
  666       }
  667   
  668   
  669       /**
  670        * Returns true if the element is a
  671        * synthesized element.  Currently we are only testing
  672        * for the p-implied tag.
  673        */
  674       protected boolean synthesizedElement(Element elem) {
  675           if (matchNameAttribute(elem.getAttributes(), HTML.Tag.IMPLIED)) {
  676               return true;
  677           }
  678           return false;
  679       }
  680   
  681   
  682       /**
  683        * Returns true if the StyleConstants.NameAttribute is
  684        * equal to the tag that is passed in as a parameter.
  685        */
  686       protected boolean matchNameAttribute(AttributeSet attr, HTML.Tag tag) {
  687           Object o = attr.getAttribute(StyleConstants.NameAttribute);
  688           if (o instanceof HTML.Tag) {
  689               HTML.Tag name = (HTML.Tag) o;
  690               if (name == tag) {
  691                   return true;
  692               }
  693           }
  694           return false;
  695       }
  696   
  697       /**
  698        * Searches for embedded tags in the AttributeSet
  699        * and writes them out.  It also stores these tags in a vector
  700        * so that when appropriate the corresponding end tags can be
  701        * written out.
  702        *
  703        * @exception IOException on any I/O error
  704        */
  705       protected void writeEmbeddedTags(AttributeSet attr) throws IOException {
  706   
  707           // translate css attributes to html
  708           attr = convertToHTML(attr, oConvAttr);
  709   
  710           Enumeration names = attr.getAttributeNames();
  711           while (names.hasMoreElements()) {
  712               Object name = names.nextElement();
  713               if (name instanceof HTML.Tag) {
  714                   HTML.Tag tag = (HTML.Tag)name;
  715                   if (tag == HTML.Tag.FORM || tags.contains(tag)) {
  716                       continue;
  717                   }
  718                   write('<');
  719                   write(tag.toString());
  720                   Object o = attr.getAttribute(tag);
  721                   if (o != null && o instanceof AttributeSet) {
  722                       writeAttributes((AttributeSet)o);
  723                   }
  724                   write('>');
  725                   tags.addElement(tag);
  726                   tagValues.addElement(o);
  727               }
  728           }
  729       }
  730   
  731   
  732       /**
  733        * Searches the attribute set for a tag, both of which
  734        * are passed in as a parameter.  Returns true if no match is found
  735        * and false otherwise.
  736        */
  737       private boolean noMatchForTagInAttributes(AttributeSet attr, HTML.Tag t,
  738                                                 Object tagValue) {
  739           if (attr != null && attr.isDefined(t)) {
  740               Object newValue = attr.getAttribute(t);
  741   
  742               if ((tagValue == null) ? (newValue == null) :
  743                   (newValue != null && tagValue.equals(newValue))) {
  744                   return false;
  745               }
  746           }
  747           return true;
  748       }
  749   
  750   
  751       /**
  752        * Searches the attribute set and for each tag
  753        * that is stored in the tag vector.  If the tag isnt found,
  754        * then the tag is removed from the vector and a corresponding
  755        * end tag is written out.
  756        *
  757        * @exception IOException on any I/O error
  758        */
  759       protected void closeOutUnwantedEmbeddedTags(AttributeSet attr) throws IOException {
  760   
  761           tagsToRemove.removeAllElements();
  762   
  763           // translate css attributes to html
  764           attr = convertToHTML(attr, null);
  765   
  766           HTML.Tag t;
  767           Object tValue;
  768           int firstIndex = -1;
  769           int size = tags.size();
  770           // First, find all the tags that need to be removed.
  771           for (int i = size - 1; i >= 0; i--) {
  772               t = tags.elementAt(i);
  773               tValue = tagValues.elementAt(i);
  774               if ((attr == null) || noMatchForTagInAttributes(attr, t, tValue)) {
  775                   firstIndex = i;
  776                   tagsToRemove.addElement(t);
  777               }
  778           }
  779           if (firstIndex != -1) {
  780               // Then close them out.
  781               boolean removeAll = ((size - firstIndex) == tagsToRemove.size());
  782               for (int i = size - 1; i >= firstIndex; i--) {
  783                   t = tags.elementAt(i);
  784                   if (removeAll || tagsToRemove.contains(t)) {
  785                       tags.removeElementAt(i);
  786                       tagValues.removeElementAt(i);
  787                   }
  788                   write('<');
  789                   write('/');
  790                   write(t.toString());
  791                   write('>');
  792               }
  793               // Have to output any tags after firstIndex that still remaing,
  794               // as we closed them out, but they should remain open.
  795               size = tags.size();
  796               for (int i = firstIndex; i < size; i++) {
  797                   t = tags.elementAt(i);
  798                   write('<');
  799                   write(t.toString());
  800                   Object o = tagValues.elementAt(i);
  801                   if (o != null && o instanceof AttributeSet) {
  802                       writeAttributes((AttributeSet)o);
  803                   }
  804                   write('>');
  805               }
  806           }
  807       }
  808   
  809   
  810       /**
  811        * Determines if the element associated with the attributeset
  812        * is a TEXTAREA or SELECT.  If true, returns true else
  813        * false
  814        */
  815       private boolean isFormElementWithContent(AttributeSet attr) {
  816           return matchNameAttribute(attr, HTML.Tag.TEXTAREA) ||
  817                   matchNameAttribute(attr, HTML.Tag.SELECT);
  818       }
  819   
  820   
  821       /**
  822        * Determines whether a the indentation needs to be
  823        * incremented.  Basically, if next is a child of current, and
  824        * next is NOT a synthesized element, the indent level will be
  825        * incremented.  If there is a parent-child relationship and "next"
  826        * is a synthesized element, then its children must be indented.
  827        * This state is maintained by the indentNext boolean.
  828        *
  829        * @return boolean that's true if indent level
  830        *         needs incrementing.
  831        */
  832       private boolean indentNext = false;
  833       private boolean indentNeedsIncrementing(Element current, Element next) {
  834           if ((next.getParentElement() == current) && !inPre) {
  835               if (indentNext) {
  836                   indentNext = false;
  837                   return true;
  838               } else if (synthesizedElement(next)) {
  839                   indentNext = true;
  840               } else if (!synthesizedElement(current)){
  841                   return true;
  842               }
  843           }
  844           return false;
  845       }
  846   
  847       /**
  848        * Outputs the maps as elements. Maps are not stored as elements in
  849        * the document, and as such this is used to output them.
  850        */
  851       void writeMaps(Enumeration maps) throws IOException {
  852           if (maps != null) {
  853               while(maps.hasMoreElements()) {
  854                   Map map = (Map)maps.nextElement();
  855                   String name = map.getName();
  856   
  857                   incrIndent();
  858                   indentSmart();
  859                   write("<map");
  860                   if (name != null) {
  861                       write(" name=\"");
  862                       write(name);
  863                       write("\">");
  864                   }
  865                   else {
  866                       write('>');
  867                   }
  868                   writeLineSeparator();
  869                   incrIndent();
  870   
  871                   // Output the areas
  872                   AttributeSet[] areas = map.getAreas();
  873                   if (areas != null) {
  874                       for (int counter = 0, maxCounter = areas.length;
  875                            counter < maxCounter; counter++) {
  876                           indentSmart();
  877                           write("<area");
  878                           writeAttributes(areas[counter]);
  879                           write("></area>");
  880                           writeLineSeparator();
  881                       }
  882                   }
  883                   decrIndent();
  884                   indentSmart();
  885                   write("</map>");
  886                   writeLineSeparator();
  887                   decrIndent();
  888               }
  889           }
  890       }
  891   
  892       /**
  893        * Outputs the styles as a single element. Styles are not stored as
  894        * elements, but part of the document. For the time being styles are
  895        * written out as a comment, inside a style tag.
  896        */
  897       void writeStyles(StyleSheet sheet) throws IOException {
  898           if (sheet != null) {
  899               Enumeration styles = sheet.getStyleNames();
  900               if (styles != null) {
  901                   boolean outputStyle = false;
  902                   while (styles.hasMoreElements()) {
  903                       String name = (String)styles.nextElement();
  904                       // Don't write out the default style.
  905                       if (!StyleContext.DEFAULT_STYLE.equals(name) &&
  906                           writeStyle(name, sheet.getStyle(name), outputStyle)) {
  907                           outputStyle = true;
  908                       }
  909                   }
  910                   if (outputStyle) {
  911                       writeStyleEndTag();
  912                   }
  913               }
  914           }
  915       }
  916   
  917       /**
  918        * Outputs the named style. <code>outputStyle</code> indicates
  919        * whether or not a style has been output yet. This will return
  920        * true if a style is written.
  921        */
  922       boolean writeStyle(String name, Style style, boolean outputStyle)
  923                    throws IOException{
  924           boolean didOutputStyle = false;
  925           Enumeration attributes = style.getAttributeNames();
  926           if (attributes != null) {
  927               while (attributes.hasMoreElements()) {
  928                   Object attribute = attributes.nextElement();
  929                   if (attribute instanceof CSS.Attribute) {
  930                       String value = style.getAttribute(attribute).toString();
  931                       if (value != null) {
  932                           if (!outputStyle) {
  933                               writeStyleStartTag();
  934                               outputStyle = true;
  935                           }
  936                           if (!didOutputStyle) {
  937                               didOutputStyle = true;
  938                               indentSmart();
  939                               write(name);
  940                               write(" {");
  941                           }
  942                           else {
  943                               write(";");
  944                           }
  945                           write(' ');
  946                           write(attribute.toString());
  947                           write(": ");
  948                           write(value);
  949                       }
  950                   }
  951               }
  952           }
  953           if (didOutputStyle) {
  954               write(" }");
  955               writeLineSeparator();
  956           }
  957           return didOutputStyle;
  958       }
  959   
  960       void writeStyleStartTag() throws IOException {
  961           indentSmart();
  962           write("<style type=\"text/css\">");
  963           incrIndent();
  964           writeLineSeparator();
  965           indentSmart();
  966           write("<!--");
  967           incrIndent();
  968           writeLineSeparator();
  969       }
  970   
  971       void writeStyleEndTag() throws IOException {
  972           decrIndent();
  973           indentSmart();
  974           write("-->");
  975           writeLineSeparator();
  976           decrIndent();
  977           indentSmart();
  978           write("</style>");
  979           writeLineSeparator();
  980           indentSmart();
  981       }
  982   
  983       // --- conversion support ---------------------------
  984   
  985       /**
  986        * Convert the give set of attributes to be html for
  987        * the purpose of writing them out.  Any keys that
  988        * have been converted will not appear in the resultant
  989        * set.  Any keys not converted will appear in the
  990        * resultant set the same as the received set.<p>
  991        * This will put the converted values into <code>to</code>, unless
  992        * it is null in which case a temporary AttributeSet will be returned.
  993        */
  994       AttributeSet convertToHTML(AttributeSet from, MutableAttributeSet to) {
  995           if (to == null) {
  996               to = convAttr;
  997           }
  998           to.removeAttributes(to);
  999           if (writeCSS) {
 1000               convertToHTML40(from, to);
 1001           } else {
 1002               convertToHTML32(from, to);
 1003           }
 1004           return to;
 1005       }
 1006   
 1007       /**
 1008        * If true, the writer will emit CSS attributes in preference
 1009        * to HTML tags/attributes (i.e. It will emit an HTML 4.0
 1010        * style).
 1011        */
 1012       private boolean writeCSS = false;
 1013   
 1014       /**
 1015        * Buffer for the purpose of attribute conversion
 1016        */
 1017       private MutableAttributeSet convAttr = new SimpleAttributeSet();
 1018   
 1019       /**
 1020        * Buffer for the purpose of attribute conversion. This can be
 1021        * used if convAttr is being used.
 1022        */
 1023       private MutableAttributeSet oConvAttr = new SimpleAttributeSet();
 1024   
 1025       /**
 1026        * Create an older style of HTML attributes.  This will
 1027        * convert character level attributes that have a StyleConstants
 1028        * mapping over to an HTML tag/attribute.  Other CSS attributes
 1029        * will be placed in an HTML style attribute.
 1030        */
 1031       private static void convertToHTML32(AttributeSet from, MutableAttributeSet to) {
 1032           if (from == null) {
 1033               return;
 1034           }
 1035           Enumeration keys = from.getAttributeNames();
 1036           String value = "";
 1037           while (keys.hasMoreElements()) {
 1038               Object key = keys.nextElement();
 1039               if (key instanceof CSS.Attribute) {
 1040                   if ((key == CSS.Attribute.FONT_FAMILY) ||
 1041                       (key == CSS.Attribute.FONT_SIZE) ||
 1042                       (key == CSS.Attribute.COLOR)) {
 1043   
 1044                       createFontAttribute((CSS.Attribute)key, from, to);
 1045                   } else if (key == CSS.Attribute.FONT_WEIGHT) {
 1046                       // add a bold tag is weight is bold
 1047                       CSS.FontWeight weightValue = (CSS.FontWeight)
 1048                           from.getAttribute(CSS.Attribute.FONT_WEIGHT);
 1049                       if ((weightValue != null) && (weightValue.getValue() > 400)) {
 1050                           addAttribute(to, HTML.Tag.B, SimpleAttributeSet.EMPTY);
 1051                       }
 1052                   } else if (key == CSS.Attribute.FONT_STYLE) {
 1053                       String s = from.getAttribute(key).toString();
 1054                       if (s.indexOf("italic") >= 0) {
 1055                           addAttribute(to, HTML.Tag.I, SimpleAttributeSet.EMPTY);
 1056                       }
 1057                   } else if (key == CSS.Attribute.TEXT_DECORATION) {
 1058                       String decor = from.getAttribute(key).toString();
 1059                       if (decor.indexOf("underline") >= 0) {
 1060                           addAttribute(to, HTML.Tag.U, SimpleAttributeSet.EMPTY);
 1061                       }
 1062                       if (decor.indexOf("line-through") >= 0) {
 1063                           addAttribute(to, HTML.Tag.STRIKE, SimpleAttributeSet.EMPTY);
 1064                       }
 1065                   } else if (key == CSS.Attribute.VERTICAL_ALIGN) {
 1066                       String vAlign = from.getAttribute(key).toString();
 1067                       if (vAlign.indexOf("sup") >= 0) {
 1068                           addAttribute(to, HTML.Tag.SUP, SimpleAttributeSet.EMPTY);
 1069                       }
 1070                       if (vAlign.indexOf("sub") >= 0) {
 1071                           addAttribute(to, HTML.Tag.SUB, SimpleAttributeSet.EMPTY);
 1072                       }
 1073                   } else if (key == CSS.Attribute.TEXT_ALIGN) {
 1074                       addAttribute(to, HTML.Attribute.ALIGN,
 1075                                       from.getAttribute(key).toString());
 1076                   } else {
 1077                       // default is to store in a HTML style attribute
 1078                       if (value.length() > 0) {
 1079                           value = value + "; ";
 1080                       }
 1081                       value = value + key + ": " + from.getAttribute(key);
 1082                   }
 1083               } else {
 1084                   Object attr = from.getAttribute(key);
 1085                   if (attr instanceof AttributeSet) {
 1086                       attr = ((AttributeSet)attr).copyAttributes();
 1087                   }
 1088                   addAttribute(to, key, attr);
 1089               }
 1090           }
 1091           if (value.length() > 0) {
 1092               to.addAttribute(HTML.Attribute.STYLE, value);
 1093           }
 1094       }
 1095   
 1096       /**
 1097        * Add an attribute only if it doesn't exist so that we don't
 1098        * loose information replacing it with SimpleAttributeSet.EMPTY
 1099        */
 1100       private static void addAttribute(MutableAttributeSet to, Object key, Object value) {
 1101           Object attr = to.getAttribute(key);
 1102           if (attr == null || attr == SimpleAttributeSet.EMPTY) {
 1103               to.addAttribute(key, value);
 1104           } else {
 1105               if (attr instanceof MutableAttributeSet &&
 1106                   value instanceof AttributeSet) {
 1107                   ((MutableAttributeSet)attr).addAttributes((AttributeSet)value);
 1108               }
 1109           }
 1110       }
 1111   
 1112       /**
 1113        * Create/update an HTML &lt;font&gt; tag attribute.  The
 1114        * value of the attribute should be a MutableAttributeSet so
 1115        * that the attributes can be updated as they are discovered.
 1116        */
 1117       private static void createFontAttribute(CSS.Attribute a, AttributeSet from,
 1118                                       MutableAttributeSet to) {
 1119           MutableAttributeSet fontAttr = (MutableAttributeSet)
 1120               to.getAttribute(HTML.Tag.FONT);
 1121           if (fontAttr == null) {
 1122               fontAttr = new SimpleAttributeSet();
 1123               to.addAttribute(HTML.Tag.FONT, fontAttr);
 1124           }
 1125           // edit the parameters to the font tag
 1126           String htmlValue = from.getAttribute(a).toString();
 1127           if (a == CSS.Attribute.FONT_FAMILY) {
 1128               fontAttr.addAttribute(HTML.Attribute.FACE, htmlValue);
 1129           } else if (a == CSS.Attribute.FONT_SIZE) {
 1130               fontAttr.addAttribute(HTML.Attribute.SIZE, htmlValue);
 1131           } else if (a == CSS.Attribute.COLOR) {
 1132               fontAttr.addAttribute(HTML.Attribute.COLOR, htmlValue);
 1133           }
 1134       }
 1135   
 1136       /**
 1137        * Copies the given AttributeSet to a new set, converting
 1138        * any CSS attributes found to arguments of an HTML style
 1139        * attribute.
 1140        */
 1141       private static void convertToHTML40(AttributeSet from, MutableAttributeSet to) {
 1142           Enumeration keys = from.getAttributeNames();
 1143           String value = "";
 1144           while (keys.hasMoreElements()) {
 1145               Object key = keys.nextElement();
 1146               if (key instanceof CSS.Attribute) {
 1147                   value = value + " " + key + "=" + from.getAttribute(key) + ";";
 1148               } else {
 1149                   to.addAttribute(key, from.getAttribute(key));
 1150               }
 1151           }
 1152           if (value.length() > 0) {
 1153               to.addAttribute(HTML.Attribute.STYLE, value);
 1154           }
 1155       }
 1156   
 1157       //
 1158       // Overrides the writing methods to only break a string when
 1159       // canBreakString is true.
 1160       // In a future release it is likely AbstractWriter will get this
 1161       // functionality.
 1162       //
 1163   
 1164       /**
 1165        * Writes the line separator. This is overriden to make sure we don't
 1166        * replace the newline content in case it is outside normal ascii.
 1167        * @since 1.3
 1168        */
 1169       protected void writeLineSeparator() throws IOException {
 1170           boolean oldReplace = replaceEntities;
 1171           replaceEntities = false;
 1172           super.writeLineSeparator();
 1173           replaceEntities = oldReplace;
 1174           indented = false;
 1175       }
 1176   
 1177       /**
 1178        * This method is overriden to map any character entities, such as
 1179        * &lt; to &amp;lt;. <code>super.output</code> will be invoked to
 1180        * write the content.
 1181        * @since 1.3
 1182        */
 1183       protected void output(char[] chars, int start, int length)
 1184                      throws IOException {
 1185           if (!replaceEntities) {
 1186               super.output(chars, start, length);
 1187               return;
 1188           }
 1189           int last = start;
 1190           length += start;
 1191           for (int counter = start; counter < length; counter++) {
 1192               // This will change, we need better support character level
 1193               // entities.
 1194               switch(chars[counter]) {
 1195                   // Character level entities.
 1196               case '<':
 1197                   if (counter > last) {
 1198                       super.output(chars, last, counter - last);
 1199                   }
 1200                   last = counter + 1;
 1201                   output("&lt;");
 1202                   break;
 1203               case '>':
 1204                   if (counter > last) {
 1205                       super.output(chars, last, counter - last);
 1206                   }
 1207                   last = counter + 1;
 1208                   output("&gt;");
 1209                   break;
 1210               case '&':
 1211                   if (counter > last) {
 1212                       super.output(chars, last, counter - last);
 1213                   }
 1214                   last = counter + 1;
 1215                   output("&amp;");
 1216                   break;
 1217               case '"':
 1218                   if (counter > last) {
 1219                       super.output(chars, last, counter - last);
 1220                   }
 1221                   last = counter + 1;
 1222                   output("&quot;");
 1223                   break;
 1224                   // Special characters
 1225               case '\n':
 1226               case '\t':
 1227               case '\r':
 1228                   break;
 1229               default:
 1230                   if (chars[counter] < ' ' || chars[counter] > 127) {
 1231                       if (counter > last) {
 1232                           super.output(chars, last, counter - last);
 1233                       }
 1234                       last = counter + 1;
 1235                       // If the character is outside of ascii, write the
 1236                       // numeric value.
 1237                       output("&#");
 1238                       output(String.valueOf((int)chars[counter]));
 1239                       output(";");
 1240                   }
 1241                   break;
 1242               }
 1243           }
 1244           if (last < length) {
 1245               super.output(chars, last, length - last);
 1246           }
 1247       }
 1248   
 1249       /**
 1250        * This directly invokes super's <code>output</code> after converting
 1251        * <code>string</code> to a char[].
 1252        */
 1253       private void output(String string) throws IOException {
 1254           int length = string.length();
 1255           if (tempChars == null || tempChars.length < length) {
 1256               tempChars = new char[length];
 1257           }
 1258           string.getChars(0, length, tempChars, 0);
 1259           super.output(tempChars, 0, length);
 1260       }
 1261   
 1262       private boolean indented = false;
 1263   
 1264       /**
 1265        * Writes indent only once per line.
 1266        */
 1267       private void indentSmart() throws IOException {
 1268           if (!indented) {
 1269               indent();
 1270               indented = true;
 1271           }
 1272       }
 1273   }

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