/*
* xulfaces : bring XUL power to Java
*
* Copyright (C) 2005 Olivier SCHMITT
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
package org.xulfaces.renderkit;
import java.io.IOException;
import java.io.Writer;
import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.context.ResponseWriter;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.xulfaces.utils.XulUtils;
/**
*
* @author kito31
* @version $Id: XULResponseWriter.java,v 1.9 2007/04/29 16:01:56 kito31 Exp $
*/
public class XULResponseWriter extends ResponseWriter {
private static final Log log = LogFactory.getLog(XULResponseWriter.class);
private static final String NULL_MSG = " must not be null !";
private static final String DEFAULT_CONTENT_TYPE = "application/vnd.mozilla.xul+xml";
private static final String DEFAULT_CHARACTER_ENCODING = "UTF-8";
private Writer writer;
private String contentType;
private String characterEncoding;
private boolean elementOpened;
private boolean mustEscape = true;
private boolean mustPrettyRender = false;
private int tabs = 0;
public XULResponseWriter(Writer writer, String contentType, String characterEncoding) {
this.writer = writer;
if(contentType == null){
this.contentType = DEFAULT_CONTENT_TYPE;
}
else {
this.contentType = contentType;
}
if(characterEncoding != null){
this.characterEncoding = characterEncoding;
}
else {
this.characterEncoding = DEFAULT_CHARACTER_ENCODING;
}
FacesContext facesContext = FacesContext.getCurrentInstance();
String prettryRenderedValue = facesContext.getExternalContext().getInitParameter(KitConstants.PRETTY_RENDERED);
this.mustPrettyRender = "true".equals(prettryRenderedValue);
}
public String getContentType() {
return this.contentType;
}
public String getCharacterEncoding() {
return this.characterEncoding;
}
public void flush() throws IOException {
}
public void startDocument() throws IOException {
}
public void endDocument() throws IOException {
flush();
writer.flush();
}
public void startElement(String name, UIComponent component) throws IOException {
if (name == null) {
throw new NullPointerException("Element name " + NULL_MSG);
}
closeCurrentStartTagIfNecessary();
char firstChar = name.charAt(0);
if ((firstChar == 's') ||
(firstChar == 'S')) {
if ("script".equalsIgnoreCase(name) ||
"style".equalsIgnoreCase(name)) {
mustEscape = false;
}
}
tabs++;
startIndentation();
writer.write('<');
writer.write(name);
elementOpened = true;
}
public void endElement(String name) throws IOException {
if (name == null) {
throw new NullPointerException("Element name " + NULL_MSG);
}
if (elementOpened) {
boolean isEmptyElement = XulUtils.isEmptyElement(name);
if (isEmptyElement) {
writer.write(" />");
elementOpened = false;
tabs--;
return;
}
writer.write(">");
elementOpened = false;
}
mustEscape = true;
startIndentation();
writer.write("</");
writer.write(name);
writer.write(">");
tabs--;
}
public void writeAttribute(String name, Object value, String componentPropertyName) throws IOException {
if (name == null) {
throw new NullPointerException("Attribute name " + NULL_MSG);
}
if (value instanceof Boolean) {
if (((Boolean) value).booleanValue()) {
writer.write(' ');
writer.write(name);
writer.write("=\"");
writer.write(name);
writer.write('"');
}
} else {
String string = value.toString();
writer.write(' ');
writer.write(name);
writer.write("=\"");
writer.write(escapeString(string));
writer.write('"');
}
}
public void writeURIAttribute(String name, Object value, String componentPropertyName) throws IOException {
if (name == null) {
throw new NullPointerException("Attribute name must " + NULL_MSG);
}
if (!elementOpened) {
throw new IllegalStateException("Must be called before the start element is closed (attribute '" + name
+ "')");
}
String strValue = value.toString();
writer.write(' ');
writer.write(name);
writer.write("=\"");
writer.write(strValue);
writer.write('"');
}
public void writeComment(Object value) throws IOException {
if (value == null) {
throw new NullPointerException("Comment " + NULL_MSG);
}
closeCurrentStartTagIfNecessary();
writer.write("<!--");
writer.write(value.toString());
writer.write("-->");
}
public void writeText(Object value, String componentPropertyName) throws IOException {
if (value == null) {
throw new NullPointerException("Text " + NULL_MSG);
}
if (value == null) {
return;
}
String strValue = value.toString();
closeCurrentStartTagIfNecessary();
if(mustEscape){
writer.write(escapeString(strValue));
}
else {
writer.write(strValue);
}
}
public void writeText(char cbuf[], int off, int len) throws IOException {
if (cbuf == null) {
throw new NullPointerException("cbuf name " + NULL_MSG);
}
if (cbuf.length < off + len) {
throw new IndexOutOfBoundsException((off + len) + " > " + cbuf.length);
}
String strValue = new String(cbuf, off, len);
if(mustEscape){
writer.write(escapeString(strValue));
}
else {
writer.write(strValue);
}
}
public ResponseWriter cloneWithWriter(Writer writer) {
XULResponseWriter newWriter = new XULResponseWriter(writer, getContentType(), getCharacterEncoding());
return newWriter;
}
public void close() throws IOException {
if (elementOpened) {
writer.write(" />");
}
writer.close();
}
public void write(char cbuf[], int off, int len) throws IOException {
String string = new String(cbuf, off, len);
writer.write(string);
}
public void write(int c) throws IOException {
closeCurrentStartTagIfNecessary();
writer.write(c);
}
public void write(char cbuf[]) throws IOException {
closeCurrentStartTagIfNecessary();
String string = new String(cbuf);
writer.write(string);
}
public void write(String string) throws IOException {
if (string.length() > 0) {
closeCurrentStartTagIfNecessary();
writer.write(string);
}
}
public void write(String string, int off, int len) throws IOException {
closeCurrentStartTagIfNecessary();
String subString = string.substring(off, off + len);
writer.write(subString);
}
protected void closeCurrentStartTagIfNecessary() throws IOException{
if (elementOpened) {
writer.write(">");
elementOpened = false;
}
}
protected String escapeString(String string){
StringBuffer result = new StringBuffer();
int length = string.length();
for (int i = 0; i < length; i++) {
char ch = string.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) {
result.append(ch);
} else if (ch >= 0x27) { // If above "'"...
// If between "'" and ";", no escaping is needed
if (ch < 0x3c) {
result.append(ch);
// Note - "<" isn't escaped in attributes, as per
// HTML spec
} else if (ch == '>') {
result.append(">");
} else if (ch == '<') {
result.append("<");
} else {
result.append(ch);
}
} else {
if (ch == '&') {
// HTML 4.0, section B.7.1: ampersands followed by
// an open brace don't get escaped
if ((i + 1 < length) && (string.charAt(i + 1) == '{')) {
result.append(ch);
} else {
result.append("&");
}
} else if (ch == '"') {
result.append(""");
} else {
result.append(ch);
}
}
} else if (ch <= 0xff) {
// Single byte character : ISO-8859-1 entities: append (the XML encoding must be ISO-8859-1)
result.append(ch);
} else {
if(KitConstants.isUnicode(this.characterEncoding)){
result.append(ch);
}
else {
throw new IllegalArgumentException("Attempt to encode an UTF char with a bad char encoding (currently "
+ this.characterEncoding
+ ").\nUse <%@ page contentType=\"application/vnd.mozilla.xul+xml; charset=UTF-X\" %> (8,16,...).");
}
}
}
return result.toString();
}
// Pretty render methods /////////////////////////////
protected void startIndentation() throws IOException {
if(this.mustPrettyRender){
writer.write('\n');
writeTabulations();
}
}
protected void writeTabulations() throws IOException {
for(int i=0; i < tabs; i++){
writer.write("\t");
}
}
}