View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.myfaces.view.facelets.tag.jsf;
20  
21  import java.io.Serializable;
22  import java.util.HashMap;
23  import java.util.Map;
24  import javax.el.ValueExpression;
25  
26  import javax.faces.component.StateHolder;
27  import javax.faces.component.UIComponentBase;
28  import javax.faces.component.UIViewRoot;
29  import javax.faces.context.FacesContext;
30  
31  public class FaceletState implements StateHolder, Serializable
32  {
33      /**
34       * 
35       */
36      private static final long serialVersionUID = -7823771271935942737L;
37      
38      private Map<String, Object> stateMap;
39      
40      private Map<String, Map<String, ValueExpression>> bindingsMap;
41      
42      public FaceletState()
43      {
44      }
45      
46      public Object getState(String key)
47      {
48          if(stateMap == null)
49          {
50              return null;
51          }
52          return stateMap.get(key);
53      }
54      
55      public Object putState(String key, Object value)
56      {
57          if (stateMap == null)
58          {
59              stateMap = new HashMap<String, Object>();
60          }
61          return stateMap.put(key, value);
62      }
63      
64  
65      public Object saveState(FacesContext context)
66      {
67          UIViewRoot root = context.getViewRoot();
68          if (root != null && root.initialStateMarked())
69          {
70              if (stateMap != null)
71              {
72                  Object values[] = new Object[1];
73                  values[0] = UIComponentBase.saveAttachedState(context, stateMap);
74                  return values;
75              }
76              return null;
77          }
78          else
79          {
80              Object values[] = new Object[2];
81              values[0] = UIComponentBase.saveAttachedState(context, stateMap);
82              // If the UIViewRoot instance was not marked with initial state, that means
83              // we need to save the bindingsMap as well because it will not be restored
84              // like with PSS, because in that case the view is built again using 
85              // facelets algorithm.
86              values[1] = UIComponentBase.saveAttachedState(context, bindingsMap);
87              return values;
88          }
89      }
90  
91      @SuppressWarnings("unchecked")
92      public void restoreState(FacesContext context, Object state)
93      {
94          if (state == null)
95          {
96              stateMap = null;
97              bindingsMap = null;
98              return;
99          }
100         Object values[] = (Object[])state;
101         if (values.length == 2)
102         {
103             // Full state
104             stateMap = (Map<String,Object>) UIComponentBase.restoreAttachedState(context, values[0]);
105             bindingsMap = (Map<String,Map<String, ValueExpression>>) 
106                 UIComponentBase.restoreAttachedState(context, values[1]);
107         }
108         else
109         {
110             if (values[0] == null)
111             {
112                 stateMap = null;
113             }
114             else
115             {
116                 stateMap = (Map<String,Object>) UIComponentBase.restoreAttachedState(context, values[0]);
117             }            
118         }
119     }
120 
121     public boolean isTransient()
122     {
123         return false;
124     }
125 
126     public void setTransient(boolean newTransientValue)
127     {
128     }
129     
130     public boolean isDynamic()
131     {
132         return stateMap == null ? false : !stateMap.isEmpty();
133     }
134 
135     public void putBinding(String uniqueId, String key, ValueExpression expr)
136     {
137         if (bindingsMap == null)
138         {
139             bindingsMap = new HashMap<String, Map<String, ValueExpression>>();
140         }
141         Map<String, ValueExpression> bindings = bindingsMap.get(uniqueId);
142         if (bindings == null)
143         {
144             bindings = new HashMap<String, ValueExpression>();
145             bindingsMap.put(uniqueId, bindings);
146         }
147         bindings.put(key, expr);
148     }
149     
150     /**
151      * A "Facelet Binding ValueExpression" is a ValueExpression used/generated by facelets algorithm
152      * associated with an uniqueId, which usually is bound to the tagId. Components like c:forEach or
153      * ui:param uses it and the reason behind this is avoid use VariableMapper to create EL Expressions
154      * that later cannot be cached. Instead, the intention is make an indirection using 2 ValueExpression
155      * instances. In that way, all EL Expressions can be cached, because VariableMapper will use an
156      * instance that contains the uniqueId and the one stored in the map will have the real value or
157      * EL Expression that points to the managed bean. (Remember each EL expression that uses a variable
158      * stored in VariableMapper will copy the EL expression bound to the variable, so if the EL expression
159      * value changes across views, all EL Expressions that contains a reference cannot be cached).
160      * 
161      * This map is something special, because its content is related to the view structure. It does not need
162      * to be saved fully into the state, and it does not have any delta state, but it "evolves" with the initial
163      * state.
164      */
165     public ValueExpression getBinding(String uniqueId, String key)
166     {
167         if (bindingsMap == null)
168         {
169             return null;
170         }
171         Map<String, ValueExpression> bindings = bindingsMap.get(uniqueId);
172         if (bindings == null)
173         {
174             return null;
175         }
176         return bindings.get(key);
177     }
178     
179     public Map<String, Map<String, ValueExpression>> getBindings()
180     {
181         return bindingsMap;
182     }
183 
184     @Override
185     public int hashCode()
186     {
187         int hash = 7;
188         hash = 79 * hash + (this.stateMap != null ? this.stateMap.hashCode() : 0);
189         return hash;
190     }
191 
192     @Override
193     public boolean equals(Object obj)
194     {
195         if (obj == null)
196         {
197             return false;
198         }
199         if (getClass() != obj.getClass())
200         {
201             return false;
202         }
203         final FaceletState other = (FaceletState) obj;
204         if (this.stateMap != other.stateMap && (this.stateMap == null || !this.stateMap.equals(other.stateMap)))
205         {
206             return false;
207         }
208         return true;
209     }
210 
211 }