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.trinidad.component;
20  
21  import java.io.Externalizable;
22  import java.io.IOException;
23  import java.io.ObjectOutput;
24  import java.io.ObjectInput;
25  
26  import javax.faces.component.UIComponent;
27  import javax.faces.context.FacesContext;
28  import java.util.List;
29  import java.util.Map;
30  import java.util.Set;
31  
32  import org.apache.myfaces.trinidad.logging.TrinidadLogger;
33  
34  
35  /**
36   * Strongly-typed object for storing UIX Tree State.
37   * <p>
38   * @todo All warnings in here map to missing features in our state management
39   *   system.
40   * @todo Add a decent toString() implementation to improve debugging.
41   */
42  class TreeState implements Externalizable
43  {
44    public TreeState()
45    {
46      _empty = true;
47    }
48  
49    public boolean isEmpty()
50    {
51      return _empty;
52    }
53  
54    @SuppressWarnings("unchecked")
55    public void restoreState(FacesContext context, UIXComponentBase component)
56    {
57      component.restoreState(context, _state);
58      int childCount = component.getChildCount();
59      int arrayCount = (_children == null) ? 0 : _children.length;
60      int transientCount = 0;
61  
62      // In Facelets land, we might have transient components *already in* the
63      // tree - but they won't have had state saved.  So, we'd better
64      // account for those before assuming that our saved state count
65      // and actual child count can't be reconciled!
66      if (childCount != arrayCount)
67      {
68        List children = component.getChildren();
69        for (int i = 0; i < childCount; i++)
70        {
71          UIComponent child = (UIComponent) children.get(i);
72          if (child.isTransient())
73            transientCount++;
74        }
75      }
76  
77      if (arrayCount + transientCount != childCount)
78      {
79        if (_LOG.isWarning())
80          _LOG.warning("SAVED_CHILD_COUNT_NOT_MATCH", new Object[] {arrayCount, childCount});
81      }
82  
83      else
84      {
85        List children = component.getChildren();
86        int arrayIndex = 0;
87        for (int i = 0; i < childCount; i++)
88        {
89          UIComponent child = (UIComponent) children.get(i);
90          if (child.isTransient())
91          {
92            continue;
93          }
94          else
95          {
96            child.processRestoreState(context,  _children[arrayIndex]);
97            arrayIndex++;
98          }
99        }
100     }
101 
102     // Restore the facets' state
103     if (_facets != null)
104     {
105       assert(_facets.length % 2 == 0);
106 
107       int facetCount = _facets.length / 2;
108       // If our count is off, log a warning
109       if (facetCount < component.getFacetCount())
110       {
111         if (_LOG.isWarning())
112           _LOG.warning("FACETS_STATE_MISSING_WILLNOT_RESTORE", component);
113       }
114 
115 
116       for (int i = 0; i < facetCount; i++)
117       {
118         assert((_facets[i * 2] == null) ||
119                (_facets[i * 2] instanceof String));
120 
121         String facetName = (String) _facets[i * 2];
122         if (facetName == null)
123           continue;
124 
125         Object facetState = _facets[i * 2 + 1];
126         UIComponent facet = component.getFacet(facetName);
127         if (facet == null)
128         {
129           if (_LOG.isWarning())
130             _LOG.warning("DISCARDING_SAVED_STATE", facetName);
131         }
132         else
133         {
134           if (facet.isTransient())
135           {
136             if (facetState != null)
137             {
138               if (_LOG.isWarning())
139                 _LOG.warning("SAVED_STATE_INCLUDE_TRANSIENT_COMPONENT_STATE", facet);
140             }
141           }
142           else
143           {
144             facet.processRestoreState(context,  facetState);
145           }
146         }
147       }
148     }
149   }
150 
151   @SuppressWarnings("unchecked")
152   public void saveState(FacesContext context, UIXComponentBase component)
153   {
154     // Save the component's state
155     _state = component.saveState(context);
156     if (_state != null)
157       _empty = false;
158 
159     // Save the children's state
160     int childCount = component.getChildCount();
161     if (childCount == 0)
162     {
163       _children = null;
164     }
165     else
166     {
167       _children = new Object[childCount];
168       List children = component.getChildren();
169       int j = 0;
170       for (int i = 0; i < childCount; i++)
171       {
172         UIComponent child = (UIComponent) children.get(i);
173         if (!child.isTransient())
174         {
175           Object childState = child.processSaveState(context);
176           if (childState != null)
177           {
178             _empty = false;
179             _children[j] = childState;
180           }
181 
182           j++;
183         }
184       }
185 
186       // OK - there were some transient components, so the array's too big;
187       // trim it down
188       if (j < childCount)
189       {
190         Object[] newChildren = new Object[j];
191         System.arraycopy(_children, 0, newChildren, 0, j);
192         _children = newChildren;
193       }
194     }
195 
196     // Save the facets' state
197     int facetCount = component.getFacetCount();
198     if (facetCount == 0)
199     {
200       _facets = null;
201     }
202     else
203     {
204       _facets = new Object[facetCount * 2];
205       int i = 0;
206       Set<Map.Entry<String, UIComponent>> entries = component.getFacets().entrySet();
207       for(Map.Entry<String, UIComponent> entry : entries)
208       {
209         UIComponent facet = entry.getValue();
210 
211         // Just skip over transient facets
212         if (facet.isTransient())
213           continue;
214 
215         Object facetState = facet.processSaveState(context);
216         if (facetState != null)
217         {
218           _empty = false;
219           _facets[2 * i] = entry.getKey();
220           _facets[2 * i + 1] = facetState;
221         }
222 
223         i++;
224       }
225 
226       // If there's no non-transient facets, then bail
227       if (i == 0)
228         _facets = null;
229     }
230   }
231 
232   //
233   // Implementation of Externalizable - just a bit easier on output
234   // size, for very little work.
235   //
236 
237   public void writeExternal(ObjectOutput out) throws IOException
238   {
239     out.writeObject(_state);
240     out.writeObject(_facets);
241     out.writeObject(_children);
242   }
243 
244   public void readExternal(ObjectInput in)
245      throws IOException, ClassNotFoundException
246   {
247     _state = in.readObject();
248     _facets = (Object[]) in.readObject();
249     _children = (Object[]) in.readObject();
250   }
251 
252 
253   private static final TrinidadLogger _LOG = TrinidadLogger.createTrinidadLogger(TreeState.class);
254 
255   private Object[] _facets;
256   private Object[] _children;
257   private Object _state;
258   private boolean _empty;
259 
260   private static final long serialVersionUID = 1L;
261 }