001: // Copyright 2006, 2007 The Apache Software Foundation
002: //
003: // Licensed under the Apache License, Version 2.0 (the "License");
004: // you may not use this file except in compliance with the License.
005: // You may obtain a copy of the License at
006: //
007: // http://www.apache.org/licenses/LICENSE-2.0
008: //
009: // Unless required by applicable law or agreed to in writing, software
010: // distributed under the License is distributed on an "AS IS" BASIS,
011: // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
012: // See the License for the specific language governing permissions and
013: // limitations under the License.
014:
015: package org.apache.tapestry.corelib.components;
016:
017: import static org.apache.tapestry.ioc.internal.util.CollectionFactory.newList;
018:
019: import java.io.Serializable;
020: import java.util.Iterator;
021: import java.util.List;
022:
023: import org.apache.tapestry.ComponentAction;
024: import org.apache.tapestry.ComponentResources;
025: import org.apache.tapestry.MarkupWriter;
026: import org.apache.tapestry.PrimaryKeyEncoder;
027: import org.apache.tapestry.annotations.AfterRender;
028: import org.apache.tapestry.annotations.BeginRender;
029: import org.apache.tapestry.annotations.Environmental;
030: import org.apache.tapestry.annotations.Inject;
031: import org.apache.tapestry.annotations.Parameter;
032: import org.apache.tapestry.annotations.SetupRender;
033: import org.apache.tapestry.annotations.SupportsInformalParameters;
034: import org.apache.tapestry.services.FormSupport;
035: import org.apache.tapestry.services.Heartbeat;
036:
037: /**
038: * Basic looping class; loops over a number of items (provided by its source parameter), rendering
039: * its body for each one. It turns out that gettting the component to <em>not</em> store its state
040: * in the Form is very tricky and, in fact, a series of commands for starting and ending heartbeats,
041: * and advancing through the iterator, are still stored. For a non-volatile Loop inside the form,
042: * the Loop stores a series of commands that start and end heartbeats and store state (either as
043: * full objects when there is not encoder, or as client-side objects when there is an encoder).
044: */
045: @SupportsInformalParameters
046: public class Loop {
047: /** Setup command for non-volatile rendering. */
048: private static final ComponentAction<Loop> RESET_INDEX = new ComponentAction<Loop>() {
049: private static final long serialVersionUID = 6477493424977597345L;
050:
051: public void execute(Loop component) {
052: component.resetIndex();
053: }
054: };
055:
056: /**
057: * Setup command for volatile rendering. Volatile rendering relies on re-acquiring the Iterator
058: * and working our way through it (and hoping for the best!).
059: */
060: private static final ComponentAction<Loop> SETUP_FOR_VOLATILE = new ComponentAction<Loop>() {
061: private static final long serialVersionUID = -977168791667037377L;
062:
063: public void execute(Loop component) {
064: component.setupForVolatile();
065: };
066: };
067:
068: /**
069: * Advances to next value in a volatile way. So, the <em>number</em> of steps is intrinsically
070: * stored in the Form (as the number of ADVANCE_VOLATILE commands), but the actual values are
071: * expressly stored only on the server.
072: */
073: private static final ComponentAction<Loop> ADVANCE_VOLATILE = new ComponentAction<Loop>() {
074: private static final long serialVersionUID = -4600281573714776832L;
075:
076: public void execute(Loop component) {
077: component.advanceVolatile();
078: }
079: };
080:
081: /**
082: * Used in both volatile and non-volatile mode to end the current heartbeat (started by either
083: * ADVANCE_VOLATILE or one of the RestoreState commands). Also increments the index.
084: */
085: private static final ComponentAction<Loop> END_HEARTBEAT = new ComponentAction<Loop>() {
086: private static final long serialVersionUID = -977168791667037377L;
087:
088: public void execute(Loop component) {
089: component.endHeartbeat();
090: };
091: };
092:
093: /**
094: * Restores a state value (this is the case when there is no encoder and the complete value is
095: * stored).
096: */
097: static class RestoreState implements ComponentAction<Loop> {
098: private static final long serialVersionUID = -3926831611368720764L;
099:
100: private final Object _storedValue;
101:
102: public RestoreState(final Object storedValue) {
103: _storedValue = storedValue;
104: }
105:
106: public void execute(Loop component) {
107: component.restoreState(_storedValue);
108: }
109: };
110:
111: /**
112: * Restores the value using a stored primary key via
113: * {@link PrimaryKeyEncoder#toValue(Serializable)}.
114: */
115: static class RestoreStateViaEncodedPrimaryKey implements
116: ComponentAction<Loop> {
117: private static final long serialVersionUID = -2422790241589517336L;
118:
119: private final Serializable _primaryKey;
120:
121: public RestoreStateViaEncodedPrimaryKey(
122: final Serializable primaryKey) {
123: _primaryKey = primaryKey;
124: }
125:
126: public void execute(Loop component) {
127: component.restoreStateViaEncodedPrimaryKey(_primaryKey);
128: }
129: };
130:
131: /**
132: * Stores a list of keys to be passed to {@link PrimaryKeyEncoder#prepareForKeys(List)}.
133: */
134: static class PrepareForKeys implements ComponentAction<Loop> {
135: private static final long serialVersionUID = -6515255627142956828L;
136:
137: /** The variable is final, the contents are mutable while the Loop renders. */
138: private final List<Serializable> _keys;
139:
140: public PrepareForKeys(final List<Serializable> keys) {
141: _keys = keys;
142: }
143:
144: public void execute(Loop component) {
145: component.prepareForKeys(_keys);
146: }
147: };
148:
149: /**
150: * Defines the collection of values for the loop to iterate over.
151: */
152: @Parameter(required=true)
153: private Iterable<?> _source;
154:
155: /**
156: * Optional primary key converter; if provided and inside a form and not volatile, then each
157: * iterated value is converted and stored into the form.
158: */
159: @Parameter
160: private PrimaryKeyEncoder<Serializable, Object> _encoder;
161:
162: /**
163: * If true and the Loop is enclosed by a Form, then the normal state saving logic is turned off.
164: * Defaults to false, enabling state saving logic within Forms.
165: */
166: @Parameter
167: private boolean _volatile;
168:
169: @Environmental(false)
170: private FormSupport _formSupport;
171:
172: /**
173: * The element to render. If not null, then the loop will render the indicated element around
174: * its body (on each pass through the loop). The default is derived from the component template.
175: */
176: @Parameter(value="prop:componentResources.elementName",defaultPrefix="literal")
177: private String _elementName;
178:
179: /**
180: * The current value, set before the component renders its body.
181: */
182: @Parameter
183: private Object _value;
184:
185: /**
186: * The index into the source items.
187: */
188: @Parameter
189: private int _index;
190:
191: private Iterator<?> _iterator;
192:
193: @Environmental
194: private Heartbeat _heartbeat;
195:
196: private boolean _storeRenderStateInForm;
197:
198: @Inject
199: private ComponentResources _resources;
200:
201: private List<Serializable> _keyList;
202:
203: @SetupRender
204: boolean setup() {
205: _index = 0;
206:
207: if (_source == null)
208: return false;
209:
210: _iterator = _source.iterator();
211:
212: _storeRenderStateInForm = _formSupport != null && !_volatile;
213:
214: // Only render the body if there is something to iterate over
215:
216: boolean result = _iterator.hasNext();
217:
218: if (_formSupport != null && result) {
219:
220: _formSupport.store(this , _volatile ? SETUP_FOR_VOLATILE
221: : RESET_INDEX);
222:
223: if (_encoder != null) {
224: _keyList = newList();
225:
226: // We'll keep updating the _keyList while the Loop renders, the values will "lock
227: // down" when the Form serializes all the data.
228:
229: _formSupport.store(this , new PrepareForKeys(_keyList));
230: }
231: }
232:
233: return result;
234: }
235:
236: private void prepareForKeys(List<Serializable> keys) {
237: // Again, the encoder existed when we rendered, we better have another available
238: // when the enclosing Form is submitted.
239:
240: _encoder.prepareForKeys(keys);
241: }
242:
243: private void setupForVolatile() {
244: _index = 0;
245: _iterator = _source.iterator();
246: }
247:
248: private void advanceVolatile() {
249: _value = _iterator.next();
250:
251: startHeartbeat();
252: }
253:
254: /** Begins a new heartbeat. */
255: @BeginRender
256: void begin() {
257: _value = _iterator.next();
258:
259: if (_storeRenderStateInForm) {
260: if (_encoder == null) {
261: _formSupport.store(this , new RestoreState(_value));
262: } else {
263: Serializable primaryKey = _encoder.toKey(_value);
264: _formSupport
265: .store(this ,
266: new RestoreStateViaEncodedPrimaryKey(
267: primaryKey));
268: }
269: }
270:
271: if (_formSupport != null && _volatile)
272: _formSupport.store(this , ADVANCE_VOLATILE);
273:
274: startHeartbeat();
275: }
276:
277: private void startHeartbeat() {
278: _heartbeat.begin();
279: }
280:
281: void beforeRenderBody(MarkupWriter writer) {
282: if (_elementName != null) {
283: writer.element(_elementName);
284: _resources.renderInformalParameters(writer);
285: }
286: }
287:
288: void afterRenderBody(MarkupWriter writer) {
289: if (_elementName != null)
290: writer.end();
291: }
292:
293: /** Ends the current heartbeat. */
294: @AfterRender
295: boolean after() {
296: endHeartbeat();
297:
298: if (_formSupport != null)
299: _formSupport.store(this , END_HEARTBEAT);
300:
301: return !_iterator.hasNext();
302: }
303:
304: private void endHeartbeat() {
305: _heartbeat.end();
306:
307: _index++;
308: }
309:
310: private void resetIndex() {
311: _index = 0;
312: }
313:
314: /** Restores state previously stored by the Loop into a Form. */
315: private void restoreState(Object storedValue) {
316: _value = storedValue;
317:
318: startHeartbeat();
319: }
320:
321: /** Restores state previously encoded by the Loop and stored into the Form. */
322: private void restoreStateViaEncodedPrimaryKey(
323: Serializable primaryKey) {
324: // We assume that if a encoder is available when we rendered, that one will be available
325: // when the form is submitted. TODO: Check for this.
326:
327: Object restoredValue = _encoder.toValue(primaryKey);
328:
329: restoreState(restoredValue);
330: }
331:
332: // For testing:
333:
334: int getIndex() {
335: return _index;
336: }
337:
338: Object getValue() {
339: return _value;
340: }
341:
342: void setSource(Iterable<?> source) {
343: _source = source;
344: }
345:
346: void setHeartbeat(Heartbeat heartbeat) {
347: _heartbeat = heartbeat;
348: }
349: }
|