/*
* 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.renderer;
import java.io.IOException;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import javax.faces.FacesException;
import javax.faces.component.UIComponent;
import javax.faces.component.ValueHolder;
import javax.faces.context.FacesContext;
import javax.faces.context.ResponseWriter;
import javax.faces.convert.Converter;
import javax.faces.convert.ConverterException;
import javax.faces.el.MethodBinding;
import javax.faces.el.ValueBinding;
import javax.faces.event.ActionEvent;
import javax.faces.render.Renderer;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.xulfaces.MultiActionSource;
import org.xulfaces.SmoothlyUpdateable;
import org.xulfaces.annotation.faces.ATTRIBUTE;
import org.xulfaces.annotation.faces.COMPONENT;
import org.xulfaces.annotation.faces.RENDERER;
import org.xulfaces.bridge.Bridge;
import org.xulfaces.bridge.Zone;
import org.xulfaces.bridge.command.UpdateZoneCommand;
import org.xulfaces.utils.XulUtils;
/**
*
* @author kito31
* @version $Id: XULRenderer.java,v 1.34 2007/06/10 19:47:06 kito31 Exp $
*/
@RENDERER(family = "xul.component.family", type = "xul.renderer.Abstract", kit = "XUL_RENDERKIT")
public abstract class XULRenderer extends Renderer {
private static final Log log = LogFactory.getLog(XULRenderer.class);
protected static final String METHOD_BINDED_PREFIX = "XF_MB_";
protected boolean isSubmitted(FacesContext facesContext, UIComponent uiComponent) {
String clientId = uiComponent.getClientId(facesContext);
Map paramMap = facesContext.getExternalContext().getRequestParameterMap();
boolean found = paramMap.containsKey(clientId);
if(log.isDebugEnabled()){
if(found){
log.debug(uiComponent.getClass().getName() + " " + uiComponent.getId() + " found in request");
}
}
return found;
}
/**
* <p></p>
* @param facesContext
* @param uiComponent
* @return <code>true</code> if a method
*/
protected boolean isMethodBindedSubmitted(FacesContext facesContext, UIComponent uiComponent) {
String clientId = METHOD_BINDED_PREFIX + uiComponent.getClientId(facesContext) ;
Map paramMap = facesContext.getExternalContext().getRequestParameterMap();
boolean found = paramMap.containsKey(clientId);
if(log.isDebugEnabled()){
if(found){
log.debug("Method binded for " + clientId + " found in request.");
}
}
return found;
}
protected Object isSubmitted(FacesContext facesContext, String clientId) {
Map paramMap = facesContext.getExternalContext().getRequestParameterMap();
return paramMap.get(clientId);
}
public static void renderChild(FacesContext context, UIComponent child) throws IOException {
if (!child.isRendered()) {
return;
}
child.encodeBegin(context);
if (child.getRendersChildren()) {
child.encodeChildren(context);
} else {
renderChildren(context, child);
}
child.encodeEnd(context);
}
public static void renderChildren(FacesContext facesContext, UIComponent component) throws IOException {
if (component.getChildCount() > 0) {
for (Iterator it = component.getChildren().iterator(); it.hasNext();) {
UIComponent child = (UIComponent) it.next();
renderChild(facesContext, child);
}
}
}
public Object getProperty(FacesContext context, String name, UIComponent component) {
Object result = null;
ValueBinding valueBinding = component.getValueBinding(name);
if (valueBinding != null) {
result = valueBinding.getValue(context);
} else {
result = component.getAttributes().get(name);
}
return result;
}
public String convertAsString(FacesContext context, UIComponent component, Object value) {
if (value != null) {
if (value instanceof String) {
return value.toString();
}
try {
Converter converter = context.getApplication().createConverter(value.getClass());
if (converter != null) {
return converter.getAsString(context, component, value);
}
} catch (FacesException e) {
return value.toString();
}
try {
return value.toString();
} catch (Exception e) {
}
}
return "";
}
protected void renderAttribute(FacesContext facesContext, UIComponent component, String name) throws IOException {
ResponseWriter responseWriter = facesContext.getResponseWriter();
Object object = component.getAttributes().get(name);
if (object != null) {
responseWriter.writeAttribute(name, "" + object, name);
}
}
/**
* <p>Render component attributes using ATTRIBUTE annotations.</p>
*
* @param facesContext The FacesContext
* @param component The component to render
* @param annotatedAttributes List of array {Field,ATTRIBUTE}
* @throws IOException
*/
protected void renderAttributes(FacesContext facesContext, UIComponent component, List annotatedAttributes)
throws IOException {
ResponseWriter responseWriter = facesContext.getResponseWriter();
Iterator iterator = annotatedAttributes.iterator();
while(iterator.hasNext()){
Object[] annotatedAttribute = (Object[]) iterator.next();
Field field = (Field) annotatedAttribute[0];
String name = field.getName();
ATTRIBUTE attribute = (ATTRIBUTE) annotatedAttribute[1];
if(attribute.automateRendering()){
if(attribute.supportsMethodBinding()){
renderBindedMethod(facesContext, component,name);
}
else {
Object attributeValue = component.getAttributes().get(name);
if (attributeValue != null) {
StringBuffer stringBuffer = new StringBuffer();
stringBuffer.append(attributeValue);
if (log.isDebugEnabled()) {
log.debug("Component " + component.getClass().getName()
+ " "
+ component.getId()
+ " attribute "
+ name
+ "="
+ stringBuffer.toString());
}
responseWriter.writeAttribute(name, stringBuffer.toString(), name);
}
}
}
}
}
/**
* <p>Render a binded method associated to multi action source component.</p>
*
* @param facesContext The faces context
* @param component The component
* @param attributeName The attribute name which is method binded
* @throws IOException An IOException if writing turns bad.
*/
protected void renderBindedMethod(FacesContext facesContext, UIComponent component, String attributeName) throws IOException {
if(!(component instanceof MultiActionSource)){
throw new IllegalArgumentException("component arg is not MultiActionSource");
}
ResponseWriter responseWriter = facesContext.getResponseWriter();
StringBuffer stringBuffer = new StringBuffer();
MultiActionSource xulComponent = (MultiActionSource) component;
MethodBinding methodBinding = xulComponent.getMethodBinding(attributeName);
if(methodBinding != null){
stringBuffer.append("triggerBindedMethod('");
stringBuffer.append(component.getClientId(facesContext));
stringBuffer.append("','");
stringBuffer.append(attributeName);
stringBuffer.append("');");
responseWriter.writeAttribute(attributeName, stringBuffer.toString(), attributeName);
}
else {
// Probably a javascript event handler ...
Object value = component.getAttributes().get(attributeName);
if(value != null){
stringBuffer.append(value);
responseWriter.writeAttribute(attributeName, stringBuffer.toString(), attributeName);
}
}
}
@Override
public void decode(FacesContext facesContext, UIComponent component) {
super.decode(facesContext, component);
if(isMethodBindedSubmitted(facesContext, component)){
Map params = facesContext.getExternalContext().getRequestParameterMap();
String value = (String) params.get(METHOD_BINDED_PREFIX+component.getClientId(facesContext));
if(component instanceof MultiActionSource){
MultiActionSource multiActionSource = (MultiActionSource) component;
MethodBinding methodBinding = multiActionSource.getMethodBinding(value);
multiActionSource.setAction(methodBinding);
if(methodBinding != null){
component.queueEvent(new ActionEvent(component));
}
}
}
}
@Override
public void encodeBegin(FacesContext facesContext, UIComponent component) throws IOException {
super.encodeBegin(facesContext, component);
processSmoothlyUpdateable(facesContext,component);
}
protected String buildTargetName(UIComponent component){
StringBuffer stringBuffer = new StringBuffer(component.getClass().getName());
stringBuffer.append(" ");
stringBuffer.append(component.getId());
return stringBuffer.toString();
}
protected void processSmoothlyUpdateable(FacesContext facesContext,UIComponent component){
// If parent component is smoothly updateable, it has no sense to add the current child to
// the bridge for client side update. The parent will be updated and then all its children.
if(!isParentSmoothlyUpdateable(component)){
if (component instanceof SmoothlyUpdateable) {
SmoothlyUpdateable smoothlyUpdateable = (SmoothlyUpdateable) component;
if (smoothlyUpdateable.needsUpdate()) {
Bridge bridge = XulUtils.getBridge();
Zone zone = new Zone(component.getClientId(facesContext));
if (log.isDebugEnabled()) {
log.debug("Add UpdateZoneCommand for DOM zone " + zone.getNodeId());
}
bridge.addCommand(new UpdateZoneCommand(zone,buildTargetName(component)));
}
}
}
}
protected boolean isParentSmoothlyUpdateable(UIComponent component){
boolean smoothlyUpdateable = false;
UIComponent parent = component.getParent();
Boolean parentUpdateSmoothly = (Boolean) parent.getAttributes().get("updateSmoothly");
if(parentUpdateSmoothly!=null){
return parentUpdateSmoothly.booleanValue();
}
return smoothlyUpdateable;
}
protected void renderDisabledAttribute(FacesContext facesContext,UIComponent component) throws IOException{
ResponseWriter responseWriter = facesContext.getResponseWriter();
Boolean disabled = (Boolean) component.getAttributes().get("disabled");
if (disabled != null) {
if (disabled.booleanValue()) {
if (log.isDebugEnabled()) {
log.debug("Component " + component.getClass().getName() + " " + component.getId() + " attribute disabled=" + disabled);
}
responseWriter.writeAttribute("disabled", disabled.toString(), "disabled");
}
}
}
/**
* Renderers override this method in case output value needs to be
* formatted
*/
protected String getFormattedValue(FacesContext context, UIComponent component,
Object currentValue)
throws ConverterException {
String result = null;
// formatting is supported only for components that support
// converting value attributes.
if (!(component instanceof ValueHolder)) {
if (currentValue != null) {
result = currentValue.toString();
}
return result;
}
Converter converter = null;
// If there is a converter attribute, use it to to ask application
// instance for a converter with this identifer.
if (component instanceof ValueHolder) {
converter = ((ValueHolder) component).getConverter();
}
// if value is null and no converter attribute is specified, then
// return a zero length String.
if (converter == null && currentValue == null) {
return "";
}
if (converter == null) {
// Do not look for "by-type" converters for Strings
if (currentValue instanceof String) {
return (String) currentValue;
}
// if converter attribute set, try to acquire a converter
// using its class type.
Class converterType = currentValue.getClass();
converter = null;//Util.getConverterForClass(converterType);
// if there is no default converter available for this identifier,
// assume the model type to be String.
if (converter == null && currentValue != null) {
result = currentValue.toString();
return result;
}
}
if (converter != null) {
result = converter.getAsString(context, component, currentValue);
return result;
} else {
// throw converter exception if no converter can be
// identified
throw new ConverterException("Converter error.");
}
}
public boolean isMethodReference(String ref){
if (ref != null) {
if ((ref.indexOf("#{") != -1) &&
(ref.indexOf("#{") < ref.indexOf('}'))) {
return true;
}
}
return false;
}
}