001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU
007: * General Public License Version 2 only ("GPL") or the Common
008: * Development and Distribution License("CDDL") (collectively, the
009: * "License"). You may not use this file except in compliance with the
010: * License. You can obtain a copy of the License at
011: * http://www.netbeans.org/cddl-gplv2.html
012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
013: * specific language governing permissions and limitations under the
014: * License. When distributing the software, include this License Header
015: * Notice in each file and include the License file at
016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
017: * particular file as subject to the "Classpath" exception as provided
018: * by Sun in the GPL Version 2 section of the License file that
019: * accompanied this code. If applicable, add the following below the
020: * License Header, with the fields enclosed by brackets [] replaced by
021: * your own identifying information:
022: * "Portions Copyrighted [year] [name of copyright owner]"
023: *
024: * Contributor(s):
025: *
026: * The Original Software is NetBeans. The Initial Developer of the Original
027: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2007 Sun
028: * Microsystems, Inc. All Rights Reserved.
029: *
030: * If you wish your version of this file to be governed by only the CDDL
031: * or only the GPL Version 2, indicate your decision by adding
032: * "[Contributor] elects to include this software in this distribution
033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
034: * single choice of license, a recipient has the option to distribute
035: * your version of this file under either the CDDL, the GPL Version 2 or
036: * to extend the choice of license to its licensees as provided above.
037: * However, if you add GPL Version 2 code and therefore, elected the GPL
038: * Version 2 license, then the option applies only if the new code is
039: * made subject to such option by the copyright holder.
040: */
041: package com.sun.rave.web.ui.component;
042:
043: import com.sun.rave.web.ui.util.LogUtil;
044:
045: import java.util.ArrayList;
046: import java.util.Iterator;
047: import java.util.List;
048:
049: import javax.faces.application.FacesMessage;
050: import javax.faces.component.UIComponent;
051: import javax.faces.component.UIInput;
052: import javax.faces.component.EditableValueHolder;
053: import javax.faces.context.ExternalContext;
054: import javax.faces.context.FacesContext;
055: import javax.faces.convert.Converter;
056: import javax.faces.el.MethodBinding;
057: import javax.faces.el.ValueBinding;
058: import javax.faces.el.ValueBinding;
059: import javax.faces.event.AbortProcessingException;
060: import javax.faces.event.FacesEvent;
061: import javax.faces.event.ValueChangeListener;
062: import javax.faces.event.ValueChangeEvent;
063: import javax.faces.validator.Validator;
064:
065: import javax.servlet.http.Cookie;
066:
067: /**
068: *
069: * @author Ken Paulsen (ken.paulsen@sun.com)
070: */
071: public class Tree extends TreeBase implements EditableValueHolder {
072:
073: /**
074: * Constructor.
075: */
076: public Tree() {
077: super ();
078: setLayoutDefinitionKey(LAYOUT_KEY);
079: }
080:
081: public String getSelected() {
082: return (String) getValue();
083: }
084:
085: public void setSelected(String s) {
086: setValue(s);
087: }
088:
089: //////////////////////////////////////////////////////////////////////
090: // ValueHolder Methods
091: //////////////////////////////////////////////////////////////////////
092:
093: /**
094: * <p> Return the <code>Converter</code> (if any) that is registered for
095: * this <code>UIComponent</code>.</p>
096: *
097: * <p> Not implemented for this component.</p>
098: */
099: public Converter getConverter() {
100: return converter;
101: }
102:
103: /**
104: * <p> Set the <code>Converter</code> (if any) that is registered for
105: * this <code>UIComponent</code>.</p>
106: *
107: * <p> Not implemented for this component.</p>
108: *
109: * @param conv New <code>Converter</code> (or <code>null</code>)
110: */
111: public void setConverter(Converter conv) {
112: converter = conv;
113: // Do nothing... throw exception?
114: }
115:
116: /**
117: * <p> Return the local value of this <code>UIComponent</code> (if any),
118: * without evaluating any associated <code>ValueBinding</code>.</p>
119: */
120: public Object getLocalValue() {
121: return value;
122: }
123:
124: /**
125: * <p> Gets the value of this {@link UIComponent}. First, consult the
126: * local value property of this component. If non-<code>null</code>
127: * return it. If non-null, see if we have a <code>ValueBinding</code>
128: * for the <code>value</code> property. If so, return the result of
129: * evaluating the property, otherwise return null.</p>
130: */
131: public Object getValue() {
132: if (value != null) {
133: return value;
134: }
135: ValueBinding vb = getValueBinding("value");
136: if (vb != null) {
137: return (vb.getValue(getFacesContext()));
138: } else {
139: return (null);
140: }
141: // return getSelectedTreeNode();
142: }
143:
144: /**
145: * <p> Set the value of this {@link UIComponent} (if any).</p>
146: *
147: * @param val The new local value
148: */
149: public void setValue(Object val) {
150: value = val;
151:
152: // Mark the local value as set.
153: setLocalValueSet(true);
154: }
155:
156: //////////////////////////////////////////////////////////////////////
157: // EditableValueHolder Methods
158: //////////////////////////////////////////////////////////////////////
159:
160: /**
161: * <p> Return the submittedValue value of this component. This method
162: * should only be used by the <code>encodeBegin()</code> and/or
163: * <code>encodeEnd()</code> methods of this component, or its
164: * corresponding <code>Renderer</code>.</p>
165: */
166: public Object getSubmittedValue() {
167: return submittedValue;
168: }
169:
170: /**
171: * <p> Set the submittedValue value of this component. This method should
172: * only be used by the <code>decode()</code> and
173: * <code>validate()</code> method of this component, or its
174: * corresponding <code>Renderer</code>.</p>
175: *
176: * @param value The new submitted value.
177: */
178: public void setSubmittedValue(Object value) {
179: submittedValue = value;
180: }
181:
182: /**
183: * <p> Return the "local value set" state for this component. Calls to
184: * <code>setValue()</code> automatically reset this property to
185: * <code>true</code>.
186: */
187: public boolean isLocalValueSet() {
188: return localValueSet;
189: }
190:
191: /**
192: * <p> Sets the "local value set" state for this component.</p>
193: */
194: public void setLocalValueSet(boolean value) {
195: localValueSet = value;
196: }
197:
198: /**
199: * <p> Return a flag indicating whether the local value of this component
200: * is valid (no conversion error has occurred).</p>
201: */
202: public boolean isValid() {
203: return valid;
204: }
205:
206: /**
207: * <p> Set a flag indicating whether the local value of this component
208: * is valid (no conversion error has occurred).</p>
209: *
210: * @param value The new valid flag.
211: */
212: public void setValid(boolean value) {
213: valid = value;
214: }
215:
216: /**
217: * <p> Return a <code>MethodBinding</code> pointing at a method that will
218: * be used to validate the current value of this component. This
219: * method will be called during the <em>Process Validations</em> or
220: * <em>Apply Request Values</em> phases (depending on the value of
221: * the <code>immediate</code> property). </p>
222: *
223: * <p> Not implemented for this component.</p>
224: */
225: public MethodBinding getValidator() {
226: return validatorBinding;
227: }
228:
229: /**
230: * <p> Set a <code>MethodBinding</code> pointing at a method that will be
231: * used to validate the current value of this component. This method
232: * will be called during the <em>Process Validations</em> or
233: * <em>Apply Request Values</em> phases (depending on the value of
234: * the <code>immediate</code> property). </p>
235: *
236: * <p> Any method referenced by such an expression must be public, with a
237: * return type of <code>void</code>, and accept parameters of type
238: * <code>FacesContext</code>, <code>UIComponent</code>, and
239: * <code>Object</code>.</p>
240: *
241: * <p> Not implemented for this component.</p>
242: *
243: * @param valBinding The new <code>MethodBinding</code> instance.
244: */
245: public void setValidator(MethodBinding valBinding) {
246: validatorBinding = valBinding;
247: }
248:
249: /**
250: * <p> Add a <code>Validator</code> instance to the set associated with
251: * this component.</p>
252: *
253: * <p> Not implemented for this component.</p>
254: *
255: * @param validator The <code>Validator</code> to add.
256: */
257: public void addValidator(Validator validator) {
258: if (validator == null) {
259: throw new NullPointerException();
260: }
261: if (validators == null) {
262: validators = new ArrayList();
263: }
264: validators.add(validator);
265: }
266:
267: /**
268: * <p> Return the set of registered <code>Validator</code>s for this
269: * component instance. If there are no registered validators, a
270: * zero-length array is returned.</p>
271: *
272: * <p> Not implemented for this component.</p>
273: */
274: public Validator[] getValidators() {
275: if (validators == null) {
276: return (new Validator[0]);
277: }
278: return ((Validator[]) validators
279: .toArray(new Validator[validators.size()]));
280: }
281:
282: /**
283: * <p> Remove a <code>Validator</code> instance from the set associated
284: * with this component, if it was previously associated. Otherwise,
285: * do nothing.</p>
286: *
287: * <p> Not implemented for this component.</p>
288: *
289: * @param validator The <code>Validator</code> to remove.
290: */
291: public void removeValidator(Validator validator) {
292: if (validators != null) {
293: validators.remove(validator);
294: }
295: }
296:
297: /**
298: * <p> Return a <code>MethodBinding</code> instance method that will be
299: * called after any registered <code>ValueChangeListener</code>s have
300: * been notified of a value change. This method will be called during
301: * the <em>Process Validations</em> or <em>Apply Request Values</em>
302: * phases (depending on the value of the <code>immediate</code>
303: * property). </p>
304: */
305: public MethodBinding getValueChangeListener() {
306: return valueChangeMethod;
307: }
308:
309: /**
310: * <p> Set a <code>MethodBinding</code> instance method that will be
311: * called after any registered <code>ValueChangeListener</code>s have
312: * been notified of a value change. This method will be called
313: * during the <em>Process Validations</em> or <em>Apply Request
314: * Values</em> phases (depending on the value of the
315: * <code>immediate</code> property).</p>
316: *
317: * @param method The new MethodBinding instance.
318: */
319: public void setValueChangeListener(MethodBinding method) {
320: valueChangeMethod = method;
321: }
322:
323: /**
324: * <p> Add a new <code>ValueChangeListener</code> to the set of listeners
325: * interested in being notified when <code>ValueChangeEvent</code>s
326: * occur.</p>
327: *
328: * @param listener The <code>ValueChangeListener</code> to be added.
329: */
330: public void addValueChangeListener(ValueChangeListener listener) {
331: addFacesListener(listener);
332: }
333:
334: /**
335: * <p> Return the set of registered <code>ValueChangeListener</code>s for
336: * this component instance. If there are no registered listeners, a
337: * zero-length array is returned.</p>
338: */
339: public ValueChangeListener[] getValueChangeListeners() {
340: return (ValueChangeListener[]) getFacesListeners(ValueChangeListener.class);
341: }
342:
343: /**
344: * <p> Remove an existing <code>ValueChangeListener</code> (if any) from
345: * the set of listeners interested in being notified when
346: * <code>ValueChangeEvent</code>s occur.</p>
347: *
348: * @param listener The <code>ValueChangeListener</code> to be removed.
349: */
350: public void removeValueChangeListener(ValueChangeListener listener) {
351: removeFacesListener(listener);
352: }
353:
354: //////////////////////////////////////////////////////////////////////
355: // Other Methods
356: //////////////////////////////////////////////////////////////////////
357:
358: /**
359: * <p> Decode any new state of this <code>UIComponent</code> from the
360: * request contained in the specified <code>FacesContext</code>, and
361: * store this state as needed.</p>
362: *
363: * <p> During decoding, events may be enqueued for later processing (by
364: * event listeners who have registered an interest), by calling
365: * <code>queueEvent()</code>.</p>
366: *
367: * @param context {@link FacesContext} for the request we are processing.
368: */
369: public void decode(FacesContext context) {
370: setValid(true);
371: super .decode(context);
372: }
373:
374: /**
375: * <p> In addition to to the default <code>UIComponent#broadcast</code>
376: * processing, pass the <code>ValueChangeEvent</code> being broadcast
377: * to the method referenced by <code>valueChangeListener</code>.</p>
378: *
379: * @param event <code>FacesEvent</code> to be broadcast
380: *
381: * @exception AbortProcessingException Signal the JSF implementation
382: * that no further processing on the current event should be performed
383: */
384: public void broadcast(FacesEvent event)
385: throws AbortProcessingException {
386: // Perform standard superclass processing
387: super .broadcast(event);
388:
389: if (event instanceof ValueChangeEvent) {
390: MethodBinding method = getValueChangeListener();
391: if (method != null) {
392: FacesContext context = getFacesContext();
393: method.invoke(context, new Object[] { event });
394: }
395: }
396: }
397:
398: /**
399: * <p> Perform the component tree processing required by the <em>Update
400: * Model Values</em> phase of the request processing lifecycle for
401: * all facets of this component, all children of this component,
402: * and this component itself, as follows.</p>
403: *
404: * <ul><li>If the <code>rendered</code> property of this
405: * <code>UIComponent</code> is <code>false</code>, skip
406: * further processing.</li>
407: * <li>Call the <code>processUpdates()</code> method of all
408: * facets and children of this {@link UIComponent}, in the
409: * order determined by a call to
410: * <code>getFacetsAndChildren()</code>.</li></ul>
411: *
412: * @param context <code>FacesContext</code> for this request
413: */
414: public void processUpdates(FacesContext context) {
415: // Skip processing if our rendered flag is false
416: if (!isRendered()) {
417: return;
418: }
419:
420: // Do the super stuff...
421: super .processUpdates(context);
422:
423: // Save model stuff...
424: try {
425: updateModel(context);
426: } catch (RuntimeException ex) {
427: context.renderResponse();
428: throw new RuntimeException(ex);
429: }
430: }
431:
432: /**
433: * <p> Perform the following algorithm to update the model data
434: * associated with this component, if any, as appropriate.</p>
435: *
436: * <ul><li>If the <code>valid</code> property of this component is
437: * <code>false</code>, take no further action.</li>
438: * <li>If the <code>localValueSet</code> property of this
439: * component is <code>false</code>, take no further action.</li>
440: * <li>If no <code>ValueBinding</code> for <code>value</code>
441: * exists, take no further action.</li>
442: * <li>Call <code>setValue()</code> method of the
443: * <code>ValueBinding</code> to update the value that the
444: * <code>ValueBinding</code> points at.</li>
445: * <li>If the <code>setValue()</code> method returns successfully:
446: * <ul><li>Clear the local value of this component.</li>
447: * <li>Set the <code>localValueSet</code> property of
448: * this component to false.</li></ul></li>
449: * <li>If the <code>setValue()</code> method call fails:
450: * <ul><li>Queue an error message by calling
451: * <code>addMessage()</code> on the specified
452: * <code>FacesContext</code> instance.</li>
453: * <li>Set the <code>valid</code> property of this
454: * component to <code>false</code>.</li></ul></li>
455: * </ul>
456: *
457: * @param context <code>FacesContext</code> for the request we are
458: * processing.
459: */
460: public void updateModel(FacesContext context) {
461: // Sanity Checks...
462: if (context == null) {
463: throw new NullPointerException();
464: }
465: if (!isValid() || !isLocalValueSet()) {
466: return;
467: }
468: ValueBinding vb = getValueBinding("value");
469: if (vb == null) {
470: return;
471: }
472:
473: try {
474: vb.setValue(context, getLocalValue());
475: setValue(null);
476: setLocalValueSet(false);
477: return;
478: } catch (Exception ex) {
479: String messageStr = ex.getMessage();
480: if (messageStr != null) {
481: FacesMessage message = null;
482: message = new FacesMessage(messageStr);
483: message.setSeverity(FacesMessage.SEVERITY_ERROR);
484: context.addMessage(getClientId(context), message);
485: }
486: setValid(false);
487: if (LogUtil.configEnabled()) {
488: LogUtil.config("Unable to update Model!", ex); // NOI18N
489: }
490: }
491: }
492:
493: /**
494: * <p> Perform the component tree processing required by the <em>Apply
495: * Request Values</em> phase of the request processing lifecycle for
496: * all facets of this component, all children of this component, and
497: * this component itself, as follows.</p>
498: *
499: * <ul><li>If the <code>rendered</code> property of this
500: * <code>UIComponent</code> is <code>false</code>, skip
501: * further processing.</li>
502: * <li>Call the <code>processDecodes()</code> method of all
503: * facets and children of this <code>UIComponent</code>, in the
504: * order determined by a call to
505: * <code>getFacetsAndChildren()</code>.</li>
506: * <li>Call the <code>decode()</code> method of this
507: * component.</li>
508: * <li>If a <code>RuntimeException</code> is thrown during decode
509: * processing, call <code>FacesContext.renderResponse</code>
510: * and re-throw the exception.</li></ul>
511: *
512: * @param context <code>FacesContext</code> for the request.
513: */
514: public void processDecodes(FacesContext context) {
515: // Skip processing if our rendered flag is false
516: if (!isRendered()) {
517: return;
518: }
519:
520: super .processDecodes(context);
521:
522: if (isImmediate()) {
523: executeValidate(context);
524: }
525: }
526:
527: /**
528: * <p> In addition to the standard <code>processValidators</code> behavior
529: * inherited from <code>UIComponentBases</code>, calls
530: * <code>validate()</code> if the <code>immediate</code> property is
531: * false (which is the default); if the component is invalid
532: * afterwards, calls <code>FacesContext.renderResponse</code>. If a
533: * <code>RuntimeException</code> is thrown during validation
534: * processing, calls <code>FacesContext.renderResponse</code> and
535: * re-throws the exception.</p>
536: */
537: public void processValidators(FacesContext context) {
538: if (context == null) {
539: throw new NullPointerException();
540: }
541:
542: // Skip processing if our rendered flag is false
543: if (!isRendered()) {
544: return;
545: }
546:
547: super .processValidators(context);
548:
549: if (!isImmediate()) {
550: executeValidate(context);
551: }
552: }
553:
554: /**
555: * Executes validation logic.
556: */
557: private void executeValidate(FacesContext context) {
558: try {
559: validate(context);
560: } catch (RuntimeException e) {
561: context.renderResponse();
562: throw e;
563: }
564:
565: if (!isValid()) {
566: context.renderResponse();
567: }
568: }
569:
570: /**
571: * <p> Perform the following algorithm to validate the local value of
572: * this <code>UIInput</code>.</p>
573: *
574: * <ul><li>Retrieve the submitted value with
575: * <code>getSubmittedValue()</code>. If this returns null,
576: * exit without further processing. (This indicates that no
577: * value was submitted for this component.)</li>
578: *
579: * <li>Convert the submitted value into a "local value" of the
580: * appropriate data type by calling
581: * <code>getConvertedValue</code>.</li>
582: *
583: * <li>Validate the property by calling
584: * <code>validateValue</code>.</li>
585: *
586: * <li>If the <code>valid</code> property of this component is
587: * still <code>true</code>, retrieve the previous value of
588: * the component (with <code>getValue()</code>), store the new
589: * local value using <code>setValue()</code>, and reset the
590: * submitted value to null. If the local value is different
591: * from the previous value of this component, fire a
592: * <code>ValueChangeEvent</code> to be broadcast to all
593: * interested listeners.</li></ul>
594: *
595: * @param context <code>FacesContext</code> for the current request.
596: */
597: public void validate(FacesContext context) {
598: if (context == null) {
599: throw new NullPointerException();
600: }
601:
602: // Submitted value == null means "the component was not submitted
603: // at all"; validation should not continue
604: Object submittedValue = getSubmittedValue();
605: if (submittedValue == null) {
606: return;
607: }
608:
609: Object newValue = submittedValue;
610: /*
611: FIXME: Decide if we ever want to the Tree to support Converters
612: try {
613: newValue = getConvertedValue(context, submittedValue);
614: }
615: catch (ConverterException ce) {
616: addConversionErrorMessage(context, ce, submittedValue);
617: setValid(false);
618: }
619: */
620:
621: // Validate the value (check for required for now)
622: validateValue(context, newValue);
623:
624: // If our value is valid, store the new value, erase the
625: // "submitted" value, and emit a ValueChangeEvent if appropriate
626: if (isValid()) {
627: Object previous = getValue();
628: setValue(newValue);
629: setSubmittedValue(null);
630: if (isDifferent(previous, newValue)) {
631: queueEvent(new ValueChangeEvent(this , previous,
632: newValue));
633: }
634: }
635: }
636:
637: /**
638: * <p> Return <code>true</code> if the objects are not equal.</p>
639: *
640: * @param val1 Value 1
641: * @param val1 Value 2
642: *
643: * @return true if the 2 values are not equal
644: */
645: protected boolean isDifferent(Object val1, Object val2) {
646: if (val1 == val2) {
647: // Same object, they're equal
648: return false;
649: }
650: if (val1 == null) {
651: // Not equal, and one is null
652: return true;
653: }
654: return !val1.equals(val2);
655: }
656:
657: protected void validateValue(FacesContext context, Object newValue) {
658: if (!isValid()) {
659: return;
660: }
661: if (isRequired()
662: && ((newValue == null) || (newValue.toString().trim()
663: .equals("")))) {
664: // FIXME: Add a message
665: // FacesMessage message =
666: // message.setSeverity(FacesMessage.SEVERITY_ERROR);
667: // context.addMessage(getClientId(context), message);
668: setValid(false);
669: }
670:
671: // FIXME: Decide if we ever want to the Tree to support Validators (See UIInput)
672: }
673:
674: /**
675: * <p> This method accepts the {@link TreeNode} which is to be selected.
676: * The previous {@link TreeNode} that was selected will unselected.
677: * No state is saved with this operation, the state is maintained on
678: * the client.</p>
679: *
680: * @deprecated Use #setValue(Object)
681: *
682: * @param treeNode The {@link TreeNode} to be selected.
683: */
684: public void selectTreeNode(TreeNode treeNode) {
685: setValue(treeNode);
686: // selectTreeNode(treeNode.getClientId(FacesContext.getCurrentInstance()));
687: }
688:
689: /**
690: * <p> This method accepts the clientId of a {@link TreeNode} which is to
691: * be selected. The previous {@link TreeNode} that was selected will
692: * unselected. No state is saved with this operation, the state is
693: * maintained on the client-side.</p>
694: *
695: * @deprecated Use #setValue(Object)
696: *
697: * @param clientId Client id of the {@link TreeNode} to be selected.
698: */
699: public void selectTreeNode(String clientId) {
700: setValue(clientId);
701: // FacesContext context = FacesContext.getCurrentInstance();
702: // context.getExternalContext().getRequestMap().put(getClientId(context)+SELECTED_SUFFIX, clientId);
703: }
704:
705: /**
706: * <p> This method returns the {@link TreeNode} client ID that is
707: * selected according the browser cookie. This method is generally
708: * only useful during the decode process.</p>
709: *
710: * @return The selected tree node (according to the cookie).
711: */
712: public String getCookieSelectedTreeNode() {
713: FacesContext context = FacesContext.getCurrentInstance();
714: String treeCID = getClientId(context);
715:
716: // If it's stull null, look at cookies...
717: Cookie cookie = getCookie(context, treeCID + COOKIE_SUFFIX);
718:
719: if (cookie != null) {
720: return cookie.getValue();
721: }
722:
723: // Not found, return null
724: return null;
725: }
726:
727: /**
728: * <p> This method will return the {@link TreeNode} client ID that is
729: * selected according the browser cookie. This method is only
730: * useful during the decode process as the cookie will typically be
731: * reset to null immediately after the request is processed.</p>
732: *
733: * @return The selected tree node (according to the cookie).
734: */
735: public String getCookieExpandNode() {
736: FacesContext context = FacesContext.getCurrentInstance();
737: String treeCID = getClientId(context);
738: Cookie cookie = getCookie(context, treeCID
739: + COOKIE_SUFFIX_EXPAND);
740:
741: if (cookie != null) {
742: return cookie.getValue();
743: }
744:
745: // Not found, return null
746: return null;
747: }
748:
749: private Cookie getCookie(FacesContext context, String name) {
750: ExternalContext extCtx = context.getExternalContext();
751:
752: return (Cookie) extCtx.getRequestCookieMap().get(name);
753: }
754:
755: //////////////////////////////////////////////////////////////////////
756: // ValueHolder Methods
757: //////////////////////////////////////////////////////////////////////
758:
759: /**
760: *
761: */
762: public Object saveState(FacesContext context) {
763: Object values[] = new Object[8];
764: values[0] = super .saveState(context);
765: values[1] = saveAttachedState(context, converter);
766: values[2] = value;
767: values[3] = localValueSet ? Boolean.TRUE : Boolean.FALSE;
768: values[4] = this .valid ? Boolean.TRUE : Boolean.FALSE;
769: values[5] = saveAttachedState(context, validators);
770: values[6] = saveAttachedState(context, validatorBinding);
771: values[7] = saveAttachedState(context, valueChangeMethod);
772:
773: return (values);
774: }
775:
776: /**
777: *
778: */
779: public void restoreState(FacesContext context, Object state) {
780: Object values[] = (Object[]) state;
781:
782: super .restoreState(context, values[0]);
783: converter = (Converter) restoreAttachedState(context, values[1]);
784: value = values[2];
785: localValueSet = ((Boolean) values[3]).booleanValue();
786: valid = ((Boolean) values[4]).booleanValue();
787:
788: List restoredValidators = null;
789: Iterator iter = null;
790: if (null != (restoredValidators = (List) restoreAttachedState(
791: context, values[5]))) {
792: // if there were some validators registered prior to this
793: // method being invoked, merge them with the list to be
794: // restored.
795: if (null != validators) {
796: iter = restoredValidators.iterator();
797: while (iter.hasNext()) {
798: validators.add(iter.next());
799: }
800: } else {
801: validators = restoredValidators;
802: }
803: }
804:
805: validatorBinding = (MethodBinding) restoreAttachedState(
806: context, values[6]);
807: valueChangeMethod = (MethodBinding) restoreAttachedState(
808: context, values[7]);
809: }
810:
811: /**
812: * <p> Converter.</p>
813: */
814: private Converter converter = null;
815:
816: /**
817: * <p> The set of {@link Validator}s associated with this
818: * <code>UIComponent</code>.</p>
819: */
820: private List validators = null;
821:
822: /**
823: *
824: */
825: private MethodBinding validatorBinding = null;
826:
827: /**
828: * <p> The submittedValue value of this component.</p>
829: */
830: private Object submittedValue = null;
831:
832: /**
833: * <p> Toggle indicating validity of this component.</p>
834: */
835: private boolean valid = true;
836:
837: /**
838: * <p> The "localValueSet" state for this component.</p>
839: */
840: private boolean localValueSet;
841:
842: /**
843: * <p> The "valueChange" MethodBinding for this component.
844: */
845: private MethodBinding valueChangeMethod = null;
846:
847: /**
848: * <p> The value of the <code>Tree</code>. This should be a String
849: * representing the client id of the selected
850: * <code>TreeNode</code>.</p>
851: */
852: private Object value = null;
853:
854: /**
855: * <p> This is the {@link com.sun.rave.web.ui.theme.Theme} key used to retrieve
856: * the JavaScript needed for this component.</p>
857: *
858: * @see com.sun.rave.web.ui.theme.Theme#getPathToJSFile(String)
859: */
860: public static final String JAVA_SCRIPT_THEME_KEY = "tree";
861:
862: /**
863: * <p> This is the location of the XML file that declares the layout for
864: * the PanelGroup. (layout/tree.xml)</p>
865: */
866: public static final String LAYOUT_KEY = "layout/tree.xml";
867:
868: /**
869: * <p> This is the suffix appended to the client id when forming a request
870: * attribute key. The value associated with the generated key
871: * indicates which node should be selected. The renderer uses this
872: * information to generate JavaScript to select this node, overriding
873: * the previous selection.</p>
874: */
875: public static final String SELECTED_SUFFIX = "_select";
876:
877: /**
878: * <p> This is the suffix appended to the client id to form the key to the
879: * cookie Map needed to retrieve the tree selection.</p>
880: */
881: public static final String COOKIE_SUFFIX = "-hi";
882:
883: /**
884: * <p> This is the suffix appended to the client id to form the key to the
885: * cookie Map needed to retrieve the node that may need to be
886: * expanded (because it was just selected).</p>
887: */
888: public static final String COOKIE_SUFFIX_EXPAND = "-expand";
889: }
|