View Javadoc

1   /*
2    * $Id: UIData2.java,v 1.9.6.1 2010/12/11 13:28:02 oeuillot Exp $
3    */
4   
5   /*
6    * The contents of this file are subject to the terms of the Common Development
7    * and Distribution License (the License). You may not use this file except in
8    * compliance with the License.
9    * 
10   * You can obtain a copy of the License at
11   * https://javaserverfaces.dev.java.net/CDDL.html or legal/CDDLv1.0.txt. See the
12   * License for the specific language governing permission and limitations under
13   * the License.
14   * 
15   * When distributing Covered Code, include this CDDL Header Notice in each file
16   * and include the License file at legal/CDDLv1.0.txt. If applicable, add the
17   * following below the CDDL Header, with the fields enclosed by brackets []
18   * replaced by your own identifying information: "Portions Copyrighted [year]
19   * [name of copyright owner]"
20   * 
21   * [Name of File] [ver.__] [Date]
22   * 
23   * Copyright 2006 Sun Microsystems Inc. All Rights Reserved
24   */
25  
26  package org.rcfaces.core.internal.component;
27  
28  import java.io.Externalizable;
29  import java.io.IOException;
30  import java.io.ObjectInput;
31  import java.io.ObjectOutput;
32  import java.util.ArrayList;
33  import java.util.HashMap;
34  import java.util.Iterator;
35  import java.util.List;
36  import java.util.Map;
37  
38  import javax.el.ValueExpression;
39  import javax.faces.component.StateHolder;
40  import javax.faces.component.UIColumn;
41  import javax.faces.component.UIComponent;
42  import javax.faces.context.FacesContext;
43  import javax.faces.event.PhaseId;
44  import javax.faces.model.DataModel;
45  
46  import org.apache.commons.logging.Log;
47  import org.apache.commons.logging.LogFactory;
48  import org.rcfaces.core.component.capability.IAdditionalInformationContainer;
49  import org.rcfaces.core.internal.Constants;
50  import org.rcfaces.core.internal.capability.IComponentEngine;
51  import org.rcfaces.core.internal.capability.IRCFacesComponent;
52  
53  /**
54   * <p>
55   * <strong>UIData</strong> is a {@link UIComponent} that supports data binding
56   * to a collection of data objects represented by a {@link DataModel} instance,
57   * which is the current value of this component itself (typically established
58   * via a {@link ValueExpression}). During iterative processing over the rows of
59   * data in the data model, the object for the current row is exposed as a
60   * request attribute under the key specified by the <code>var</code> property.
61   * </p>
62   * 
63   * <p>
64   * Only children of type {@link UIColumn} should be processed by renderers
65   * associated with this component.
66   * </p>
67   * 
68   * <p>
69   * By default, the <code>rendererType</code> property is set to
70   * <code>javax.faces.Table</code>. This value can be changed by calling the
71   * <code>setRendererType()</code> method.
72   * </p>
73   */
74  
75  public class UIData2 extends UIData0 {
76  
77      private static final Log LOG = LogFactory.getLog(UIData2.class);
78  
79      private static boolean DEBUG_ENABLED = LOG.isDebugEnabled();
80  
81      private transient List<int[]> decodedIndexes;
82  
83      private Map saved = new HashMap();
84  
85      private boolean saveCompleteState = true;
86  
87      private boolean iterateMode;
88  
89      private int iterateModeFirst;
90  
91      private int iterateModeIndex;
92  
93      private int iterateModeRows;
94  
95      public UIData2() {
96          // Certains logs sont positionnés APRES !
97          DEBUG_ENABLED = LOG.isDebugEnabled();
98      }
99  
100     public void encodeBegin(FacesContext context) throws IOException {
101 
102         decodedIndexes = null;
103 
104         super.encodeBegin(context);
105     }
106 
107     protected void iterate(FacesContext context, PhaseId phaseId) {
108         if (decodedIndexes == null || decodedIndexes.isEmpty()) {
109             super.iterate(context, phaseId);
110             return;
111         }
112 
113         if (true) {
114             super.iterate(context, phaseId);
115             return;
116         }
117 
118         iterateModeFirst = 0;
119         iterateModeRows = 0;
120         iterateModeIndex = 0;
121 
122         for (int[] is : decodedIndexes) {
123             iterateModeRows += is[1];
124         }
125 
126         iterateMode = true;
127         try {
128 
129             super.iterate(context, phaseId);
130 
131         } finally {
132             iterateMode = false;
133         }
134     }
135 
136     protected boolean renderColumn(UIColumn column, PhaseId phaseId) {
137         if (column instanceof IAdditionalInformationContainer) {
138             return decodeAdditionalInformation((IAdditionalInformationContainer) column);
139         }
140 
141         return true;
142     }
143 
144     public int getFirst() {
145         if (iterateMode == false) {
146             int first = super.getFirst();
147 
148             if (DEBUG_ENABLED) {
149                 LOG.debug("getFirst returns " + first);
150             }
151 
152             return first;
153         }
154 
155         if (DEBUG_ENABLED) {
156             LOG.debug("Iterate translate mode: return first="
157                     + iterateModeFirst);
158         }
159 
160         return iterateModeFirst;
161     }
162 
163     public int getRowCount() {
164         int rowCount = super.getRowCount();
165 
166         if (DEBUG_ENABLED) {
167             LOG.debug("getRowCount() returns " + rowCount);
168         }
169 
170         return rowCount;
171     }
172 
173     public int getRowIndex() {
174         int rowIndex = super.getRowIndex();
175 
176         if (DEBUG_ENABLED) {
177             LOG.debug("getRowIndex() returns " + rowIndex);
178         }
179 
180         return rowIndex;
181     }
182 
183     public boolean isRowAvailable() {
184         boolean rowAvailable = super.isRowAvailable();
185 
186         if (DEBUG_ENABLED) {
187             LOG.debug("isRowAvailable() returns " + rowAvailable);
188         }
189 
190         return rowAvailable;
191     }
192 
193     public void setFirst(int first) {
194 
195         if (DEBUG_ENABLED) {
196             LOG.debug("setFirst(" + first + ")");
197         }
198 
199         super.setFirst(first);
200     }
201 
202     public void setRows(int rows) {
203 
204         if (DEBUG_ENABLED) {
205             LOG.debug("setRows(" + rows + ")");
206         }
207 
208         super.setRows(rows);
209     }
210 
211     public int getRows() {
212         if (iterateMode == false) {
213             int rows = super.getRows();
214 
215             if (DEBUG_ENABLED) {
216                 LOG.debug("getRows() returns " + rows);
217             }
218 
219             return rows;
220         }
221 
222         if (DEBUG_ENABLED) {
223             LOG.debug("getRows(): Iterate translate mode returns rows="
224                     + iterateModeRows);
225         }
226 
227         return iterateModeRows;
228     }
229 
230     public void setRowIndex(int rowIndex) {
231         if (iterateMode == false || rowIndex < 0) {
232 
233             if (DEBUG_ENABLED) {
234                 LOG.debug("setRowIndex(" + rowIndex + ")");
235             }
236 
237             super.setRowIndex(rowIndex);
238             return;
239         }
240 
241         int translatedRowIndex = 0;
242         for (int[] is : decodedIndexes) {
243             if (rowIndex >= is[1]) {
244                 rowIndex -= is[1];
245                 continue;
246             }
247 
248             translatedRowIndex = is[0] + rowIndex;
249             break;
250         }
251 
252         if (DEBUG_ENABLED) {
253             LOG.debug("setRowIndex(" + rowIndex
254                     + ") Iterate translate mode => rowIndex="
255                     + translatedRowIndex);
256         }
257 
258         super.setRowIndex(translatedRowIndex);
259     }
260 
261     public void addDecodedIndexes(int first, int rows) {
262 
263         if (DEBUG_ENABLED) {
264             LOG.debug("Add decoded indexes first=" + first + " rows=" + rows);
265         }
266 
267         if (decodedIndexes == null) {
268             decodedIndexes = new ArrayList<int[]>();
269         }
270 
271         if (rows > 0) {
272             decodedIndexes.add(new int[] { first, rows });
273         }
274     }
275 
276     public void restoreState(FacesContext context, Object state) {
277         Object states[] = (Object[]) state;
278 
279         super.restoreState(context, states[0]);
280 
281         Object[] ss = (Object[]) states[1];
282 
283         saved = new HashMap(ss.length / 2);
284         if (ss.length > 0) {
285             for (int i = 0; i < ss.length;) {
286                 Object key = ss[i++];
287 
288                 SavedState2 ss2 = new SavedState2();
289                 ss2.restoreState(context, ss[i++]);
290 
291                 saved.put(key, ss2);
292             }
293         }
294 
295     }
296 
297     public Object saveState(FacesContext context) {
298         Object ret[] = new Object[2];
299 
300         ret[0] = super.saveState(context);
301 
302         Object ss[] = new Object[saved.size() * 2];
303         ret[1] = ss;
304 
305         if (ss.length > 0) {
306             int index = 0;
307             for (Iterator it = saved.entrySet().iterator(); it.hasNext();) {
308                 Map.Entry entry = (Map.Entry) it.next();
309 
310                 ss[index++] = entry.getKey();
311                 ss[index++] = ((SavedState2) entry.getValue())
312                         .saveState(context);
313             }
314         }
315 
316         return ret;
317     }
318 
319     /**
320      * <p>
321      * Restore state information for the specified component and its
322      * descendants.
323      * </p>
324      * 
325      * @param component
326      *            Component for which to restore state information
327      * @param context
328      *            {@link FacesContext} for the current request
329      */
330 
331     protected void restoreDescendantState(UIComponent component,
332             FacesContext context) {
333 
334         // Reset the client identifier for this component
335         String id = component.getId();
336         component.setId(id); // Forces client id to be reset
337 
338         if (component instanceof IRCFacesComponent) {
339             String clientId = component.getClientId(context);
340             SavedState2 state = (SavedState2) saved.get(clientId);
341 
342             if (state != null) {
343                 IComponentEngine componentEngine = state.getComponentEngine(
344                         context, component);
345                 if (componentEngine != null) {
346                     ComponentEngineManager.setComponentEngine(
347                             (IRCFacesComponent) component, componentEngine);
348                 }
349             } else {
350                 ComponentEngineManager
351                         .cloneComponentEngine((IRCFacesComponent) component);
352             }
353 
354             if (LOG.isDebugEnabled()) {
355                 LOG.debug("Restore state of '" + clientId + "' => " + state);
356             }
357         }
358 
359         super.restoreDescendantState(component, context);
360     }
361 
362     /**
363      * <p>
364      * Save state information for the specified component and its descendants.
365      * </p>
366      * 
367      * @param component
368      *            Component for which to save state information
369      * @param context
370      *            {@link FacesContext} for the current request
371      */
372 
373     protected void saveDescendantState(UIComponent component,
374             FacesContext context) {
375 
376         super.saveDescendantState(component, context);
377 
378         if (isSaveCompleteState()) {
379             if (component instanceof IRCFacesComponent) {
380                 IComponentEngine componentEngine = ComponentEngineManager
381                         .getComponentEngine((IRCFacesComponent) component);
382 
383                 String clientId = component.getClientId(context);
384 
385                 SavedState2 state = (SavedState2) saved.get(clientId);
386                 if (state == null) {
387                     state = new SavedState2();
388                     saved.put(clientId, state);
389                 }
390 
391                 state.setComponentEngine(componentEngine);
392 
393                 if (LOG.isDebugEnabled()) {
394                     LOG.debug("Save state of '" + clientId + "' => " + state);
395                 }
396             }
397         }
398     }
399 
400     public final boolean isSaveCompleteState() {
401         return saveCompleteState;
402     }
403 
404     public final void setSaveCompleteState(boolean saveCompleteState) {
405         this.saveCompleteState = saveCompleteState;
406     }
407 
408     // Private class to represent saved state information
409     public static class SavedState2 implements Externalizable, StateHolder {
410 
411         private IComponentEngine componentEngine;
412 
413         private Object serializedComponentEngine;
414 
415         public SavedState2() {
416 
417         }
418 
419         public final IComponentEngine getComponentEngine(
420                 FacesContext facesContext, UIComponent component) {
421             if (serializedComponentEngine == null) {
422                 return componentEngine;
423             }
424 
425             IFactory factory = Constants.getCameliaFactory();
426 
427             IComponentEngine componentEngine = factory.createComponentEngine();
428 
429             componentEngine.restoreState(facesContext,
430                     serializedComponentEngine);
431 
432             serializedComponentEngine = null;
433 
434             return componentEngine;
435         }
436 
437         public final void setComponentEngine(IComponentEngine componentEngine) {
438             this.componentEngine = componentEngine;
439             this.serializedComponentEngine = null;
440         }
441 
442         public String toString() {
443             return "componentEngine=" + componentEngine;
444         }
445 
446         public void readExternal(ObjectInput in) throws IOException,
447                 ClassNotFoundException {
448 
449             serializedComponentEngine = in.readObject();
450         }
451 
452         public void writeExternal(ObjectOutput out) throws IOException {
453             if (componentEngine != null) {
454                 serializedComponentEngine = componentEngine
455                         .saveState(FacesContext.getCurrentInstance());
456             }
457 
458             out.writeObject(serializedComponentEngine);
459         }
460 
461         public Object saveState(FacesContext context) {
462             if (componentEngine != null) {
463                 serializedComponentEngine = componentEngine
464                         .saveState(FacesContext.getCurrentInstance());
465             }
466 
467             return serializedComponentEngine;
468         }
469 
470         public void restoreState(FacesContext context, Object state) {
471             serializedComponentEngine = state;
472         }
473 
474         public boolean isTransient() {
475             return false;
476         }
477 
478         public void setTransient(boolean newTransientValue) {
479         }
480 
481     }
482 
483     public boolean decodeAdditionalInformation(
484             IAdditionalInformationContainer additionalInformationComponent) {
485         if (decodedIndexes == null) {
486             if (DEBUG_ENABLED) {
487                 int rowIndex = getRowIndex();
488 
489                 LOG.debug("Decode additional #" + rowIndex + " ("
490                         + decodedIndexesToString() + ") => FALSE");
491             }
492             return false;
493         }
494 
495         int rowIndex = getRowIndex();
496 
497         for (int[] is : decodedIndexes) {
498             if (rowIndex >= is[0] && rowIndex < is[0] + is[1]) {
499                 if (DEBUG_ENABLED) {
500                     LOG.debug("Decode additional #" + rowIndex + " ("
501                             + decodedIndexesToString() + ") => TRUE");
502                 }
503                 return true;
504             }
505         }
506 
507         if (DEBUG_ENABLED) {
508             LOG.debug("Decode additional #" + rowIndex + " ("
509                     + decodedIndexesToString() + ") => FALSE");
510         }
511 
512         return false;
513     }
514 
515     private String decodedIndexesToString() {
516         if (decodedIndexes == null) {
517             return "null";
518         }
519 
520         if (decodedIndexes.isEmpty()) {
521             return "[]";
522         }
523 
524         StringBuffer sb = new StringBuffer();
525 
526         for (int is[] : decodedIndexes) {
527             if (sb.length() > 0) {
528                 sb.append(',');
529             }
530             sb.append("[" + is[0] + "->" + (is[0] + is[1] - 1) + "]");
531         }
532 
533         return sb.toString();
534     }
535 }