KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > myfaces > custom > tree2 > UITreeData


1 /*
2  * Copyright 2005 The Apache Software Foundation.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */

16 package org.apache.myfaces.custom.tree2;
17
18 import javax.faces.component.UIComponentBase;
19 import javax.faces.component.NamingContainer;
20 import javax.faces.component.UIComponent;
21 import javax.faces.component.EditableValueHolder;
22 import javax.faces.context.FacesContext;
23 import javax.faces.event.AbortProcessingException;
24 import javax.faces.event.FacesEvent;
25 import javax.faces.event.PhaseId;
26 import javax.faces.event.FacesListener;
27 import javax.faces.el.ValueBinding;
28 import javax.faces.application.FacesMessage;
29
30 import java.io.Serializable JavaDoc;
31 import java.io.IOException JavaDoc;
32 import java.util.Iterator JavaDoc;
33 import java.util.Map JavaDoc;
34 import java.util.HashMap JavaDoc;
35 import java.util.List JavaDoc;
36
37
38 /**
39  * TreeData is a {@link UIComponent} that supports binding data stored in a tree represented
40  * by a {@link TreeNode} instance. During iterative processing over the tree nodes in the
41  * data model, the object for the current node is exposed as a request attribute under the key
42  * specified by the <code>var</code> property. {@link javax.faces.render.Renderer}s of this component should use
43  * the appropriate {@link Facet} to assist in rendering.
44  *
45  * @author Sean Schofield
46  * @author Hans Bergsten (Some code taken from an example in his O'Reilly JavaServer Faces book. Copied with permission)
47  * @version $Revision: 1.5 $ $Date: 2005/02/26 00:26:00 $
48  */

