/* * � Copyright IBM Corp. 2013 * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. See the License for the specific language governing * permissions and limitations under the License. */ /* * Author: Maire Kehoe (mkehoe@ie.ibm.com) * Date: 1 Feb 2008 * ResponseBuffer.java */ package com.ibm.xsp.test.framework.render; import java.io.IOException; import java.io.Writer; import java.util.HashSet; import java.util.Set; import javax.faces.component.UIComponent; import javax.faces.context.FacesContext; import javax.faces.context.ResponseWriter; import com.ibm.xsp.util.FacesUtil; public class ResponseBuffer extends ResponseWriter{ /** * initialize the context for encoding * @param context */ public static FacesContext initContext(FacesContext context) { // initialize the context for encoding context.setResponseWriter(new ResponseBuffer()); return context; } public static String encode(UIComponent comp, FacesContext context)throws IOException { FacesUtil.renderComponent(context, comp); return readBuffer(context); } public static void clear(FacesContext context){ // note, must call // context.setResponseWriter(new ResponseBuffer()); // before can clear the buffer ResponseBuffer buf = (ResponseBuffer)context.getResponseWriter(); buf.clear(); } private static String readBuffer(FacesContext context) { // note, must call // context.setResponseWriter(new ResponseBuffer()); // before encoding and reading this buffer ResponseBuffer buf = (ResponseBuffer)context.getResponseWriter(); String output = buf.toString(); buf.clear(); return output; } private StringBuffer b = new StringBuffer(); boolean openForAttributes = false; private Set<String> attrNames = new HashSet<String>(); @Override public String toString(){ return b.toString(); } public void clear(){ openForAttributes = false; b = new StringBuffer(); } // === ResponseWriter methods @Override public void endElement(String element) throws IOException { endAttributes(); b.append("</").append(element).append(">"); } private void endAttributes() { if( openForAttributes ){ b.append(">"); openForAttributes = false; attrNames.clear(); } } @Override public void endDocument() throws IOException { endAttributes(); } @Override public ResponseWriter cloneWithWriter(Writer writer) { throw new UnsupportedOperationException(); } @Override public void flush() throws IOException { // do nothing } @Override public String getCharacterEncoding() { throw new UnsupportedOperationException(); } @Override public String getContentType() { throw new UnsupportedOperationException(); } @Override public void startDocument() throws IOException { // do nothing } @Override public void startElement(String element, UIComponent comp) throws IOException { endAttributes(); b.append("<").append(element); openForAttributes = true; } @Override public void writeAttribute(String name, Object value, String componentProperty) throws IOException { if( !openForAttributes ){ throw new UnsupportedOperationException(); } if( value instanceof Boolean ){ // Boolean indicates HTML should use attribute minimization // when the attribute name is output without the attribute value // like <input type="checkbox" checked> // or the attribute is not output. // XHTML does not allow attribute minimization, // instead for true values, it's recommended to output // values like checked="checked" // See http://www.w3.org/TR/2002/REC-xhtml1-20020801/#h-4.5 if( !((Boolean)value).booleanValue() ){ // do not output the attribute. return; }else{ // output checked="checked" // Trim to workaround an issue in SelectManyCheckboxListRenderer // where the name is " checked" instead of "checked" value = name.trim(); // fall through } } if( attrNames.contains(name) ){ throw new UnsupportedOperationException("Writing duplicate attribute name: "+name); } attrNames.add(name); b.append(" ").append(name).append("=\"").append(doEscape(value, true)).append("\""); } @Override public void writeComment(Object value) throws IOException { endAttributes(); b.append("<!-- ").append(value).append(" -->"); } @Override public void writeText(Object value, String componentProperty) throws IOException { endAttributes(); b.append(doEscape(value, false)); } @Override public void writeText(char[] chars, int from, int to) throws IOException { String toWrite = new String(chars).substring(from, to); toWrite = doEscape(toWrite, false); writeText(toWrite, null); } private String doEscape(Object obj, boolean inAttr) { if( null == obj ){ return null; } String toWrite = obj.toString(); toWrite = toWrite.replace(">", ">"); if( ! inAttr ){ toWrite = toWrite.replace("<", "<"); } return toWrite; } @Override public void writeURIAttribute(String name, Object value, String componentProperty) throws IOException { writeAttribute(name, value, componentProperty); } @Override public void close() throws IOException { // do nothing } @Override public void write(char[] chars, int from, int to) throws IOException { endAttributes(); b.append(new String(chars).substring(from, to)); } }