Save This Page
Home » mojarra-1.2_09-b02-FCS-source » com.sun.faces.renderkit.html_basic » [javadoc | source]
    1   /*
    2    * $Id: HtmlResponseWriter.java,v 1.47.4.12 2008/07/13 23:40:27 rlubke Exp $
    3    */
    4   
    5   /*
    6    * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
    7    * 
    8    * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
    9    * 
   10    * The contents of this file are subject to the terms of either the GNU
   11    * General Public License Version 2 only ("GPL") or the Common Development
   12    * and Distribution License("CDDL") (collectively, the "License").  You
   13    * may not use this file except in compliance with the License. You can obtain
   14    * a copy of the License at https://glassfish.dev.java.net/public/CDDL+GPL.html
   15    * or glassfish/bootstrap/legal/LICENSE.txt.  See the License for the specific
   16    * language governing permissions and limitations under the License.
   17    * 
   18    * When distributing the software, include this License Header Notice in each
   19    * file and include the License file at glassfish/bootstrap/legal/LICENSE.txt.
   20    * Sun designates this particular file as subject to the "Classpath" exception
   21    * as provided by Sun in the GPL Version 2 section of the License file that
   22    * accompanied this code.  If applicable, add the following below the License
   23    * Header, with the fields enclosed by brackets [] replaced by your own
   24    * identifying information: "Portions Copyrighted [year]
   25    * [name of copyright owner]"
   26    * 
   27    * Contributor(s):
   28    * 
   29    * If you wish your version of this file to be governed by only the CDDL or
   30    * only the GPL Version 2, indicate your decision by adding "[Contributor]
   31    * elects to include this software in this distribution under the [CDDL or GPL
   32    * Version 2] license."  If you don't indicate a single choice of license, a
   33    * recipient has the option to distribute your version of this file under
   34    * either the CDDL, the GPL Version 2 or to extend the choice of license to
   35    * its licensees as provided above.  However, if you add GPL Version 2 code
   36    * and therefore, elected the GPL Version 2 license, then the option applies
   37    * only if the new code is made subject to such option by the copyright
   38    * holder.
   39    */
   40   
   41   package com.sun.faces.renderkit.html_basic;
   42   
   43   import java.io.IOException;
   44   import java.io.Writer;
   45   import java.util.regex.Matcher;
   46   import java.util.regex.Pattern;
   47   
   48   import javax.faces.FacesException;
   49   import javax.faces.component.UIComponent;
   50   import javax.faces.context.FacesContext;
   51   import javax.faces.context.ResponseWriter;
   52   
   53   import com.sun.faces.RIConstants;
   54   import com.sun.faces.config.WebConfiguration;
   55   import com.sun.faces.config.WebConfiguration.BooleanWebContextInitParameter;
   56   import com.sun.faces.io.FastStringWriter;
   57   import com.sun.faces.util.HtmlUtils;
   58   import com.sun.faces.util.MessageUtils;
   59   import javax.faces.context.ExternalContext;
   60   
   61   
   62   /**
   63    * <p><strong>HtmlResponseWriter</strong> is an Html specific implementation
   64    * of the <code>ResponseWriter</code> abstract class.
   65    * Kudos to Adam Winer (Oracle) for much of this code.
   66    */
   67   public class HtmlResponseWriter extends ResponseWriter {
   68   
   69   
   70       // Content Type for this Writer.
   71       //
   72       private String contentType = "text/html";
   73   
   74       // Character encoding of that Writer - this may be null
   75       // if the encoding isn't known.
   76       //
   77       private String encoding = null;
   78   
   79       // Writer to use for output;
   80       //
   81       private Writer writer = null;
   82   
   83       // True when we need to close a start tag
   84       //
   85       private boolean closeStart;
   86   
   87       // Configuration flag regarding disableUnicodeEscaping
   88       //
   89       private WebConfiguration.DisableUnicodeEscaping disableUnicodeEscaping;
   90   
   91       //Flag to escape Unicode
   92       //
   93       private boolean escapeUnicode;
   94   
   95       // Flag to escape ISO-8859-1 codes
   96       //
   97       private boolean escapeIso;
   98   
   99       // True when we shouldn't be escaping output (basically,
  100       // inside of <script> and <style> elements).
  101       //
  102       private boolean dontEscape;
  103   
  104       // flag to indicate we're writing a CDATA section
  105       private boolean writingCdata;
  106   
  107       // flat to indicate the current element is CDATA
  108       private boolean isCdata;
  109   
  110       // flag to indicate that we're writing a 'script' or 'style' element
  111       private boolean isScript;
  112   
  113       // flag to indicate that we're writing a 'style' element
  114       private boolean isStyle;
  115   
  116       // flag to indicate that we're writing a 'src' attribute as part of
  117       // 'script' or 'style' element
  118       private boolean scriptOrStyleSrc;
  119   
  120       // flag to indicate if the content type is Xhtml
  121       private boolean isXhtml;
  122   
  123       // HtmlResponseWriter to use when buffering is required
  124       private Writer origWriter;
  125   
  126       // Keep one instance of the script buffer per Writer
  127       private FastStringWriter scriptBuffer;
  128   
  129       // Keep one instance of attributesBuffer to buffer the writting
  130       // of all attributes for a particular element to reducr the number
  131       // of writes
  132       private FastStringWriter attributesBuffer;
  133       
  134       // Enables hiding of inlined script and style
  135       // elements from old browsers
  136       private Boolean isScriptHidingEnabled;
  137   
  138       // Enables scripts to be included in attribute values
  139       private Boolean isScriptInAttributeValueEnabled;
  140   
  141       // Internal buffer used when outputting properly escaped information
  142       // using HtmlUtils class.
  143       //
  144       private char[] buffer = new char[1028];
  145   
  146       // Internal buffer for to store the result of String.getChars() for
  147       // values passed to the writer as String to reduce the overhead
  148       // of String.charAt().  This buffer will be grown, if necessary, to
  149       // accomodate larger values.
  150       private char[] textBuffer = new char[128];
  151   
  152       private char[] charHolder = new char[1];
  153   
  154       static final Pattern CDATA_START_SLASH_SLASH;
  155   
  156       static final Pattern CDATA_END_SLASH_SLASH;
  157   
  158       static final Pattern CDATA_START_SLASH_STAR;
  159   
  160       static final Pattern CDATA_END_SLASH_STAR;
  161   
  162       static {
  163           // At the beginning of a line, match // followed by any amount of
  164           // whitespace, followed by <![CDATA[
  165           CDATA_START_SLASH_SLASH = Pattern.compile("^//\\s*\\Q<![CDATA[\\E");
  166   
  167           // At the end of a line, match // followed by any amout of whitespace,
  168           // followed by ]]>
  169           CDATA_END_SLASH_SLASH = Pattern.compile("//\\s*\\Q]]>\\E$");
  170   
  171           // At the beginning of a line, match /* followed by any amout of
  172           // whitespace, followed by <![CDATA[, followed by any amount of whitespace,
  173           // followed by */
  174           CDATA_START_SLASH_STAR = Pattern.compile("^/\\*\\s*\\Q<![CDATA[\\E\\s*\\*/");
  175   
  176           // At the end of a line, match /* followed by any amount of whitespace,
  177           // followed by ]]> followed by any amount of whitespace, followed by */
  178           CDATA_END_SLASH_STAR = Pattern.compile("/\\*\\s*\\Q]]>\\E\\s*\\*/$");
  179   
  180       }
  181   
  182       // ------------------------------------------------------------ Constructors
  183   
  184   
  185       /**
  186        * Constructor sets the <code>ResponseWriter</code> and
  187        * encoding, and enables script hiding by default.
  188        *
  189        * @param writer      the <code>ResponseWriter</code>
  190        * @param contentType the content type.
  191        * @param encoding    the character encoding.
  192        *
  193        * @throws javax.faces.FacesException the encoding is not recognized.
  194        */
  195       public HtmlResponseWriter(Writer writer,
  196                                 String contentType,
  197                                 String encoding)
  198       throws FacesException {
  199           this(writer, contentType, encoding, null, null, null);
  200       }
  201   
  202       /**
  203        * <p>Constructor sets the <code>ResponseWriter</code> and
  204        * encoding.</p>
  205        *
  206        * <p>The argument configPrefs is a map of configurable prefs that affect
  207        * this instance's behavior.  Supported keys are:</p>
  208        *
  209        * <p>BooleanWebContextInitParameter.EnableJSStyleHiding: <code>true</code>
  210        * if the writer should attempt to hide JS from older browsers</p>
  211        *
  212        * @param writer      the <code>ResponseWriter</code>
  213        * @param contentType the content type.
  214        * @param encoding    the character encoding.
  215        *
  216        * @throws javax.faces.FacesException the encoding is not recognized.
  217        */
  218       public HtmlResponseWriter(Writer writer,
  219                                 String contentType,
  220                                 String encoding,
  221                                 Boolean isScriptHidingEnabled,
  222                                 Boolean isScriptInAttributeValueEnabled,
  223                                 WebConfiguration.DisableUnicodeEscaping disableUnicodeEscaping)
  224       throws FacesException {
  225   
  226           this.writer = writer;
  227   
  228           if (null != contentType) {
  229               this.contentType = contentType;
  230           }
  231   
  232           this.encoding = encoding;
  233   
  234           // init those configuration parameters not yet initialized
  235           WebConfiguration webConfig = null;
  236           if (isScriptHidingEnabled == null) {
  237               webConfig = getWebConfiguration(webConfig);
  238               isScriptHidingEnabled = (null == webConfig) ? BooleanWebContextInitParameter.EnableJSStyleHiding.getDefaultValue() :
  239                                   webConfig.isOptionEnabled(
  240                                   BooleanWebContextInitParameter.EnableJSStyleHiding);
  241           }
  242   
  243           if (isScriptInAttributeValueEnabled == null) {
  244               webConfig = getWebConfiguration(webConfig);
  245               isScriptInAttributeValueEnabled = (null == webConfig) ? BooleanWebContextInitParameter.EnableScriptInAttributeValue.getDefaultValue() :
  246                                webConfig.isOptionEnabled(
  247                                BooleanWebContextInitParameter.EnableScriptInAttributeValue);
  248           }
  249   
  250           if (disableUnicodeEscaping == null) {
  251               webConfig = getWebConfiguration(webConfig);
  252               disableUnicodeEscaping =
  253                       WebConfiguration.DisableUnicodeEscaping.getByValue(
  254                           (null == webConfig) ? WebConfiguration.WebContextInitParameter.DisableUnicodeEscaping.getDefaultValue() :
  255                                   webConfig.getOptionValue(
  256                                    WebConfiguration.WebContextInitParameter.DisableUnicodeEscaping));
  257               if (disableUnicodeEscaping == null) {
  258                   disableUnicodeEscaping = WebConfiguration.DisableUnicodeEscaping.False;
  259               }
  260           }
  261   
  262           // and store them for later use
  263           this.isScriptHidingEnabled = isScriptHidingEnabled;
  264           this.isScriptInAttributeValueEnabled = isScriptInAttributeValueEnabled;
  265           this.disableUnicodeEscaping = disableUnicodeEscaping;
  266   
  267           this.attributesBuffer = new FastStringWriter(128);
  268   
  269           // Check the character encoding
  270           if (!HtmlUtils.validateEncoding(encoding)) {
  271               throw new IllegalArgumentException(MessageUtils.getExceptionMessageString(
  272                     MessageUtils.ENCODING_ERROR_MESSAGE_ID));
  273           }
  274   
  275           String charsetName = encoding.toUpperCase();
  276   
  277           switch (disableUnicodeEscaping)
  278           {
  279               case True:
  280                   // html escape noting (except the dangerous characters like "<>'" etc
  281                   escapeUnicode = false;
  282                   escapeIso = false;
  283                   break;
  284               case False:
  285                   // html escape any non-ascii character
  286                   escapeUnicode = true;
  287                   escapeIso = true;
  288                   break;
  289               case Auto:
  290                   // is stream capable of rendering unicode, do not escape
  291                   escapeUnicode = !HtmlUtils.isUTFencoding(charsetName);
  292                   // is stream capable of rendering unicode or iso-8859-1, do not escape
  293                   escapeIso = !HtmlUtils.isISO8859_1encoding(charsetName) && !HtmlUtils.isUTFencoding(charsetName);
  294                   break;
  295           }
  296       }
  297   
  298       private WebConfiguration getWebConfiguration(WebConfiguration webConfig) {
  299           if (webConfig != null) {
  300               return webConfig;
  301           }
  302   
  303           FacesContext context = FacesContext.getCurrentInstance();
  304           if (null != context) {
  305               ExternalContext extContext = context.getExternalContext();
  306               if (null != extContext) {
  307                   webConfig = WebConfiguration.getInstance(extContext);
  308               }
  309           }
  310           return webConfig;
  311       }
  312   
  313       // -------------------------------------------------- Methods From Closeable
  314   
  315   
  316       /** Methods From <code>java.io.Writer</code> */
  317   
  318       public void close() throws IOException {
  319   
  320           closeStartIfNecessary();
  321           writer.close();
  322   
  323       }
  324   
  325       // -------------------------------------------------- Methods From Flushable
  326   
  327   
  328       /**
  329        * Flush any buffered output to the contained writer.
  330        *
  331        * @throws IOException if an input/output error occurs.
  332        */
  333       public void flush() throws IOException {
  334   
  335           // NOTE: Internal buffer's contents (the ivar "buffer") is
  336           // written to the contained writer in the HtmlUtils class - see
  337           // HtmlUtils.flushBuffer method; Buffering is done during
  338           // writeAttribute/writeText - otherwise, output is written
  339           // directly to the writer (ex: writer.write(....)..
  340           //
  341           // close any previously started element, if necessary
  342           closeStartIfNecessary();
  343   
  344       }
  345   
  346       // ---------------------------------------------------------- Public Methods
  347   
  348   
  349       /** @return the content type such as "text/html" for this ResponseWriter. */
  350       public String getContentType() {
  351   
  352           return contentType;
  353   
  354       }
  355   
  356   
  357       /**
  358        * <p>Create a new instance of this <code>ResponseWriter</code> using
  359        * a different <code>Writer</code>.
  360        *
  361        * @param writer The <code>Writer</code> that will be used to create
  362        *               another <code>ResponseWriter</code>.
  363        */
  364       public ResponseWriter cloneWithWriter(Writer writer) {
  365   
  366           try {
  367               return new HtmlResponseWriter(writer,
  368                                             getContentType(),
  369                                             getCharacterEncoding(),
  370                                             isScriptHidingEnabled,
  371                                             isScriptInAttributeValueEnabled,
  372                                             disableUnicodeEscaping);
  373           } catch (FacesException e) {
  374               // This should never happen
  375               throw new IllegalStateException();
  376           }
  377   
  378       }
  379   
  380   
  381       /** Output the text for the end of a document. */
  382       public void endDocument() throws IOException {
  383   
  384           writer.flush();
  385   
  386       }
  387   
  388   
  389       /**
  390        * <p>Write the end of an element. This method will first
  391        * close any open element created by a call to
  392        * <code>startElement()</code>.
  393        *
  394        * @param name Name of the element to be ended
  395        *
  396        * @throws IOException          if an input/output error occurs
  397        * @throws NullPointerException if <code>name</code>
  398        *                              is <code>null</code>
  399        */
  400       public void endElement(String name) throws IOException {
  401   
  402           if (name == null) {
  403               throw new NullPointerException(MessageUtils.getExceptionMessageString(
  404                     MessageUtils.NULL_PARAMETERS_ERROR_MESSAGE_ID, "name"));
  405           }
  406   
  407           if (!writingCdata) {
  408               // always turn escaping back on once an element ends unless we're
  409               // still writing cdata content
  410               dontEscape = false;
  411           }
  412           isXhtml = getContentType().equals(
  413               RIConstants.XHTML_CONTENT_TYPE);
  414   
  415           if (isScriptOrStyle(name)
  416                && !scriptOrStyleSrc
  417                && writer instanceof FastStringWriter) {
  418               String result = ((FastStringWriter) writer).getBuffer().toString();
  419               writer = origWriter;
  420   
  421               if (result != null) {
  422                   String trim = result.trim();
  423                   if (isXhtml) {
  424                       if (isScript) {
  425                           Matcher
  426                               cdataStartSlashSlash =
  427                                 CDATA_START_SLASH_SLASH.matcher(trim),
  428                               cdataEndSlashSlash =
  429                                 CDATA_END_SLASH_SLASH.matcher(trim),
  430                               cdataStartSlashStar =
  431                                 CDATA_START_SLASH_STAR.matcher(trim),
  432                               cdataEndSlashStar =
  433                                 CDATA_END_SLASH_STAR.matcher(trim);
  434                           int trimLen = trim.length(), start, end;
  435                           // case 1 start is // end is //
  436                           if (cdataStartSlashSlash.find() &&
  437                               cdataEndSlashSlash.find()) {
  438                               start = cdataStartSlashSlash.end() - cdataStartSlashSlash.start();
  439                               end = trimLen - (cdataEndSlashSlash.end() - cdataEndSlashSlash.start());
  440                               writer.write(trim.substring(start, end));
  441                           }
  442                           // case 2 start is // end is /* */
  443                           else if ((null != cdataStartSlashSlash.reset() && cdataStartSlashSlash.find()) &&
  444                                    cdataEndSlashStar.find()) {
  445                               start = cdataStartSlashSlash.end() - cdataStartSlashSlash.start();
  446                               end = trimLen - (cdataEndSlashStar.end() - cdataEndSlashStar.start());
  447                               writer.write(trim.substring(start, end));
  448                           }
  449                           // case 3 start is /* */ end is /* */
  450                           else if (cdataStartSlashStar.find() &&
  451                                    (null != cdataEndSlashStar.reset() && cdataEndSlashStar.find())) {
  452                               start = cdataStartSlashStar.end() - cdataStartSlashStar.start();
  453                               end = trimLen - (cdataEndSlashStar.end() - cdataEndSlashStar.start());
  454                               writer.write(trim.substring(start, end));
  455                           }
  456                           // case 4 start is /* */ end is //
  457                           else if ((null != cdataStartSlashStar.reset() && cdataStartSlashStar.find()) &&
  458                                    (null != cdataEndSlashStar.reset() && cdataEndSlashSlash.find())) {
  459                               start = cdataStartSlashStar.end() - cdataStartSlashStar.start();
  460                               end = trimLen - (cdataEndSlashSlash.end() - cdataEndSlashSlash.start());
  461                               writer.write(trim.substring(start, end));
  462                           }
  463                           // case 5 no commented out cdata present.
  464                           else {
  465                               writer.write(result);
  466                           }
  467                       } else {
  468                           if (trim.startsWith("<![CDATA[") && trim.endsWith("]]>")) {
  469                               writer.write(trim.substring(9, trim.length() - 3));
  470                           } else {
  471                               writer.write(result);
  472                           }
  473                       }
  474                   } else {
  475                       if (trim.startsWith("<!--") && trim.endsWith("//-->")) {
  476                           writer.write(trim.substring(4, trim.length() - 5));
  477                       } else {
  478                           writer.write(result);
  479                       }
  480                   }
  481               }
  482               if (isXhtml) {
  483                   if (!writingCdata) {
  484                       if (isScript) {
  485                           writer.write("\n//]]>\n");
  486                       } else {
  487                           writer.write("\n]]>\n");
  488                       }
  489                   }
  490               } else {
  491                   if (isScriptHidingEnabled) {
  492                       writer.write("\n//-->\n");
  493                   }
  494               }
  495           }
  496           isScript = false;
  497           isStyle = false;
  498           if ("cdata".equalsIgnoreCase(name)) {
  499               writer.write("]]>");
  500               writingCdata = false;
  501               isCdata = false;
  502               dontEscape = false;
  503               return;
  504           }
  505           // See if we need to close the start of the last element
  506           if (closeStart) {
  507               boolean isEmptyElement = HtmlUtils.isEmptyElement(name);
  508   
  509               // Tricky: we need to use the writer ivar here, rather than the
  510               // one from the FacesContext because we don't want
  511               // spurious /> characters to appear in the output.
  512               if (isEmptyElement) {
  513                   flushAttributes();
  514                   writer.write(" />");
  515                   closeStart = false;
  516                   return;
  517               }
  518               flushAttributes();
  519               writer.write('>');
  520               closeStart = false;
  521           }
  522   
  523           writer.write("</");
  524           writer.write(name);
  525           writer.write('>');
  526   
  527       }
  528   
  529   
  530       /**
  531        * @return the character encoding, such as "ISO-8859-1" for this
  532        *         ResponseWriter.  Refer to:
  533        *         <a href="http://www.iana.org/assignments/character-sets">theIANA</a>
  534        *         for a list of character encodings.
  535        */
  536       public String getCharacterEncoding() {
  537   
  538           return encoding;
  539   
  540       }
  541   
  542   
  543       /**
  544        * <p>Write the text that should begin a response.</p>
  545        *
  546        * @throws IOException if an input/output error occurs
  547        */
  548       public void startDocument() throws IOException {
  549   
  550           // do nothing;
  551   
  552       }
  553   
  554   
  555       /**
  556        * <p>Write the start of an element, up to and including the
  557        * element name.  Clients call <code>writeAttribute()</code> or
  558        * <code>writeURIAttribute()</code> methods to add attributes after
  559        * calling this method.
  560        *
  561        * @param name                Name of the starting element
  562        * @param componentForElement The UIComponent instance that applies to this
  563        *                            element.  This argument may be <code>null</code>.
  564        *
  565        * @throws IOException          if an input/output error occurs
  566        * @throws NullPointerException if <code>name</code>
  567        *                              is <code>null</code>
  568        */
  569       public void startElement(String name, UIComponent componentForElement)
  570             throws IOException {
  571   
  572           if (name == null) {
  573               throw new NullPointerException(MessageUtils.getExceptionMessageString(
  574                     MessageUtils.NULL_PARAMETERS_ERROR_MESSAGE_ID, "name"));
  575           }
  576           closeStartIfNecessary();
  577           isScriptOrStyle(name);
  578           scriptOrStyleSrc = false;
  579           if ("cdata".equalsIgnoreCase(name)) {
  580               isCdata = true;
  581               writingCdata = true;
  582               dontEscape = true;
  583               writer.write("<![CDATA[");
  584               closeStart = false;
  585               return;
  586           } else if (writingCdata) {
  587               // starting an element within a cdata section,
  588               // keep escaping disabled
  589               isCdata = false;
  590               writingCdata = true;
  591               dontEscape = true;
  592           }
  593   
  594           writer.write('<');
  595           writer.write(name);
  596           closeStart = true;
  597   
  598       }
  599   
  600       @Override
  601       public void write(char[] cbuf) throws IOException {
  602   
  603           closeStartIfNecessary();
  604           writer.write(cbuf);
  605   
  606       }
  607   
  608       @Override
  609       public void write(int c) throws IOException {
  610   
  611           closeStartIfNecessary();
  612           writer.write(c);
  613   
  614       }
  615   
  616       @Override
  617       public void write(String str) throws IOException {
  618   
  619           closeStartIfNecessary();
  620           writer.write(str);
  621   
  622       }
  623   
  624   
  625       public void write(char[] cbuf, int off, int len) throws IOException {
  626   
  627           closeStartIfNecessary();
  628           writer.write(cbuf, off, len);
  629   
  630       }
  631   
  632       @Override
  633       public void write(String str, int off, int len) throws IOException {
  634   
  635           closeStartIfNecessary();
  636           writer.write(str, off, len);
  637   
  638       }
  639   
  640   
  641       /**
  642        * <p>Write a properly escaped attribute name and the corresponding
  643        * value.  The value text will be converted to a String if
  644        * necessary.  This method may only be called after a call to
  645        * <code>startElement()</code>, and before the opened element has been
  646        * closed.</p>
  647        *
  648        * @param name                  Attribute name to be added
  649        * @param value                 Attribute value to be added
  650        * @param componentPropertyName The name of the component property to
  651        *                              which this attribute argument applies.  This argument may be
  652        *                              <code>null</code>.
  653        *
  654        * @throws IllegalStateException if this method is called when there
  655        *                               is no currently open element
  656        * @throws IOException           if an input/output error occurs
  657        * @throws NullPointerException  if <code>name</code> is <code>null</code>
  658        */
  659       public void writeAttribute(String name, Object value,
  660                                  String componentPropertyName)
  661             throws IOException {
  662   
  663           if (name == null) {
  664               throw new NullPointerException(MessageUtils.getExceptionMessageString(
  665                     MessageUtils.NULL_PARAMETERS_ERROR_MESSAGE_ID, "name"));
  666           }
  667           if (value == null) {
  668               return;
  669           }
  670   
  671           if (isCdata) {
  672               return;
  673           }
  674   
  675           if (name.equalsIgnoreCase("src") && isScriptOrStyle()) {
  676               scriptOrStyleSrc = true;
  677           }
  678   
  679           Class valueClass = value.getClass();
  680   
  681           // Output Boolean values specially
  682           if (valueClass == Boolean.class) {
  683               if (Boolean.TRUE.equals(value)) {
  684                   // NOTE:  HTML 4.01 states that boolean attributes
  685                   //        may legally take a single value which is the
  686                   //        name of the attribute itself or appear using
  687                   //        minimization.
  688                   //  http://www.w3.org/TR/html401/intro/sgmltut.html#h-3.3.4.2
  689                   attributesBuffer.write(' ');
  690                   attributesBuffer.write(name);
  691                   attributesBuffer.write("=\"");
  692                   attributesBuffer.write(name);
  693                   attributesBuffer.write('"');
  694               }
  695           } else {
  696               attributesBuffer.write(' ');
  697               attributesBuffer.write(name);
  698               attributesBuffer.write("=\"");
  699               // write the attribute value
  700               String val = value.toString();
  701               ensureTextBufferCapacity(val);
  702               HtmlUtils.writeAttribute(attributesBuffer,
  703                                        escapeUnicode,
  704                                        escapeIso,
  705                                        buffer,
  706                                        val,
  707                                        textBuffer,
  708                                        isScriptInAttributeValueEnabled);
  709               attributesBuffer.write('"');
  710           }
  711   
  712       }
  713   
  714   
  715       /**
  716        * <p>Write a comment string containing the specified text.
  717        * The text will be converted to a String if necessary.
  718        * If there is an open element that has been created by a call
  719        * to <code>startElement()</code>, that element will be closed
  720        * first.</p>
  721        *
  722        * @param comment Text content of the comment
  723        *
  724        * @throws IOException          if an input/output error occurs
  725        * @throws NullPointerException if <code>comment</code>
  726        *                              is <code>null</code>
  727        */
  728       public void writeComment(Object comment) throws IOException {
  729   
  730           if (comment == null) {
  731               throw new NullPointerException(MessageUtils.getExceptionMessageString(
  732                     MessageUtils.NULL_PARAMETERS_ERROR_MESSAGE_ID));
  733           }
  734   
  735           if (writingCdata) {
  736               return;
  737           }
  738   
  739           closeStartIfNecessary();
  740           // Don't include a trailing space after the '<!--'
  741           // or a leading space before the '-->' to support
  742           // IE conditional commentsoth
  743           writer.write("<!--");
  744           writer.write(comment.toString());
  745           writer.write("-->");
  746   
  747       }
  748   
  749   
  750       /**
  751        * <p>Write a properly escaped single character, If there
  752        * is an open element that has been created by a call to
  753        * <code>startElement()</code>, that element will be closed first.</p>
  754        * <p/>
  755        * <p>All angle bracket occurrences in the argument must be escaped
  756        * using the &amp;gt; &amp;lt; syntax.</p>
  757        *
  758        * @param text Text to be written
  759        *
  760        * @throws IOException if an input/output error occurs
  761        */
  762       public void writeText(char text) throws IOException {
  763   
  764           closeStartIfNecessary();
  765           if (dontEscape) {
  766               writer.write(text);
  767           } else {
  768               charHolder[0] = text;
  769               HtmlUtils.writeText(writer, escapeUnicode, escapeIso, buffer, charHolder);
  770           }
  771   
  772       }
  773   
  774   
  775       /**
  776        * <p>Write properly escaped text from a character array.
  777        * The output from this command is identical to the invocation:
  778        * <code>writeText(c, 0, c.length)</code>.
  779        * If there is an open element that has been created by a call to
  780        * <code>startElement()</code>, that element will be closed first.</p>
  781        * </p>
  782        * <p/>
  783        * <p>All angle bracket occurrences in the argument must be escaped
  784        * using the &amp;gt; &amp;lt; syntax.</p>
  785        *
  786        * @param text Text to be written
  787        *
  788        * @throws IOException          if an input/output error occurs
  789        * @throws NullPointerException if <code>text</code>
  790        *                              is <code>null</code>
  791        */
  792       public void writeText(char text[]) throws IOException {
  793   
  794           if (text == null) {
  795               throw new NullPointerException(MessageUtils.getExceptionMessageString(
  796                     MessageUtils.NULL_PARAMETERS_ERROR_MESSAGE_ID, "text"));
  797           }
  798           closeStartIfNecessary();
  799           if (dontEscape) {
  800               writer.write(text);
  801           } else {
  802               HtmlUtils.writeText(writer, escapeUnicode, escapeIso, buffer, text);
  803           }
  804   
  805       }
  806   
  807   
  808       /**
  809        * <p>Write a properly escaped object. The object will be converted
  810        * to a String if necessary.  If there is an open element
  811        * that has been created by a call to <code>startElement()</code>,
  812        * that element will be closed first.</p>
  813        *
  814        * @param text                  Text to be written
  815        * @param componentPropertyName The name of the component property to
  816        *                              which this text argument applies.  This argument may be <code>null</code>.
  817        *
  818        * @throws IOException          if an input/output error occurs
  819        * @throws NullPointerException if <code>text</code>
  820        *                              is <code>null</code>
  821        */
  822       public void writeText(Object text, String componentPropertyName)
  823             throws IOException {
  824   
  825           if (text == null) {
  826               throw new NullPointerException(MessageUtils.getExceptionMessageString(
  827                     MessageUtils.NULL_PARAMETERS_ERROR_MESSAGE_ID, "text"));
  828           }
  829           closeStartIfNecessary();
  830           if (dontEscape) {
  831               writer.write(text.toString());
  832           } else {
  833               String val = text.toString();
  834               ensureTextBufferCapacity(val);
  835               HtmlUtils.writeText(writer,
  836                                   escapeUnicode,
  837   					            escapeIso,
  838                                   buffer,
  839                                   val,
  840                                   textBuffer);
  841           }
  842   
  843       }
  844   
  845   
  846       /**
  847        * <p>Write properly escaped text from a character array.
  848        * If there is an open element that has been created by a call
  849        * to <code>startElement()</code>, that element will be closed
  850        * first.</p>
  851        * <p/>
  852        * <p>All angle bracket occurrences in the argument must be escaped
  853        * using the &amp;gt; &amp;lt; syntax.</p>
  854        *
  855        * @param text Text to be written
  856        * @param off  Starting offset (zero-relative)
  857        * @param len  Number of characters to be written
  858        *
  859        * @throws IndexOutOfBoundsException if the calculated starting or
  860        *                                   ending position is outside the bounds of the character array
  861        * @throws IOException               if an input/output error occurs
  862        * @throws NullPointerException      if <code>text</code>
  863        *                                   is <code>null</code>
  864        */
  865       public void writeText(char text[], int off, int len)
  866             throws IOException {
  867   
  868           if (text == null) {
  869               throw new NullPointerException(MessageUtils.getExceptionMessageString(
  870                     MessageUtils.NULL_PARAMETERS_ERROR_MESSAGE_ID, "text"));
  871           }
  872           if (off < 0 || off > text.length || len < 0 || len > text.length) {
  873               throw new IndexOutOfBoundsException();
  874           }
  875           closeStartIfNecessary();
  876           if (dontEscape) {
  877               writer.write(text, off, len);
  878           } else {
  879               HtmlUtils.writeText(writer, escapeUnicode, escapeIso, buffer, text, off, len);
  880           }
  881   
  882       }
  883   
  884   
  885       /**
  886        * <p>Write a properly encoded URI attribute name and the corresponding
  887        * value. The value text will be converted to a String if necessary).
  888        * This method may only be called after a call to
  889        * <code>startElement()</code>, and before the opened element has been
  890        * closed.</p>
  891        *
  892        * @param name                  Attribute name to be added
  893        * @param value                 Attribute value to be added
  894        * @param componentPropertyName The name of the component property to
  895        *                              which this attribute argument applies.  This argument may be
  896        *                              <code>null</code>.
  897        *
  898        * @throws IllegalStateException if this method is called when there
  899        *                               is no currently open element
  900        * @throws IOException           if an input/output error occurs
  901        * @throws NullPointerException  if <code>name</code> or
  902        *                               <code>value</code> is <code>null</code>
  903        */
  904       public void writeURIAttribute(String name, Object value,
  905                                     String componentPropertyName)
  906             throws IOException {
  907   
  908           if (name == null) {
  909               throw new NullPointerException(MessageUtils.getExceptionMessageString(
  910                     MessageUtils.NULL_PARAMETERS_ERROR_MESSAGE_ID, "name"));
  911           }
  912           if (value == null) {
  913               throw new NullPointerException(MessageUtils.getExceptionMessageString(
  914                     MessageUtils.NULL_PARAMETERS_ERROR_MESSAGE_ID, "value"));
  915           }
  916   
  917           if (isCdata) {
  918               return;
  919           }
  920   
  921           if (name.equalsIgnoreCase("src") && isScriptOrStyle()) {
  922               scriptOrStyleSrc = true;
  923           }
  924   
  925           attributesBuffer.write(' ');
  926           attributesBuffer.write(name);
  927           attributesBuffer.write("=\"");
  928   
  929           String stringValue = value.toString();
  930           ensureTextBufferCapacity(stringValue);
  931           // Javascript URLs should not be URL-encoded
  932           if (stringValue.startsWith("javascript:")) {
  933               HtmlUtils.writeAttribute(attributesBuffer,
  934                                        escapeUnicode,
  935                                        escapeIso,
  936                                        buffer,
  937                                        stringValue,
  938                                        textBuffer,
  939                                        isScriptInAttributeValueEnabled);
  940           } else {
  941               HtmlUtils.writeURL(attributesBuffer,
  942                                  stringValue,
  943                                  textBuffer,
  944                                  encoding,
  945                                  getContentType());
  946           }
  947   
  948           attributesBuffer.write('"');
  949   
  950       }
  951   
  952       // --------------------------------------------------------- Private Methods
  953   
  954       private void ensureTextBufferCapacity(String source) {
  955           int len = source.length();
  956           if (textBuffer.length < len) {
  957               textBuffer = new char[len * 2];
  958           }
  959       }
  960   
  961       /**
  962        * This method automatically closes a previous element (if not
  963        * already closed).
  964        * @throws IOException if an error occurs writing
  965        */
  966       private void closeStartIfNecessary() throws IOException {
  967   
  968           if (closeStart) {
  969               flushAttributes();
  970               writer.write('>');
  971               closeStart = false;
  972               if (isScriptOrStyle() && !scriptOrStyleSrc) {
  973                   isXhtml = getContentType().equals(
  974                        RIConstants.XHTML_CONTENT_TYPE);
  975                   if (isXhtml) {
  976                       if (!writingCdata) {
  977                           if (isScript) {
  978                               writer.write("\n//<![CDATA[\n");
  979                           } else {
  980                               writer.write("\n<![CDATA[\n");
  981                           }
  982                       }
  983                   } else {
  984                       if (isScriptHidingEnabled) {
  985                           writer.write("\n<!--\n");
  986                       }
  987                   }
  988                   origWriter = writer;
  989                   if (scriptBuffer == null) {
  990                       scriptBuffer = new FastStringWriter(1024);
  991                   }
  992                   scriptBuffer.reset();
  993                   writer = scriptBuffer;
  994                   isScript = false;
  995                   isStyle = false;
  996               }
  997           }
  998   
  999       }
 1000   
 1001   
 1002       private void flushAttributes() throws IOException {
 1003   
 1004           // a little complex, but the end result is, potentially, two
 1005           // fewer temp objects created per call.
 1006           StringBuilder b = attributesBuffer.getBuffer();
 1007           int totalLength = b.length();
 1008           if (totalLength != 0) {
 1009               int curIdx = 0;
 1010               while (curIdx < totalLength) {
 1011                   if ((totalLength - curIdx) > buffer.length) {
 1012                       int end = curIdx + buffer.length;
 1013                       b.getChars(curIdx, end, buffer, 0);
 1014                       writer.write(buffer);
 1015                       curIdx += buffer.length;
 1016                   } else {
 1017                       int len = totalLength - curIdx;
 1018                       b.getChars(curIdx, curIdx + len, buffer, 0);
 1019                       writer.write(buffer, 0, len);
 1020                       curIdx += len;
 1021                   }
 1022               }
 1023               attributesBuffer.reset();
 1024           }
 1025   
 1026       }
 1027   
 1028   
 1029       private boolean isScriptOrStyle(String name) {
 1030           if ("script".equalsIgnoreCase(name)) {
 1031               isScript = true;
 1032               dontEscape = true;
 1033           } else if ("style".equalsIgnoreCase(name)) {
 1034               isStyle = true;
 1035               dontEscape = true;
 1036           } else {
 1037               isScript = false;
 1038               isStyle = false;
 1039               dontEscape = false;
 1040           }
 1041   
 1042           return (isScript || isStyle);
 1043       }
 1044   
 1045       private boolean isScriptOrStyle() {
 1046           return (isScript || isStyle);
 1047       }
 1048   
 1049   }

Save This Page
Home » mojarra-1.2_09-b02-FCS-source » com.sun.faces.renderkit.html_basic » [javadoc | source]