KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > enhydra > barracuda > core > comp > BComponent


1 /*
2  * Copyright (C) 2003 Christian Cryder [christianc@granitepeaks.com]
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2.1 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the Free Software
16  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17  *
18  * $Id: BComponent.java,v 1.35 2004/02/01 05:16:27 christianc Exp $
19  */

20 package org.enhydra.barracuda.core.comp;
21
22 import java.io.*;
23 import java.util.*;
24
25 import org.apache.log4j.*;
26 import org.w3c.dom.*;
27 import org.w3c.dom.html.*;
28
29 import org.enhydra.barracuda.core.comp.renderer.*;
30 import org.enhydra.barracuda.core.comp.renderer.html.*;
31 import org.enhydra.barracuda.core.comp.renderer.xml.*;
32 import org.enhydra.barracuda.core.util.dom.*;
33 import org.enhydra.barracuda.core.view.*;
34 import org.enhydra.barracuda.plankton.data.*;
35
36
37 /**
38  * Defines the base component class from which all other Barracuda
39  * components are derived. Its Swing counterpart would be JComponent.
40  */

41 public class BComponent extends AbstractBComponent {
42     //public constants
43
protected static final Logger logger = Logger.getLogger(BComponent.class.getName());
44
45     //constants
46
public static final String JavaDoc VISIBILITY_MARKER = "visdom";
47
48     //private vars
49
//NOTE: the name must remain null unless specifically set; setting the name
50
//will cause the name attribute to be set in the markup to which the component
51
//is bound (meaning, if the markup specifies a form name that will get overwritten
52
//if you specify a name)
53
// protected String name = this.getClass().getName();
54
protected String JavaDoc name = null;
55     protected boolean visible = true;
56     protected boolean enabled = true;
57     protected Map attrs = null;
58     protected RenderStrategy rs = null; //csc_110201.1
59

60     /**
61      * @clientCardinality 0..*
62      */

63     private View lnkView;
64     
65     //--------------- Renderer -----------------------------------
66
/**
67      * Default component renderer factory registrations
68      */

69     static {
70         HTMLRendererFactory rfHTML = new HTMLRendererFactory();
71         installRendererFactory(rfHTML, BComponent.class, HTMLElement.class);
72         installRendererFactory(rfHTML, BComponent.class, HTMLDocument.class);
73
74         XMLRendererFactory rfXML = new XMLRendererFactory();
75         installRendererFactory(rfXML, BComponent.class, Node.class);
76     }
77
78     /**
79      * HTML RendererFactory
80      */

81     static class HTMLRendererFactory implements RendererFactory {
82         public Renderer getInstance() {return new HTMLComponentRenderer();}
83     }
84
85     /**
86      * XML RendererFactory
87      */

88     static class XMLRendererFactory implements RendererFactory {
89         public Renderer getInstance() {return new XMLComponentRenderer();}
90     }
91
92
93
94     //--------------- BComponent ---------------------------------
95
/**
96      * Set the name for this component.
97      *
98      * <p>Note that for several types of views (HTMLAnchorElement, HTMLAppletElement,
99      * HTMLButtonElement, HTMLFormElement, HTMLFrameElement, HTMLIFrameElement,
100      * HTMLInputElement, HTMLMapElement, HTMLMetaElement, HTMLObjectElement,
101      * HTMLParamElement, HTMLSelectElement, and HTMLTextAreaElement) this property
102      * will be used in rendering if it is actually set. This means that if you set
103      * the component name, and it is bound to a view that is backed by one of these
104      * nodes, then the name attribute in that node will be overridden. So be careful!!!
105      *
106      * @param iname the name for this component
107      */

108     public void setName(String JavaDoc iname) {
109         name = iname;
110     }
111     
112     /**
113      * Get the name for this component
114      *
115      * @return the name for this component
116      */

117     public String JavaDoc getName() {
118         return name;
119     }
120         
121     /**
122      * Set the component visibility
123      *
124      * @param val true if the component should be visible
125      */

126     public void setVisible(boolean val) {
127         setVisible(val, false);
128     }
129     
130     /**
131      * Set the component visibility recursively
132      *
133      * @param val true if the component should be visible
134      * @param recurse true if we want to set this value recursively
135      */

136     public void setVisible(boolean val, boolean recurse) {
137         visible = val;
138         if (recurse) {
139             Iterator it = children.iterator();
140             while (it.hasNext()) {
141                 BContainer child = (BContainer) it.next();
142                 if (child!=null && child instanceof BComponent) {
143                     BComponent wcomp = (BComponent) child;
144                     wcomp.setVisible(val, recurse);
145                 }
146             }
147         }
148         invalidate();
149     }
150     
151     /**
152      * Get the component visibility
153      *
154      * @return true if the component is visible
155      */

156     public boolean isVisible() {
157         return visible;
158     }
159     
160     /**
161      * Enable/disable the component
162      *
163      * @param val true if the component should be enabled
164      */

165     public void setEnabled(boolean val) {
166         setEnabled(val, false);
167     }
168     
169     /**
170      * Enable/disable the component recursively
171      *
172      * @param val true if the component should be enabled
173      * @param recurse true if we want to set this value recursively
174      */

175     public void setEnabled(boolean val, boolean recurse) {
176         enabled = val;
177         if (recurse) {
178             Iterator it = children.iterator();
179             while (it.hasNext()) {
180                 BContainer child = (BContainer) it.next();
181                 if (child!=null && child instanceof BComponent) {
182                     BComponent wcomp = (BComponent) child;
183                     wcomp.setEnabled(val, recurse);
184                 }
185             }
186         }
187         invalidate();
188     }
189
190     /**
191      * See if the component is enabled
192      *
193      * @return true if the component is enabled
194      */

195     public boolean isEnabled() {
196         return enabled;
197     }
198     
199     /**
200      * Set the components primary view. This method effectively
201      * removes any other views and binds the component to the
202      * newly specified view.
203      *
204      * @param view the view to which this component is bound
205      */

206     public void setView(View view) {
207         removeAllViews();
208         addView(view);
209     }
210     
211     /**
212      * Bind a component to a view
213      *
214      * @param view the view to which this component is bound
215      */

216     public void addView(View view) {
217         // dbr_20020415.1_start
218
if (view==null || (views!=null && views.contains(view))) return;
219         if (views==null) {
220             views = new ArrayList();
221         }
222         // dbr_20020415.1_end
223
views.add(view);
224         invalidate();
225     }
226     
227     /**
228      * Remove a view from this component
229      *
230      * @return true if we were able to remove the view from the component
231      */

232     public boolean removeView(View view) {
233         if (view==null) return false;
234         invalidate();
235         // dbr_20020415.2
236
return (views!=null && views.remove(view));
237     }
238     
239     /**
240      * Remove all views from this component
241      */

242     public void removeAllViews() {
243         if (views!=null) views.clear();
244         invalidate();
245     }
246     
247     /**
248      * Get a list of all the views for this component. This returns
249      * a copy of the underlying view list.
250      *
251      * @return a List of all the views for this component
252      */

253     public List getViews() {
254 //jrk_20020414.1_start
255
//make sure we don't send a null views object to the
256
//ArrayList constructor or we'll get a NullPointerException
257
//just return null if views is null
258
//Note: views only seems to be null when the Barracuda libraries are in
259
//the servlet container's common webapp lib directory (eg.. $TOMCAT_HOME/lib)
260
//and not when they exist in the local webapp's WEB-INF/lib directory.
261
//Why is this???? Probably should find the root cause. Likely has to do with
262
//more classloader problems...
263
//see if we have any views
264
if (views!=null) {
265             return new ArrayList(views);
266         }
267         return null;
268 //jrk_20020414.1_end
269
}
270     
271     protected View getFirstView() {
272         //see if we have any views
273
if (views!=null && views.size()>0) {
274             return (View) views.get(0);
275         }
276         
277         //if not, see if the children have any views
278
View firstView = null;
279         if (children!=null) {
280             Iterator it = children.iterator();
281             while (it.hasNext()) {
282                 Object JavaDoc o = it.next();
283                 if (o instanceof BComponent) {
284                     firstView = ((BComponent) o).getFirstView();
285                     if (firstView!=null) break;
286                 }
287             }
288         }
289         return firstView;
290     }
291
292
293     
294     /**
295      * set an attribute for this particular component. When the component
296      * is rendered, component attributes will be shown as element attributes
297      * in the elements that back each of the views associated with this component.
298      * This means that if you set an attribute for the component, it will
299      * affect all views associated with the component.If you wish to set an
300      * attribute for a specific view alone, then you should get the view, find
301      * the node that backs it, and then set the attribute manually that way.
302      *
303      * @param attr the attribute name
304      * @param val the attribute value
305      */

306     public void setAttr(Object JavaDoc attr, Object JavaDoc val) {
307 // if (attrs==null) attrs = new HashMap();
308
if (attrs==null) attrs = new TreeMap();
309         attrs.put(attr,val);
310         invalidate();
311     }
312     
313     /**
314      * get an attribute associated with this particular component. Note that
315      * the attribute map that backs this method only keeps tracks of specific
316      * attributes you have added to the component. It does not look at attributes
317      * that are physically associated with the underlying elements that back each
318      * of the views associated with this component. What this means is that if
319      * the template that backs a view has some attribute "foo" and you try to
320      * see the value of that attribute using this method, you will not be able
321      * to find it unless you have actually associated an attribute named "foo"
322      * with the specific component.
323      *
324      * @param attr the attribute name
325      * @return the value for the given attribute (may be null)
326      */

327     public Object JavaDoc getAttr(Object JavaDoc attr) {
328         if (attrs==null) return null;
329         return attrs.get(attr);
330     }
331     
332     /**
333      * get a copy of the underlying component attribute Map
334      *
335      * @return a copy of the underlying component attribute Map
336      */

337     public Map getAttrMap() {
338         if (attrs==null) return null;
339 // return new HashMap(attrs);
340
return new TreeMap(attrs);
341     }
342
343     //csc_110201.1 - added
344
/**
345      * Set the render strategy for this component and all its children.
346      * Valid values include:
347      * <ul>
348      * <li>RenderStrategy.SCRIPT_AS_NEEDED</li>
349      * <li>RenderStrategy.NEVER_SCRIPT</li>
350      * <li>RenderStrategy.CUSTOM_SCRIPT</li>
351      * <li>null</li>
352      * <ul>
353      *
354      * <p>If the value is null, it will inherit setting from parent. If the
355      * setting for the root parent is null, it will default to
356      * RenderStrategy.DEFAULT_RENDER_STRATEGY
357      *
358      * @param irs the RenderStrategy
359      */

360     public void setRenderStrategy(RenderStrategy irs) {
361         rs = irs;
362     }
363     
364     //csc_110201.1 - added
365
/**
366      * Get the render strategy for this component
367      *
368      * @return the render strategy for this component
369      */

370     public RenderStrategy getRenderStrategy() {
371         if (rs!=null) return rs;
372         else if (parent==null) return RenderStrategy.DEFAULT_RENDER_STRATEGY;
373         else if (parent instanceof BComponent) return ((BComponent) parent).getRenderStrategy();
374         else return RenderStrategy.DEFAULT_RENDER_STRATEGY;
375     }
376         
377     /**
378      * Render the component for a view with the specified
379      * ViewContext. You shouldn't override this method unless
380      * you really know what you're doing...
381      *
382      * @param vc ViewContext for the client view
383      * @throws RenderException if the particular View cannot be rendered
384      */

385     public void render(ViewContext vc) throws RenderException {
386         render(vc, 0);
387     }
388     
389     protected void render(ViewContext vc, int depth) throws RenderException {
390     
391         //if the component is not already validated, re-render it
392
Iterator it = null;
393         if (!validated) {
394             try {
395                 //prepare for rendering
396
if (logger.isInfoEnabled()) logger.info("preparing to render comp "+this.toRef());
397 // preRender(vc, 0);
398
preRender(vc, depth);
399
400                 //add in any temp views
401
if (logger.isInfoEnabled()) logger.info("adding in temp views "+this.toRef());
402                 if (tempViews!=null) {
403                     //jrk_20021018.1 - added null check for views
404
if (views==null) views = new ArrayList();
405                     views.addAll(tempViews);
406                 }
407             
408                 //render our own views
409
if (logger.isDebugEnabled()) logger.debug("rendering our own view in comp "+this.toRef());
410                 View view = null;
411                 it = views.iterator();
412                 while (it.hasNext()) {
413                     view = (View) it.next();
414                     if (view==null) continue;
415                     try {
416                         if (view.getNode() instanceof Element) {
417                             Element el = (Element) view.getNode();
418
419                             //set visibility
420
if (isVisible()) {
421                                 el.removeAttribute(VISIBILITY_MARKER);
422                             } else {
423                                 el.setAttribute(VISIBILITY_MARKER, "false");
424                                 return; //if the component is not visible, there is no need to go any further!!!
425
}
426                         
427                             //set any attributes
428
if (attrs!=null) {
429                                 Iterator it2 = attrs.keySet().iterator();
430                                 while (it2.hasNext()) {
431                                     Object JavaDoc key = it2.next();
432                                     Object JavaDoc val = attrs.get(key);
433                             // Begin ndc_073002.1 - I noticed a problem where I was getting two
434
// attribute of the same name in my html output. In my template html
435
// lets say I had something like this: <img SRC="foo.png" alt="foo image">.
436
// In my model I had some code like this:
437
// BComponent img = new BComponent();
438
// img.setAttr(" alt", "foobar");
439
// return img;
440
//
441
// The resulting html would look like this:
442
// <img SRC="foo.png" alt="foo image" alt="foobar">
443
//
444
// Because of the extra space in " alt" it does not match this
445
// attribute with the attribute in the html template. So instead of
446
// overwriting the attribute, it adds the attribute. I am adding a
447
// trim() to the key.toString() instructions below.
448
if (val==null) {
449                                         el.removeAttribute(key.toString().trim());
450                                     } else {
451                                         el.setAttribute(key.toString().trim(), val.toString());
452                                     }
453                             // End ndc_073002.1
454
}
455                             }
456                         }
457                     
458                         //render the view
459
vc.putState(ViewContext.ELEMENT_FACTORY, view.getElementFactory());
460                         renderView(view, vc, depth+1);
461                     } catch (RenderException e) {
462                         //for now just consume the exceptions
463
logger.warn("RenderException:"+e+" for View:"+view, e);
464                     }
465                 }
466             
467                 //now invoke render in all of our children
468
if (logger.isDebugEnabled()) logger.debug("rendering our children in comp "+this.toRef()+(this.hasChildren() ? "" : "(n/a)"));
469                 BContainer child = null;
470                 it = children.iterator();
471                 while (it.hasNext()) {
472                     child = (BContainer) it.next();
473                     if (child==null) continue;
474                     try {
475                         if (child instanceof BComponent) {
476                             BComponent wcomp = (BComponent) child;
477                             if (wcomp.supports(vc)) {
478                                 wcomp.render(vc, depth+1);
479                             }
480                         }
481                     } catch (RenderException e) {
482                         //for now just consume the exceptions
483
logger.warn("RenderException:"+e+" for Child:"+child, e);
484                     }
485                 }
486             } finally {
487                 //allow for any cleanup after render
488
if (logger.isDebugEnabled()) logger.debug("cleaning up after render in comp "+this.toRef()+(this.hasChildren() ? "" : "(n/a)"));
489 // postRender(vc, 0);
490
postRender(vc, depth);
491
492                 //cleanup any step children
493
removeAllStepChildren();
494
495                 //cleanup any temp views
496
if (tempViews!=null)
497                 if (tempViews!=null) {
498                     it = tempViews.iterator();
499                     while (it.hasNext()) {
500                         View tempView = (View) it.next();
501                         this.removeView(tempView);
502                     }
503                     tempViews = null;
504                 }
505             
506                 //now consider ourselves validated
507
validated = true;
508             }
509         }
510
511         if (logger.isInfoEnabled()) logger.info("rendering complete in comp "+this.toRef());
512     }
513     
514     /**
515      * Prep phase before rendering. This is typically where you would put
516      * any pre-rendering specific logic.
517      */

518 // protected final void preRender(ViewContext vc) {
519
// preRender(vc, 0);
520
// }
521

522     protected void preRender(ViewContext vc, int depth) {
523         //this is actually the method you should override when implementing
524
//pre-render logic
525

526         //(--n/a--)
527
}
528
529     /**
530      * Render a specific view for the component.
531      *
532      * @param view View to be rendered
533      * @param vc ViewContext for the client view
534      * @throws RenderException if the particular View cannot be rendered
535      */

536     protected final void renderView (View view, ViewContext vc) throws RenderException {
537         renderView(view, vc, 0);
538     }
539     
540     protected void renderView (View view, ViewContext vc, int depth) throws RenderException {
541         //021102.3_csc_start - this method didn't used to do anything, which meant that
542
//if you tried to use a plain-jane BComponent to control visibility it would never
543
//actually get rendered. Dumb. THis should make it work now...
544
if (logger.isInfoEnabled()) logger.info("rendering view: "+view);
545
546         //actually render the view according to known interfaces
547
try {
548             Renderer r = getRenderer(view);
549             r.renderComponent(this, view, vc);
550             
551         } catch (DOMException e) {
552             logger.warn("DOM Error:", e);
553             throw new DOMAccessException("Error rendering component in view:"+e, e);
554         }
555         //021102.3_csc_end
556
}
557
558     /**
559      * Cleanup after rendering. This method is guaranteed to be invoked, even
560      * if there is an error during rendering. This is typically where you would
561      * put any custom post-rendering cleanup.
562      */

563 // protected final void postRender(ViewContext vc) {
564
// postRender(vc, 0);
565
// }
566

567     protected void postRender(ViewContext vc, int depth) {
568         //this is actually the method you should override when implementing
569
//post-render logic
570

571         //(--n/a--)
572
}
573
574     /**
575      * Determine if a specific ViewContext is supported
576      * by this component
577      *
578      * @param vc ViewContext for the client view
579      * @return true if the specified ViewCapabilites are supported
580      */

581     public boolean supports(ViewContext vc) {
582         return true;
583     }
584
585 }
586
Popular Tags