KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > cocoon > forms > formmodel > AbstractWidget


1 /*
2  * Copyright 1999-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.cocoon.forms.formmodel;
17
18 import java.util.ArrayList JavaDoc;
19 import java.util.HashMap JavaDoc;
20 import java.util.Iterator JavaDoc;
21 import java.util.List JavaDoc;
22 import java.util.Locale JavaDoc;
23 import java.util.Map JavaDoc;
24
25 import org.apache.cocoon.forms.FormsConstants;
26 import org.apache.cocoon.forms.event.CreateEvent;
27 import org.apache.cocoon.forms.event.ValueChangedListenerEnabled;
28 import org.apache.cocoon.forms.event.WidgetEvent;
29 import org.apache.cocoon.forms.validation.ValidationErrorAware;
30 import org.apache.cocoon.forms.validation.WidgetValidator;
31 import org.apache.cocoon.util.location.Location;
32 import org.apache.cocoon.xml.AttributesImpl;
33 import org.xml.sax.ContentHandler JavaDoc;
34 import org.xml.sax.SAXException JavaDoc;
35
36 /**
37  * Abstract base class for Widget implementations. Provides functionality
38  * common to many widgets.
39  *
40  * @version $Id: AbstractWidget.java 326930 2005-10-20 16:03:19Z sylvain $
41  */

