blob: 9bb572b41328e0236e3fabd7e14c8da86abdede0 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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 org.apache.myfaces.view.facelets.tag.jsf;
import java.io.Serializable;
import java.util.HashMap;
import java.util.Map;
import javax.el.ValueExpression;
import javax.faces.component.StateHolder;
import javax.faces.component.UIComponentBase;
import javax.faces.component.UIViewRoot;
import javax.faces.context.FacesContext;
public class FaceletState implements StateHolder, Serializable
{
private static final long serialVersionUID = -7823771271935942737L;
private Map<String, Object> stateMap;
private Map<String, Map<String, ValueExpression>> bindingsMap;
public FaceletState()
{
}
public Object getState(String key)
{
if(stateMap == null)
{
return null;
}
return stateMap.get(key);
}
public Object putState(String key, Object value)
{
if (stateMap == null)
{
stateMap = new HashMap<String, Object>();
}
return stateMap.put(key, value);
}
@Override
public Object saveState(FacesContext context)
{
UIViewRoot root = context.getViewRoot();
if (root != null && root.initialStateMarked())
{
if (stateMap != null)
{
Object values[] = new Object[1];
values[0] = UIComponentBase.saveAttachedState(context, stateMap);
return values;
}
return null;
}
else
{
Object values[] = new Object[2];
values[0] = UIComponentBase.saveAttachedState(context, stateMap);
// If the UIViewRoot instance was not marked with initial state, that means
// we need to save the bindingsMap as well because it will not be restored
// like with PSS, because in that case the view is built again using
// facelets algorithm.
values[1] = UIComponentBase.saveAttachedState(context, bindingsMap);
return values;
}
}
@SuppressWarnings("unchecked")
@Override
public void restoreState(FacesContext context, Object state)
{
if (state == null)
{
stateMap = null;
bindingsMap = null;
return;
}
Object values[] = (Object[])state;
if (values.length == 2)
{
// Full state
stateMap = (Map<String,Object>) UIComponentBase.restoreAttachedState(context, values[0]);
bindingsMap = (Map<String,Map<String, ValueExpression>>)
UIComponentBase.restoreAttachedState(context, values[1]);
}
else
{
if (values[0] == null)
{
stateMap = null;
}
else
{
stateMap = (Map<String,Object>) UIComponentBase.restoreAttachedState(context, values[0]);
}
}
}
@Override
public boolean isTransient()
{
return false;
}
@Override
public void setTransient(boolean newTransientValue)
{
}
public boolean isDynamic()
{
return stateMap == null ? false : !stateMap.isEmpty();
}
public void putBinding(String uniqueId, String key, ValueExpression expr)
{
if (bindingsMap == null)
{
bindingsMap = new HashMap<>();
}
Map<String, ValueExpression> bindings = bindingsMap.get(uniqueId);
if (bindings == null)
{
bindings = new HashMap<>();
bindingsMap.put(uniqueId, bindings);
}
bindings.put(key, expr);
}
/**
* A "Facelet Binding ValueExpression" is a ValueExpression used/generated by facelets algorithm
* associated with an uniqueId, which usually is bound to the tagId. Components like c:forEach or
* ui:param uses it and the reason behind this is avoid use VariableMapper to create EL Expressions
* that later cannot be cached. Instead, the intention is make an indirection using 2 ValueExpression
* instances. In that way, all EL Expressions can be cached, because VariableMapper will use an
* instance that contains the uniqueId and the one stored in the map will have the real value or
* EL Expression that points to the managed bean. (Remember each EL expression that uses a variable
* stored in VariableMapper will copy the EL expression bound to the variable, so if the EL expression
* value changes across views, all EL Expressions that contains a reference cannot be cached).
*
* This map is something special, because its content is related to the view structure. It does not need
* to be saved fully into the state, and it does not have any delta state, but it "evolves" with the initial
* state.
*/
public ValueExpression getBinding(String uniqueId, String key)
{
if (bindingsMap == null)
{
return null;
}
Map<String, ValueExpression> bindings = bindingsMap.get(uniqueId);
if (bindings == null)
{
return null;
}
return bindings.get(key);
}
public Map<String, Map<String, ValueExpression>> getBindings()
{
return bindingsMap;
}
@Override
public int hashCode()
{
int hash = 7;
hash = 79 * hash + (this.stateMap != null ? this.stateMap.hashCode() : 0);
return hash;
}
@Override
public boolean equals(Object obj)
{
if (obj == null)
{
return false;
}
if (getClass() != obj.getClass())
{
return false;
}
final FaceletState other = (FaceletState) obj;
if (this.stateMap != other.stateMap && (this.stateMap == null || !this.stateMap.equals(other.stateMap)))
{
return false;
}
return true;
}
}