001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: *
017: * $Header:$
018: */
019: package org.apache.beehive.netui.tags.databinding.repeater;
020:
021: import javax.servlet.jsp.JspException;
022: import javax.servlet.jsp.tagext.*;
023: import java.util.Iterator;
024: import java.util.List;
025: import java.util.Collections;
026:
027: import org.apache.beehive.netui.script.common.DataAccessProviderStack;
028: import org.apache.beehive.netui.script.common.IDataAccessProvider;
029: import org.apache.beehive.netui.tags.AbstractClassicTag;
030: import org.apache.beehive.netui.tags.ExpressionHandling;
031: import org.apache.beehive.netui.tags.databinding.repeater.pad.PadContext;
032: import org.apache.beehive.netui.util.Bundle;
033: import org.apache.beehive.netui.util.exception.LocalizedUnsupportedOperationException;
034: import org.apache.beehive.netui.util.internal.InternalStringBuilder;
035: import org.apache.beehive.netui.util.iterator.IteratorFactory;
036: import org.apache.beehive.netui.util.logging.Logger;
037:
038: /**
039: * The <netui-data:repeater> tag is a markup-generic tag that repeats over a data set.
040: * The repeater tag set is used to render data from a data set into a page. The repeater
041: * itself does not render any markup. Instead, the markup from its contained tags is
042: * rendered to create the content generated by this tag. The tags in the repeater tag
043: * set are as follows:
044: * <table border="1" cellspacing="0" cellpadding="5" width="75%">
045: * <tr><td><b>Tag</b></td><td><b>Description</b></td></tr>
046: * <tr><td>{@link RepeaterHeader}</td><td>Renders once in the {@link #HEADER} state.</td></tr>
047: * <tr><td>{@link RepeaterItem}</td><td>Renders once in the {@link #ITEM} state.</td></tr>
048: * <tr><td>{@link RepeaterFooter}</td><td>Renders once in the {@link #FOOTER} state.</td></tr>
049: * <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>
050: * </table>
051: * <p>The repeater can render in two modes; the first mode is a simple mode where the body of
052: * the repeater is rendered once for each item in the data set. In this case, none of the
053: * other tags above are present in the repeater body. For example, the following will
054: * render an unordered HTML list of items that are list items which contain the lastName, firstName
055: * of the current "customer" in the data set.</p>
056: * <pre>
057: * <ul>
058: * <netui-data:repeater dataSource="pageInput.customers">
059: * <li><netui:span value="${container.item.lastName}, ${container.item.firstName}"/></li>
060: * </netui-data:repeater>
061: * </ul>
062: * </pre>
063: * <p>The second mode is a more structured mode
064: * of rendering where the tags above are used to delineate iteration boundaries on the body of
065: * a <netui-data:repeater> tag. In this case, if one of the above tags is present,
066: * any content directly in the body of the repeater is not rendered; rather, the content
067: * inside the structured tags of the repeater is rendered.</p>
068: * <p>For example, the following will render the same output as the example
069: * shown above, but it uses the structured tags for rendering the
070: * <code>pageFlow.customers</code> expression:</p>
071: * <pre>
072: * <netui-data:repeater dataSource="pageInput.customers">
073: * <netui-data:repeaterHeader>
074: * <ul>
075: * </netui-data:repeaterHeader>
076: * <netui-data:repeaterItem>
077: * <li><netui:span value="${container.item.lastName}, ${container.item.firstName}"/></li>
078: * </netui-data:repeaterItem>
079: * <netui-data:repeaterFooter>
080: * </ul>
081: * </netui-data:repeaterFooter>
082: * </netui-data:repeater>
083: * </pre>
084: *
085: * @jsptagref.tagdescription <p>Iterates over a data set to render it as HTML.
086: * The HTML is specified either directly within the the body of
087: * <netui-data:repeater> tag or within an associated set of "helper" tags.
088: * The "helper" tags are listed below.
089: * <p/>
090: * <blockquote>
091: * <table border="1" cellspacing="0" cellpadding="5" width="90%">
092: * <tr><td><b>Tag</b></td><td><b>Description</b></td></tr>
093: * <tr><td>{@link RepeaterHeader}</td><td>Renders once at the start of the iteration.</td></tr>
094: * <tr><td>{@link RepeaterItem}</td><td>Renders once for each iteration.</td></tr>
095: * <tr><td>{@link RepeaterFooter}</td><td>Renders once at the end of the iteration.</td></tr>
096: * <tr><td>{@link org.apache.beehive.netui.tags.databinding.repeater.pad.Pad}</td><td>Used to convert
097: * irregular data sets into regular data sets through padding
098: * or truncating the output.</td></tr>
099: * </table>
100: * </blockquote>
101: * <p/>
102: * <p>The <netui-data:repeater> tag can render in two modes; the first mode is a simple mode where the body of
103: * the <netui-data:repeater> tag is rendered once for each item in the data set. In this case, none of the
104: * other tags above are present in the repeater body. For example, the following will
105: * render the items in the "customers" data set as an unordered HTML list.</p>
106: * <p/>
107: * <pre> <ul>
108: * <netui-data:repeater dataSource="pageInput.customers">
109: * <li><netui:span value="${container.item.lastName}, ${container.item.firstName}"/></li>
110: * </netui-data:repeater>
111: * </ul></pre>
112: * <p/>
113: * <p>The second mode is a more structured mode
114: * of rendering where the "helper" tags
115: * are used to define the rendering of the data set. In this case, if one of the helper tags
116: * is present,
117: * any HTML markup directly in the body of the <netui-data:repeater> tag is not rendered; rather, the HTML markup
118: * inside the helper tags is rendered.</p>
119: * <p>For example, the following will render the same output as the example
120: * shown above, but it uses the "helper" tags for rendering the
121: * HTML markup:</p>
122: * <p/>
123: * <pre>
124: * <netui-data:repeater dataSource="pageInput.customers">
125: * <netui-data:repeaterHeader>
126: * <ul>
127: * </netui-data:repeaterHeader>
128: * <netui-data:repeaterItem>
129: * <li><netui:span value="${container.item.lastName}, ${container.item.firstName}"/></li>
130: * </netui-data:repeaterItem>
131: * <netui-data:repeaterFooter>
132: * </ul>
133: * </netui-data:repeaterFooter>
134: * </netui-data:repeater></pre>
135: * @example The following sample renders the data set as an HTML table. The table has two columns, "index" and "name",
136: * and each iteration over the data set is rendered as a row of the table.
137: * <p/>
138: * <pre> <netui-data:repeater dataSource="pageInput.myDataSet">
139: * <netui-data:repeaterHeader>
140: * <table border="1">
141: * <tr>
142: * <td><b>index</b></td>
143: * <td><b>name</b></td>
144: * </tr>
145: * </netui-data:repeaterHeader>
146: * <netui-data:repeaterItem>
147: * <tr>
148: * <td>
149: * <netui:span value="${container.index}" />
150: * </td>
151: * <td>
152: * <netui: value="${container.item}" />
153: * </td>
154: * </tr>
155: * </netui-data:repeaterItem>
156: * <netui-data:repeaterFooter>
157: * </table>
158: * </netui-data:repeaterFooter>
159: * </netui-data:repeater></pre>
160: * @netui:tag name="repeater" description="A markup-generic tag that repeats over a data set, and renders the data onto the page."
161: */
162: public class Repeater extends AbstractClassicTag implements
163: IDataAccessProvider, TryCatchFinally {
164:
165: private static final Logger LOGGER = Logger
166: .getInstance(Repeater.class);
167:
168: /**
169: * A Repeater rendering state that signals the beginning of repeater rendering.
170: */
171: public static final int INIT = 0;
172:
173: /**
174: * A Repeater rendering state that signals the rendering of the HEADER.
175: * The body renders in the HEADER state once.
176: */
177: public static final int HEADER = 1;
178:
179: /**
180: * A Repeater rendering state that signals the rendering of the ITEM.
181: * The body renders in the ITEM state once for each item in the
182: * data set.
183: */
184: public static final int ITEM = 2;
185:
186: /**
187: * A Repeater rendering state that signals the rendering of the FOOTER.
188: * The body renders in the FOOTER state once.
189: */
190: public static final int FOOTER = 3;
191:
192: /**
193: * A Repeater rendering state that signals the end of repeater rendering.
194: */
195: public static final int END = 4;
196:
197: private boolean _ignoreNulls = false;
198: private boolean _haveKids = false;
199: private boolean _containerInPageContext = false;
200: private int _currentIndex = -1;
201: private int _renderedItems = 0;
202: private int _renderState = INIT;
203: private Object _defaultText = null;
204: private Object _currentItem = null;
205: private String _dataSource = null;
206: private Iterator _iterator = null;
207: private PadContext _padContext = null;
208: private InternalStringBuilder _contentBuffer = null;
209:
210: /**
211: * Get the name of this tag. This is used to identify the type of this tag
212: * for reporting tag errors.
213: *
214: * @return a constant String representing the name of this tag.
215: */
216: public String getTagName() {
217: return "Repeater";
218: }
219:
220: /**
221: * Set a boolean that describes whether the repeater should ignore null
222: * items encountered while iterating over a data set.
223: *
224: * @param ignoreNulls whether or not to ignore nulls
225: * @jsptagref.attributedescription
226: * Boolean. If set to true, any null iteration items in the data set will be ignored.
227: * @jsptagref.attributesyntaxvalue <i>boolean_ignoreNulls</i>
228: * @netui:attribute required="false"
229: */
230: public void setIgnoreNulls(boolean ignoreNulls) {
231: _ignoreNulls = ignoreNulls;
232: }
233:
234: /**
235: * @param padContext
236: */
237: public void setPadContext(PadContext padContext) {
238: if (_padContext == null)
239: _padContext = padContext;
240:
241: LOGGER.debug("Repeater has a padContext with text: "
242: + _padContext);
243:
244: return;
245: }
246:
247: /**
248: * Set the text that will be rendered if the dataSource expression
249: * references a null object and the defaultText attribute is non-null.
250: *
251: * @param defaultText the default text
252: * @jsptagref.attributedescription
253: * The text to render if the <code>dataSource</code> attribute references a null data set.
254: * @jsptagref.attributesyntaxvalue <i>string_defaultText</i>
255: * @netui:attribute required="false" rtexprvalue="true"
256: */
257: public void setDefaultText(Object defaultText) {
258: _defaultText = defaultText;
259: }
260:
261: /**
262: * Get the index of the current iteration through the body of this tag. This
263: * data can be accessed using the expression <code>container.index</code>
264: * on an attribute of a databindable NetUI tag that is contained within the
265: * repeating body of this tag. This expression is only valid when the dataset
266: * is being rendered.
267: *
268: * @return the integer index of the current data item in the data set
269: * @see org.apache.beehive.netui.script.common.IDataAccessProvider
270: */
271: public int getCurrentIndex() {
272: return _currentIndex;
273: }
274:
275: /**
276: * Get the item that is currently being rendered by this repeating tag.
277: * This can be accessed using the expression <code>expression.item</code>
278: * on an attribute of a databindable NetUI tag that is contained within
279: * the repeating body of this tag. The expression is only valid when the dataset
280: * is being rendered.
281: *
282: * @return the current item in the data set
283: * @see org.apache.beehive.netui.script.common.IDataAccessProvider
284: */
285: public Object getCurrentItem() {
286: return _currentItem;
287: }
288:
289: /**
290: * Get the metadata for the current item. This method is not supported by
291: * this tag.
292: *
293: * @throws UnsupportedOperationException this tag does not support this method from the IDataAccessProvider interface
294: * @see org.apache.beehive.netui.script.common.IDataAccessProvider
295: */
296: public Object getCurrentMetadata() {
297: LocalizedUnsupportedOperationException uoe = new LocalizedUnsupportedOperationException(
298: "The "
299: + getTagName()
300: + "does not export metadata for its iterated items.");
301: uoe.setLocalizedMessage(Bundle.getErrorString(
302: "Tags_DataAccessProvider_metadataUnsupported",
303: new Object[] { getTagName() }));
304: throw uoe;
305: }
306:
307: /**
308: * Get the parent IDataAccessProvider for this tag. If this tag is contained within
309: * a IDataAccessProvider, the containing IDataAccessProvider is available through the
310: * expression <code>container.container</code>. Any valid properties of the
311: * parent IDataAccessProvider can be accessed through this expression. This method
312: * will return null if there is no parent IDataAccessProvider
313: *
314: * @return a containing IDataAccessProvider if one exists, null otherwise.
315: * @see org.apache.beehive.netui.script.common.IDataAccessProvider
316: */
317: public IDataAccessProvider getProviderParent() {
318: IDataAccessProvider dap = (IDataAccessProvider) SimpleTagSupport
319: .findAncestorWithClass(this , IDataAccessProvider.class);
320: return dap;
321: }
322:
323: /**
324: * Get the current render state for the repeater. This tag is used by child tags
325: * to access the current location in the repeater's rendering lifecycle.
326: *
327: * @return an integer that represents the current state of the grid; this is one
328: * of {@link #INIT}, {@link #HEADER}, {@link #ITEM},{@link #FOOTER}, or {@link #END}.
329: */
330: public int getRenderState() {
331: return _renderState;
332: }
333:
334: /**
335: * Add content to the content that is being buffered by this tag. All content
336: * written by the body of this tag is added to this buffer. The buffer is rendered
337: * at the end of the tag's lifecycle if no fatal errors have occurred during this
338: * tag's lifecycle.
339: *
340: * @param content content that this tag should render.
341: */
342: public void addContent(String content) {
343: if (_contentBuffer == null) {
344: int size = (content != null ? (5 * content.length()) : 1024);
345: _contentBuffer = new InternalStringBuilder(size);
346: }
347:
348: _contentBuffer.append(content);
349: }
350:
351: /**
352: * Method used by tags in the repeater tag set to register the presence of a contained
353: * tag. When registered, the repeater will change the way in which it renders to
354: * either use structured or non-structured rendering.
355: * @param repeaterComponent {@link RepeaterComponent} to register with the Repeater parent
356: */
357: public void registerChildTag(RepeaterComponent repeaterComponent) {
358: _haveKids = true;
359: }
360:
361: /**
362: * Start rendering the repeater.
363: * @return {@link #SKIP_BODY} if an error occurs; {@link #EVAL_BODY_BUFFERED} otherwise
364: * @throws JspException if an error occurs that can not be reported in the page
365: */
366: public int doStartTag() throws JspException {
367: Object source = evaluateDataSource();
368:
369: // report any errors that may have occured
370: if (hasErrors())
371: return SKIP_BODY;
372:
373: _renderState = INIT;
374:
375: boolean empty = false;
376: if (source != null) {
377: _iterator = IteratorFactory.createIterator(source);
378: if (_iterator == null) {
379: LOGGER.warn(Bundle
380: .getString("Tags_Repeater_nullIterator"));
381: _iterator = Collections.EMPTY_LIST.iterator();
382: }
383:
384: if (_iterator.hasNext()) {
385: _currentIndex = 0;
386: _currentItem = _iterator.next();
387:
388: if (_ignoreNulls && _currentItem == null) {
389: /*
390: doStartTag doesn't know if the repeater is structured or unstructured
391: thus, if ignoreNulls is true, it's going to make an attempt to go
392: through the body with a non-null item. if there are no non-null
393: items in the data structure, the doAfterBody method will handle
394: this correctly, but a data structure with a null first item
395: will render the same as a data structure with a null second item
396: */
397: advanceToNonNullItem();
398:
399: /*
400: this null check needs to re-run here because the advanceToNonNullItem method
401: side-effects the _currentItem.
402: */
403: if (_currentItem == null)
404: empty = true;
405: }
406: }
407: /* there is no data set of there are zero items in the iterator */
408: else
409: empty = true;
410: }
411: /* the dataSource evaluated to null */
412: else {
413: _iterator = Collections.EMPTY_LIST.iterator();
414: empty = true;
415: }
416:
417: if (empty) {
418: /* if the defaultText attribute is non-null, it will be evaluated as an expression and rendered to the page */
419: if (_defaultText != null)
420: addContent(_defaultText.toString());
421: return SKIP_BODY;
422: } else {
423: DataAccessProviderStack.addDataAccessProvider(this ,
424: pageContext);
425: _containerInPageContext = true;
426: return EVAL_BODY_BUFFERED;
427: }
428: }
429:
430: /**
431: * <p>
432: * Continue rendering the repeater changing the render state or advancing to a new data item
433: * as needed.
434: * </p>
435: * @return {@link #SKIP_BODY} if an error occurs or the data set has been rendered; {@link #EVAL_BODY_AGAIN} otherwise
436: */
437: public int doAfterBody() {
438:
439: if (hasErrors())
440: return SKIP_BODY;
441:
442: LOGGER
443: .debug("structured repeater: " + _haveKids
444: + " render state: "
445: + renderStateToString(_renderState));
446:
447: /*
448: structured rendering of the repeater; body content is ignored and real
449: content is rendered through cooperating nested tags
450: */
451: if (_haveKids)
452: return renderStructured();
453: /*
454: unstructured rendering of the repeater; this means that there isn't
455: a repeater(Header|Item|Footer) inside the body of the repeater.
456: */
457: else {
458: if (bodyContent != null) {
459: addContent(bodyContent.getString());
460: bodyContent.clearBody();
461: }
462:
463: if (_iterator.hasNext()) {
464: _currentIndex++;
465: _currentItem = _iterator.next();
466:
467: if (_ignoreNulls && _currentItem == null) {
468: advanceToNonNullItem();
469:
470: /* ignoring null items and no more non-null items, so skip to doEndTag() */
471: if (_currentItem == null)
472: return SKIP_BODY;
473: }
474:
475: /* found another item; re-render the repeater's body */
476: return EVAL_BODY_AGAIN;
477: }
478: /* no more items; skip to doEndTag() */
479: else
480: return SKIP_BODY;
481: }
482: }
483:
484: /**
485: * Complete rendering the repeater.
486: * @return {@link #EVAL_PAGE}
487: * @throws JspException if an error occurs that can not be reported in the page
488: */
489: public int doEndTag() throws JspException {
490:
491: if (hasErrors())
492: reportErrors();
493: else if (_contentBuffer != null)
494: write(_contentBuffer.toString());
495:
496: return EVAL_PAGE;
497: }
498:
499: public void doFinally() {
500: localRelease();
501: }
502:
503: public void doCatch(Throwable t) throws Throwable {
504: throw t;
505: }
506:
507: /**
508: * Reset all of the fields of this tag.
509: */
510: protected void localRelease() {
511: super .localRelease();
512: _currentItem = null;
513: _currentIndex = -1;
514: _iterator = null;
515: _defaultText = null;
516: _renderState = INIT;
517: _haveKids = false;
518: _contentBuffer = null;
519: _padContext = null;
520: _ignoreNulls = false;
521: _renderedItems = 0;
522: if (_containerInPageContext) {
523: DataAccessProviderStack
524: .removeDataAccessProvider(pageContext);
525: _containerInPageContext = false;
526: }
527: }
528:
529: /**
530: * Render a the repeater using the full repeater lifecycle. This
531: * method is executed after each pass through the body if there
532: * are tags from the repeater tag set in the body of this repeater.
533: * This method ensures that the repeater tag runs as a full state
534: * machine for these tags.
535: *
536: * @return EVAL_BODY_AGAIN unless the lifecycle has completed; then return SKIP_BODY
537: */
538: /* todo: perf -- optimize the number of trips through the body by ignoring the header / footer when necessary */
539: private int renderStructured() {
540: if (LOGGER.isDebugEnabled() && _padContext != null)
541: LOGGER.debug("\ncurrentIndex: " + _currentIndex + "\n"
542: + "checkMaxRepeat: "
543: + _padContext.checkMaxRepeat(_currentIndex) + "\n"
544: + "checkMinRepeat: "
545: + _padContext.checkMinRepeat(_currentIndex) + "\n");
546:
547: if (_renderState == INIT) {
548: _renderState = HEADER;
549: return EVAL_BODY_AGAIN;
550: }
551:
552: if (_renderState == HEADER) {
553: assert _renderedItems == 0;
554:
555: /* this would only happen if Pad.maxRepeat == 0 */
556: if (_padContext != null
557: && _padContext.checkMaxRepeat(_renderedItems)) {
558: _renderState = FOOTER;
559: return EVAL_BODY_AGAIN;
560: }
561:
562: if (_currentItem == null && _ignoreNulls) {
563: advanceToNonNullItem();
564:
565: /* no non-null item was found; render the footer */
566: if (_currentItem == null) {
567: doPadding();
568: // render the header
569: _renderState = FOOTER;
570: }
571: /* non-null item found; it's not the 0th item; render it */
572: else
573: _renderState = ITEM;
574: }
575: /* 0th item is non-null; render it */
576: else
577: _renderState = ITEM;
578:
579: return EVAL_BODY_AGAIN;
580: }
581:
582: if (_renderState == ITEM) {
583: _renderedItems++;
584:
585: /* check that the maximum number of items to render has *not* been reached */
586: if (_iterator.hasNext()
587: && (_padContext == null || (_padContext != null && !_padContext
588: .checkMaxRepeat(_renderedItems)))) {
589: _currentIndex++;
590: _currentItem = _iterator.next();
591:
592: if (_ignoreNulls && _currentItem == null) {
593: advanceToNonNullItem();
594:
595: /* last item */
596: if (_currentItem == null) {
597: doPadding();
598:
599: /* render the header */
600: _renderState = FOOTER;
601: return EVAL_BODY_AGAIN;
602: }
603: }
604:
605: /* if _ignoreNulls is false, the _currentItem may be null here */
606: return EVAL_BODY_AGAIN;
607: }
608: /*
609: have finished rendering items for some reason:
610: 1) there isn't a next item
611: 2) reached the maximum number of items to render
612: So:
613: 1) pad if necessary
614: 2) render the footer
615: */
616: else {
617: doPadding();
618:
619: _renderState = FOOTER;
620: return EVAL_BODY_AGAIN;
621: }
622: }
623:
624: if (_renderState == FOOTER) {
625: _renderState = END;
626: return SKIP_BODY;
627: }
628:
629: return SKIP_BODY;
630: }
631:
632: /**
633: * <p/>
634: * Internal utility method.
635: * </p>
636: * </p>
637: * This is called in places where the repeater needs to move to the next
638: * non-null item in the data set to render. This occurs when the
639: * current data item is null and the <code>ignoreNulls</code>
640: * flag has been set to skip rendering null items in the data set.
641: * </p>
642: * <p/>
643: * This method side-effects to advance the iterator to the next
644: * non-null item or the end if there are zero remaining non-null
645: * items.
646: * </p>
647: * <p/>
648: * At the end, the <code>currentItem</code> may be null, and the
649: * <code>currentIndex</code> will reference either the integer
650: * location in the data structure of the non-null data item, or
651: * it will reference the end of the data structure.
652: * </p>
653: */
654: private final void advanceToNonNullItem() {
655: assert _iterator != null;
656: assert _currentItem == null;
657:
658: while (_iterator.hasNext() && _currentItem == null) {
659: _currentItem = _iterator.next();
660: _currentIndex++;
661: }
662: }
663:
664: /**
665: * When using the repeater's pad tag, it is possible to require a minimum number of
666: * items render in the repeater. This method pads out the number of items until it
667: * reaches the {@link org.apache.beehive.netui.tags.databinding.repeater.pad.PadContext}'s
668: * <code>minRepeat</code> property.
669: */
670: private final void doPadding() {
671: if (_padContext != null
672: && !_padContext.checkMinRepeat(_renderedItems)) {
673: /*
674: since padding is now running, un-set the current item so that the last
675: item isn't accessible during any later data binding
676: */
677: _currentItem = null;
678:
679: for (int i = _renderedItems; !_padContext.checkMinRepeat(i); i++) {
680: _currentIndex++;
681: addContent(_padContext.getPadText());
682: }
683: }
684: }
685:
686: /**
687: * An internal method that turns the current render state into a string for debugging.
688: */
689: private static final String renderStateToString(int state) {
690: switch (state) {
691: case INIT:
692: return "INIT";
693: case HEADER:
694: return "HEADER";
695: case ITEM:
696: return "ITEM";
697: case FOOTER:
698: return "FOOTER";
699: case END:
700: return "END";
701: default:
702: return "INVALID STATE";
703: }
704: }
705:
706: /**
707: * Return an <code>ArrayList</code> which represents a chain of <code>INameInterceptor</code>
708: * objects. This method by default returns <code>null</code> and should be overridden
709: * by objects that support naming.
710: * @return an <code>ArrayList</code> that will contain <code>INameInterceptor</code> objects.
711: */
712: protected List getNamingChain() {
713: return AbstractClassicTag.DefaultNamingChain;
714: }
715:
716: /**
717: * Sets the tag's data source (can be an expression).
718: * @param dataSource - the data source
719: * @jsptagref.attributedescription <p>The <code>dataSource</code> attribute determines both
720: * (1) the source of populating data for the tag and
721: * (2) the object to which the tag submits data.
722: *
723: * <p>For example, assume that the Controller file (= JPF file) contains
724: * a Form Bean with the property foo. Then the following <netui:textBox> tag will
725: * (1) draw populating data from the Form Bean's foo property and (2)
726: * submit user defined data to the same property.
727: *
728: * <p> <code><netui:textBox dataSource="actionForm.foo" /></code>
729: *
730: * <p>The <code>dataSource</code> attribute takes either a data binding expression or
731: * the name of a Form Bean property. In the
732: * above example, <code><netui:textBox dataSource="foo" /></code>
733: * would have the exactly same behavior.
734: *
735: * <p>When the tag is used to submit data, the data binding expression must
736: * refer to a Form Bean property.
737: * In cases where the tag is not used to submit data, but is used for
738: * displaying data only, the data
739: * binding expression need not refer to a Form Bean property. For example,
740: * assume that myIterativeData is a member variable on
741: * the Controller file ( = JPF file). The following <netui-data:repeater>
742: * tag draws its data from myIterativeData.
743: *
744: * <p> <code><netui-data:repeater dataSource="pageFlow.myIterativeData"></code>
745: * @jsptagref.attributesyntaxvalue <i>expression_datasource</i>
746: * @netui:attribute required="true"
747: */
748: public void setDataSource(String dataSource) {
749: _dataSource = dataSource;
750: }
751:
752: /**
753: * Gets the tag's data source (can be an expression).
754: * @return the data source
755: */
756: public String getDataSource() {
757: return "{" + _dataSource + "}";
758: }
759:
760: /**
761: * Return the Object that is represented by the specified data source.
762: * @return Object
763: * @throws JspException
764: */
765: private Object evaluateDataSource() throws JspException {
766: ExpressionHandling expr = new ExpressionHandling(this );
767: String dataSource = getDataSource();
768: String ds = expr.ensureValidExpression(dataSource,
769: "dataSource", "DataSourceError");
770: if (ds == null)
771: return null;
772:
773: /* have a valid expression */
774: return expr.evaluateExpression(dataSource, "dataSource",
775: pageContext);
776: }
777: }
|