42 public abstract class AbstractWidget implements Widget {
43
44     /**
45      * Containing parent-widget to this widget.
46      * NOTE: avoid directly accessing this member since subclasses can mask this
47      * property through own implemented getParent()
48      */

49     private Widget parent;
50
51     /**
52      * The widget's own state
53      */

54     private WidgetState state = WidgetState.ACTIVE;
55
56     /**
57      * Lazy loaded reference to the top-level form.
58      */

59     private Form form;
60
61     /**
62      * Validation-rules local to the widget instance
63      */

64     private List JavaDoc validators;
65
66     /**
67      * Storage for the widget allocated attributes
68      */

69     private Map JavaDoc attributes;
70
71     /**
72      * The result of the last call to {@link #validate()}.
73      */

74     protected boolean wasValid = true;
75
76
77     protected AbstractWidget(AbstractWidgetDefinition definition) {
78         this.state = definition.getState();
79     }
80
81     /**
82      * Called after widget's environment has been setup,
83      * to allow for any contextual initalization, such as
84      * looking up case widgets for union widgets.
85      */

86     public void initialize() {
87         ((AbstractWidgetDefinition)getDefinition()).widgetCreated(this);
88     }
89
90     /**
91      * Gets the id of this widget.
92      */

93     public String JavaDoc getId() {
94         return getDefinition().getId();
95     }
96     
97     public String JavaDoc getName() {
98         return getId();
99     }
100
101     /**
102      * Concrete subclasses should allow access to their underlaying Definition
103      * through this method.
104      *
105      * If subclasses decide to return <code>null</code> they should also organize
106      * own implementations of {@link #getId()}, {@link #getLocation()},
107      * {@link #validate()}, {@link #generateLabel(ContentHandler)} and
108      * {@link #generateDisplayData(ContentHandler)} to avoid NPE's.
109      *
110      * @return the widgetDefinition from which this widget was instantiated.
111      * (@link WidgetDefinition#createInstance()}
112      */

113     public abstract WidgetDefinition getDefinition();
114
115     /**
116      * @return the location-information (file, line and column) where this widget was
117      * configured.
118      */

119     public Location getLocation() {
120         return getDefinition().getLocation();
121     }
122
123     /**
124      * @return The parent-widget of this widget.
125      */

126     // This method is final in order for other methods in this class to use this.parent
127
public final Widget getParent() {
128         return this.parent;
129     }
130
131     /**
132      * Sets the parent-widget of this widget.
133      * This is a write-once property.
134      *
135      * @param widget the parent-widget of this one.
136      * @throws IllegalStateException when the parent had already been set.
137      */

138     public void setParent(Widget widget) {
139         if (this.parent != null) {
140             throw new IllegalStateException JavaDoc("The parent of widget " + getRequestParameterName() + " should only be set once.");
141         }
142         this.parent = widget;
143     }
144
145     /**
146      * @return the form where this widget belongs to.
147      */

148     public Form getForm() {
149         if (this.form == null) {
150             Widget myParent = getParent();
151             if (myParent == null) {
152                 this.form = (Form)this;
153             } else {
154                 this.form = myParent.getForm();
155             }
156         }
157         return this.form;
158     }
159
160     public WidgetState getState() {
161         return this.state;
162     }
163
164     public void setState(WidgetState state) {
165         if (state == null) {
166             throw new IllegalArgumentException JavaDoc("A widget state cannot be set to null");
167         }
168         this.state = state;
169
170         // Update the browser
171
getForm().addWidgetUpdate(this);
172     }
173
174     public WidgetState getCombinedState() {
175         if (this.parent == null) {
176             return this.state;
177         }
178         return WidgetState.strictest(this.state, this.parent.getCombinedState());
179     }
180
181     // Cached param names, used to speed up execution of the method below while
182
// still allowing ids to change (e.g. repeater rows when they are reordered).
183
private String JavaDoc cachedParentParamName;
184     private String JavaDoc cachedParamName;
185     
186     /**
187      * Should be called when a widget's own name has changed, in order to clear
188      * internal caches used to compute request parameters.
189      */

190     protected void widgetNameChanged() {
191         this.cachedParentParamName = null;
192         this.cachedParamName = null;
193     }
194     
195     public String JavaDoc getFullName() {
196         return getRequestParameterName();
197     }
198     
199     public String JavaDoc getRequestParameterName() {
200
201         if (this.parent == null) {
202             return getId();
203         }
204
205         String JavaDoc parentParamName = parent.getRequestParameterName();
206         if (parentParamName == this.cachedParentParamName) {
207             // Parent name hasn't changed, so ours hasn't changed too
208
return this.cachedParamName;
209         }
210
211         // Compute our name and cache it
212
this.cachedParentParamName = parentParamName;
213         if (this.cachedParentParamName.length() == 0) {
214             // the top level form returns an id == ""
215
this.cachedParamName = getId();
216         } else {
217             this.cachedParamName = this.cachedParentParamName + "." + getId();
218         }
219
220         return this.cachedParamName;
221     }
222
223     public Widget lookupWidget(String JavaDoc path) {
224
225         if (path == null || path.equals("")) {
226             return this;
227         }
228
229         Widget relativeWidget;
230         String JavaDoc relativePath = null;
231         int sepPosition = path.indexOf("" + Widget.PATH_SEPARATOR);
232
233         if (sepPosition < 0) {
234             //last step
235
if (path.startsWith("..")) return getParent();
236             return getChild(path);
237         } else if (sepPosition == 0) {
238             //absolute path
239
relativeWidget = getForm();
240             relativePath = path.substring(1);
241         } else {
242             if (path.startsWith(".." + Widget.PATH_SEPARATOR)) {
243                 relativeWidget = getParent();
244                 relativePath = path.substring(3);
245             } else {
246                 String JavaDoc childId = path.substring(0, sepPosition );
247                 relativeWidget = getChild(childId);
248                 relativePath = path.substring(sepPosition+1);
249             }
250         }
251
252         if (relativeWidget == null) return null;
253         return relativeWidget.lookupWidget(relativePath);
254     }
255
256     /**
257      * Concrete widgets that contain actual child widgets should override to
258      * return the actual child-widget.
259      *
260      * @param id of the child-widget
261      * @return <code>null</code> if not overriden.
262      */

263     protected Widget getChild(String JavaDoc id) {
264         return null;
265     }
266
267     public Widget getWidget(String JavaDoc id) {
268         throw new UnsupportedOperationException JavaDoc("getWidget(id) got deprecated from the API. \n" +
269                 "Consider using getChild(id) or even lookupWidget(path) instead.");
270     }
271
272     public Object JavaDoc getValue() {
273         throw new UnsupportedOperationException JavaDoc("Widget " + toString() + " has no value, at " + getLocation());
274     }
275
276     public void setValue(Object JavaDoc object) {
277         throw new UnsupportedOperationException JavaDoc("Widget " + toString() + " has no value, at " + getLocation());
278     }
279
280     public boolean isRequired() {
281         return false;
282     }
283
284     /**
285      * {@inheritDoc}
286      *
287      * Abstract implementation throws a {@link UnsupportedOperationException}.
288      * Concrete subclass widgets need to override when supporting event broadcasting.
289      */

290     public void broadcastEvent(WidgetEvent event) {
291         if (event instanceof CreateEvent) {
292             ((AbstractWidgetDefinition)getDefinition()).fireCreateEvent((CreateEvent)event);
293         } else {
294             throw new UnsupportedOperationException JavaDoc("Widget " + this.getRequestParameterName() + " doesn't handle events.");
295         }
296     }
297
298     /**
299      * Add a validator to this widget instance.
300      *
301      * @param validator
302      */

303     public void addValidator(WidgetValidator validator) {
304         if (this.validators == null) {
305             this.validators = new ArrayList JavaDoc();
306         }
307         this.validators.add(validator);
308     }
309
310     /**
311      * Remove a validator from this widget instance
312      *
313      * @param validator
314      * @return <code>true</code> if the validator was found.
315      */

316     public boolean removeValidator(WidgetValidator validator) {
317         if (this.validators != null) {
318             return this.validators.remove(validator);
319         }
320         return false;
321     }
322
323     /**
324      * @see org.apache.cocoon.forms.formmodel.Widget#validate()
325      */

326     public boolean validate() {
327         // Consider widget valid if it is not validating values.
328
if (!getCombinedState().isValidatingValues()) {
329             this.wasValid = true;
330             return true;
331         }
332
333         // Test validators from the widget definition
334
if (!getDefinition().validate(this)) {
335             // Failed
336
this.wasValid = false;
337             return false;
338         }
339         // Definition successful, test local validators
340
if (this.validators != null) {
341             Iterator JavaDoc iter = this.validators.iterator();
342             while(iter.hasNext()) {
343                 WidgetValidator validator = (WidgetValidator)iter.next();
344                 if (!validator.validate(this)) {
345                     this.wasValid = false;
346                     return false;
347                 }
348             }
349         }
350
351         // Successful validation
352

353         if (this instanceof ValidationErrorAware) {
354             // Clear validation error if any
355
((ValidationErrorAware)this).setValidationError(null);
356         }
357
358         this.wasValid = true;
359         return true;
360     }
361
362     /**
363      * @see org.apache.cocoon.forms.formmodel.Widget#isValid()
364      */

365     public boolean isValid() {
366         return this.wasValid;
367     }
368
369     /**
370      * {@inheritDoc}
371      *
372      * Delegates to the {@link #getDefinition()} to generate the 'label' part of
373      * the display-data of this widget.
374      *
375      * Subclasses should override if the getDefinition can return <code>null</code>
376      * to avoid NPE's
377      *
378      * @param contentHandler
379      * @throws SAXException
380      */

381     public void generateLabel(ContentHandler JavaDoc contentHandler) throws SAXException JavaDoc {
382         if (getCombinedState().isDisplayingValues()) {
383             getDefinition().generateDisplayData("label", contentHandler);
384         }
385     }
386
387     /**
388      * Generates nested additional content nested inside the main element for this
389      * widget which is generated by {@link #generateSaxFragment(ContentHandler, Locale)}
390      *
391      * The implementation on the AbstractWidget level inserts no additional XML.
392      * Subclasses need to override to insert widget specific content.
393      *
394      * @param contentHandler to send the SAX events to
395      * @param locale in which context potential content needs to be put.
396      * @throws SAXException
397      */

398     protected void generateItemSaxFragment(ContentHandler JavaDoc contentHandler, Locale JavaDoc locale) throws SAXException JavaDoc {
399         // Do nothing
400
}
401
402     /**
403      * The XML element name used in {@link #generateSaxFragment(ContentHandler, Locale)}
404      * to produce the wrapping element for all the XML-instance-content of this Widget.
405      *
406      * @return the main elementname for this widget's sax-fragment.
407      */

408     protected abstract String JavaDoc getXMLElementName();
409
410     /**
411      * The XML attributes used in {@link #generateSaxFragment(ContentHandler, Locale)}
412      * to be placed on the wrapping element for all the XML-instance-content of this Widget.
413      *
414      * This automatically adds @id={@link #getRequestParameterName()} to that element.
415      * Concrete subclasses should call super.getXMLElementAttributes and possibly
416      * add additional attributes.
417      *
418      * Note: the @id is not added for those widgets who's getId() returns <code>null</code>
419      * (e.g. top-level container widgets like 'form'). The contract of returning a non-null
420      * {@link AttributesImpl} is however maintained.
421      *
422      * @return the attributes for the main element for this widget's sax-fragment.
423      */

424     protected AttributesImpl getXMLElementAttributes() {
425         AttributesImpl attrs = new AttributesImpl();
426         // top-level widget-containers like forms will have their id set to ""
427
// for those the @id should not be included.
428
if (getId().length() != 0) {
429             attrs.addCDATAAttribute("id", getRequestParameterName());
430         }
431
432         // Add the "state" attribute if different from active (the default state)
433
WidgetState state = getCombinedState();
434         if (state != WidgetState.ACTIVE) {
435             attrs.addCDATAAttribute("state", getCombinedState().getName());
436         }
437         
438         // Add the "listening" attribute is the value has change listeners
439
if (this instanceof ValueChangedListenerEnabled &&
440             ((ValueChangedListenerEnabled)this).hasValueChangedListeners()) {
441             attrs.addCDATAAttribute("listening", "true");
442         }
443         return attrs;
444     }
445
446     /**
447      * Delegates to the {@link #getDefinition()} of this widget to generate a common
448      * set of 'display' data. (i.e. help, label, hint,...)
449      *
450      * Subclasses should override if the getDefinition can return <code>null</code>
451      * to avoid NPE's.
452      *
453      * @param contentHandler where to send the SAX events to.
454      * @throws SAXException
455      *
456      * @see WidgetDefinition#generateDisplayData(ContentHandler)
457      */

458     protected void generateDisplayData(ContentHandler JavaDoc contentHandler) throws SAXException JavaDoc {
459         getDefinition().generateDisplayData(contentHandler);
460     }
461
462     /**
463      * {@inheritDoc}
464      *
465      * This will generate some standard XML consisting of a simple wrapper
466      * element (name provided by {@link #getXMLElementName()}) with attributes
467      * (provided by {@link #getXMLElementAttributes()} around anything injected
468      * in by both {@link #generateDisplayData(ContentHandler)} and
469      * {@link #generateItemSaxFragment(ContentHandler, Locale)}.
470      *
471      * <pre>
472      * &lt;fi:{@link #getXMLElementName()} {@link #getXMLElementAttributes()} &gt;
473      * {@link #generateDisplayData(ContentHandler)} (i.e. help, label, ...)
474      *
475      * {@link #generateItemSaxFragment(ContentHandler, Locale)}
476      * &lt;/fi:{@link #getXMLElementName()} &gt;
477      * </pre>
478      *
479      * @param contentHandler to send the SAX events to
480      * @param locale in which context potential content needs to be put.
481      * @throws SAXException
482      */

483     public void generateSaxFragment(ContentHandler JavaDoc contentHandler, Locale JavaDoc locale)
484     throws SAXException JavaDoc {
485
486         if (getCombinedState().isDisplayingValues()) {
487             // FIXME: we may want to strip out completely widgets that aren't updated when in AJAX mode
488
String JavaDoc element = this.getXMLElementName();
489             AttributesImpl attrs = getXMLElementAttributes();
490             contentHandler.startElement(FormsConstants.INSTANCE_NS, element, FormsConstants.INSTANCE_PREFIX_COLON + element, attrs);
491
492             generateDisplayData(contentHandler);
493
494             if (locale == null) {
495                 locale = getForm().getLocale();
496             }
497
498             generateItemSaxFragment(contentHandler, locale);
499
500             contentHandler.endElement(FormsConstants.INSTANCE_NS, element, FormsConstants.INSTANCE_PREFIX_COLON + element);
501
502         } else {
503             // Generate a placeholder that can be used later by AJAX updates
504
AttributesImpl attrs = new AttributesImpl();
505             attrs.addCDATAAttribute("id", getRequestParameterName());
506             contentHandler.startElement(FormsConstants.INSTANCE_NS, "placeholder", FormsConstants.INSTANCE_PREFIX_COLON + "placeholder", attrs);
507             contentHandler.endElement(FormsConstants.INSTANCE_NS, "placeholder", FormsConstants.INSTANCE_PREFIX_COLON + "placeholder");
508         }
509     }
510
511     public Object JavaDoc getAttribute(String JavaDoc name) {
512         Object JavaDoc result = null;
513
514         // First check locally
515
if (this.attributes != null) {
516             result = this.attributes.get(name);
517         }
518
519         // Fall back to the definition's attributes
520
if (result == null) {
521             result = getDefinition().getAttribute(name);
522         }
523
524         return result;
525     }
526
527     public void setAttribute(String JavaDoc name, Object JavaDoc value) {
528         if (this.attributes == null) {
529             this.attributes = new HashMap JavaDoc();
530         }
531         this.attributes.put(name, value);
532     }
533
534     public void removeAttribute(String JavaDoc name) {
535         if (this.attributes != null) {
536             this.attributes.remove(name);
537         }
538     }
539
540     public String JavaDoc toString() {
541         String JavaDoc className = this.getClass().getName();
542         int last = className.lastIndexOf('.');
543         if (last != -1) {
544             className = className.substring(last+1);
545         }
546         
547         String JavaDoc name = getRequestParameterName();
548         return name.length() == 0 ? className : className + " '" + getRequestParameterName() + "'";
549     }
550 }
551
Popular Tags