001: /**
002: * Copyright 2006 Webmedia Group Ltd.
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: **/package org.araneaframework.uilib.form;
016:
017: import java.util.ArrayList;
018: import java.util.Iterator;
019: import java.util.List;
020: import org.araneaframework.Environment;
021: import org.araneaframework.InputData;
022: import org.araneaframework.OutputData;
023: import org.araneaframework.Path;
024: import org.araneaframework.core.ActionListener;
025: import org.araneaframework.core.Assert;
026: import org.araneaframework.core.StandardEnvironment;
027: import org.araneaframework.framework.core.RenderStateAware;
028: import org.araneaframework.uilib.ConfigurationContext;
029: import org.araneaframework.uilib.ConverterNotFoundException;
030: import org.araneaframework.uilib.form.control.BaseControl;
031: import org.araneaframework.uilib.form.converter.BaseConverter;
032: import org.araneaframework.uilib.form.converter.ConverterFactory;
033: import org.araneaframework.uilib.form.visitor.FormElementVisitor;
034: import org.araneaframework.uilib.util.ConfigurationContextUtil;
035: import org.araneaframework.uilib.util.Event;
036: import org.araneaframework.uilib.util.UilibEnvironmentUtil;
037:
038: /**
039: * Represents a simple "leaf" form element that holds a {@link Control} and its {@link Data}.
040: *
041: * @author Jevgeni Kabanov (ekabanov <i>at</i> araneaframework <i>dot</i> org)
042: */
043: public class FormElement extends GenericFormElement implements
044: FormElementContext, RenderStateAware {
045: /**
046: * The property key for custom {@link FormElementValidationErrorRenderer} that may be set
047: * for this {@link FormElement}.
048: * @since 1.1 */
049: public static final String ERROR_RENDERER_PROPERTY_KEY = "FormElementValidationErrorRenderer";
050:
051: //*******************************************************************
052: // FIELDS
053: //*******************************************************************
054: private List initEvents = new ArrayList();
055:
056: protected Control control;
057: protected Converter converter;
058: protected Data data;
059:
060: protected String label;
061:
062: private boolean rendered = false;
063: private boolean ignoreEvents = true;
064:
065: protected boolean mandatory = false;
066: protected boolean disabled;
067:
068: //*********************************************************************
069: //* PUBLIC METHODS
070: //*********************************************************************
071:
072: /**
073: * Returns control label.
074: *
075: * @return control label.
076: */
077: public String getLabel() {
078: return this .label;
079: }
080:
081: /**
082: * Sets control label.
083: *
084: * @param label control label.
085: */
086: public void setLabel(String label) {
087: this .label = label;
088: }
089:
090: /**
091: * Returns {@link BaseConverter}.
092: *
093: * @return the {@link BaseConverter}.
094: */
095: public Converter getConverter() {
096: return converter;
097: }
098:
099: /**
100: * Sets {@link BaseConverter}.
101: *
102: * @param converter The {@link BaseConverter}to set.
103: */
104: public void setConverter(Converter converter) {
105: this .converter = converter;
106:
107: if (converter != null)
108: converter.setFormElementCtx(this );
109: }
110:
111: /**
112: * Returns {@link Data}.
113: *
114: * @return {@link Data}.
115: */
116: public Data getData() {
117: return data;
118: }
119:
120: /**
121: * Sets {@link Data}.
122: *
123: * @param data {@link Data}.
124: */
125: public void setData(Data data) {
126: this .data = data;
127: data.setFormElementCtx(this );
128: }
129:
130: /**
131: * Returns {@link Control}.
132: *
133: * @return {@link Control}.
134: */
135: public Control getControl() {
136: return control;
137: }
138:
139: /**
140: * Sets {@link Control}.
141: *
142: * @param control {@link Control}.
143: */
144: public void setControl(Control control) {
145: Assert.notNullParam(control, "control");
146:
147: destroyControl();
148:
149: this .control = control;
150:
151: control.setFormElementCtx(this );
152:
153: if (isInitialized())
154: control._getComponent().init(getScope(), getEnvironment());
155: }
156:
157: /** @since 1.1 */
158: protected void destroyControl() {
159: if (this .control != null && this .control.isAlive()) {
160: this .control._getComponent().destroy();
161: }
162: }
163:
164: /**
165: * Finds a {@link BaseConverter}corresponding to current control and data item.
166: *
167: * @throws ConverterNotFoundException if converter cannot be found.
168: */
169: public Converter findConverter() throws ConverterNotFoundException {
170: ConfigurationContext confCtx = (ConfigurationContext) getEnvironment()
171: .requireEntry(ConfigurationContext.class);
172: return ConverterFactory.getInstance(confCtx).findConverter(
173: getControl().getRawValueType(),
174: getData().getValueType(), getEnvironment());
175: }
176:
177: /**
178: * Returns whether the form element was present in the last request.
179: *
180: * @return whether the form element was present in the last request.
181: */
182: public boolean isRead() {
183: return (getControl() != null && getControl().isRead());
184: }
185:
186: public void setDisabled(boolean disabled) {
187: this .disabled = disabled;
188: }
189:
190: public boolean isDisabled() {
191: return this .disabled;
192: }
193:
194: public void markBaseState() {
195: if (getData() != null)
196: getData().markBaseState();
197: }
198:
199: public void restoreBaseState() {
200: if (getData() != null)
201: getData().restoreBaseState();
202: }
203:
204: public boolean isStateChanged() {
205: if (getData() != null)
206: return getData().isStateChanged();
207: return false;
208: }
209:
210: public Object getValue() {
211: if (getData() != null)
212: return data.getValue();
213: return null;
214: }
215:
216: public void setValue(Object value) {
217: if (getData() != null)
218: getData().setValue(value);
219: }
220:
221: public boolean isMandatory() {
222: return this .mandatory;
223: }
224:
225: public void setMandatory(boolean mandatory) {
226: this .mandatory = mandatory;
227: }
228:
229: public void addError(String error) {
230: super .addError(error);
231: getFormElementValidationErrorRenderer().addError(this , error);
232: }
233:
234: public void clearErrors() {
235: getFormElementValidationErrorRenderer().clearErrors(this );
236: super .clearErrors();
237: }
238:
239: /**
240: * Sets the action listener that deals with background validation of form. It should
241: * be used when custom background validation logic or behaviour is wanted.
242: * Just for using default {@link FormElementValidationActionListener}, only
243: * {@link FormElement#setBackgroundValidation(boolean)} needs to be called with
244: * parameter <code>true</code>.
245: *
246: * @param actionListener custom listener that should handle validation of this {@link FormElement}
247: * @since 1.1
248: */
249: public void setBackgroundValidationListener(
250: ActionListener actionListener) {
251: clearActionListeners(SEAMLESS_VALIDATION_ACTION_ID);
252: addActionListener(SEAMLESS_VALIDATION_ACTION_ID, actionListener);
253: }
254:
255: /**
256: * @return {@link FormElementValidationErrorRenderer} which will take care of rendering validation error messages produced by this {@link FormElement}.
257: * @since 1.1 */
258: public FormElementValidationErrorRenderer getFormElementValidationErrorRenderer() {
259: FormElementValidationErrorRenderer result = ConfigurationContextUtil
260: .getFormElementValidationErrorRenderer(UilibEnvironmentUtil
261: .getConfigurationContext(getEnvironment()));
262: if (result == null) {
263: result = (FormElementValidationErrorRenderer) getProperty(ERROR_RENDERER_PROPERTY_KEY);
264: }
265:
266: if (result == null)
267: return StandardFormElementValidationErrorRenderer.INSTANCE;
268:
269: return result;
270: }
271:
272: /** @since 1.1 */
273: public void setFormElementValidationErrorRenderer(
274: FormElementValidationErrorRenderer renderer) {
275: setProperty(ERROR_RENDERER_PROPERTY_KEY, renderer);
276: }
277:
278: //*********************************************************************
279: //* INTERNAL METHODS
280: //*********************************************************************
281:
282: protected void update(InputData input) throws Exception {
283: if (isDisabled() || !isRendered()) {
284: setIgnoreEvents(true);
285: return;
286: }
287: setIgnoreEvents(false);
288:
289: super .update(input);
290:
291: //There is only point to read from request if we have a control
292: if (getControl() != null) {
293: //Read the control
294: getControl()._getWidget().update(input);
295: }
296: }
297:
298: protected void action(Path path, InputData input, OutputData output)
299: throws Exception {
300: if (!isDisabled() && isRendered()) {
301: super .action(path, input, output);
302: }
303: }
304:
305: protected void event(Path path, InputData input) throws Exception {
306: if (!path.hasNext() && !isDisabled() && !isIgnoreEvents())
307: getControl()._getWidget().event(path, input);
308: }
309:
310: protected void handleAction(InputData input, OutputData output)
311: throws Exception {
312: update(input);
313: super .handleAction(input, output);
314: if (control != null
315: && !getActionId(input).equals(
316: SEAMLESS_VALIDATION_ACTION_ID))
317: control._getService().action(null, input, output);
318: }
319:
320: public Environment getConstraintEnvironment() {
321: return new StandardEnvironment(
322: super .getConstraintEnvironment(),
323: FormElementContext.class, this );
324: }
325:
326: /**
327: * Returns {@link ViewModel}.
328: * @return {@link ViewModel}.
329: * @throws Exception
330: */
331: public Object getViewModel() throws Exception {
332: return new ViewModel();
333: }
334:
335: /** @since 1.0.5 */
336: public void addInitEvent(Event event) {
337: if (isAlive()) {
338: event.run();
339: } else if (!isInitialized()) {
340: if (initEvents == null)
341: initEvents = new ArrayList();
342: initEvents.add(event);
343: }
344: }
345:
346: protected void init() throws Exception {
347: super .init();
348:
349: if (getConverter() == null && getData() != null
350: && getControl() != null)
351: setConverter(findConverter());
352:
353: if (getControl() != null)
354: getControl()._getComponent().init(getScope(),
355: getEnvironment());
356:
357: runInitEvents();
358: setBackgroundValidationListener(getDefaultBackgroundValidationListener());
359: }
360:
361: /**
362: * Returns new instance of {@link FormElementValidationActionListener} tied to
363: * this {@link FormElement}.
364: *
365: * @since 1.1
366: */
367: protected FormElementValidationActionListener getDefaultBackgroundValidationListener() {
368: return new FormElementValidationActionListener(this );
369: }
370:
371: protected void destroy() throws Exception {
372: destroyControl();
373: }
374:
375: /**
376: * Uses {@link BaseConverter}to convert the {@link BaseControl}value to the {@link Data}value.
377: */
378: protected void convertInternal() {
379: Object newDataValue = null;
380:
381: //There is only point to convert and set the data if it is present
382: if (getData() != null && getControl() != null) {
383:
384: getControl().convert();
385:
386: //The data should be set only if control is valid
387: if (isValid()) {
388: //We assume that the convertor is present, if control and data are
389: // here
390: newDataValue = getConverter().convert(
391: getControl().getRawValue());
392: }
393: }
394:
395: if (getData() != null && isValid()) {
396: // converting should not affect Control's value -- so setDataValue() instead of setValue()
397: getData().setDataValue(newDataValue);
398: }
399: }
400:
401: protected boolean validateInternal() throws Exception {
402: if (getControl() != null)
403: getControl().validate();
404:
405: return super .validateInternal();
406: }
407:
408: public void accept(String id, FormElementVisitor visitor) {
409: visitor.visit(id, this );
410: }
411:
412: /** Called from {@link FormElement#init()} to run queued events.
413: * @since 1.0.5 */
414: protected void runInitEvents() {
415: if (initEvents != null) {
416: for (Iterator it = initEvents.iterator(); it.hasNext();) {
417: Runnable event = (Runnable) it.next();
418: event.run();
419: }
420: }
421: initEvents = null;
422: }
423:
424: /**
425: * Returns whether this {@link GenericFormElement} was rendered
426: * in response. Only formelements that were rendered should be read from request.
427: * @return whether this {@link GenericFormElement} was rendered
428: */
429: public boolean isRendered() {
430: return this .rendered;
431: }
432:
433: /**
434: * Marks status of this {@link FormElement} rendered.
435: * Only rendered {@link FormElement}s may read the data from subsequent request.
436: */
437: public void rendered() {
438: _setRendered(true);
439: }
440:
441: /**
442: * @since 1.1
443: */
444: public void _setRendered(boolean rendered) {
445: this .rendered = rendered;
446: }
447:
448: /**
449: * When this returns true,
450: * @since 1.1
451: */
452: protected boolean isIgnoreEvents() {
453: return ignoreEvents;
454: }
455:
456: /**
457: * When set
458: * @since 1.1
459: */
460: protected void setIgnoreEvents(boolean ignoreEvents) {
461: this .ignoreEvents = ignoreEvents;
462: }
463:
464: //*********************************************************************
465: //* VIEW MODEL
466: //*********************************************************************
467:
468: /**
469: * Represents a simple form element view model.
470: *
471: * @author Jevgeni Kabanov (ekabanov <i>at</i> araneaframework <i>dot</i> org)
472: *
473: */
474: public class ViewModel extends GenericFormElement.ViewModel {
475:
476: private Control.ViewModel control;
477: private String label;
478: private boolean valid;
479: private Object value;
480: protected boolean mandatory;
481:
482: /**
483: * Takes an outer class snapshot.
484: * @throws Exception
485: */
486: public ViewModel() throws Exception {
487: this .control = (Control.ViewModel) FormElement.this
488: .getControl()._getViewable().getViewModel();
489: this .label = FormElement.this .getLabel();
490: this .valid = FormElement.this .isValid();
491: this .value = FormElement.this .getData() != null ? FormElement.this
492: .getData().getValue()
493: : null;
494: this .mandatory = FormElement.this .mandatory;
495: }
496:
497: /**
498: * Returns control.
499: * @return control.
500: */
501: public Control.ViewModel getControl() {
502: return this .control;
503: }
504:
505: /**
506: * Returns label.
507: * @return label.
508: */
509: public String getLabel() {
510: return this .label;
511: }
512:
513: /**
514: * @return {@link FormElementValidationErrorRenderer} which will take care of rendering validation error messages produced
515: * by {@link FormElement} represented by this {@link FormElement.ViewModel}
516: * @since 1.1 */
517: public FormElementValidationErrorRenderer getFormElementValidationErrorRenderer() {
518: return FormElement.this
519: .getFormElementValidationErrorRenderer();
520: }
521:
522: /**
523: * Returns whether the element is valid.
524: * @return whether the element is valid.
525: */
526: public boolean isValid() {
527: return valid;
528: }
529:
530: public boolean isMandatory() {
531: return this .mandatory;
532: }
533:
534: public Object getValue() {
535: return this.value;
536: }
537: }
538: }
|