com.icesoft.faces.utils.UpdatableProperty Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of icefaces-compat Show documentation
Show all versions of icefaces-compat Show documentation
${icefaces.product.name} Compat Component Library
/*
* Copyright 2004-2012 ICEsoft Technologies Canada Corp.
*
* 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.
*/
package com.icesoft.faces.utils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import javax.faces.component.UIComponent;
import javax.faces.component.UIInput;
import javax.faces.el.ValueBinding;
import javax.faces.el.PropertyNotFoundException;
import javax.faces.context.FacesContext;
import javax.faces.application.FacesMessage;
import java.io.Serializable;
import com.icesoft.faces.util.CoreUtils;
/**
* There are some UIComponent properties which we want to set based on
* both user interactions and from changing ValueBinding values. If
* we follow the typical pattern for regular component properties,
* then the local field value takes precedence over the ValueBinding,
* so once a user interaction has occured, then the ValueBinding won't
* work. What we'd instead prefer, it to follow the UIInput value
* property pattern, where newly set values are pushed into the
* ValueBinding, so that both means of changing the value work.
*
* Most of the methods here are loose adaptations of UIInput's equivalents.
*
* @author mcollette
*/
public class UpdatableProperty implements Serializable {
private static final Log log = LogFactory.getLog(UpdatableProperty.class);
private String name;
private Object savedValue;
private boolean haveSavedValue;
private Object submittedValue;
private boolean setSubmittedInLocalValue;
private Object value;
private boolean setLocalValueInValueBinding;
public UpdatableProperty(String name) {
this.name = name;
}
/**
* Before a component does all the phases, and goes through the several
* steps of updating its value, we sometimes need to save away the
* original value, so we can refer to it unmolested
*/
public void saveValue(FacesContext context, UIComponent comp) {
//System.out.println(getDesc(comp, "SET_SAVE"));
savedValue = getValue(context, comp);
haveSavedValue = true;
//System.out.println(" savedValue: " + savedValue);
}
/**
* If we did save away the value, then just access it, but if we didn't
* save it away, then we'll want the most up-to-date value
*/
public Object getSavedValue(FacesContext context, UIComponent comp) {
//System.out.println(getDesc(comp, "GET_SAVE"));
//System.out.println(" savedValue: " + savedValue);
//System.out.println(" haveSavedValue: " + haveSavedValue);
if(haveSavedValue)
return savedValue;
else
return getValue(context, comp);
}
public Object getSubmittedValue(UIComponent comp) {
//System.out.println(getDesc(comp, "GET_SUB"));
//System.out.println(" submittedValue: " + submittedValue);
return submittedValue;
}
public void setSubmittedValue(UIComponent comp, Object newSubmittedValue) {
//System.out.println(getDesc(comp, "SET_SUB"));
submittedValue = newSubmittedValue;
setSubmittedInLocalValue = true;
//System.out.println(" submittedValue: " + submittedValue);
}
protected void clearSubmittedValue() {
submittedValue = null;
setSubmittedInLocalValue = false;
}
public Object getValue(FacesContext context, UIComponent comp) {
//System.out.println(getDesc(comp, "GET"));
//System.out.println(" value: " + value);
if(value != null) {
return value;
}
ValueBinding vb = comp.getValueBinding(name);
//System.out.println(" vb: " + vb);
if(vb != null) {
Object modelValue = vb.getValue(context);
//System.out.println(" modelValue: " + modelValue);
return modelValue;
}
return null;
}
/**
* This method be called as a setter, but not from the decode, since
* for things to happen in the right phases, you should call
* setSubmittedValue(-) in the component or renderer's decode(-) method,
* and allow the validate(-) method here to bubble the submittedValue
* into value in the correct phase.
*/
public void setValue(UIComponent comp, Object newValue) {
//System.out.println(getDesc(comp, "SET"));
value = newValue;
setLocalValueInValueBinding = true;
//System.out.println(" value: " + value);
//StackTraceElement[] ste = Thread.currentThread().getStackTrace();
//for(int i = 0; i < ste.length; i++)
// System.out.println(" " + ste[i]);
}
protected void clearValue() {
value = null;
setLocalValueInValueBinding = false;
}
protected void discontinueSettingLocalValueInValueBinding() {
setLocalValueInValueBinding = false;
}
public void validate(FacesContext context, UIComponent comp) {
//System.out.println(getDesc(comp, "VALIDATE"));
//System.out.println(" valid: " + isComponentValid(comp));
//System.out.println(" submittedValue: " + submittedValue);
//System.out.println(" setSubmittedInLocalValue: " + setSubmittedInLocalValue);
//System.out.println(" old value: " + value);
if( !isComponentValid(comp) ) {
return;
}
// Unlike UIInput.value treatment, these properties require
// no conversion nor validation
if(setSubmittedInLocalValue) {
setValue(comp, submittedValue);
clearSubmittedValue();
}
}
public void updateModel(FacesContext context, UIComponent comp) {
//System.out.println(getDesc(comp, "UPDATE"));
//System.out.println(" valid: " + isComponentValid(comp));
//System.out.println(" setLocalValueInValueBinding: " + setLocalValueInValueBinding);
if( !isComponentValid(comp) || !setLocalValueInValueBinding) {
return;
}
//System.out.println(" value: " + value);
ValueBinding vb = comp.getValueBinding(name);
//System.out.println(" vb: " + vb);
if(vb == null)
return;
try {
vb.setValue(context, value);
//System.out.println(" vb.setValue worked");
clearValue();
}
catch(PropertyNotFoundException e) {
//System.out.println("PropertyNotFoundException: " + e);
// It's ok for the application to not use a settable ValueBinding,
// since the UIComponent can keep the value. But they probably
// don't mean to, so we should log something
log.info(e);
// Don't keep trying to set it
discontinueSettingLocalValueInValueBinding();
}
catch(Exception e) {
//System.out.println("Exception: " + e);
String msgId = comp.getClass().getName() + '.' + name;
Object label = MessageUtils.getComponentLabel(context, comp);
FacesMessage msg = MessageUtils.getMessage(
context, msgId, new Object[]{label, value});
if(msg != null)
context.addMessage(comp.getClientId(context), msg);
setInvalid(comp);
}
}
protected boolean isComponentValid(UIComponent comp) {
if(comp instanceof UIInput)
return ((UIInput)comp).isValid();
return true;
}
protected void setInvalid(UIComponent comp) {
if(comp instanceof UIInput)
((UIInput)comp).setValid(false);
}
private String getDesc(UIComponent comp, String meth) {
StringBuffer sb = new StringBuffer(256);
if(comp != null) {
String className = comp.getClass().getName();
int index = className.lastIndexOf('.');
if(index >= 0)
className = className.substring(index+1);
sb.append(className);
sb.append('.');
}
sb.append(meth);
sb.append(" name: ");
sb.append(name);
if(comp != null) {
sb.append(" clientId: ");
sb.append(comp.getClientId(FacesContext.getCurrentInstance()));
}
return sb.toString();
}
public Object[] saveState(UIComponent comp) {
//System.out.println(getDesc(comp, "SAVE_STATE"));
//System.out.println(" name: " + name);
//System.out.println(" savedValue: " + savedValue);
//System.out.println(" haveSavedValue: " + haveSavedValue);
//System.out.println(" submittedValue: " + submittedValue);
//System.out.println(" setSubmittedInLocalValue: " + setSubmittedInLocalValue);
//System.out.println(" value: " + value);
//System.out.println(" setLocalValueInValueBinding: " + setLocalValueInValueBinding);
Object[] state = new Object[] {
name,
savedValue,
new Boolean(haveSavedValue),
submittedValue,
new Boolean(setSubmittedInLocalValue),
value,
new Boolean(setLocalValueInValueBinding)
};
return state;
}
public void restoreState(UIComponent comp, Object[] state) {
name = (String) state[0];
savedValue = state[1];
haveSavedValue = ((Boolean) state[2]).booleanValue();
submittedValue = state[3];
setSubmittedInLocalValue = ((Boolean) state[4]).booleanValue();
value = state[5];
setLocalValueInValueBinding = ((Boolean) state[6]).booleanValue();
//System.out.println(getDesc(comp, "RES_STATE"));
//System.out.println(" name: " + name);
//System.out.println(" savedValue: " + savedValue);
//System.out.println(" haveSavedValue: " + haveSavedValue);
//System.out.println(" submittedValue: " + submittedValue);
//System.out.println(" setSubmittedInLocalValue: " + setSubmittedInLocalValue);
//System.out.println(" value: " + value);
//System.out.println(" setLocalValueInValueBinding: " + setLocalValueInValueBinding);
}
public boolean equals(Object obj) {
if (!(obj instanceof UpdatableProperty)) {
return false;
}
UpdatableProperty up = (UpdatableProperty) obj;
if (haveSavedValue != up.haveSavedValue) {
return false;
}
if (setSubmittedInLocalValue != up.setSubmittedInLocalValue) {
return false;
}
if (setLocalValueInValueBinding != up.setLocalValueInValueBinding) {
return false;
}
if (!CoreUtils.objectsEqual(name, up.name)) {
return false;
}
if (!CoreUtils.objectsEqual(savedValue, up.savedValue)) {
return false;
}
if (!CoreUtils.objectsEqual(submittedValue, up.submittedValue)) {
return false;
}
if (!CoreUtils.objectsEqual(value, up.value)) {
return false;
}
return true;
}
}