/*
* � Copyright IBM Corp. 2016
*
* 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: 15 Jan 2016
* InputDateDetectRenderer.java
*/
package com.ibm.xsp.theme.bootstrap.renderkit.html.extlib.form;
import java.io.IOException;
import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.convert.ConverterException;
import javax.faces.render.Renderer;
import com.ibm.xsp.extlib.beans.DeviceBean;
import com.ibm.xsp.extlib.util.ExtLibUtil;
import com.ibm.xsp.util.FacesUtil;
import com.ibm.xsp.util.HtmlUtil;
/**
* @author Maire Kehoe (mkehoe@ie.ibm.com)
*/
public class InputDateDetectRenderer extends Renderer {
/* (non-Javadoc)
* @see javax.faces.render.Renderer#encodeBegin(javax.faces.context.FacesContext, javax.faces.component.UIComponent)
*/
@Override
public void encodeBegin(FacesContext context, UIComponent component) throws IOException {
String newRendererType = detectNewRendererType(context, component);
// modify the control, so that this renderer won't be used in future,
// avoiding the overhead of delegation. Once this encode phase ends
// the detected renderer will be used instead of this renderer.
component.setRendererType(newRendererType);
// save the value for use in encodeChildren and encodeEnd.
HtmlUtil.storeEncodeParameter(context, component, "newRendererType", newRendererType); //$NON-NLS-1$
// the default implementation here and in the rest of the methods
// is to delegate the rendering & decoding to the detected renderer
Renderer delegate = findDelegate(context, component, newRendererType);
delegate.encodeBegin(context, component);
}
private String detectNewRendererType(FacesContext context, UIComponent component) {
Object deviceBeanObj = ExtLibUtil.resolveVariable(context, "deviceBean"); //$NON-NLS-1$
if( deviceBeanObj instanceof DeviceBean ){
DeviceBean deviceBean = (DeviceBean)deviceBeanObj;
if( deviceBean.isMobile() || deviceBean.isTablet() || deviceBean.isIpod() ){
// SPR#LHEY9QKFZ8 use mobile InputDateRenderer (type=date)
// because the web renderer isn't accessible on mobile.
return "com.ibm.xsp.extlib.mobile.InputDate"; //$NON-NLS-1$
}
}
// default to web renderer.
return "com.ibm.xsp.DateTimeHelper"; //$NON-NLS-1$
}
private Renderer findDelegate(FacesContext context, UIComponent component, String newRendererType) {
String componentFamily = component.getFamily();
Renderer delegate = FacesUtil.getRenderer(context, componentFamily, newRendererType);
if( null == delegate ){
// won't happen, the 2 renderer-types in the detect method both have registered renderers.
throw new NullPointerException("Renderer is null for componentFamily="+componentFamily+" rendererType="+newRendererType); //$NON-NLS-1$ //$NON-NLS-2$
}
return delegate;
}
@Override
public boolean getRendersChildren() {
// true - will implement here if the delegate doesn't implement it.
return true;
}
@Override
public void encodeChildren(FacesContext context, UIComponent component) throws IOException {
String newRendererType = (String)HtmlUtil.readEncodeParameter(context, component, "newRendererType", /*remove*/false); //$NON-NLS-1$
Renderer delegate = findDelegate(context, component, newRendererType);
if( delegate.getRendersChildren() ){
delegate.encodeChildren(context, component);
}else{
// else implement here using the default implementation.
FacesUtil.renderChildren(context, component);
}
}
@Override
public void encodeEnd(FacesContext context, UIComponent component) throws IOException {
String newRendererType = (String)HtmlUtil.readEncodeParameter(context, component, "newRendererType", /*remove*/false); //$NON-NLS-1$
Renderer delegate = findDelegate(context, component, newRendererType);
delegate.encodeEnd(context, component);
HtmlUtil.removeEncodeParameter(context, component, "newRendererType"); //$NON-NLS-1$
}
/* (non-Javadoc)
* @see javax.faces.render.Renderer#decode(javax.faces.context.FacesContext, javax.faces.component.UIComponent)
*/
@Override
public void decode(FacesContext context, UIComponent component) {
// shouldn't usually happen, but default implementation provided in case of unusual use-cases,
// like if some rendered property computation means that the control is not initially visible
// during the encode phase but it then evaluates differently in the decode phase so that the control is decoded.
String newRendererType = detectNewRendererType(context, component);
Renderer delegate = findDelegate(context, component, newRendererType);
delegate.decode(context, component);
}
/* (non-Javadoc)
* @see javax.faces.render.Renderer#convertClientId(javax.faces.context.FacesContext, java.lang.String)
*/
@Override
public String convertClientId(FacesContext context, String clientId) {
// don't have access to the component here, so doing the default behavior (no change to the clientId).
return super.convertClientId(context, clientId);
}
/* (non-Javadoc)
* @see javax.faces.render.Renderer#getConvertedValue(javax.faces.context.FacesContext, javax.faces.component.UIComponent, java.lang.Object)
*/
@Override
public Object getConvertedValue(FacesContext context, UIComponent component, Object submittedValue) throws ConverterException {
// shouldn't usually happen, can be invoked in some unusual use-cases (see comment in decode).
// This occurs in the Process Validations phase, so the encode parameter won't be available
// so re-compute the detectRendererType
String newRendererType = detectNewRendererType(context, component);
Renderer delegate = findDelegate(context, component, newRendererType);
return delegate.getConvertedValue(context, component, submittedValue);
}
}