001: /*
002: * Version: MPL 1.1/GPL 2.0/LGPL 2.1
003: *
004: * "The contents of this file are subject to the Mozilla Public License
005: * Version 1.1 (the "License"); you may not use this file except in
006: * compliance with the License. You may obtain a copy of the License at
007: * http://www.mozilla.org/MPL/
008: *
009: * Software distributed under the License is distributed on an "AS IS"
010: * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
011: * License for the specific language governing rights and limitations under
012: * the License.
013: *
014: * The Original Code is ICEfaces 1.5 open source software code, released
015: * November 5, 2006. The Initial Developer of the Original Code is ICEsoft
016: * Technologies Canada, Corp. Portions created by ICEsoft are Copyright (C)
017: * 2004-2006 ICEsoft Technologies Canada, Corp. All Rights Reserved.
018: *
019: * Contributor(s): _____________________.
020: *
021: * Alternatively, the contents of this file may be used under the terms of
022: * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"
023: * License), in which case the provisions of the LGPL License are
024: * applicable instead of those above. If you wish to allow use of your
025: * version of this file only under the terms of the LGPL License and not to
026: * allow others to use your version of this file under the MPL, indicate
027: * your decision by deleting the provisions above and replace them with
028: * the notice and other provisions required by the LGPL License. If you do
029: * not delete the provisions above, a recipient may use your version of
030: * this file under either the MPL or the LGPL License."
031: *
032: */
033:
034: package com.icesoft.faces.component.panelseries;
035:
036: import com.icesoft.faces.component.tree.TreeDataModel;
037:
038: import javax.faces.application.FacesMessage;
039: import javax.faces.component.EditableValueHolder;
040: import javax.faces.component.NamingContainer;
041: import javax.faces.component.UIComponent;
042: import javax.faces.component.UIData;
043: import javax.faces.component.html.HtmlDataTable;
044: import javax.faces.component.html.HtmlForm;
045: import javax.faces.context.FacesContext;
046: import javax.faces.el.ValueBinding;
047: import javax.faces.event.AbortProcessingException;
048: import javax.faces.event.FacesEvent;
049: import javax.faces.event.FacesListener;
050: import javax.faces.event.PhaseId;
051: import javax.faces.model.ArrayDataModel;
052: import javax.faces.model.DataModel;
053: import javax.faces.model.ListDataModel;
054: import javax.faces.model.ResultDataModel;
055: import javax.faces.model.ResultSetDataModel;
056: import javax.faces.model.ScalarDataModel;
057: import javax.servlet.jsp.jstl.sql.Result;
058: import javax.swing.tree.TreeModel;
059: import java.io.IOException;
060: import java.io.Serializable;
061: import java.sql.ResultSet;
062: import java.util.Collections;
063: import java.util.HashMap;
064: import java.util.Iterator;
065: import java.util.List;
066: import java.util.Map;
067:
068: /**
069: * This is an extended version of UIData, which allows any UISeries type of
070: * component to have any type of children, it is not restricted to use the
071: * column component as a first child.
072: */
073: public class UISeries extends HtmlDataTable {
074: public static final String COMPONENT_TYPE = "com.icesoft.faces.series";
075: public static final String RENDERER_TYPE = "com.icesoft.faces.seriesRenderer";
076: protected transient DataModel dataModel = null;
077: private int rowIndex = -1;
078: protected Map savedChildren = new HashMap();
079: private String var = null;
080:
081: public UISeries() {
082: super ();
083: setRendererType(RENDERER_TYPE);
084: }
085:
086: /**
087: * @see javax.faces.component.UIData#isRowAvailable()
088: */
089: public boolean isRowAvailable() {
090: return (getDataModel().isRowAvailable());
091: }
092:
093: /**
094: * @see javax.faces.component.UIData#getRowCount()
095: */
096: public int getRowCount() {
097: return (getDataModel().getRowCount());
098: }
099:
100: /**
101: * @see javax.faces.component.UIData#getRowData()
102: */
103: public Object getRowData() {
104: return (getDataModel().getRowData());
105: }
106:
107: /**
108: * @see javax.faces.component.UIData#getRowIndex()
109: */
110: public int getRowIndex() {
111: return (this .rowIndex);
112: }
113:
114: public void setRowIndex(int rowIndex) {
115: FacesContext facesContext = getFacesContext();
116: // Save current state for the previous row index
117: saveChildrenState(facesContext);
118: // remove or load the current row data as a request scope attribute
119: processCurrentRowData(facesContext, rowIndex);
120: // Reset current state information for the new row index
121: restoreChildrenState(facesContext);
122: }
123:
124: private void processCurrentRowData(FacesContext facesContext,
125: int rowIndex) {
126: this .rowIndex = rowIndex;
127: DataModel model = getDataModel();
128: model.setRowIndex(rowIndex);
129:
130: if (var != null) {
131: Map requestMap = facesContext.getExternalContext()
132: .getRequestMap();
133: if (rowIndex == -1) {
134: loadRowToRequestMap(requestMap, false);
135: } else if (isRowAvailable()) {
136: loadRowToRequestMap(requestMap, true);
137: } else {
138: loadRowToRequestMap(requestMap, false);
139: }
140: }
141: }
142:
143: private void loadRowToRequestMap(Map requestMap, boolean loadRow) {
144: if (loadRow) {
145: requestMap.put(var, getRowData());
146: } else {
147: requestMap.remove(var);
148: }
149: }
150:
151: /**
152: * @see javax.faces.component.UIData#getVar()
153: */
154: public String getVar() {
155: return (this .var);
156: }
157:
158: /**
159: * @see javax.faces.component.UIData#setVar(Stringvar)
160: */
161: public void setVar(String var) {
162: this .var = var;
163: }
164:
165: /**
166: * @see javax.faces.component.UIData#setValue(Objectvalue)
167: */
168: public void setValue(Object value) {
169: this .dataModel = null;
170: super .setValue(value);
171: }
172:
173: public void setValueBinding(String name, ValueBinding binding) {
174: if ("value".equals(name)) {
175: this .dataModel = null;
176: } else if ("var".equals(name) || "rowIndex".equals(name)) {
177: throw new IllegalArgumentException();
178: }
179: super .setValueBinding(name, binding);
180: }
181:
182: /**
183: * @see javax.faces.component.UIData#getClientId(FacesContextcontext)
184: */
185: public String getClientId(FacesContext context) {
186: if (context == null) {
187: throw new NullPointerException();
188: }
189: String baseClientId = super .getClientId(context);
190: if (getRowIndex() >= 0) {
191: //this extra if is to produce the same ids among myfaces and sunri
192: //myfaces uses the getRowIndex() and SunRI directly using the rowIndex
193: //variable inside its getClientId()
194: if (!baseClientId.endsWith(""
195: + NamingContainer.SEPARATOR_CHAR + getRowIndex())) {
196: return (baseClientId + NamingContainer.SEPARATOR_CHAR + getRowIndex());
197: }
198: return (baseClientId);
199: } else {
200: return (baseClientId);
201: }
202: }
203:
204: /**
205: * @see javax.faces.component.UIData#queueEvent(FacesEventevent)
206: */
207: public void queueEvent(FacesEvent event) {
208: super .queueEvent(new RowEvent(this , event, getRowIndex()));
209: }
210:
211: /**
212: * @see javax.faces.component.UIData#broadcast(FacesEventevent)
213: */
214: public void broadcast(FacesEvent event)
215: throws AbortProcessingException {
216: if (!(event instanceof RowEvent)) {
217: super .broadcast(event);
218: return;
219: }
220:
221: // fire row specific event
222: ((RowEvent) event).broadcast();
223: return;
224: }
225:
226: /**
227: * @see javax.faces.component.UIData#encodeBegin(FacesContextcontext)
228: */
229: public void encodeBegin(FacesContext context) throws IOException {
230: dataModel = null;
231: if (!keepSaved(context)) {
232: savedChildren = new HashMap();
233: }
234: super .encodeBegin(context);
235: }
236:
237: public void processDecodes(FacesContext context) {
238: if (context == null) {
239: throw new NullPointerException();
240: }
241: if (!isRendered()) {
242: return;
243: }
244:
245: dataModel = null;
246: if (null == savedChildren || !keepSaved(context)) {
247: savedChildren = new HashMap();
248: }
249:
250: iterate(context, PhaseId.APPLY_REQUEST_VALUES);
251: decode(context);
252: }
253:
254: public void processValidators(FacesContext context) {
255: if (context == null) {
256: throw new NullPointerException();
257: }
258: if (!isRendered()) {
259: return;
260: }
261: if (isNestedWithinUIData()) {
262: dataModel = null;
263: }
264: iterate(context, PhaseId.PROCESS_VALIDATIONS);
265: }
266:
267: public void processUpdates(FacesContext context) {
268: if (context == null) {
269: throw new NullPointerException();
270: }
271: if (!isRendered()) {
272: return;
273: }
274: if (isNestedWithinUIData()) {
275: dataModel = null;
276: }
277: iterate(context, PhaseId.UPDATE_MODEL_VALUES);
278: }
279:
280: /**
281: * <p>Return the DataModel containing the Objects that will be iterated over
282: * when this component is rendered.</p>
283: *
284: * @return
285: */
286: private DataModel getDataModel() {
287: if (null != this .dataModel) {
288: return (dataModel);
289: }
290:
291: Object currentValue = getValue();
292:
293: if (null == currentValue) {
294: this .dataModel = new ListDataModel(Collections.EMPTY_LIST);
295: } else if (currentValue instanceof DataModel) {
296: this .dataModel = (DataModel) currentValue;
297: } else if (currentValue instanceof List) {
298: this .dataModel = new ListDataModel((List) currentValue);
299: } else if (Object[].class.isAssignableFrom(currentValue
300: .getClass())) {
301: this .dataModel = new ArrayDataModel((Object[]) currentValue);
302: } else if (currentValue instanceof ResultSet) {
303: this .dataModel = new ResultSetDataModel(
304: (ResultSet) currentValue);
305: } else if (currentValue instanceof Result) {
306: this .dataModel = new ResultDataModel((Result) currentValue);
307: } else if (currentValue instanceof TreeModel) {
308: this .dataModel = new TreeDataModel((TreeModel) currentValue);
309: } else {
310: this .dataModel = new ScalarDataModel(currentValue);
311: }
312:
313: return (dataModel);
314: }
315:
316: protected void iterate(FacesContext facesContext, PhaseId phase) {
317: // clear rowIndex
318: setRowIndex(-1);
319:
320: int rowsProcessed = 0;
321: int currentRowIndex = getFirst() - 1;
322: int displayedRows = getRows();
323: // loop over dataModel processing each row once
324: while (1 == 1) {
325: // break if we have processed the number of rows requested
326: if ((displayedRows > 0)
327: && (++rowsProcessed > displayedRows)) {
328: break;
329: }
330: // process the row at currentRowIndex
331: setRowIndex(++currentRowIndex);
332: // break if we've moved past the last row
333: if (!isRowAvailable()) {
334: break;
335: }
336: // loop over children and facets
337: Iterator children = getFacetsAndChildren();
338: while (children.hasNext()) {
339: UIComponent child = (UIComponent) children.next();
340: if (phase == PhaseId.APPLY_REQUEST_VALUES) {
341: child.processDecodes(facesContext);
342: } else if (phase == PhaseId.PROCESS_VALIDATIONS) {
343: child.processValidators(facesContext);
344: } else if (phase == PhaseId.UPDATE_MODEL_VALUES) {
345: child.processUpdates(facesContext);
346: } else {
347: throw new IllegalArgumentException();
348: }
349: }
350: }
351:
352: // clear rowIndex
353: setRowIndex(-1);
354: }
355:
356: /**
357: * <p>Return true when we need to keep the child state to display error
358: * messages.</p>
359: *
360: * @param facesContext
361: * @return
362: */
363: private boolean keepSaved(FacesContext facesContext) {
364: Iterator childIds = savedChildren.keySet().iterator();
365: while (childIds.hasNext()) {
366: String childId = (String) childIds.next();
367: Iterator childMessages = facesContext.getMessages(childId);
368: while (childMessages.hasNext()) {
369: FacesMessage childMessage = (FacesMessage) childMessages
370: .next();
371: if (childMessage.getSeverity().compareTo(
372: FacesMessage.SEVERITY_ERROR) >= 0) {
373: return (true);
374: }
375: }
376: }
377: // return true if this component is nested inside a UIData
378: return (isNestedWithinUIData());
379: }
380:
381: private boolean isNestedWithinUIData() {
382: UIComponent parent = this ;
383: while (null != (parent = parent.getParent())) {
384: if (parent instanceof UIData) {
385: return true;
386: }
387: }
388: return (false);
389: }
390:
391: protected void restoreChildrenState(FacesContext facesContext) {
392: Iterator children = getFacetsAndChildren();
393: while (children.hasNext()) {
394: UIComponent child = (UIComponent) children.next();
395: restoreChildState(facesContext, child);
396: }
397: }
398:
399: protected void restoreChildState(FacesContext facesContext,
400: UIComponent component) {
401: String id = component.getId();
402: if (!isValid(id)) {
403: return;
404: }
405: component.setId(id);
406: restoreChild(facesContext, component);
407: Iterator children = component.getFacetsAndChildren();
408: while (children.hasNext()) {
409: restoreChildState(facesContext, (UIComponent) children
410: .next());
411: }
412: }
413:
414: protected void saveChild(FacesContext facesContext,
415: UIComponent component) {
416: if (component instanceof EditableValueHolder) {
417: EditableValueHolder input = (EditableValueHolder) component;
418: String clientId = component.getClientId(facesContext);
419: ChildState state = (ChildState) savedChildren.get(clientId);
420: if (state == null) {
421: state = new ChildState();
422: savedChildren.put(clientId, state);
423: }
424: state.setValue(input.getLocalValue());
425: state.setValid(input.isValid());
426: state.setSubmittedValue(input.getSubmittedValue());
427: state.setLocalValueSet(input.isLocalValueSet());
428: } else if (component instanceof HtmlForm) {
429: String clientId = component.getClientId(facesContext);
430: Boolean isThisFormSubmitted = (Boolean) savedChildren
431: .get(clientId);
432: if (isThisFormSubmitted == null) {
433: isThisFormSubmitted = new Boolean(
434: ((HtmlForm) component).isSubmitted());
435: savedChildren.put(clientId, isThisFormSubmitted);
436: }
437: }
438: }
439:
440: protected void restoreChild(FacesContext facesContext,
441: UIComponent component) {
442: if (component instanceof EditableValueHolder) {
443: EditableValueHolder input = (EditableValueHolder) component;
444: String clientId = component.getClientId(facesContext);
445: ChildState state = (ChildState) savedChildren.get(clientId);
446: if (state == null) {
447: state = new ChildState();
448: }
449: input.setValue(state.getValue());
450: input.setValid(state.isValid());
451: input.setSubmittedValue(state.getSubmittedValue());
452: input.setLocalValueSet(state.isLocalValueSet());
453: } else if (component instanceof HtmlForm) {
454: String clientId = component.getClientId(facesContext);
455: Boolean isThisFormSubmitted = (Boolean) savedChildren
456: .get(clientId);
457: if (isThisFormSubmitted == null) {
458: isThisFormSubmitted = Boolean.FALSE;
459: }
460: ((HtmlForm) component).setSubmitted(isThisFormSubmitted
461: .booleanValue());
462: }
463: }
464:
465: protected void saveChildrenState(FacesContext facesContext) {
466: Iterator children = getFacetsAndChildren();
467: while (children.hasNext()) {
468: UIComponent child = (UIComponent) children.next();
469: saveChildState(facesContext, child);
470: }
471: }
472:
473: protected void saveChildState(FacesContext facesContext,
474: UIComponent component) {
475: saveChild(facesContext, component);
476: Iterator children = component.getFacetsAndChildren();
477: while (children.hasNext()) {
478: saveChildState(facesContext, (UIComponent) children.next());
479: }
480: }
481:
482: // Event associated with the specific rowindex
483: class RowEvent extends FacesEvent {
484: private FacesEvent event = null;
485: private int eventRowIndex = -1;
486:
487: public RowEvent(UIComponent component, FacesEvent event,
488: int eventRowIndex) {
489: super (component);
490: this .event = event;
491: this .eventRowIndex = eventRowIndex;
492: }
493:
494: public FacesEvent getFacesEvent() {
495: return (this .event);
496: }
497:
498: public boolean isAppropriateListener(FacesListener listener) {
499: return false;
500: }
501:
502: public void processListener(FacesListener listener) {
503: throw new IllegalStateException();
504: }
505:
506: public PhaseId getPhaseId() {
507: return (this .event.getPhaseId());
508: }
509:
510: public void setPhaseId(PhaseId phaseId) {
511: this .event.setPhaseId(phaseId);
512: }
513:
514: public void broadcast() {
515: int oldRowIndex = getRowIndex();
516: setRowIndex(eventRowIndex);
517: event.getComponent().broadcast(event);
518: setRowIndex(oldRowIndex);
519: }
520: }
521:
522: public Object saveState(FacesContext context) {
523: Object values[] = new Object[4];
524: values[0] = super .saveState(context);
525: values[1] = new Integer(rowIndex);
526: values[2] = savedChildren;
527: values[3] = var;
528: return (values);
529: }
530:
531: public void restoreState(FacesContext context, Object state) {
532: Object values[] = (Object[]) state;
533: super .restoreState(context, values[0]);
534: rowIndex = ((Integer) values[1]).intValue();
535: savedChildren = (Map) values[2];
536: var = (String) values[3];
537: }
538:
539: private boolean isValid(String id) {
540: if (id == null) {
541: return false;
542: }
543: if (!Character.isLetter(id.charAt(0)) && (id.charAt(0) != '_')) {
544: return false;
545: }
546: return true;
547: }
548:
549: }
550:
551: class ChildState implements Serializable {
552:
553: private Object submittedValue;
554: private boolean valid = true;
555: private Object value;
556: private boolean localValueSet;
557:
558: Object getSubmittedValue() {
559: return submittedValue;
560: }
561:
562: void setSubmittedValue(Object submittedValue) {
563: this .submittedValue = submittedValue;
564: }
565:
566: boolean isValid() {
567: return valid;
568: }
569:
570: void setValid(boolean valid) {
571: this .valid = valid;
572: }
573:
574: Object getValue() {
575: return value;
576: }
577:
578: public void setValue(Object value) {
579: this .value = value;
580: }
581:
582: boolean isLocalValueSet() {
583: return localValueSet;
584: }
585:
586: public void setLocalValueSet(boolean localValueSet) {
587: this.localValueSet = localValueSet;
588: }
589: }
|