All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.sun.webui.jsf.model.Markup Maven / Gradle / Ivy

There is a newer version: 4.4.0.1
Show newest version
/*
 * The contents of this file are subject to the terms
 * of the Common Development and Distribution License
 * (the License).  You may not use this file except in
 * compliance with the License.
 * 
 * You can obtain a copy of the license at
 * https://woodstock.dev.java.net/public/CDDLv1.0.html.
 * See the License for the specific language governing
 * permissions and limitations under the License.
 * 
 * When distributing Covered Code, include this CDDL
 * Header Notice in each file and include the License file
 * at https://woodstock.dev.java.net/public/CDDLv1.0.html.
 * If applicable, add the following below the CDDL Header,
 * with the fields enclosed by brackets [] replaced by
 * you own identifying information:
 * "Portions Copyrighted [year] [name of copyright owner]"
 * 
 * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
 */
/*
 * $Id: Markup.java,v 1.1.20.1 2009-12-29 03:47:57 jyeary Exp $
 */
package com.sun.webui.jsf.model;

import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import javax.faces.FacesException;
import javax.faces.component.UIComponent;

/**
 * 

Utility bean that serves as an accumulating buffer for * well formed markup fragments typically generated by renderers. * The fundamental API is modelled after ResponseWriter * in JavaServer Faces.

*/ public class Markup { // ------------------------------------------------------- Static Variables /* *

Entities from HTML 4.0, section 24.2.1; * character codes 0xA0 to 0xFF

*/ private static String[] ISO8859_1_Entities = new String[]{ "nbsp", "iexcl", "cent", "pound", "curren", "yen", "brvbar", "sect", "uml", "copy", "ordf", "laquo", "not", "shy", "reg", "macr", "deg", "plusmn", "sup2", "sup3", "acute", "micro", "para", "middot", "cedil", "sup1", "ordm", "raquo", "frac14", "frac12", "frac34", "iquest", "Agrave", "Aacute", "Acirc", "Atilde", "Auml", "Aring", "AElig", "Ccedil", "Egrave", "Eacute", "Ecirc", "Euml", "Igrave", "Iacute", "Icirc", "Iuml", "ETH", "Ntilde", "Ograve", "Oacute", "Ocirc", "Otilde", "Ouml", "times", "Oslash", "Ugrave", "Uacute", "Ucirc", "Uuml", "Yacute", "THORN", "szlig", "agrave", "aacute", "acirc", "atilde", "auml", "aring", "aelig", "ccedil", "egrave", "eacute", "ecirc", "euml", "igrave", "iacute", "icirc", "iuml", "eth", "ntilde", "ograve", "oacute", "ocirc", "otilde", "ouml", "divide", "oslash", "ugrave", "uacute", "ucirc", "uuml", "yacute", "thorn", "yuml" }; // ----------------------------------------------------- Instance Variables /** *

Buffer into which we accumulate the created markup.

*/ private StringBuffer buffer = new StringBuffer(); /** *

The character encoding that we assume will be used when * the markup contained in this instance is rendered. The * default value ("ISO-8859-1") is an attempt to be conservative.

*/ private String encoding = "ISO-8859-1"; /** *

Flag indicating that an element is currently open.

*/ private boolean open = false; // ------------------------------------------------------------- Properties /** *

Return the character encoding assumed to be used when the * markup contained in this instance is ultimately rendered.

*/ public String getEncoding() { return this.encoding; } /** *

Set the character encoding assumed to be used when the * markup contained in this instance is ultimately rendered.

* * @param encoding The new character encoding */ public void setEncoding(String encoding) { this.encoding = encoding; } /** *

Return the markup that has been accumulated in this element, * as a String suitable for direct transcription to the response * buffer.

*/ public String getMarkup() { close(); return buffer.toString(); } // --------------------------------------------------------- Public Methods /** *

Clear any accumulated markup stored in this object, * making it suitable for reuse.

*/ public void clear() { buffer.setLength(0); open = false; } /** *

Return the markup that has been accumulated in this element. * This is an alias for the getMarkup() method.

*/ @Override public String toString() { return getMarkup(); } /** *

Accumulate the start of a new element, up to and including * the element name. Once this method has been called, clients * can call writeAttribute() or * writeURIAttriute() to add attributes and their * corresponding values. The starting element will be closed * on any subsequent call to startElement(), * writeComment(), writeText(), * writeRaw(), endElement(), or * getMarkup().

* * @param name Name of the element to be started * @param component The UIComponent (if any) * to which this element corresponds * * @exception NullPointerException if name * is null */ public void startElement(String name, UIComponent component) { if (name == null) { throw new NullPointerException(); } close(); buffer.append('<'); //NOI18N buffer.append(name); open = true; } /** *

Accumulate the end of an element, after closing any open element * created by a call to startElement(). Elements must be * closed in the inverse order from which they were opened; it is an * error to do otherwise.

* * @param name Name of the element to be ended * * @exception NullPointerException if name * is null */ public void endElement(String name) { if (name == null) { throw new NullPointerException(); } if (open) { buffer.append('/'); //NOI18N close(); } else { buffer.append("'); //NOI18N } } /** *

Accumulate an attribute name and corresponding value. This * method may only be called after a call to startElement() * and before the opened element has been closed.

* * @param name Attribute name to be added * @param value Attribute value to be added * @param property Name of the component property or attribute (if any) * of the UIComponent associated with the containing * element, to which the generated attribute corresponds * * @exception IllegalStateException if this method is called * when there is no currently open element * @exception NullPointerException if name * or value is null */ public void writeAttribute(String name, Object value, String property) { if ((name == null) || (value == null)) { throw new NullPointerException(); } if (!open) { throw new IllegalStateException("No element is currently open"); //I18N - FIXME } // Handle boolean values specially Class clazz = value.getClass(); if (clazz == Boolean.class) { if (Boolean.TRUE.equals(value)) { // No attribute minimization for XHTML like markup buffer.append(' '); //NOI18N buffer.append(name); buffer.append("=\""); //NOI18N buffer.append(name); buffer.append('"'); //NOI18N // } else { // Write nothing for false boolean attributes } return; } // Render the attribute name and beginning of the value buffer.append(' '); //NOI18N buffer.append(name); buffer.append("=\""); //NOI18N // Render the value itself String text = value.toString(); int length = text.length(); for (int i = 0; i < length; i++) { char ch = text.charAt(i); // Tilde or less... if (ch < 0xA0) { // If "?" or over, no escaping is needed (this covers // most of the Latin alphabet) if (ch >= 0x3f) { buffer.append(ch); } else if (ch >= 0x27) { // If above "'"... // If between "'" and ";", no escaping is needed if (ch < 0x3c) { buffer.append(ch); // Note - "<" isn't escaped in attributes, as per // HTML spec } else if (ch == '>') { //NOI18N buffer.append(">"); //NOI18N } else { buffer.append(ch); } } else { if (ch == '&') { //NOI18N // HTML 4.0, section B.7.1: ampersands followed by // an open brace don't get escaped if ((i + 1 < length) && (text.charAt(i + 1) == '{')) //NOI18N { buffer.append(ch); } else { buffer.append("&"); //NOI18N } } else if (ch == '"') { buffer.append("""); //NOI18N } else { buffer.append(ch); } } } else if (ch <= 0xff) { // ISO-8859-1 entities: encode as needed buffer.append('&'); //NOI18N buffer.append(ISO8859_1_Entities[ch - 0xA0]); buffer.append(';'); //NOI18N } else { // Double-byte characters to encode. // PENDING: when outputting to an encoding that // supports double-byte characters (UTF-8, for example), // we should not be encoding numeric(ch); } } // Render the end of the value buffer.append('"'); //NOI18N } /** *

Accumulate an attribute name and corresponding URI value. This * method may only be called after a call to startElement() * and before the opened element has been closed.

* * @param name Attribute name to be added * @param value Attribute value to be added * @param property Name of the component property or attribute (if any) * of the UIComponent associated with the containing * element, to which the generated attribute corresponds * * @exception IllegalStateException if this method is called * when there is no currently open element * @exception NullPointerException if name * or value is null */ public void writeURIAttribute(String name, Object value, String property) { if ((name == null) || (value == null)) { throw new NullPointerException(); } if (!open) { throw new IllegalStateException("No element is currently open"); //I18N - FIXME } String text = value.toString(); if (text.startsWith("javascript:")) { writeAttribute(name, value, property); return; } // Render the attribute name and beginning of the value buffer.append(' '); //NOI18N buffer.append(name); buffer.append("=\""); //NOI18N // Render the value itself int length = text.length(); for (int i = 0; i < length; i++) { char ch = text.charAt(i); if ((ch < 33) || (ch > 126)) { if (ch == ' ') { //NOI18N buffer.append('+'); //NOI18N } else { // ISO-8859-1. Blindly assume the character will be < 255. // Not much we can do if it isn't. hexadecimals(ch); } } // DO NOT encode '%'. If you do, then for starters, // we'll double-encode anything that's pre-encoded. // And, what's worse, there becomes no way to use // characters that must be encoded if you // don't want them to be interpreted, like '?' or '&'. // else if('%' == ch) // { // hexadecimals(ch); // } else if (ch == '"') { buffer.append("%22"); //NOI18N } // Everything in the query parameters will be decoded // as if it were in the request's character set. So use // the real encoding for those! else if (ch == '?') { //NOI18N buffer.append('?'); //NOI18N try { buffer.append(URLEncoder.encode(text.substring(i + 1), encoding)); } catch (UnsupportedEncodingException e) { throw new FacesException(e); } break; } else { buffer.append(ch); } } // Render the end of the value buffer.append('"'); //NOI18N } /** *

Accumulate a comment containing the specified text, after * converting that text to a String (if necessary) and performing * any escaping appropriate for the markup language being rendered.

* *

If there is an open element that has been created by a call to * startElement(), that element will be closed first.

* * @param comment Text content of the comment * * @exception NullPointerException if comment * is null */ public void writeComment(Object comment) { if (comment == null) { throw new NullPointerException(); } close(); buffer.append(""); //NOI18N } /** *

Accumulate an object, after converting it to a String (if necessary) * WITHOUT performing escaping appropriate for the * markup language being rendered.

*

If there is an open element that has been created by a call to * startElement(), that element will be closed first.

* * @param raw Raw content to be written * @param property Name of the component property or attribute (if any) * of the UIComponent associated with the containing * element, to which the generated content corresponds * * @exception NullPointerException if text * is null */ public void writeRaw(Object raw, String property) { if (raw == null) { throw new NullPointerException(); } close(); buffer.append(raw.toString()); } /** *

Accumulate an object, after converting it to a String (if necessary) * and after performing any escaping appropriate for the markup * language being rendered.

* *

If there is an open element that has been created by a call to * startElement(), that element will be closed first.

* * @param text Text to be written * @param property Name of the component property or attribute (if any) * of the UIComponent associated with the containing * element, to which the generated attribute corresponds * * @exception NullPointerException if text * is null */ public void writeText(Object text, String property) { if (text == null) { throw new NullPointerException(); } // Close any open element close(); // Render the filtered version of the specified text String stext = text.toString(); int length = stext.length(); for (int i = 0; i < length; i++) { char ch = stext.charAt(i); // Tilde or less... if (ch < 0xA0) { // If "?" or over, no escaping is needed (this covers // most of the Latin alphabet) if (ch >= 0x3f) { buffer.append(ch); } else if (ch >= 0x27) { // If above "'"... // If between "'" and ";", no escaping is needed if (ch < 0x3c) { buffer.append(ch); } else if (ch == '<') { buffer.append("<"); //NOI18N } else if (ch == '>') { buffer.append(">"); //NOI18N } else { buffer.append(ch); } } else { if (ch == '&') { buffer.append("&"); //NOI18N } else { buffer.append(ch); } } } else if (ch <= 0xff) { // ISO-8859-1 entities: encode as needed buffer.append('&'); //NOI18N buffer.append(ISO8859_1_Entities[ch - 0xA0]); buffer.append(';'); //NOI18N } else { // Double-byte characters to encode. // PENDING: when outputting to an encoding that // supports double-byte characters (UTF-8, for example), // we should not be encoding numeric(ch); } } } // ------------------------------------------------------ Protected Methods /** *

Close the currently open starting element, if any.

*/ protected void close() { if (open) { buffer.append('>'); open = false; } } /** *

Append the hexadecimal equivalent of the specified * numeric value.

*/ protected void hexadecimal(int i) { if (i < 10) { buffer.append((char) ('0' + i)); } else { buffer.append((char) ('A' + (i - 10))); } } /** *

Append the specified character as an escaped two-hex-digit value.

* * @param ch Character to be escaped */ protected void hexadecimals(char ch) { buffer.append('%'); //NOI18N hexadecimal((int) ((ch >> 4) % 0x10)); hexadecimal((int) (ch % 0x10)); } /** *

Append a numeric escape for the specified character.

* * @param ch Character to be escaped */ protected void numeric(char ch) { if (ch == '\u20ac') { //NOI18N buffer.append("€"); //NOI18N return; } // Formerly used String.valueOf(). This version tests out // about 40% faster in a microbenchmark (and on systems where GC is // going gonzo, it should be even better) int i = (int) ch; if (i > 10000) { buffer.append('0' + (i / 10000)); i = i % 10000; buffer.append('0' + (i / 1000)); i = i % 1000; buffer.append('0' + (i / 100)); i = i % 100; buffer.append('0' + (i / 10)); i = i % 10; buffer.append('0' + i); } else if (i > 1000) { buffer.append('0' + (i / 1000)); i = i % 1000; buffer.append('0' + (i / 100)); i = i % 100; buffer.append('0' + (i / 10)); i = i % 10; buffer.append('0' + i); } else { buffer.append('0' + (i / 100)); i = i % 100; buffer.append('0' + (i / 10)); i = i % 10; buffer.append('0' + i); } buffer.append(';'); //NOI18N } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy