KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > beehive > netui > tags > databinding > repeater > Repeater


1 /*
2  * Copyright 2004 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  * $Header:$
17  */

18 package org.apache.beehive.netui.tags.databinding.repeater;
19
20 import javax.servlet.jsp.JspException JavaDoc;
21 import javax.servlet.jsp.tagext.*;
22 import java.util.Iterator JavaDoc;
23 import java.util.List JavaDoc;
24 import java.util.Collections JavaDoc;
25
26 import org.apache.beehive.netui.script.common.DataAccessProviderStack;
27 import org.apache.beehive.netui.script.common.IDataAccessProvider;
28 import org.apache.beehive.netui.tags.AbstractClassicTag;
29 import org.apache.beehive.netui.tags.ExpressionHandling;
30 import org.apache.beehive.netui.tags.databinding.repeater.pad.PadContext;
31 import org.apache.beehive.netui.util.Bundle;
32 import org.apache.beehive.netui.util.exception.LocalizedUnsupportedOperationException;
33 import org.apache.beehive.netui.util.internal.InternalStringBuilder;
34 import org.apache.beehive.netui.util.iterator.IteratorFactory;
35 import org.apache.beehive.netui.util.logging.Logger;
36
37 /**
38  * The <netui-data:repeater> tag is a markup-generic tag that repeats over a data set.
39  * The repeater tag set is used to render data from a data set into a page. The repeater
40  * itself does not render any markup. Instead, the markup from its contained tags is
41  * rendered to create the content generated by this tag. The tags in the repeater tag
42  * set are as follows:
43  * <table border="1" cellspacing="0" cellpadding="5" width="75%">
44  * <tr><td><b>Tag</b></td><td><b>Description</b></td></tr>
45  * <tr><td>{@link RepeaterHeader}</td><td>Renders once in the {@link #HEADER} state.</td></tr>
46  * <tr><td>{@link RepeaterItem}</td><td>Renders once in the {@link #ITEM} state.</td></tr>
47  * <tr><td>{@link RepeaterFooter}</td><td>Renders once in the {@link #FOOTER} state.</td></tr>
48  * <tr><td>{@link org.apache.beehive.netui.tags.databinding.repeater.pad.Pad}</td><td>Used to convert irregular data sets into regular data sets through padding or truncating the output.</td></tr>
49  * </table>
50  * <p>The repeater can render in two modes; the first mode is a simple mode where the body of
51  * the repeater is rendered once for each item in the data set. In this case, none of the
52  * other tags above are present in the repeater body. For example, the following will
53  * render an unordered HTML list of items that are list items which contain the lastName, firstName
54  * of the current "customer" in the data set.</p>
55  * <pre>
56  * &lt;ul&gt;
57  * &lt;netui-data:repeater dataSource="pageInput.customers"&gt;
58  * &lt;li&gt;&lt;netui:span value="${container.item.lastName}, ${container.item.firstName}"/&gt;&lt;/li&gt;
59  * &lt;/netui-data:repeater&gt;
60  * &lt;/ul&gt;
61  * </pre>
62  * <p>The second mode is a more structured mode
63  * of rendering where the tags above are used to delineate iteration boundaries on the body of
64  * a &lt;netui-data:repeater> tag. In this case, if one of the above tags is present,
65  * any content directly in the body of the repeater is not rendered; rather, the content
66  * inside the structured tags of the repeater is rendered.</p>
67  * <p>For example, the following will render the same output as the example
68  * shown above, but it uses the structured tags for rendering the
69  * <code>pageFlow.customers</code> expression:</p>
70  * <pre>
71  * &lt;netui-data:repeater dataSource="pageInput.customers"&gt;
72  * &lt;netui-data:repeaterHeader&gt;
73  * &lt;ul&gt;
74  * &lt;/netui-data:repeaterHeader&gt;
75  * &lt;netui-data:repeaterItem&gt;
76  * &lt;li&gt;&lt;netui:span value="${container.item.lastName}, ${container.item.firstName}"/&gt;&lt;/li&gt;
77  * &lt;/netui-data:repeaterItem&gt;
78  * &lt;netui-data:repeaterFooter&gt;
79  * &lt;/ul&gt;
80  * &lt;/netui-data:repeaterFooter&gt;
81  * &lt;/netui-data:repeater&gt;
82  * </pre>
83  *
84  * @jsptagref.tagdescription <p>Iterates over a data set to render it as HTML.
85  * The HTML is specified either directly within the the body of
86  * &lt;netui-data:repeater> tag or within an associated set of "helper" tags.
87  * The "helper" tags are listed below.
88  * <p/>
89  * <blockquote>
90  * <table border="1" cellspacing="0" cellpadding="5" width="90%">
91  * <tr><td><b>Tag</b></td><td><b>Description</b></td></tr>
92  * <tr><td>{@link RepeaterHeader}</td><td>Renders once at the start of the iteration.</td></tr>
93  * <tr><td>{@link RepeaterItem}</td><td>Renders once for each iteration.</td></tr>
94  * <tr><td>{@link RepeaterFooter}</td><td>Renders once at the end of the iteration.</td></tr>
95  * <tr><td>{@link org.apache.beehive.netui.tags.databinding.repeater.pad.Pad}</td><td>Used to convert
96  * irregular data sets into regular data sets through padding
97  * or truncating the output.</td></tr>
98  * </table>
99  * </blockquote>
100  * <p/>
101  * <p>The &lt;netui-data:repeater> tag can render in two modes; the first mode is a simple mode where the body of
102  * the &lt;netui-data:repeater> tag is rendered once for each item in the data set. In this case, none of the
103  * other tags above are present in the repeater body. For example, the following will
104  * render the items in the "customers" data set as an unordered HTML list.</p>
105  * <p/>
106  * <pre> &lt;ul&gt;
107  * &lt;netui-data:repeater dataSource="pageInput.customers"&gt;
108  * &lt;li&gt;&lt;netui:span value="${container.item.lastName}, ${container.item.firstName}"/&gt;&lt;/li&gt;
109  * &lt;/netui-data:repeater&gt;
110  * &lt;/ul&gt;</pre>
111  * <p/>
112  * <p>The second mode is a more structured mode
113  * of rendering where the "helper" tags
114  * are used to define the rendering of the data set. In this case, if one of the helper tags
115  * is present,
116  * any HTML markup directly in the body of the &lt;netui-data:repeater> tag is not rendered; rather, the HTML markup
117  * inside the helper tags is rendered.</p>
118  * <p>For example, the following will render the same output as the example
119  * shown above, but it uses the "helper" tags for rendering the
120  * HTML markup:</p>
121  * <p/>
122  * <pre>
123  * &lt;netui-data:repeater dataSource="pageInput.customers"&gt;
124  * &lt;netui-data:repeaterHeader&gt;
125  * &lt;ul&gt;
126  * &lt;/netui-data:repeaterHeader&gt;
127  * &lt;netui-data:repeaterItem&gt;
128  * &lt;li&gt;&lt;netui:span value="${container.item.lastName}, ${container.item.firstName}"/&gt;&lt;/li&gt;
129  * &lt;/netui-data:repeaterItem&gt;
130  * &lt;netui-data:repeaterFooter&gt;
131  * &lt;/ul&gt;
132  * &lt;/netui-data:repeaterFooter&gt;
133  * &lt;/netui-data:repeater&gt;</pre>
134  * @example The following sample renders the data set as an HTML table. The table has two columns, "index" and "name",
135  * and each iteration over the data set is rendered as a row of the table.
136  * <p/>
137  * <pre> &lt;netui-data:repeater dataSource="pageInput.myDataSet">
138  * &lt;netui-data:repeaterHeader>
139  * &lt;table border="1">
140  * &lt;tr>
141  * &lt;td>&lt;b>index&lt;/b>&lt;/td>
142  * &lt;td>&lt;b>name&lt;/b>&lt;/td>
143  * &lt;/tr>
144  * &lt;/netui-data:repeaterHeader>
145  * &lt;netui-data:repeaterItem>
146  * &lt;tr>
147  * &lt;td>
148  * &lt;netui:span value="${container.index}" />
149  * &lt;/td>
150  * &lt;td>
151  * &lt;netui: value="${container.item}" />
152  * &lt;/td>
153  * &lt;/tr>
154  * &lt;/netui-data:repeaterItem>
155  * &lt;netui-data:repeaterFooter>
156  * &lt;/table>
157  * &lt;/netui-data:repeaterFooter>
158  * &lt;/netui-data:repeater></pre>
159  * @netui:tag name="repeater" description="A markup-generic tag that repeats over a data set, and renders the data onto the page."
160  */

161 public class Repeater
162         extends AbstractClassicTag
163         implements IDataAccessProvider, TryCatchFinally {
164
165     private static final Logger LOGGER = Logger.getInstance(Repeater.class);
166
167     /**
168      * A Repeater rendering state that signals the beginning of repeater rendering.
169      */

170     public static final int INIT = 0;
171
172     /**
173      * A Repeater rendering state that signals the rendering of the HEADER.
174      * The body renders in the HEADER state once.
175      */

176     public static final int HEADER = 1;
177
178     /**
179      * A Repeater rendering state that signals the rendering of the ITEM.
180      * The body renders in the ITEM state once for each item in the
181      * data set.
182      */

183     public static final int ITEM = 2;
184
185     /**
186      * A Repeater rendering state that signals the rendering of the FOOTER.
187      * The body renders in the FOOTER state once.
188      */

189     public static final int FOOTER = 3;
190
191     /**
192      * A Repeater rendering state that signals the end of repeater rendering.
193      */

194     public static final int END = 4;
195
196     private boolean _ignoreNulls = false;
197     private boolean _haveKids = false;
198     private boolean _containerInPageContext = false;
199     private int _currentIndex = -1;
200     private int _renderedItems = 0;
201     private int _renderState = INIT;
202     private Object JavaDoc _defaultText = null;
203     private Object JavaDoc _currentItem = null;
204     private String JavaDoc _dataSource = null;
205     private Iterator JavaDoc _iterator = null;
206     private PadContext _padContext = null;
207     private InternalStringBuilder _contentBuffer = null;
208
209     /**
210      * Get the name of this tag. This is used to identify the type of this tag
211      * for reporting tag errors.
212      *
213      * @return a constant String representing the name of this tag.
214      */

215     public String JavaDoc getTagName() {
216         return "Repeater";
217     }
218
219     /**
220      * Set a boolean that describes whether the repeater should ignore null
221      * items encountered while iterating over a data set.
222      *
223      * @param ignoreNulls whether or not to ignore nulls
224      * @jsptagref.attributedescription
225      * Boolean. If set to true, any null iteration items in the data set will be ignored.
226      * @jsptagref.attributesyntaxvalue <i>boolean_ignoreNulls</i>
227      * @netui:attribute required="false"
228      */

229     public void setIgnoreNulls(boolean ignoreNulls) {
230         _ignoreNulls = ignoreNulls;
231     }
232
233     /**
234      * @param padContext
235      */

236     public void setPadContext(PadContext padContext) {
237         if(_padContext == null)
238             _padContext = padContext;
239
240         LOGGER.debug("Repeater has a padContext with text: " + _padContext);
241
242         return;
243     }
244
245     /**
246      * Set the text that will be rendered if the dataSource expression
247      * references a null object and the defaultText attribute is non-null.
248      *
249      * @param defaultText the default text
250      * @jsptagref.attributedescription
251      * The text to render if the <code>dataSource</code> attribute references a null data set.
252      * @jsptagref.attributesyntaxvalue <i>string_defaultText</i>
253      * @netui:attribute required="false" rtexprvalue="true"
254      */

255     public void setDefaultText(Object JavaDoc defaultText) {
256         _defaultText = defaultText;
257     }
258
259     /**
260      * Get the index of the current iteration through the body of this tag. This
261      * data can be accessed using the expression <code>container.index</code>
262      * on an attribute of a databindable NetUI tag that is contained within the
263      * repeating body of this tag. This expression is only valid when the dataset
264      * is being rendered.
265      *
266      * @return the integer index of the current data item in the data set
267      * @see org.apache.beehive.netui.script.common.IDataAccessProvider
268      */

269     public int getCurrentIndex() {
270         return _currentIndex;
271     }
272
273     /**
274      * Get the item that is currently being rendered by this repeating tag.
275      * This can be accessed using the expression <code>expression.item</code>
276      * on an attribute of a databindable NetUI tag that is contained within
277      * the repeating body of this tag. The expression is only valid when the dataset
278      * is being rendered.
279      *
280      * @return the current item in the data set
281      * @see org.apache.beehive.netui.script.common.IDataAccessProvider
282      */

283     public Object JavaDoc getCurrentItem() {
284         return _currentItem;
285     }
286
287     /**
288      * Get the metadata for the current item. This method is not supported by
289      * this tag.
290      *
291      * @throws UnsupportedOperationException this tag does not support this method from the IDataAccessProvider interface
292      * @see org.apache.beehive.netui.script.common.IDataAccessProvider
293      */

294     public Object JavaDoc getCurrentMetadata() {
295         LocalizedUnsupportedOperationException uoe =
296                 new LocalizedUnsupportedOperationException("The " + getTagName() + "does not export metadata for its iterated items.");
297         uoe.setLocalizedMessage(Bundle.getErrorString("Tags_DataAccessProvider_metadataUnsupported", new Object JavaDoc[]{getTagName()}));
298         throw uoe;
299     }
300
301     /**
302      * Get the parent IDataAccessProvider for this tag. If this tag is contained within
303      * a IDataAccessProvider, the containing IDataAccessProvider is available through the
304      * expression <code>container.container</code>. Any valid properties of the
305      * parent IDataAccessProvider can be accessed through this expression. This method
306      * will return null if there is no parent IDataAccessProvider
307      *
308      * @return a containing IDataAccessProvider if one exists, null otherwise.
309      * @see org.apache.beehive.netui.script.common.IDataAccessProvider
310      */

311     public IDataAccessProvider getProviderParent() {
312         IDataAccessProvider dap = (IDataAccessProvider)SimpleTagSupport.findAncestorWithClass(this, IDataAccessProvider.class);
313         return dap;
314     }
315
316     /**
317      * Get the current render state for the repeater. This tag is used by child tags
318      * to access the current location in the repeater's rendering lifecycle.
319      *
320      * @return an integer that represents the current state of the grid; this is one
321      * of {@link #INIT}, {@link #HEADER}, {@link #ITEM},{@link #FOOTER}, or {@link #END}.
322      */

323     public int getRenderState() {
324         return _renderState;
325     }
326
327     /**
328      * Add content to the content that is being buffered by this tag. All content
329      * written by the body of this tag is added to this buffer. The buffer is rendered
330      * at the end of the tag's lifecycle if no fatal errors have occurred during this
331      * tag's lifecycle.
332      *
333      * @param content content that this tag should render.
334      */

335     public void addContent(String JavaDoc content) {
336         if(_contentBuffer == null) {
337             int size = (content != null ? (5 * content.length()) : 1024);
338             _contentBuffer = new InternalStringBuilder(size);
339         }
340
341         _contentBuffer.append(content);
342     }
343
344     /**
345      * Method used by tags in the repeater tag set to register the presence of a contained
346      * tag. When registered, the repeater will change the way in which it renders to
347      * either use structured or non-structured rendering.
348      * @param repeaterComponent {@link RepeaterComponent} to register with the Repeater parent
349      */

350     public void registerChildTag(RepeaterComponent repeaterComponent) {
351         _haveKids = true;
352     }
353
354     /**
355      * Start rendering the repeater.
356      * @return {@link #SKIP_BODY} if an error occurs; {@link #EVAL_BODY_BUFFERED} otherwise
357      * @throws JspException if an error occurs that can not be reported in the page
358      */

359     public int doStartTag()
360             throws JspException JavaDoc {
361         Object JavaDoc source = evaluateDataSource();
362
363         // report any errors that may have occured
364
if(hasErrors())
365             return SKIP_BODY;
366
367         _renderState = INIT;
368
369         boolean empty = false;
370         if(source != null) {
371             _iterator = IteratorFactory.createIterator(source);
372             if(_iterator == null) {
373                 LOGGER.warn(Bundle.getString("Tags_Repeater_nullIterator"));
374                 _iterator = Collections.EMPTY_LIST.iterator();
375             }
376
377             if(_iterator.hasNext()) {
378                 _currentIndex = 0;
379                 _currentItem = _iterator.next();
380
381                 if(_ignoreNulls && _currentItem == null) {
382                     /*
383                       doStartTag doesn't know if the repeater is structured or unstructured
384                       thus, if ignoreNulls is true, it's going to make an attempt to go
385                       through the body with a non-null item. if there are no non-null
386                       items in the data structure, the doAfterBody method will handle
387                       this correctly, but a data structure with a null first item
388                       will render the same as a data structure with a null second item
389                      */

390                     advanceToNonNullItem();
391
392                     /*
393                        this null check needs to re-run here because the advanceToNonNullItem method
394                        side-effects the _currentItem.
395                      */

396                     if(_currentItem == null)
397                         empty = true;
398                 }
399             }
400             /* there is no data set of there are zero items in the iterator */
401             else empty = true;
402         }
403         /* the dataSource evaluated to null */
404         else {
405             _iterator = Collections.EMPTY_LIST.iterator();
406             empty = true;
407         }
408
409         if(empty) {
410             /* if the defaultText attribute is non-null, it will be evaluated as an expression and rendered to the page */
411             if(_defaultText != null)
412                 addContent(_defaultText.toString());
413             return SKIP_BODY;
414         }
415         else {
416             DataAccessProviderStack.addDataAccessProvider(this, pageContext);
417             _containerInPageContext = true;
418             return EVAL_BODY_BUFFERED;
419         }
420     }
421
422     /**
423      * <p>
424      * Continue rendering the repeater changing the render state or advancing to a new data item
425      * as needed.
426      * </p>
427      * @return {@link #SKIP_BODY} if an error occurs or the data set has been rendered; {@link #EVAL_BODY_AGAIN} otherwise
428      */

429     public int doAfterBody() {
430
431         if(hasErrors())
432             return SKIP_BODY;
433
434         LOGGER.debug("structured repeater: " + _haveKids + " render state: " + renderStateToString(_renderState));
435
436         /*
437           structured rendering of the repeater; body content is ignored and real
438           content is rendered through cooperating nested tags
439          */

440         if(_haveKids)
441             return renderStructured();
442         /*
443           unstructured rendering of the repeater; this means that there isn't
444           a repeater(Header|Item|Footer) inside the body of the repeater.
445          */

446         else {
447             if(bodyContent != null) {
448                 addContent(bodyContent.getString());
449                 bodyContent.clearBody();
450             }
451
452             if(_iterator.hasNext()) {
453                 _currentIndex++;
454                 _currentItem = _iterator.next();
455
456                 if(_ignoreNulls && _currentItem == null) {
457                     advanceToNonNullItem();
458
459                     /* ignoring null items and no more non-null items, so skip to doEndTag() */
460                     if(_currentItem == null)
461                         return SKIP_BODY;
462                 }
463
464                 /* found another item; re-render the repeater's body */
465                 return EVAL_BODY_AGAIN;
466             }
467             /* no more items; skip to doEndTag() */
468             else return SKIP_BODY;
469         }
470     }
471
472     /**
473      * Complete rendering the repeater.
474      * @return {@link #EVAL_PAGE}
475      * @throws JspException if an error occurs that can not be reported in the page
476      */

477     public int doEndTag()
478             throws JspException JavaDoc {
479
480         if(hasErrors())
481             reportErrors();
482         else if(_contentBuffer != null)
483             write(_contentBuffer.toString());
484
485         return EVAL_PAGE;
486     }
487
488     public void doFinally() {
489         localRelease();
490     }
491
492     public void doCatch(Throwable JavaDoc t)
493             throws Throwable JavaDoc {
494         throw t;
495     }
496
497     /**
498      * Reset all of the fields of this tag.
499      */

500     protected void localRelease() {
501         super.localRelease();
502         _currentItem = null;
503         _currentIndex = -1;
504         _iterator = null;
505         _defaultText = null;
506         _renderState = INIT;
507         _haveKids = false;
508         _contentBuffer = null;
509         _padContext = null;
510         _ignoreNulls = false;
511         _renderedItems = 0;
512         if(_containerInPageContext) {
513             DataAccessProviderStack.removeDataAccessProvider(pageContext);
514             _containerInPageContext = false;
515         }
516     }
517
518     /**
519      * Render a the repeater using the full repeater lifecycle. This
520      * method is executed after each pass through the body if there
521      * are tags from the repeater tag set in the body of this repeater.
522      * This method ensures that the repeater tag runs as a full state
523      * machine for these tags.
524      *
525      * @return EVAL_BODY_AGAIN unless the lifecycle has completed; then return SKIP_BODY
526      */

527     /* todo: perf -- optimize the number of trips through the body by ignoring the header / footer when necessary */
528     private int renderStructured() {
529         if(LOGGER.isDebugEnabled() && _padContext != null)
530             LOGGER.debug("\ncurrentIndex: " + _currentIndex + "\n" +
531                     "checkMaxRepeat: " + _padContext.checkMaxRepeat(_currentIndex) + "\n" +
532                     "checkMinRepeat: " + _padContext.checkMinRepeat(_currentIndex) + "\n");
533
534         if(_renderState == INIT) {
535             _renderState = HEADER;
536             return EVAL_BODY_AGAIN;
537         }
538
539         if(_renderState == HEADER) {
540             assert _renderedItems == 0;
541
542             /* this would only happen if Pad.maxRepeat == 0 */
543             if(_padContext != null && _padContext.checkMaxRepeat(_renderedItems)) {
544                 _renderState = FOOTER;
545                 return EVAL_BODY_AGAIN;
546             }
547
548             if(_currentItem == null && _ignoreNulls) {
549                 advanceToNonNullItem();
550
551                 /* no non-null item was found; render the footer */
552                 if(_currentItem == null) {
553                     doPadding();
554                     // render the header
555
_renderState = FOOTER;
556                 }
557                 /* non-null item found; it's not the 0th item; render it */
558                 else _renderState = ITEM;
559             }
560             /* 0th item is non-null; render it */
561             else _renderState = ITEM;
562
563             return EVAL_BODY_AGAIN;
564         }
565
566         if(_renderState == ITEM) {
567             _renderedItems++;
568
569             /* check that the maximum number of items to render has *not* been reached */
570                 if(_iterator.hasNext() && (_padContext == null || (_padContext != null && !_padContext.checkMaxRepeat(_renderedItems)))) {
571                 _currentIndex++;
572                 _currentItem = _iterator.next();
573
574                 if(_ignoreNulls && _currentItem == null) {
575                     advanceToNonNullItem();
576
577                     /* last item */
578                     if(_currentItem == null) {
579                         doPadding();
580
581                         /* render the header */
582                         _renderState = FOOTER;
583                         return EVAL_BODY_AGAIN;
584                     }
585                 }
586
587                 /* if _ignoreNulls is false, the _currentItem may be null here */
588                 return EVAL_BODY_AGAIN;
589             }
590             /*
591              have finished rendering items for some reason:
592               1) there isn't a next item
593               2) reached the maximum number of items to render
594               So:
595               1) pad if necessary
596               2) render the footer
597              */

598             else {
599                 doPadding();
600
601                 _renderState = FOOTER;
602                 return EVAL_BODY_AGAIN;
603             }
604         }
605
606         if(_renderState == FOOTER) {
607             _renderState = END;
608             return SKIP_BODY;
609         }
610
611         return SKIP_BODY;
612     }
613
614     /**
615      * <p/>
616      * Internal utility method.
617      * </p>
618      * </p>
619      * This is called in places where the repeater needs to move to the next
620      * non-null item in the data set to render. This occurs when the
621      * current data item is null and the <code>ignoreNulls</code>
622      * flag has been set to skip rendering null items in the data set.
623      * </p>
624      * <p/>
625      * This method side-effects to advance the iterator to the next
626      * non-null item or the end if there are zero remaining non-null
627      * items.
628      * </p>
629      * <p/>
630      * At the end, the <code>currentItem</code> may be null, and the
631      * <code>currentIndex</code> will reference either the integer
632      * location in the data structure of the non-null data item, or
633      * it will reference the end of the data structure.
634      * </p>
635      */

636     private final void advanceToNonNullItem() {
637         assert _iterator != null;
638         assert _currentItem == null;
639
640         while(_iterator.hasNext() && _currentItem == null) {
641             _currentItem = _iterator.next();
642             _currentIndex++;
643         }
644     }
645
646     /**
647      * When using the repeater's pad tag, it is possible to require a minimum number of
648      * items render in the repeater. This method pads out the number of items until it
649      * reaches the {@link org.apache.beehive.netui.tags.databinding.repeater.pad.PadContext}'s
650      * <code>minRepeat</code> property.
651      */

652     private final void doPadding() {
653         if(_padContext != null && !_padContext.checkMinRepeat(_renderedItems)) {
654             /*
655               since padding is now running, un-set the current item so that the last
656               item isn't accessible during any later data binding
657              */

658             _currentItem = null;
659
660             for(int i = _renderedItems; !_padContext.checkMinRepeat(i); i++) {
661                 _currentIndex++;
662                 addContent(_padContext.getPadText());
663             }
664         }
665     }
666
667     /**
668      * An internal method that turns the current render state into a string for debugging.
669      */

670     private static final String JavaDoc renderStateToString(int state) {
671         switch(state) {
672             case INIT:
673                 return "INIT";
674             case HEADER:
675                 return "HEADER";
676             case ITEM:
677                 return "ITEM";
678             case FOOTER:
679                 return "FOOTER";
680             case END:
681                 return "END";
682             default:
683                 return "INVALID STATE";
684         }
685     }
686
687     /**
688      * Return an <code>ArrayList</code> which represents a chain of <code>INameInterceptor</code>
689      * objects. This method by default returns <code>null</code> and should be overridden
690      * by objects that support naming.
691      * @return an <code>ArrayList</code> that will contain <code>INameInterceptor</code> objects.
692      */

693     protected List JavaDoc getNamingChain() {
694         return AbstractClassicTag.DefaultNamingChain;
695     }
696
697     /**
698      * Sets the tag's data source (can be an expression).
699      * @param dataSource - the data source
700      * @jsptagref.attributedescription <p>The <code>dataSource</code> attribute determines both
701      * (1) the source of populating data for the tag and
702      * (2) the object to which the tag submits data.
703      *
704      * <p>For example, assume that the Controller file (= JPF file) contains
705      * a Form Bean with the property foo. Then the following &lt;netui:textBox> tag will
706      * (1) draw populating data from the Form Bean's foo property and (2)
707      * submit user defined data to the same property.
708      *
709      * <p>&nbsp;&nbsp;&nbsp;&nbsp;<code>&lt;netui:textBox dataSource="actionForm.foo" /></code>
710      *
711      * <p>The <code>dataSource</code> attribute takes either a data binding expression or
712      * the name of a Form Bean property. In the
713      * above example, <code>&lt;netui:textBox dataSource="foo" /></code>
714      * would have the exactly same behavior.
715      *
716      * <p>When the tag is used to submit data, the data binding expression must
717      * refer to a Form Bean property.
718      * In cases where the tag is not used to submit data, but is used for
719      * displaying data only, the data
720      * binding expression need not refer to a Form Bean property. For example,
721      * assume that myIterativeData is a member variable on
722      * the Controller file ( = JPF file). The following &lt;netui-data:repeater>
723      * tag draws its data from myIterativeData.
724      *
725      * <p>&nbsp;&nbsp;&nbsp;&nbsp;<code>&lt;netui-data:repeater dataSource="pageFlow.myIterativeData"></code>
726      * @jsptagref.attributesyntaxvalue <i>expression_datasource</i>
727      * @netui:attribute required="true"
728      */

729     public void setDataSource(String JavaDoc dataSource) {
730         _dataSource = dataSource;
731     }
732
733     /**
734      * Gets the tag's data source (can be an expression).
735      * @return the data source
736      */

737     public String JavaDoc getDataSource() {
738         return "{" + _dataSource + "}";
739     }
740
741     /**
742      * Return the Object that is represented by the specified data source.
743      * @return Object
744      * @throws JspException
745      */

746     private Object JavaDoc evaluateDataSource()
747             throws JspException JavaDoc {
748         ExpressionHandling expr = new ExpressionHandling(this);
749         String JavaDoc dataSource = getDataSource();
750         String JavaDoc ds = expr.ensureValidExpression(dataSource, "dataSource", "DataSourceError");
751         if (ds == null)
752             return null;
753
754         /* have a valid expression */
755         return expr.evaluateExpression(dataSource, "dataSource", pageContext);
756     }
757 }
758
Popular Tags