49 public class UITreeData extends UIComponentBase implements NamingContainer
50 {
51
52     public static final String JavaDoc COMPONENT_TYPE = "org.apache.myfaces.Tree2";
53     public static final String JavaDoc COMPONENT_FAMILY = "org.apache.myfaces.HtmlTree2"; //"javax.faces.Data";
54
private static final String JavaDoc DEFAULT_RENDERER_TYPE = "org.apache.myfaces.Tree2";
55
56     private static final int PROCESS_DECODES = 1;
57     private static final int PROCESS_VALIDATORS = 2;
58     private static final int PROCESS_UPDATES = 3;
59
60     private TreeModel _model;
61     private TreeNode _value;
62     private String JavaDoc _var;
63     private String JavaDoc _nodeId;
64     private Map JavaDoc _saved = new HashMap JavaDoc();
65
66     /**
67      * Constructor
68      */

69     public UITreeData()
70     {
71         setRendererType(DEFAULT_RENDERER_TYPE);
72     }
73
74
75     // see superclass for documentation
76
public String JavaDoc getFamily()
77     {
78         return COMPONENT_FAMILY;
79     }
80
81     // see superclass for documentation
82
public Object JavaDoc saveState(FacesContext context)
83     {
84         Object JavaDoc values[] = new Object JavaDoc[3];
85         values[0] = super.saveState(context);
86         values[1] = _value;
87         values[2] = _var;
88         return ((Object JavaDoc) (values));
89     }
90
91
92     // see superclass for documentation
93
public void restoreState(FacesContext context, Object JavaDoc state)
94     {
95         Object JavaDoc values[] = (Object JavaDoc[]) state;
96         super.restoreState(context, values[0]);
97
98         _value = (TreeNode)values[1];
99         _var = (String JavaDoc)values[2];
100     }
101
102     public void queueEvent(FacesEvent event)
103     {
104         super.queueEvent(new FacesEventWrapper(event, getNodeId(), this));
105     }
106
107
108     public void broadcast(FacesEvent event) throws AbortProcessingException
109     {
110         if (event instanceof FacesEventWrapper)
111         {
112             FacesEventWrapper childEvent = (FacesEventWrapper) event;
113             String JavaDoc currNodeId = getNodeId();
114             setNodeId(childEvent.getNodeId());
115             FacesEvent nodeEvent = childEvent.getFacesEvent();
116             nodeEvent.getComponent().broadcast(nodeEvent);
117             setNodeId(currNodeId);
118             return;
119         } else
120         {
121             super.broadcast(event);
122             return;
123         }
124     }
125
126
127     // see superclass for documentation
128
public void processDecodes(FacesContext context)
129     {
130         if (context == null) throw new NullPointerException JavaDoc("context");
131         if (!isRendered()) return;
132
133         _model = null;
134         _saved = new HashMap JavaDoc();
135
136         processNodes(context, PROCESS_DECODES, null, 0);
137
138         setNodeId(null);
139         decode(context);
140     }
141
142     // see superclass for documentation
143
public void processValidators(FacesContext context)
144     {
145         if (context == null) throw new NullPointerException JavaDoc("context");
146         if (!isRendered()) return;
147
148         processNodes(context, PROCESS_VALIDATORS, null, 0);
149
150         setNodeId(null);
151     }
152
153
154     // see superclass for documentation
155
public void processUpdates(FacesContext context)
156     {
157         if (context == null) throw new NullPointerException JavaDoc("context");
158         if (!isRendered()) return;
159
160         processNodes(context, PROCESS_UPDATES, null, 0);
161
162         setNodeId(null);
163     }
164
165     // see superclass for documentation
166
public String JavaDoc getClientId(FacesContext context)
167     {
168         String JavaDoc ownClientId = super.getClientId(context);
169         if (_nodeId != null)
170         {
171             return ownClientId + NamingContainer.SEPARATOR_CHAR + _nodeId;
172         } else
173         {
174             return ownClientId;
175         }
176     }
177
178     // see superclass for documentation
179
public void setValueBinding(String JavaDoc name, ValueBinding binding)
180     {
181         if ("value".equals(name))
182         {
183             _model = null;
184         } else if ("nodeVar".equals(name) || "nodeId".equals(name) || "treeVar".equals(name))
185         {
186             throw new IllegalArgumentException JavaDoc("name " + name);
187         }
188         super.setValueBinding(name, binding);
189     }
190
191     // see superclass for documentation
192
public void encodeBegin(FacesContext context) throws IOException JavaDoc
193     {
194         /**
195          * The renderer will handle most of the encoding, but if there are any
196          * error messages queued for the components (validation errors), we
197          * do want to keep the saved state so that we can render the node with
198          * the invalid value.
199          */

200         if (!keepSaved(context))
201         {
202             _saved = new HashMap JavaDoc();
203         }
204
205         super.encodeBegin(context);
206     }
207
208     /**
209      * Sets the value of the TreeData.
210      *
211      * @param value The new value
212      */

213     public void setValue(TreeNode value)
214     {
215         _model = null;
216         _value = value;
217     }
218
219
220     /**
221      * Gets the value of the TreeData.
222      *
223      * @return The value
224      */

225     public Object JavaDoc getValue()
226     {
227         if (_value != null) return _value;
228         ValueBinding vb = getValueBinding("value");
229         return vb != null ? vb.getValue(getFacesContext()) : null;
230     }
231
232     /**
233      * Set the request-scope attribute under which the data object for the current node wil be exposed
234      * when iterating.
235      *
236      * @param var The new request-scope attribute name
237      */

238     public void setVar(String JavaDoc var)
239     {
240         _var = var;
241     }
242
243
244     /**
245      * Return the request-scope attribute under which the data object for the current node will be exposed
246      * when iterating. This property is not enabled for value binding expressions.
247      *
248      * @return The iterrator attribute
249      */

250     public String JavaDoc getVar()
251     {
252         return _var;
253     }
254
255     /**
256      * Calls through to the {@link TreeModel} and returns the current {@link TreeNode} or <code>null</code>.
257      *
258      * @return The current node
259      */

260     public TreeNode getNode()
261     {
262         TreeModel model = getDataModel();
263
264         if (model == null)
265         {
266             return null;
267         }
268
269         return model.getNode();
270     }
271
272
273     public String JavaDoc getNodeId()
274     {
275         return _nodeId;
276     }
277
278
279     public void setNodeId(String JavaDoc nodeId)
280     {
281         saveDescendantState();
282
283         _nodeId = nodeId;
284
285         TreeModel model = getDataModel();
286         if (model == null)
287         {
288             return;
289         }
290         model.setNodeId(nodeId);
291
292         restoreDescendantState();
293
294         Map JavaDoc requestMap = getFacesContext().getExternalContext().getRequestMap();
295
296         if (_var != null)
297         {
298             if (nodeId == null)
299             {
300                 requestMap.remove(_var);
301             } else
302             {
303                 requestMap.put(_var, getNode());
304             }
305         }
306     }
307
308
309     /**
310      * Gets an array of String containing the ID's of all of the {@link TreeNode}s in the path to
311      * the specified node. The path information will be an array of <code>String</code> objects
312      * representing node ID's. The array will starting with the ID of the root node and end with
313      * the ID of the specified node.
314      *
315      * @param nodeId The id of the node for whom the path information is needed.
316      * @return String[]
317      */

318     public String JavaDoc[] getPathInformation(String JavaDoc nodeId)
319     {
320         return getDataModel().getPathInformation(nodeId);
321     }
322
323     /**
324      * Indicates whether or not the specified {@link TreeNode} is the last child in the <code>List</code>
325      * of children. If the node id provided corresponds to the root node, this returns <code>true</code>.
326      *
327      * @param nodeId The ID of the node to check
328      * @return boolean
329      */

330     public boolean isLastChild(String JavaDoc nodeId)
331     {
332         return getDataModel().isLastChild(nodeId);
333     }
334
335     /**
336      * Returns a previously cached {@link TreeModel}, if any, or sets the cache variable to either the
337      * current value (if its a {@link TreeModel}) or to a new instance of {@link TreeModel} (if it's a
338      * {@link TreeNode}) with the provided value object as the root node.
339      *
340      * @return TreeModel
341      */

342     private TreeModel getDataModel()
343     {
344         if (_model != null)
345         {
346             return _model;
347         }
348
349         Object JavaDoc value = getValue();
350         if (value != null)
351         {
352             if (value instanceof TreeModel)
353             {
354                 _model = (TreeModel) value;
355             } else if (value instanceof TreeNode)
356             {
357                 _model = new TreeModel((TreeNode) value);
358             }
359         }
360
361         return _model;
362     }
363
364     private void processNodes(FacesContext context, int processAction, String JavaDoc parentId, int childLevel)
365     {
366         UIComponent facet = null;
367         setNodeId(parentId != null ? parentId + NamingContainer.SEPARATOR_CHAR + childLevel : "0");
368         TreeNode node = getNode();
369
370         facet = getFacet(node.getType());
371
372         if (facet == null)
373         {
374             throw new IllegalArgumentException JavaDoc("Unable to locate facet with the name: " + node.getType());
375         }
376
377         switch (processAction)
378         {
379             case PROCESS_DECODES:
380
381                 facet.processDecodes(context);
382                 break;
383
384             case PROCESS_VALIDATORS:
385
386                 facet.processValidators(context);
387                 break;
388
389             case PROCESS_UPDATES:
390
391                 facet.processUpdates(context);
392                 break;
393         }
394
395         processChildNodes(context, node, processAction);
396     }
397
398     /**
399      * Process the child nodes of the supplied parent @{link TreeNode}. This method is protected so that
400      * it can be overriden by a subclass that may want to control how child nodes are processed.
401      *
402      * @param context FacesContext
403      * @param parentNode The parent node whose children are to be processed
404      * @param processAction An <code>int</code> representing the type of action to process
405      */

406     protected void processChildNodes(FacesContext context, TreeNode parentNode, int processAction)
407     {
408         int kidId = 0;
409         String JavaDoc currId = getNodeId();
410
411         List JavaDoc children = parentNode.getChildren();
412
413         for (int i = 0; i < children.size(); i++)
414         {
415             processNodes(context, processAction, currId, kidId++);
416         }
417     }
418
419     /**
420      * To support using input components for the nodes (e.g., input fields, checkboxes, and selection
421      * lists) while still only using one set of components for all nodes, the state held by the components
422      * for the current node must be saved for a new node is selected.
423      */

424     private void saveDescendantState()
425     {
426         FacesContext context = getFacesContext();
427         Iterator JavaDoc i = getFacets().values().iterator();
428         while (i.hasNext())
429         {
430             UIComponent facet = (UIComponent) i.next();
431             saveDescendantState(facet, context);
432         }
433     }
434
435     /**
436      * Overloaded helper method for the no argument version of this method.
437      *
438      * @param component The component whose state needs to be saved
439      * @param context FacesContext
440      */

441     private void saveDescendantState(UIComponent component, FacesContext context)
442     {
443         if (component instanceof EditableValueHolder)
444         {
445             EditableValueHolder input = (EditableValueHolder) component;
446             String JavaDoc clientId = component.getClientId(context);
447             SavedState state = (SavedState) _saved.get(clientId);
448             if (state == null)
449             {
450                 state = new SavedState();
451                 _saved.put(clientId, state);
452             }
453             state.setValue(input.getLocalValue());
454             state.setValid(input.isValid());
455             state.setSubmittedValue(input.getSubmittedValue());
456             state.setLocalValueSet(input.isLocalValueSet());
457         }
458
459         List JavaDoc kids = component.getChildren();
460         for (int i = 0; i < kids.size(); i++)
461         {
462             saveDescendantState((UIComponent) kids.get(i), context);
463         }
464     }
465
466
467     /**
468      * Used to configure a new node with the state stored previously.
469      */

470     private void restoreDescendantState()
471     {
472         FacesContext context = getFacesContext();
473         Iterator JavaDoc i = getFacets().values().iterator();
474         while (i.hasNext())
475         {
476             UIComponent facet = (UIComponent) i.next();
477             restoreDescendantState(facet, context);
478         }
479     }
480
481     /**
482      * Overloaded helper method for the no argument version of this method.
483      *
484      * @param component The component whose state needs to be restored
485      * @param context FacesContext
486      */

487     private void restoreDescendantState(UIComponent component, FacesContext context)
488     {
489         String JavaDoc id = component.getId();
490         component.setId(id); // forces the cilent id to be reset
491

492         if (component instanceof EditableValueHolder)
493         {
494             EditableValueHolder input = (EditableValueHolder) component;
495             String JavaDoc clientId = component.getClientId(context);
496             SavedState state = (SavedState) _saved.get(clientId);
497             if (state == null)
498             {
499                 state = new SavedState();
500             }
501             input.setValue(state.getValue());
502             input.setValid(state.isValid());
503             input.setSubmittedValue(state.getSubmittedValue());
504             input.setLocalValueSet(state.isLocalValueSet());
505         }
506
507         List JavaDoc kids = component.getChildren();
508         for (int i = 0; i < kids.size(); i++)
509         {
510             restoreDescendantState((UIComponent)kids.get(i), context);
511         }
512     }
513
514     /**
515      * A regular bean with accessor methods for all state variables.
516      *
517      * @author Sean Schofield
518      * @author Hans Bergsten (Some code taken from an example in his O'Reilly JavaServer Faces book. Copied with permission)
519      * @version $Revision: 1.5 $ $Date: 2005/02/26 00:26:00 $
520      */

521     private static class SavedState implements Serializable JavaDoc
522     {
523
524         private Object JavaDoc submittedValue;
525         private boolean valid = true;
526         private Object JavaDoc value;
527         private boolean localValueSet;
528
529         Object JavaDoc getSubmittedValue()
530         {
531             return submittedValue;
532         }
533
534         void setSubmittedValue(Object JavaDoc submittedValue)
535         {
536             this.submittedValue = submittedValue;
537         }
538
539         boolean isValid()
540         {
541             return valid;
542         }
543
544         void setValid(boolean valid)
545         {
546             this.valid = valid;
547         }
548
549         Object JavaDoc getValue()
550         {
551             return value;
552         }
553
554         void setValue(Object JavaDoc value)
555         {
556             this.value = value;
557         }
558
559         boolean isLocalValueSet()
560         {
561             return localValueSet;
562         }
563
564         void setLocalValueSet(boolean localValueSet)
565         {
566             this.localValueSet = localValueSet;
567         }
568     }
569
570     /**
571      * Inner class used to wrap the original events produced by child components in the tree.
572      * This will allow the tree to find the appropriate component later when its time to
573      * broadcast the events to registered listeners. Code is based on a similar private
574      * class for UIData.
575      */

576     private static class FacesEventWrapper extends FacesEvent
577     {
578
579         private FacesEvent _wrappedFacesEvent;
580         private String JavaDoc _nodeId;
581
582
583         public FacesEventWrapper(FacesEvent facesEvent, String JavaDoc nodeId, UIComponent component)
584         {
585             super(component);
586             _wrappedFacesEvent = facesEvent;
587             _nodeId = nodeId;
588         }
589
590
591         public PhaseId getPhaseId()
592         {
593             return _wrappedFacesEvent.getPhaseId();
594         }
595
596
597         public void setPhaseId(PhaseId phaseId)
598         {
599             _wrappedFacesEvent.setPhaseId(phaseId);
600         }
601
602
603         public void queue()
604         {
605             _wrappedFacesEvent.queue();
606         }
607
608
609         public String JavaDoc toString()
610         {
611             return _wrappedFacesEvent.toString();
612         }
613
614
615         public boolean isAppropriateListener(FacesListener faceslistener)
616         {
617             // this event type is only intended for wrapping a real event
618
return false;
619         }
620
621
622         public void processListener(FacesListener faceslistener)
623         {
624             throw new UnsupportedOperationException JavaDoc("This event type is only intended for wrapping a real event");
625         }
626
627
628         public FacesEvent getFacesEvent()
629         {
630             return _wrappedFacesEvent;
631         }
632
633
634         public String JavaDoc getNodeId()
635         {
636             return _nodeId;
637         }
638     }
639
640     /**
641      * Returns true if there is an error message queued for at least one of the nodes.
642      *
643      * @param context FacesContext
644      * @return whether an error message is present
645      */

646     private boolean keepSaved(FacesContext context)
647     {
648         Iterator JavaDoc clientIds = _saved.keySet().iterator();
649         while (clientIds.hasNext())
650         {
651             String JavaDoc clientId = (String JavaDoc) clientIds.next();
652             Iterator JavaDoc messages = context.getMessages(clientId);
653             while (messages.hasNext())
654             {
655                 FacesMessage message = (FacesMessage) messages.next();
656                 if (message.getSeverity().compareTo(FacesMessage.SEVERITY_ERROR) >= 0)
657                 {
658                     return true;
659                 }
660             }
661         }
662
663         return false;
664     }
665 }
666
Popular Tags