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 javax.faces.component;
20
21 import java.io.IOException;
22 import java.sql.ResultSet;
23 import java.util.*;
24 import javax.el.ValueExpression;
25 import javax.faces.FacesException;
26 import javax.faces.application.FacesMessage;
27 import javax.faces.context.FacesContext;
28 import javax.faces.event.AbortProcessingException;
29 import javax.faces.event.FacesEvent;
30 import javax.faces.event.FacesListener;
31 import javax.faces.event.PhaseId;
32 import javax.faces.model.*;
33 import javax.servlet.jsp.jstl.sql.Result;
34 import org.apache.myfaces.buildtools.maven2.plugin.builder.annotation.JSFComponent;
35 import org.apache.myfaces.buildtools.maven2.plugin.builder.annotation.JSFFacet;
36 import org.apache.myfaces.buildtools.maven2.plugin.builder.annotation.JSFProperty;
37
38 /**
39 * Represents an abstraction of a component which has multiple "rows" of data.
40 * <p>
41 * The children of this component are expected to be UIColumn components.
42 * <p>
43 * Note that the same set of child components are reused to implement each row of the table in turn during
44 * such phases as apply-request-values and render-response. Altering any of the members of these components
45 * therefore affects the attribute for every row, except for the following members:
46 * <ul>
47 * <li>submittedValue
48 * <li>value (where no EL binding is used)
49 * <li>valid
50 * </ul>
51 * <p>
52 * This reuse of the child components also means that it is not possible to save a reference to a component
53 * during table processing, then access it later and expect it to still represent the same row of the table.
54 * <h1>
55 * Implementation Notes
56 * </h1>
57 * <p>
58 * Each of the UIColumn children of this component has a few component children of its own to render the contents
59 * of the table cell. However there can be a very large number of rows in a table, so it isn't efficient for the
60 * UIColumn and all its child objects to be duplicated for each row in the table. Instead the "flyweight" pattern
61 * is used where a serialized state is held for each row. When setRowIndex is invoked, the UIColumn objects and
62 * their children serialize their current state then reinitialise themselves from the appropriate saved state.
63 * This allows a single set of real objects to represent multiple objects which have the same types but potentially
64 * different internal state. When a row is selected for the first time, its state is set to a clean "initial" state.
65 * Transient components (including any read-only component) do not save their state; they are just reinitialised as
66 * required. The state saved/restored when changing rows is not the complete component state, just the fields that
67 * are expected to vary between rows: "submittedValue", "value", "isValid".
68 * </p>
69 * <p>
70 * Note that a table is a "naming container", so that components within the table have their ids prefixed with the
71 * id of the table. Actually, when setRowIndex has been called on a table with id of "zzz" the table pretends to
72 * its children that its ID is "zzz_n" where n is the row index. This means that renderers for child components which
73 * call component.getClientId automatically get ids of form "zzz_n:childId" thus ensuring that components in
74 * different rows of the table get different ids.
75 * </p>
76 * <p>
77 * When decoding a submitted page, this class iterates over all its possible rowIndex values, restoring the
78 * appropriate serialized row state then calling processDecodes on the child components. Because the child
79 * components (or their renderers) use getClientId to get the request key to look for parameter data, and because
80 * this object pretends to have a different id per row ("zzz_n") a single child component can decode data from each
81 * table row in turn without being aware that it is within a table. The table's data model is updated before each
82 * call to child.processDecodes, so the child decode method can assume that the data model's rowData points to the
83 * model object associated with the row currently being decoded. Exactly the same process applies for the later
84 * validation and updateModel phases.
85 * </p>
86 * <p>
87 * When the data model for the table is bound to a backing bean property, and no validation errors have occured
88 * during processing of a postback, the data model is refetched at the start of the rendering phase (ie after the
89 * update model phase) so that the contents of the data model can be changed as a result of the latest form
90 * submission. Because the saved row state must correspond to the elements within the data model, the row state
91 * must be discarded whenever a new data model is fetched; not doing this would cause all sorts of inconsistency
92 * issues. This does imply that changing the state of any of the members "submittedValue", "value" or "valid" of
93 * a component within the table during the invokeApplication phase has no effect on the rendering of the table.
94 * When a validation error has occurred, a new DataModel is <i>not</i> fetched, and the saved state of the child
95 * components is <i>not</i> discarded.
96 * </p>
97 * see Javadoc of the <a href="http://java.sun.com/j2ee/javaserverfaces/1.2/docs/api/index.html">JSF Specification</a>
98 * for more information.
99 *
100 * @author Manfred Geiler (latest modification by $Author: lu4242 $)
101 * @version $Revision: 1203044 $ $Date: 2011-11-16 23:22:07 -0500 (Wed, 16 Nov 2011) $
102 */
103 @JSFComponent(defaultRendererType = "javax.faces.Table")
104 public class UIData extends UIComponentBase
105 implements NamingContainer
106 {
107 public static final String COMPONENT_FAMILY = "javax.faces.Data";
108 public static final String COMPONENT_TYPE = "javax.faces.Data"; // for unit tests
109
110 private static final String FOOTER_FACET_NAME = "footer";
111 private static final String HEADER_FACET_NAME = "header";
112 private static final Class OBJECT_ARRAY_CLASS = (new Object[0]).getClass();
113 private static final int PROCESS_DECODES = 1;
114 private static final int PROCESS_VALIDATORS = 2;
115 private static final int PROCESS_UPDATES = 3;
116
117 private static final Object[] LEAF_NO_STATE = new Object[]{null,null};
118
119 private int _rowIndex = -1;
120 private String _var;
121
122 // Holds for each row the states of the child components of this UIData.
123 // Note that only "partial" component state is saved: the component fields
124 // that are expected to vary between rows.
125 private Map _rowStates = new HashMap();
126
127 /**
128 * Handle case where this table is nested inside another table.
129 * See method getDataModel for more details.
130 * <p>
131 * Key: parentClientId (aka rowId when nested within a parent table)
132 * Value: DataModel
133 */
134 private Map _dataModelMap = new HashMap();
135
136 // will be set to false if the data should not be refreshed at the beginning of the encode phase
137 private boolean _isValidChilds = true;
138
139 private Object _initialDescendantComponentState = null;
140
141 private int _first;
142 private boolean _firstSet;
143 private int _rows;
144 private boolean _rowsSet;
145 private Object _value;
146
147 private static class FacesEventWrapper extends FacesEvent
148 {
149 private static final long serialVersionUID = 6648047974065628773L;
150 private FacesEvent _wrappedFacesEvent;
151 private int _rowIndex;
152
153 public FacesEventWrapper(FacesEvent facesEvent, int rowIndex,
154 UIData redirectComponent)
155 {
156 super(redirectComponent);
157 _wrappedFacesEvent = facesEvent;
158 _rowIndex = rowIndex;
159 }
160
161 @Override
162 public PhaseId getPhaseId()
163 {
164 return _wrappedFacesEvent.getPhaseId();
165 }
166
167 @Override
168 public void setPhaseId(PhaseId phaseId)
169 {
170 _wrappedFacesEvent.setPhaseId(phaseId);
171 }
172
173 @Override
174 public void queue()
175 {
176 _wrappedFacesEvent.queue();
177 }
178
179 @Override
180 public String toString()
181 {
182 return _wrappedFacesEvent.toString();
183 }
184
185 @Override
186 public boolean isAppropriateListener(FacesListener faceslistener)
187 {
188 return _wrappedFacesEvent.isAppropriateListener(faceslistener);
189 }
190
191 @Override
192 public void processListener(FacesListener faceslistener)
193 {
194 _wrappedFacesEvent.processListener(faceslistener);
195 }
196
197 public FacesEvent getWrappedFacesEvent()
198 {
199 return _wrappedFacesEvent;
200 }
201
202 public int getRowIndex()
203 {
204 return _rowIndex;
205 }
206 }
207
208
209 private static final DataModel EMPTY_DATA_MODEL = new DataModel()
210 {
211 @Override
212 public boolean isRowAvailable()
213 {
214 return false;
215 }
216
217 @Override
218 public int getRowCount()
219 {
220 return 0;
221 }
222
223 @Override
224 public Object getRowData()
225 {
226 throw new IllegalArgumentException();
227 }
228
229 @Override
230 public int getRowIndex()
231 {
232 return -1;
233 }
234
235 @Override
236 public void setRowIndex(int i)
237 {
238 if (i < -1)
239 throw new IllegalArgumentException();
240 }
241
242 @Override
243 public Object getWrappedData()
244 {
245 return null;
246 }
247
248 @Override
249 public void setWrappedData(Object obj)
250 {
251 if (obj == null)
252 {
253 return; //Clearing is allowed
254 }
255 throw new UnsupportedOperationException(this.getClass().getName()
256 + " UnsupportedOperationException");
257 }
258 };
259
260 private class EditableValueHolderState
261 {
262 private final Object _value;
263 private final boolean _localValueSet;
264 private final boolean _valid;
265 private final Object _submittedValue;
266
267 public EditableValueHolderState(EditableValueHolder evh)
268 {
269 _value = evh.getLocalValue();
270 _localValueSet = evh.isLocalValueSet();
271 _valid = evh.isValid();
272 _submittedValue = evh.getSubmittedValue();
273 }
274
275 public void restoreState(EditableValueHolder evh)
276 {
277 evh.setValue(_value);
278 evh.setLocalValueSet(_localValueSet);
279 evh.setValid(_valid);
280 evh.setSubmittedValue(_submittedValue);
281 }
282 }
283
284 /**
285 * Construct an instance of the UIData.
286 */
287 public UIData()
288 {
289 setRendererType("javax.faces.Table");
290 }
291
292 @Override
293 public boolean invokeOnComponent(FacesContext context, String clientId, ContextCallback callback) throws FacesException {
294 if (context == null || clientId == null || callback == null)
295 {
296 throw new NullPointerException();
297 }
298
299 //searching for this component?
300 boolean returnValue = this.getClientId(context).equals(clientId);
301
302 boolean isCachedFacesContext = isCachedFacesContext();
303 if (!isCachedFacesContext)
304 {
305 setCachedFacesContext(context);
306 }
307
308 try
309 {
310 if (returnValue)
311 {
312 try
313 {
314 callback.invokeContextCallback(context, this);
315 }
316 catch (Exception e)
317 {
318 throw new FacesException(e);
319 }
320 return returnValue;
321 }
322
323 //Now Look throught facets on this UIComponent
324 for (Iterator<UIComponent> it = this.getFacets().values().iterator(); !returnValue && it.hasNext();)
325 {
326 returnValue = it.next().invokeOnComponent(context, clientId, callback);
327 }
328
329 if (returnValue == true)
330 {
331 return returnValue;
332 }
333
334 //Now we have to check if it is searching an inner component
335 String baseClientId = super.getClientId(context);
336
337 // is the component an inner component?
338 if (clientId.startsWith(baseClientId))
339 {
340 // Check if the clientId for the component, which we
341 // are looking for, has a rowIndex attached
342 String subId = clientId.substring(baseClientId.length() + 1);
343 //If the char next to baseClientId is the separator one and
344 //the subId matches the regular expression
345 if (clientId.charAt(baseClientId.length()) == NamingContainer.SEPARATOR_CHAR &&
346 subId.matches("[0-9]+"+NamingContainer.SEPARATOR_CHAR+".*"))
347 {
348 String clientRow = subId.substring(0, subId.indexOf(NamingContainer.SEPARATOR_CHAR));
349
350 //Now we save the current position
351 int oldRow = this.getRowIndex();
352
353 // try-finally --> make sure, that the old row index is restored
354 try
355 {
356 //The conversion is safe, because its already checked on the
357 //regular expresion
358 this.setRowIndex(Integer.parseInt(clientRow));
359
360 // check, if the row is available
361 if (!isRowAvailable())
362 {
363 return false;
364 }
365
366 for (Iterator<UIComponent> it1 = getChildren().iterator();
367 !returnValue && it1.hasNext();)
368 {
369 //recursive call to find the component
370 returnValue = it1.next().invokeOnComponent(context, clientId, callback);
371 }
372 }
373 finally
374 {
375 //Restore the old position. Doing this prevent
376 //side effects.
377 this.setRowIndex(oldRow);
378 }
379 }
380 else
381 {
382 // MYFACES-2370: search the component in the childrens' facets too.
383 // We have to check the childrens' facets here, because in MyFaces
384 // the rowIndex is not attached to the clientId for the children of
385 // facets of the UIColumns. However, in RI the rowIndex is
386 // attached to the clientId of UIColumns' Facets' children.
387 for (Iterator<UIComponent> itChildren = this.getChildren().iterator();
388 !returnValue && itChildren.hasNext();)
389 {
390 UIComponent child = itChildren.next();
391 if (child instanceof UIColumn && clientId.equals(child.getClientId(context)))
392 {
393 try {
394 callback.invokeContextCallback(context, child);
395 } catch (Exception e) {
396 throw new FacesException(e);
397 }
398 returnValue = true;
399 }
400 // process the child's facets
401 for (Iterator<UIComponent> itChildFacets = child.getFacets().values().iterator();
402 !returnValue && itChildFacets.hasNext();)
403 {
404 //recursive call to find the component
405 returnValue = itChildFacets.next().invokeOnComponent(context, clientId, callback);
406 }
407 }
408 }
409 }
410 }
411 finally
412 {
413 if (!isCachedFacesContext)
414 {
415 setCachedFacesContext(null);
416 }
417 }
418
419 return returnValue;
420 }
421
422 public void setFooter(UIComponent footer)
423 {
424 getFacets().put(FOOTER_FACET_NAME, footer);
425 }
426
427 @JSFFacet
428 public UIComponent getFooter()
429 {
430 return (UIComponent) getFacets().get(FOOTER_FACET_NAME);
431 }
432
433 public void setHeader(UIComponent header)
434 {
435 getFacets().put(HEADER_FACET_NAME, header);
436 }
437
438 @JSFFacet
439 public UIComponent getHeader()
440 {
441 return (UIComponent) getFacets().get(HEADER_FACET_NAME);
442 }
443
444 public boolean isRowAvailable()
445 {
446 return getDataModel().isRowAvailable();
447 }
448
449 public int getRowCount()
450 {
451 return getDataModel().getRowCount();
452 }
453
454 public Object getRowData()
455 {
456 return getDataModel().getRowData();
457 }
458
459 public int getRowIndex()
460 {
461 return _rowIndex;
462 }
463
464 /**
465 * Set the current row index that methods like getRowData use.
466 * <p>
467 * Param rowIndex can be -1, meaning "no row".
468 * <p>
469 *
470 * @param rowIndex
471 */
472 public void setRowIndex(int rowIndex)
473 {
474 if (rowIndex < -1)
475 {
476 throw new IllegalArgumentException("rowIndex is less than -1");
477 }
478
479 if (_rowIndex == rowIndex)
480 {
481 return;
482 }
483
484 FacesContext facesContext = getFacesContext();
485
486 if (_rowIndex == -1)
487 {
488 if (_initialDescendantComponentState == null)
489 {
490 // Create a template that can be used to initialise any row
491 // that we haven't visited before, ie a "saved state" that can
492 // be pushed to the "restoreState" method of all the child
493 // components to set them up to represent a clean row.
494 _initialDescendantComponentState = saveDescendantComponentStates(this, false, false);
495 }
496 }
497 else
498 {
499 // If no initial component state, there are no EditableValueHolder instances,
500 // and that means there is no state to be saved for the current row, so we can
501 // skip row state saving code safely.
502 if (_initialDescendantComponentState != null)
503 {
504 // We are currently positioned on some row, and are about to
505 // move off it, so save the (partial) state of the components
506 // representing the current row. Later if this row is revisited
507 // then we can restore this state.
508 Collection<Object[]> savedRowState = saveDescendantComponentStates(this, false, false);
509 if (savedRowState != null)
510 {
511 _rowStates.put(getClientId(facesContext), savedRowState);
512 }
513 }
514 }
515
516 _rowIndex = rowIndex;
517
518 DataModel dataModel = getDataModel();
519 dataModel.setRowIndex(rowIndex);
520
521 String var = _var;
522 if (rowIndex == -1)
523 {
524 if (var != null)
525 {
526 facesContext.getExternalContext().getRequestMap().remove(var);
527 }
528 }
529 else
530 {
531 if (var != null)
532 {
533 if (isRowAvailable())
534 {
535 Object rowData = dataModel.getRowData();
536 facesContext.getExternalContext().getRequestMap().put(var,
537 rowData);
538 }
539 else
540 {
541 facesContext.getExternalContext().getRequestMap().remove(
542 var);
543 }
544 }
545 }
546
547 if (_rowIndex == -1)
548 {
549 // reset components to initial state
550 // If no initial state, skip row restore state code
551 if (_initialDescendantComponentState != null)
552 {
553 restoreDescendantComponentStates(this, false, _initialDescendantComponentState, false);
554 }
555 else
556 {
557 restoreDescendantComponentWithoutRestoreState(this, false, false);
558 }
559 }
560 else
561 {
562 Object rowState = _rowStates.get(getClientId(facesContext));
563 if (rowState == null)
564 {
565 // We haven't been positioned on this row before, so just
566 // configure the child components of this component with
567 // the standard "initial" state
568 // If no initial state, skip row restore state code
569 if (_initialDescendantComponentState != null)
570 {
571 restoreDescendantComponentStates(this, false, _initialDescendantComponentState, false);
572 }
573 else
574 {
575 restoreDescendantComponentWithoutRestoreState(this, false, false);
576 }
577 }
578 else
579 {
580 // We have been positioned on this row before, so configure
581 // the child components of this component with the (partial)
582 // state that was previously saved. Fields not in the
583 // partial saved state are left with their original values.
584 restoreDescendantComponentStates(this, false, rowState, false);
585 }
586 }
587 }
588
589 /**
590 * Overwrite the state of the child components of this component with data previously saved by method
591 * saveDescendantComponentStates.
592 * <p>
593 * The saved state info only covers those fields that are expected to vary between rows of a table. Other fields are
594 * not modified.
595 */
596 @SuppressWarnings("unchecked")
597 private void restoreDescendantComponentStates(UIComponent parent, boolean iterateFacets, Object state,
598 boolean restoreChildFacets)
599 {
600 int descendantStateIndex = -1;
601 List<? extends Object[]> stateCollection = null;
602
603 if (iterateFacets && parent.getFacetCount() > 0)
604 {
605 Iterator<UIComponent> childIterator = parent.getFacets().values().iterator();
606
607 while (childIterator.hasNext())
608 {
609 UIComponent component = childIterator.next();
610
611 // reset the client id (see spec 3.1.6)
612 component.setId(component.getId());
613 if (!component.isTransient())
614 {
615 if (descendantStateIndex == -1)
616 {
617 stateCollection = ((List<? extends Object[]>) state);
618 descendantStateIndex = stateCollection.isEmpty() ? -1 : 0;
619 }
620
621 if (descendantStateIndex != -1 && descendantStateIndex < stateCollection.size())
622 {
623 Object[] object = stateCollection.get(descendantStateIndex);
624 if (object[0] != null && component instanceof EditableValueHolder)
625 {
626 ((EditableValueHolderState) object[0]).restoreState((EditableValueHolder) component);
627 }
628 // If there is descendant state to restore, call it recursively, otherwise
629 // it is safe to skip iteration.
630 if (object[1] != null)
631 {
632 restoreDescendantComponentStates(component, restoreChildFacets, object[1], true);
633 }
634 else
635 {
636 restoreDescendantComponentWithoutRestoreState(component, restoreChildFacets, true);
637 }
638 }
639 else
640 {
641 restoreDescendantComponentWithoutRestoreState(component, restoreChildFacets, true);
642 }
643 descendantStateIndex++;
644 }
645 }
646 }
647
648 if (parent.getChildCount() > 0)
649 {
650 for (int i = 0; i < parent.getChildCount(); i++)
651 {
652 UIComponent component = parent.getChildren().get(i);
653
654 // reset the client id (see spec 3.1.6)
655 component.setId(component.getId());
656 if (!component.isTransient())
657 {
658 if (descendantStateIndex == -1)
659 {
660 stateCollection = ((List<? extends Object[]>) state);
661 descendantStateIndex = stateCollection.isEmpty() ? -1 : 0;
662 }
663
664 if (descendantStateIndex != -1 && descendantStateIndex < stateCollection.size())
665 {
666 Object[] object = stateCollection.get(descendantStateIndex);
667 if (object[0] != null && component instanceof EditableValueHolder)
668 {
669 ((EditableValueHolderState) object[0]).restoreState((EditableValueHolder) component);
670 }
671 // If there is descendant state to restore, call it recursively, otherwise
672 // it is safe to skip iteration.
673 if (object[1] != null)
674 {
675 restoreDescendantComponentStates(component, restoreChildFacets, object[1], true);
676 }
677 else
678 {
679 restoreDescendantComponentWithoutRestoreState(component, restoreChildFacets, true);
680 }
681 }
682 else
683 {
684 restoreDescendantComponentWithoutRestoreState(component, restoreChildFacets, true);
685 }
686 descendantStateIndex++;
687 }
688 }
689 }
690 }
691
692 /**
693 * Just call component.setId(component.getId()) to reset all client ids and
694 * ensure they will be calculated for the current row, but do not waste time
695 * dealing with row state code.
696 *
697 * @param parent
698 * @param iterateFacets
699 * @param restoreChildFacets
700 */
701 private void restoreDescendantComponentWithoutRestoreState(UIComponent parent, boolean iterateFacets, boolean restoreChildFacets)
702 {
703 if (iterateFacets && parent.getFacetCount() > 0)
704 {
705 Iterator<UIComponent> childIterator = parent.getFacets().values().iterator();
706
707 while (childIterator.hasNext())
708 {
709 UIComponent component = childIterator.next();
710
711 // reset the client id (see spec 3.1.6)
712 component.setId(component.getId());
713 if (!component.isTransient())
714 {
715 restoreDescendantComponentWithoutRestoreState(component, restoreChildFacets, true);
716 }
717 }
718 }
719
720 if (parent.getChildCount() > 0)
721 {
722 for (int i = 0; i < parent.getChildCount(); i++)
723 {
724 UIComponent component = parent.getChildren().get(i);
725
726 // reset the client id (see spec 3.1.6)
727 component.setId(component.getId());
728 if (!component.isTransient())
729 {
730 restoreDescendantComponentWithoutRestoreState(component, restoreChildFacets, true);
731 }
732 }
733 }
734 }
735
736 /**
737 * Walk the tree of child components of this UIData, saving the parts of their state that can vary between rows.
738 * <p>
739 * This is very similar to the process that occurs for normal components when the view is serialized. Transient
740 * components are skipped (no state is saved for them).
741 * <p>
742 * If there are no children then null is returned. If there are one or more children, and all children are transient
743 * then an empty collection is returned; this will happen whenever a table contains only read-only components.
744 * <p>
745 * Otherwise a collection is returned which contains an object for every non-transient child component; that object
746 * may itself contain a collection of the state of that child's child components.
747 */
748 private Collection<Object[]> saveDescendantComponentStates(UIComponent parent, boolean iterateFacets,
749 boolean saveChildFacets)
750 {
751 Collection<Object[]> childStates = null;
752 // Index to indicate how many components has been passed without state to save.
753 int childEmptyIndex = 0;
754 int totalChildCount = 0;
755
756 if (iterateFacets && parent.getFacetCount() > 0)
757 {
758 Iterator<UIComponent> childIterator = parent.getFacets().values().iterator();
759
760 while (childIterator.hasNext())
761 {
762 UIComponent child = childIterator.next();
763 if (!child.isTransient())
764 {
765 // Add an entry to the collection, being an array of two
766 // elements. The first element is the state of the children
767 // of this component; the second is the state of the current
768 // child itself.
769
770 if (child instanceof EditableValueHolder)
771 {
772 if (childStates == null)
773 {
774 childStates = new ArrayList<Object[]>(
775 parent.getFacetCount()
776 + parent.getChildCount()
777 - totalChildCount
778 + childEmptyIndex);
779 for (int ci = 0; ci < childEmptyIndex; ci++)
780 {
781 childStates.add(LEAF_NO_STATE);
782 }
783 }
784
785 childStates.add(child.getChildCount() > 0 ?
786 new Object[]{new EditableValueHolderState((EditableValueHolder) child),
787 saveDescendantComponentStates(child, saveChildFacets, true)} :
788 new Object[]{new EditableValueHolderState((EditableValueHolder) child),
789 null});
790 }
791 else if (child.getChildCount() > 0 || (saveChildFacets && child.getFacetCount() > 0))
792 {
793 Object descendantSavedState = saveDescendantComponentStates(child, saveChildFacets, true);
794
795 if (descendantSavedState == null)
796 {
797 if (childStates == null)
798 {
799 childEmptyIndex++;
800 }
801 else
802 {
803 childStates.add(LEAF_NO_STATE);
804 }
805 }
806 else
807 {
808 if (childStates == null)
809 {
810 childStates = new ArrayList<Object[]>(
811 parent.getFacetCount()
812 + parent.getChildCount()
813 - totalChildCount
814 + childEmptyIndex);
815 for (int ci = 0; ci < childEmptyIndex; ci++)
816 {
817 childStates.add(LEAF_NO_STATE);
818 }
819 }
820 childStates.add(new Object[]{null, descendantSavedState});
821 }
822 }
823 else
824 {
825 if (childStates == null)
826 {
827 childEmptyIndex++;
828 }
829 else
830 {
831 childStates.add(LEAF_NO_STATE);
832 }
833 }
834 }
835 totalChildCount++;
836 }
837 }
838
839 if (parent.getChildCount() > 0)
840 {
841 for (int i = 0; i < parent.getChildCount(); i++)
842 {
843 UIComponent child = parent.getChildren().get(i);
844 if (!child.isTransient())
845 {
846 // Add an entry to the collection, being an array of two
847 // elements. The first element is the state of the children
848 // of this component; the second is the state of the current
849 // child itself.
850
851 if (child instanceof EditableValueHolder)
852 {
853 if (childStates == null)
854 {
855 childStates = new ArrayList<Object[]>(
856 parent.getFacetCount()
857 + parent.getChildCount()
858 - totalChildCount
859 + childEmptyIndex);
860 for (int ci = 0; ci < childEmptyIndex; ci++)
861 {
862 childStates.add(LEAF_NO_STATE);
863 }
864 }
865
866 childStates.add(child.getChildCount() > 0 ?
867 new Object[]{new EditableValueHolderState((EditableValueHolder) child),
868 saveDescendantComponentStates(child, saveChildFacets, true)} :
869 new Object[]{new EditableValueHolderState((EditableValueHolder) child),
870 null});
871 }
872 else if (child.getChildCount() > 0 || (saveChildFacets && child.getFacetCount() > 0))
873 {
874 Object descendantSavedState = saveDescendantComponentStates(child, saveChildFacets, true);
875
876 if (descendantSavedState == null)
877 {
878 if (childStates == null)
879 {
880 childEmptyIndex++;
881 }
882 else
883 {
884 childStates.add(LEAF_NO_STATE);
885 }
886 }
887 else
888 {
889 if (childStates == null)
890 {
891 childStates = new ArrayList<Object[]>(
892 parent.getFacetCount()
893 + parent.getChildCount()
894 - totalChildCount
895 + childEmptyIndex);
896 for (int ci = 0; ci < childEmptyIndex; ci++)
897 {
898 childStates.add(LEAF_NO_STATE);
899 }
900 }
901 childStates.add(new Object[]{null, descendantSavedState});
902 }
903 }
904 else
905 {
906 if (childStates == null)
907 {
908 childEmptyIndex++;
909 }
910 else
911 {
912 childStates.add(LEAF_NO_STATE);
913 }
914 }
915 }
916 totalChildCount++;
917 }
918 }
919
920 return childStates;
921 }
922
923 @Override
924 public void setValueExpression(String name, ValueExpression binding) {
925 if (name == null)
926 {
927 throw new NullPointerException("name");
928 }
929 else if (name.equals("value"))
930 {
931 _dataModelMap.clear();
932 }
933 else if (name.equals("rowIndex"))
934 {
935 throw new IllegalArgumentException("name " + name);
936 }
937 super.setValueExpression(name, binding);
938 }
939
940 @Override
941 public String getClientId(FacesContext context)
942 {
943 String clientId = super.getClientId(context);
944 int rowIndex = getRowIndex();
945 if (rowIndex == -1)
946 {
947 return clientId;
948 }
949
950 StringBuilder bld = __getSharedStringBuilder();
951 return bld.append(clientId).append(NamingContainer.SEPARATOR_CHAR).append(rowIndex).toString();
952 }
953
954 /**
955 * Modify events queued for any child components so that the
956 * UIData state will be correctly configured before the event's
957 * listeners are executed.
958 * <p>
959 * Child components or their renderers may register events against
960 * those child components. When the listener for that event is
961 * eventually invoked, it may expect the uidata's rowData and
962 * rowIndex to be referring to the same object that caused the
963 * event to fire.
964 * <p>
965 * The original queueEvent call against the child component has been
966 * forwarded up the chain of ancestors in the standard way, making
967 * it possible here to wrap the event in a new event whose source
968 * is <i>this</i> component, not the original one. When the event
969 * finally is executed, this component's broadcast method is invoked,
970 * which ensures that the UIData is set to be at the correct row
971 * before executing the original event.
972 */
973 @Override
974 public void queueEvent(FacesEvent event)
975 {
976 super.queueEvent(new FacesEventWrapper(event, getRowIndex(), this));
977 }
978
979 /**
980 * Ensure that before the event's listeners are invoked this UIData
981 * component's "current row" is set to the row associated with the event.
982 * <p>
983 * See queueEvent for more details.
984 */
985 @Override
986 public void broadcast(FacesEvent event) throws AbortProcessingException
987 {
988 if (event instanceof FacesEventWrapper)
989 {
990 FacesEvent originalEvent = ((FacesEventWrapper) event)
991 .getWrappedFacesEvent();
992 int eventRowIndex = ((FacesEventWrapper) event).getRowIndex();
993 int currentRowIndex = getRowIndex();
994 setRowIndex(eventRowIndex);
995 try
996 {
997 originalEvent.getComponent().broadcast(originalEvent);
998 }
999 finally
1000 {
1001 setRowIndex(currentRowIndex);
1002 }
1003 }
1004 else
1005 {
1006 super.broadcast(event);
1007 }
1008 }
1009
1010 /**
1011 * Perform necessary actions when rendering of this component starts,
1012 * before delegating to the inherited implementation which calls the
1013 * associated renderer's encodeBegin method.
1014 */
1015 @Override
1016 public void encodeBegin(FacesContext context) throws IOException
1017 {
1018 _initialDescendantComponentState = null;
1019 if (_isValidChilds && !hasErrorMessages(context))
1020 {
1021 // Clear the data model so that when rendering code calls
1022 // getDataModel a fresh model is fetched from the backing
1023 // bean via the value-binding.
1024 _dataModelMap.clear();
1025
1026 // When the data model is cleared it is also necessary to
1027 // clear the saved row state, as there is an implicit 1:1
1028 // relation between objects in the _rowStates and the
1029 // corresponding DataModel element.
1030 _rowStates.clear();
1031 }
1032 super.encodeBegin(context);
1033 }
1034
1035 private boolean hasErrorMessages(FacesContext context)
1036 {
1037 for(Iterator iter = context.getMessages(); iter.hasNext();)
1038 {
1039 FacesMessage message = (FacesMessage) iter.next();
1040 if(FacesMessage.SEVERITY_ERROR.compareTo(message.getSeverity()) <= 0)
1041 {
1042 return true;
1043 }
1044 }
1045 return false;
1046 }
1047
1048 /**
1049 * @see javax.faces.component.UIComponentBase#encodeEnd(javax.faces.context.FacesContext)
1050 */
1051 @Override
1052 public void encodeEnd(FacesContext context) throws IOException
1053 {
1054 try
1055 {
1056 setCachedFacesContext(context);
1057 setRowIndex(-1);
1058 }
1059 finally
1060 {
1061 setCachedFacesContext(null);
1062 }
1063 super.encodeEnd(context);
1064 }
1065
1066 @Override
1067 public void processDecodes(FacesContext context)
1068 {
1069 if (context == null)
1070 {
1071 throw new NullPointerException("context");
1072 }
1073 try
1074 {
1075 setCachedFacesContext(context);
1076 if (!isRendered())
1077 {
1078 return;
1079 }
1080 setRowIndex(-1);
1081 processFacets(context, PROCESS_DECODES);
1082 processColumnFacets(context, PROCESS_DECODES);
1083 processColumnChildren(context, PROCESS_DECODES);
1084 setRowIndex(-1);
1085 try
1086 {
1087 decode(context);
1088 }
1089 catch (RuntimeException e)
1090 {
1091 context.renderResponse();
1092 throw e;
1093 }
1094 }
1095 finally
1096 {
1097 setCachedFacesContext(null);
1098 }
1099 }
1100
1101 @Override
1102 public void processValidators(FacesContext context)
1103 {
1104 if (context == null)
1105 {
1106 throw new NullPointerException("context");
1107 }
1108
1109 try
1110 {
1111 setCachedFacesContext(context);
1112 if (!isRendered())
1113 {
1114 return;
1115 }
1116
1117 setRowIndex(-1);
1118 processFacets(context, PROCESS_VALIDATORS);
1119 processColumnFacets(context, PROCESS_VALIDATORS);
1120 processColumnChildren(context, PROCESS_VALIDATORS);
1121 setRowIndex(-1);
1122
1123 // check if an validation error forces the render response for our data
1124 if (context.getRenderResponse())
1125 {
1126 _isValidChilds = false;
1127 }
1128 }
1129 finally
1130 {
1131 setCachedFacesContext(null);
1132 }
1133 }
1134
1135 @Override
1136 public void processUpdates(FacesContext context)
1137 {
1138 if (context == null)
1139 {
1140 throw new NullPointerException("context");
1141 }
1142 try
1143 {
1144 setCachedFacesContext(context);
1145 if (!isRendered())
1146 {
1147 return;
1148 }
1149 setRowIndex(-1);
1150 processFacets(context, PROCESS_UPDATES);
1151 processColumnFacets(context, PROCESS_UPDATES);
1152 processColumnChildren(context, PROCESS_UPDATES);
1153 setRowIndex(-1);
1154
1155 if (context.getRenderResponse())
1156 {
1157 _isValidChilds = false;
1158 }
1159 }
1160 finally
1161 {
1162 setCachedFacesContext(null);
1163 }
1164 }
1165
1166 private void processFacets(FacesContext context, int processAction)
1167 {
1168 for (Iterator it = getFacets().values().iterator(); it.hasNext();)
1169 {
1170 UIComponent facet = (UIComponent) it.next();
1171 process(context, facet, processAction);
1172 }
1173 }
1174
1175 /**
1176 * Invoke the specified phase on all facets of all UIColumn children
1177 * of this component. Note that no methods are called on the UIColumn
1178 * child objects themselves.
1179 *
1180 * @param context is the current faces context.
1181 * @param processAction specifies a JSF phase: decode, validate or update.
1182 */
1183 private void processColumnFacets(FacesContext context, int processAction)
1184 {
1185 for (Iterator childIter = getChildren().iterator(); childIter.hasNext();)
1186 {
1187 UIComponent child = (UIComponent) childIter.next();
1188 if (child instanceof UIColumn)
1189 {
1190 if (!child.isRendered())
1191 {
1192 //Column is not visible
1193 continue;
1194 }
1195 for (Iterator facetsIter = child.getFacets().values()
1196 .iterator(); facetsIter.hasNext();)
1197 {
1198 UIComponent facet = (UIComponent) facetsIter.next();
1199 process(context, facet, processAction);
1200 }
1201 }
1202 }
1203 }
1204
1205 /**
1206 * Invoke the specified phase on all non-facet children of all UIColumn
1207 * children of this component. Note that no methods are called on the
1208 * UIColumn child objects themselves.
1209 *
1210 * @param context is the current faces context.
1211 * @param processAction specifies a JSF phase: decode, validate or update.
1212 */
1213 private void processColumnChildren(FacesContext context, int processAction)
1214 {
1215 int first = getFirst();
1216 int rows = getRows();
1217 int last;
1218 if (rows == 0)
1219 {
1220 last = getRowCount();
1221 }
1222 else
1223 {
1224 last = first + rows;
1225 }
1226 for (int rowIndex = first; last==-1 || rowIndex < last; rowIndex++)
1227 {
1228 setRowIndex(rowIndex);
1229
1230 //scrolled past the last row
1231 if (!isRowAvailable())
1232 {
1233 break;
1234 }
1235
1236 for (Iterator it = getChildren().iterator(); it.hasNext();)
1237 {
1238 UIComponent child = (UIComponent) it.next();
1239 if (child instanceof UIColumn)
1240 {
1241 if (!child.isRendered())
1242 {
1243 //Column is not visible
1244 continue;
1245 }
1246 for (Iterator columnChildIter = child.getChildren()
1247 .iterator(); columnChildIter.hasNext();)
1248 {
1249 UIComponent columnChild = (UIComponent) columnChildIter
1250 .next();
1251 process(context, columnChild, processAction);
1252 }
1253 }
1254 }
1255 }
1256 }
1257
1258 private void process(FacesContext context, UIComponent component,
1259 int processAction)
1260 {
1261 switch (processAction)
1262 {
1263 case PROCESS_DECODES:
1264 component.processDecodes(context);
1265 break;
1266 case PROCESS_VALIDATORS:
1267 component.processValidators(context);
1268 break;
1269 case PROCESS_UPDATES:
1270 component.processUpdates(context);
1271 break;
1272 }
1273 }
1274
1275 /**
1276 * Return the datamodel for this table, potentially fetching the data from
1277 * a backing bean via a value-binding if this is the first time this method
1278 * has been called.
1279 * <p>
1280 * This is complicated by the fact that this table may be nested within
1281 * another table. In this case a different datamodel should be fetched
1282 * for each row. When nested within a parent table, the parent reference
1283 * won't change but parent.getClientId() will, as the suffix changes
1284 * depending upon the current row index. A map object on this component
1285 * is therefore used to cache the datamodel for each row of the table.
1286 * In the normal case where this table is not nested inside a component
1287 * that changes its id (like a table does) then this map only ever has
1288 * one entry.
1289 */
1290 protected DataModel getDataModel()
1291 {
1292 DataModel dataModel;
1293 String clientID = "";
1294
1295 UIComponent parent = getParent();
1296 if (parent != null)
1297 {
1298 clientID = parent.getClientId(getFacesContext());
1299 }
1300 dataModel = (DataModel) _dataModelMap.get(clientID);
1301 if (dataModel == null)
1302 {
1303 dataModel = createDataModel();
1304 _dataModelMap.put(clientID, dataModel);
1305 }
1306 return dataModel;
1307 }
1308
1309 protected void setDataModel(DataModel dataModel)
1310 {
1311 throw new UnsupportedOperationException(
1312 "this method is here only to maintain binary compatibility w/ the RI");
1313 }
1314
1315 /**
1316 * Evaluate this object's value property and convert the result into a
1317 * DataModel. Normally this object's value property will be a value-binding
1318 * which will cause the value to be fetched from some backing bean.
1319 * <p>
1320 * The result of fetching the value may be a DataModel object, in which
1321 * case that object is returned directly. If the value is of type
1322 * List, Array, ResultSet, Result, other object or null then an appropriate
1323 * wrapper is created and returned.
1324 * <p>
1325 * Null is never returned by this method.
1326 */
1327 private DataModel createDataModel()
1328 {
1329 Object value = getValue();
1330
1331 if (value == null)
1332 {
1333 return EMPTY_DATA_MODEL;
1334 }
1335 else if (value instanceof DataModel)
1336 {
1337 return (DataModel) value;
1338 }
1339 else if (value instanceof List)
1340 {
1341 return new ListDataModel((List) value);
1342 }
1343 else if (OBJECT_ARRAY_CLASS.isAssignableFrom(value.getClass()))
1344 {
1345 return new ArrayDataModel((Object[]) value);
1346 }
1347 else if (value instanceof ResultSet)
1348 {
1349 return new ResultSetDataModel((ResultSet) value);
1350 }
1351 else if (value instanceof Result)
1352 {
1353 return new ResultDataModel((Result) value);
1354 }
1355 else
1356 {
1357 return new ScalarDataModel(value);
1358 }
1359 }
1360
1361 /**
1362 * An EL expression that specifies the data model that backs this table.
1363 * <p>
1364 * The value referenced by the EL expression can be of any type.
1365 * </p>
1366 * <ul>
1367 * <li>A value of type DataModel is used directly.</li>
1368 * <li>Array-like parameters of type array-of-Object, java.util.List, java.sql.ResultSet
1369 * or javax.servlet.jsp.jstl.sql.Result are wrapped in a corresponding DataModel that
1370 * knows how to iterate over the elements.</li>
1371 * <li>Other values are wrapped in a DataModel as a single row.</li>
1372 * </ul>
1373 * <p>
1374 * Note in particular that unordered collections, eg Set are not supported. Therefore if the
1375 * value expression references such an object then the table will be considered to contain just
1376 * one element - the collection itself.
1377 * </p>
1378 */
1379 @JSFProperty
1380 public Object getValue()
1381 {
1382 if (_value != null)
1383 {
1384 return _value;
1385 }
1386 ValueExpression expression = getValueExpression("value");
1387 if (expression != null)
1388 {
1389 return expression.getValue(getFacesContext().getELContext());
1390 }
1391 return null;
1392 }
1393
1394 public void setValue(Object value)
1395 {
1396 _value = value;
1397 _dataModelMap.clear();
1398 _rowStates.clear();
1399 _isValidChilds = true;
1400 }
1401
1402 /**
1403 * Defines the index of the first row to be displayed, starting from 0.
1404 */
1405 @JSFProperty
1406 public int getFirst()
1407 {
1408 if (_firstSet)
1409 {
1410 return _first;
1411 }
1412 ValueExpression expression = getValueExpression("first");
1413 if (expression != null)
1414 {
1415 return (Integer)expression.getValue(getFacesContext().getELContext());
1416 }
1417 return 0;
1418 }
1419
1420 public void setFirst(int first)
1421 {
1422 if (first < 0)
1423 {
1424 throw new IllegalArgumentException("Illegal value for first row: " + first);
1425 }
1426 _first = first;
1427 _firstSet=true;
1428 }
1429
1430 /**
1431 * Defines the maximum number of rows of data to be displayed.
1432 * <p>
1433 * Specify zero to display all rows from the "first" row to the end
1434 * of available data.
1435 * </p>
1436 */
1437 @JSFProperty
1438 public int getRows()
1439 {
1440 if (_rowsSet)
1441 {
1442 return _rows;
1443 }
1444 ValueExpression expression = getValueExpression("rows");
1445 if (expression != null)
1446 {
1447 return (Integer)expression.getValue(getFacesContext().getELContext());
1448 }
1449 return 0;
1450 }
1451
1452 /**
1453 * Set the maximum number of rows displayed in the table.
1454 */
1455 public void setRows(int rows)
1456 {
1457 if (rows < 0)
1458 {
1459 throw new IllegalArgumentException("rows: " + rows);
1460 }
1461 _rows = rows;
1462 _rowsSet = true;
1463 }
1464
1465 /**
1466 * Defines the name of the request-scope variable that will hold the current row during iteration.
1467 * <p>
1468 * During rendering of child components of this UIData, the variable with this name can be read to
1469 * learn what the "rowData" object for the row currently being rendered is.
1470 * </p>
1471 * <p>
1472 * This value must be a static value, ie an EL expression is not permitted.
1473 * </p>
1474 */
1475 @JSFProperty(literalOnly = true)
1476 public String getVar()
1477 {
1478 return _var;
1479 }
1480
1481 public void setVar(String var)
1482 {
1483 this._var = var;
1484 }
1485
1486
1487 @Override
1488 public Object saveState(FacesContext facesContext)
1489 {
1490 Object[] values = new Object[7];
1491 values[0] = super.saveState(facesContext);
1492 values[1] = _value;
1493 values[2] = _var;
1494 values[3] = _rows;
1495 values[4] = _rowsSet;
1496 values[5] = _first;
1497 values[6] = _firstSet;
1498
1499 return values;
1500 }
1501
1502 @Override
1503 public void restoreState(FacesContext facesContext, Object state)
1504 {
1505 Object[] values = (Object[])state;
1506 super.restoreState(facesContext,values[0]);
1507 _value = values[1];
1508 _var = (String)values[2];
1509 _rows = (Integer)values[3];
1510 _rowsSet = (Boolean)values[4];
1511 _first = (Integer)values[5];
1512 _firstSet = (Boolean)values[6];
1513 }
1514
1515 @Override
1516 public String getFamily()
1517 {
1518 return COMPONENT_FAMILY;
1519 }
1520 }