001: /* *************************************************************************
002:
003: Millstone(TM)
004: Open Sourced User Interface Library for
005: Internet Development with Java
006:
007: Millstone is a registered trademark of IT Mill Ltd
008: Copyright (C) 2000-2005 IT Mill Ltd
009:
010: *************************************************************************
011:
012: This library is free software; you can redistribute it and/or
013: modify it under the terms of the GNU Lesser General Public
014: license version 2.1 as published by the Free Software Foundation.
015:
016: This library is distributed in the hope that it will be useful,
017: but WITHOUT ANY WARRANTY; without even the implied warranty of
018: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
019: Lesser General Public License for more details.
020:
021: You should have received a copy of the GNU Lesser General Public
022: License along with this library; if not, write to the Free Software
023: Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
024:
025: *************************************************************************
026:
027: For more information, contact:
028:
029: IT Mill Ltd phone: +358 2 4802 7180
030: Ruukinkatu 2-4 fax: +358 2 4802 7181
031: 20540, Turku email: info@itmill.com
032: Finland company www: www.itmill.com
033:
034: Primary source for MillStone information and releases: www.millstone.org
035:
036: ********************************************************************** */
037:
038: package org.millstone.base.ui;
039:
040: import java.lang.reflect.Method;
041: import java.util.Collection;
042: import java.util.Collections;
043: import java.util.Date;
044: import java.util.Iterator;
045: import java.util.LinkedList;
046:
047: import org.millstone.base.data.Buffered;
048: import org.millstone.base.data.Property;
049: import org.millstone.base.data.Validatable;
050: import org.millstone.base.data.Validator;
051: import org.millstone.base.terminal.ErrorMessage;
052: import org.millstone.base.terminal.PaintException;
053: import org.millstone.base.terminal.PaintTarget;
054:
055: /**
056: * <p>
057: * Abstract field component for implementing buffered property editors. The
058: * field may hold an internal value, or it may be connected to any data source
059: * that implements the {@link org.millstone.base.data.Property}interface.
060: * <code>AbstractField</code> implements that interface itself, too, so
061: * accessing the Property value represented by it is straightforward.
062: * </p>
063: *
064: * <p>
065: * AbstractField also provides the {@link org.millstone.base.data.Buffered}
066: * interface for buffering the data source value. By default the Field is in
067: * write through-mode and {@link #setWriteThrough(boolean)}should be called to
068: * enable buffering.
069: * </p>
070: *
071: * <p>
072: * The class also supports {@link org.millstone.base.data.Validator validators}
073: * to make sure the value contained in the field is valid.
074: * </p>
075: *
076: * @author IT Mill Ltd.
077: * @version
078: * 3.1.1
079: * @since 3.0
080: */
081: public abstract class AbstractField extends AbstractComponent implements
082: Field, Property.ReadOnlyStatusChangeNotifier {
083:
084: /* Private members ************************************************* */
085:
086: private boolean delayedFocus;
087:
088: /** Value of the datafield */
089: private Object value;
090:
091: /** Connected data-source. */
092: private Property dataSource = null;
093:
094: /** The list of validators. */
095: private LinkedList validators = null;
096:
097: /** Auto commit mode. */
098: private boolean writeTroughMode = true;
099:
100: /** Read the value from data-source, when it is not modified. */
101: private boolean readTroughMode = true;
102:
103: /** Is the field modified but not committed. */
104: private boolean modified = false;
105:
106: /** Current source exception */
107: private Buffered.SourceException currentBufferedSourceException = null;
108:
109: /** Are the invalid values alloved in fields ? */
110: private boolean invalidAllowed = true;
111:
112: /** Are the invalid values committed */
113: private boolean invalidCommitted = false;
114:
115: /** The tab order number of this field */
116: private int tabIndex = 0;
117:
118: /** Unique focusable id */
119: private long focusableId = -1;
120:
121: /** Required field */
122: private boolean required = false;
123:
124: /* Component basics ************************************************ */
125:
126: public AbstractField() {
127: this .focusableId = Window.getNewFocusableId(this );
128: }
129:
130: /*
131: * Paint the field. Don't add a JavaDoc comment here, we use the default
132: * documentation from the implemented interface.
133: */
134: public void paintContent(PaintTarget target) throws PaintException {
135:
136: // Focus control id
137: if (this .focusableId > 0) {
138: target.addAttribute("focusid", this .focusableId);
139: }
140:
141: // The tab ordering number
142: if (this .tabIndex > 0)
143: target.addAttribute("tabindex", this .tabIndex);
144:
145: // If the field is modified, but not committed, set modified attribute
146: if (isModified())
147: target.addAttribute("modified", true);
148:
149: // Add required attribute
150: if (isRequired())
151: target.addAttribute("required", true);
152:
153: }
154:
155: /*
156: * Gets the field type Don't add a JavaDoc comment here, we use the default
157: * documentation from the implemented interface.
158: */
159: public abstract Class getType();
160:
161: /**
162: * The abstract field is read only also if the data source is in readonly
163: * mode.
164: */
165: public boolean isReadOnly() {
166: return super .isReadOnly()
167: || (dataSource != null && dataSource.isReadOnly());
168: }
169:
170: /**
171: * Change the readonly state and throw read-only status change events.
172: *
173: * @see org.millstone.base.ui.Component#setReadOnly(boolean)
174: */
175: public void setReadOnly(boolean readOnly) {
176: super .setReadOnly(readOnly);
177: fireReadOnlyStatusChange();
178: }
179:
180: /* Buffering ******************************************************* */
181:
182: public boolean isInvalidCommitted() {
183: return invalidCommitted;
184: }
185:
186: public void setInvalidCommitted(boolean isCommitted) {
187: this .invalidCommitted = isCommitted;
188: }
189:
190: /*
191: * Save the current value to the data source Don't add a JavaDoc comment
192: * here, we use the default documentation from the implemented interface.
193: */
194: public void commit() throws Buffered.SourceException {
195: if (dataSource != null && (isInvalidCommitted() || isValid())
196: && !dataSource.isReadOnly()) {
197: Object newValue = getValue();
198: try {
199:
200: // Commit the value to datasource
201: dataSource.setValue(newValue);
202:
203: } catch (Throwable e) {
204:
205: // Set the buffering state
206: currentBufferedSourceException = new Buffered.SourceException(
207: this , e);
208: requestRepaint();
209:
210: // Throw the source exception
211: throw currentBufferedSourceException;
212: }
213: }
214:
215: boolean repaintNeeded = false;
216:
217: // The abstract field is not modified anymore
218: if (modified) {
219: modified = false;
220: repaintNeeded = true;
221: }
222:
223: // If successful, remove set the buffering state to be ok
224: if (currentBufferedSourceException != null) {
225: currentBufferedSourceException = null;
226: repaintNeeded = true;
227: }
228:
229: if (repaintNeeded)
230: requestRepaint();
231: }
232:
233: /*
234: * Update the value from the data source. Don't add a JavaDoc comment here,
235: * we use the default documentation from the implemented interface.
236: */
237: public void discard() throws Buffered.SourceException {
238: if (dataSource != null) {
239:
240: // Get the correct value from datasource
241: Object newValue;
242: try {
243:
244: // Discard buffer by overwriting from datasource
245: newValue = dataSource.getValue();
246:
247: // If successful, remove set the buffering state to be ok
248: if (currentBufferedSourceException != null) {
249: currentBufferedSourceException = null;
250: requestRepaint();
251: }
252: } catch (Throwable e) {
253:
254: // Set the buffering state
255: currentBufferedSourceException = new Buffered.SourceException(
256: this , e);
257: requestRepaint();
258:
259: // Throw the source exception
260: throw currentBufferedSourceException;
261: }
262:
263: boolean wasModified = isModified();
264: modified = false;
265:
266: // If the new value differs from the previous one
267: if ((newValue == null && value != null)
268: || (newValue != null && !newValue.equals(value))) {
269: setInternalValue(newValue);
270: fireValueChange();
271: }
272:
273: // If the value did not change, but the modification status did
274: else if (wasModified) {
275: requestRepaint();
276: }
277: }
278: }
279:
280: /*
281: * Has the field been modified since the last commit()? Don't add a JavaDoc
282: * comment here, we use the default documentation from the implemented
283: * interface.
284: */
285: public boolean isModified() {
286: return modified;
287: }
288:
289: /*
290: * Test if the field is in write-through mode. Don't add a JavaDoc comment
291: * here, we use the default documentation from the implemented interface.
292: */
293: public boolean isWriteThrough() {
294: return writeTroughMode;
295: }
296:
297: /*
298: * Set the field's write-through mode to the specified status Don't add a
299: * JavaDoc comment here, we use the default documentation from the
300: * implemented interface.
301: */
302: public void setWriteThrough(boolean writeTrough)
303: throws Buffered.SourceException {
304: if (writeTroughMode == writeTrough)
305: return;
306: writeTroughMode = writeTrough;
307: if (writeTroughMode)
308: commit();
309: }
310:
311: /*
312: * Test if the field is in read-through mode. Don't add a JavaDoc comment
313: * here, we use the default documentation from the implemented interface.
314: */
315: public boolean isReadThrough() {
316: return readTroughMode;
317: }
318:
319: /*
320: * Set the field's read-through mode to the specified status Don't add a
321: * JavaDoc comment here, we use the default documentation from the
322: * implemented interface.
323: */
324: public void setReadThrough(boolean readTrough)
325: throws Buffered.SourceException {
326: if (readTroughMode == readTrough)
327: return;
328: readTroughMode = readTrough;
329: if (!isModified() && readTroughMode && dataSource != null) {
330: setInternalValue(dataSource.getValue());
331: fireValueChange();
332: }
333: }
334:
335: /* Property interface implementation ******************************* */
336:
337: /**
338: * Returns the value of the Property in human readable textual format.
339: *
340: * @return <code>String</code> representation of the value stored in the
341: * Property
342: */
343: public String toString() {
344: Object value = getValue();
345: if (value == null)
346: return null;
347: return getValue().toString();
348: }
349:
350: /**
351: * Gets the current value of the field. This is the visible, modified and
352: * possible invalid value the user have entered to the field. In the
353: * read-through mode, the abstract buffer is also updated and validation is
354: * performed.
355: *
356: * @return the current value of the field
357: */
358: public Object getValue() {
359:
360: // Give the value from abstract buffers if the field if possible
361: if (dataSource == null || !isReadThrough() || isModified())
362: return value;
363:
364: Object newValue = dataSource.getValue();
365: if ((newValue == null && value != null)
366: || (newValue != null && !newValue.equals(value))) {
367: setInternalValue(newValue);
368: fireValueChange();
369: }
370:
371: return newValue;
372: }
373:
374: /**
375: * Set the value of the field.
376: *
377: * @param newValue
378: * New value of the field.
379: */
380: public void setValue(Object newValue)
381: throws Property.ReadOnlyException,
382: Property.ConversionException {
383:
384: if ((newValue == null && value != null)
385: || (newValue != null && !newValue.equals(value))) {
386:
387: // Read only fields can not be changed
388: if (isReadOnly())
389: throw new Property.ReadOnlyException();
390:
391: // If invalid values are not allowed, the value must be checked
392: if (!isInvalidAllowed()) {
393: Collection v = getValidators();
394: if (v != null)
395: for (Iterator i = v.iterator(); i.hasNext();)
396: ((Validator) i.next()).validate(newValue);
397: }
398:
399: // Change the value
400: setInternalValue(newValue);
401: modified = dataSource != null;
402:
403: // In write trough mode , try to commit
404: if (isWriteThrough() && dataSource != null
405: && (isInvalidCommitted() || isValid())) {
406: try {
407:
408: // Commit the value to datasource
409: dataSource.setValue(newValue);
410:
411: // The buffer is now unmodified
412: modified = false;
413:
414: } catch (Throwable e) {
415:
416: // Set the buffering state
417: currentBufferedSourceException = new Buffered.SourceException(
418: this , e);
419: requestRepaint();
420:
421: // Throw the source exception
422: throw currentBufferedSourceException;
423: }
424: }
425:
426: // If successful, remove set the buffering state to be ok
427: if (currentBufferedSourceException != null) {
428: currentBufferedSourceException = null;
429: requestRepaint();
430: }
431:
432: // Fire value change
433: fireValueChange();
434: }
435: }
436:
437: /* External data source ******************************************** */
438:
439: /**
440: * Gets the current data source of the field, if any.
441: *
442: * @return The current data source as a Property, or <code>null</code> if
443: * none defined.
444: */
445: public Property getPropertyDataSource() {
446: return dataSource;
447: }
448:
449: /**
450: * <p>
451: * Sets the specified Property as the data source for the field. All
452: * uncommitted changes to the field are discarded and the value is refreshed
453: * from the new data source.
454: * </p>
455: *
456: * <p>
457: * If the datasource has any validators, the same validators are added to
458: * the field. Because the default behavior of the field is to allow invalid
459: * values, but not to allow committing them, this only adds visual error
460: * messages to fields and do not allow committing them as long as the value
461: * is invalid. After the value is valid, the error message is not shown and
462: * the commit can be done normally.
463: * </p>
464: *
465: * @param newDataSource
466: * the new data source Property
467: */
468: public void setPropertyDataSource(Property newDataSource) {
469:
470: // Save the old value
471: Object oldValue = value;
472:
473: // Discard all changes to old datasource
474: try {
475: discard();
476: } catch (Buffered.SourceException ignored) {
477: }
478:
479: // Stop listening the old data source changes
480: if (dataSource != null
481: && Property.ValueChangeNotifier.class
482: .isAssignableFrom(dataSource.getClass()))
483: ((Property.ValueChangeNotifier) dataSource)
484: .removeListener(this );
485:
486: // Set the new data source
487: dataSource = newDataSource;
488:
489: // Get the value from source
490: try {
491: if (dataSource != null)
492: setInternalValue(dataSource.getValue());
493: modified = false;
494: } catch (Throwable e) {
495: currentBufferedSourceException = new Buffered.SourceException(
496: this , e);
497: modified = true;
498: }
499:
500: // Listen the new data source if possible
501: if (dataSource instanceof Property.ValueChangeNotifier)
502: ((Property.ValueChangeNotifier) dataSource)
503: .addListener(this );
504:
505: // Copy the validators from the data source
506: if (dataSource instanceof Validatable) {
507: Collection validators = ((Validatable) dataSource)
508: .getValidators();
509: if (validators != null)
510: for (Iterator i = validators.iterator(); i.hasNext();)
511: addValidator((Validator) i.next());
512: }
513:
514: // Fire value change if the value has changed
515: if ((value != oldValue)
516: && ((value != null && !value.equals(oldValue)) || value == null))
517: fireValueChange();
518: }
519:
520: /* Validation ****************************************************** */
521:
522: /**
523: * Adds a new validator for the field's value. All validators added to a
524: * field are checked each time the its value changes.
525: *
526: * @param validator
527: * the new validator to be added
528: */
529: public void addValidator(Validator validator) {
530: if (validators == null)
531: validators = new LinkedList();
532: validators.add(validator);
533: }
534:
535: /**
536: * Gets the validators of the field.
537: *
538: * @return Unmodifiable collection that holds all validators for the field.
539: */
540: public Collection getValidators() {
541: if (validators == null || validators.isEmpty())
542: return null;
543: return Collections.unmodifiableCollection(validators);
544: }
545:
546: /**
547: * Removes a validator from the field.
548: *
549: * @param validator
550: * the validator to remove
551: */
552: public void removeValidator(Validator validator) {
553: if (validators != null)
554: validators.remove(validator);
555: }
556:
557: /**
558: * Tests the current value against all registered validators.
559: *
560: * @return <code>true</code> if all registered validators claim that the
561: * current value is valid, <code>false</code> otherwise
562: */
563: public boolean isValid() {
564:
565: if (validators == null)
566: return true;
567:
568: Object value = getValue();
569: for (Iterator i = validators.iterator(); i.hasNext();)
570: if (!((Validator) i.next()).isValid(value))
571: return false;
572:
573: return true;
574: }
575:
576: public void validate() throws Validator.InvalidValueException {
577:
578: // If there is no validator, there can not be any errors
579: if (validators == null)
580: return;
581:
582: // Initialize temps
583: Validator.InvalidValueException firstError = null;
584: LinkedList errors = null;
585: Object value = getValue();
586:
587: // Get all the validation errors
588: for (Iterator i = validators.iterator(); i.hasNext();)
589: try {
590: ((Validator) i.next()).validate(value);
591: } catch (Validator.InvalidValueException e) {
592: if (firstError == null)
593: firstError = e;
594: else {
595: if (errors == null) {
596: errors = new LinkedList();
597: errors.add(firstError);
598: }
599: errors.add(e);
600: }
601: }
602:
603: // If there were no error
604: if (firstError == null)
605: return;
606:
607: // If only one error occurred, throw it forwards
608: if (errors == null)
609: throw firstError;
610:
611: // Create composite validator
612: Validator.InvalidValueException[] exceptions = new Validator.InvalidValueException[errors
613: .size()];
614: int index = 0;
615: for (Iterator i = errors.iterator(); i.hasNext();)
616: exceptions[index++] = (Validator.InvalidValueException) i
617: .next();
618:
619: throw new Validator.InvalidValueException(null, exceptions);
620: }
621:
622: /**
623: * Fields allow invalid values by default. In most cases this is wanted,
624: * because the field otherwise visually forget the user input immediately.
625: *
626: * @see org.millstone.base.data.Validatable#isInvalidAllowed()
627: *
628: * @return true iff the invalid values are allowed.
629: */
630: public boolean isInvalidAllowed() {
631: return invalidAllowed;
632: }
633:
634: /**
635: * Fields allow invalid values by default. In most cases this is wanted,
636: * because the field otherwise visually forget the user input immediately.
637: * In common setting where the user wants to assure the correctness of the
638: * datasource, but allow temporarily invalid contents in the field, the user
639: * should add the validators to datasource, that should not allow invalid
640: * values. The validators are automatically copied to the field when the
641: * datasource is set.
642: *
643: * @see org.millstone.base.data.Validatable#setInvalidAllowed(boolean)
644: */
645: public void setInvalidAllowed(boolean invalidAllowed)
646: throws UnsupportedOperationException {
647: this .invalidAllowed = invalidAllowed;
648: }
649:
650: /**
651: * Error messages shown by the fields are composites of the error message
652: * thrown by the superclasses (that is the component error message),
653: * validation errors and buffered source errors.
654: *
655: * @see org.millstone.base.ui.AbstractComponent#getErrorMessage()
656: */
657: public ErrorMessage getErrorMessage() {
658: ErrorMessage super Error = super .getErrorMessage();
659: return super Error;
660: /*
661: * TODO: Check the logic of this ErrorMessage validationError = null;
662: * try { validate(); } catch (Validator.InvalidValueException e) {
663: * validationError = e; }
664: *
665: * if (superError == null && validationError == null &&
666: * currentBufferedSourceException == null) return null; // Throw
667: * combination of the error types return new CompositeErrorMessage( new
668: * ErrorMessage[] { superError, validationError,
669: * currentBufferedSourceException });
670: */
671:
672: }
673:
674: /* Value change events ****************************************** */
675:
676: private static final Method VALUE_CHANGE_METHOD;
677:
678: static {
679: try {
680: VALUE_CHANGE_METHOD = Property.ValueChangeListener.class
681: .getDeclaredMethod(
682: "valueChange",
683: new Class[] { Property.ValueChangeEvent.class });
684: } catch (java.lang.NoSuchMethodException e) {
685: // This should never happen
686: throw new java.lang.RuntimeException();
687: }
688: }
689:
690: /*
691: * Add a value change listener for the field. Don't add a JavaDoc comment
692: * here, we use the default documentation from the implemented interface.
693: */
694: public void addListener(Property.ValueChangeListener listener) {
695: addListener(AbstractField.ValueChangeEvent.class, listener,
696: VALUE_CHANGE_METHOD);
697: }
698:
699: /*
700: * Remove a value change listener from the field. Don't add a JavaDoc
701: * comment here, we use the default documentation from the implemented
702: * interface.
703: */
704: public void removeListener(Property.ValueChangeListener listener) {
705: removeListener(AbstractField.ValueChangeEvent.class, listener,
706: VALUE_CHANGE_METHOD);
707: }
708:
709: /**
710: * Emit a value change event. The value contained in the field is validated
711: * before the event is created.
712: */
713: protected void fireValueChange() {
714: fireEvent(new AbstractField.ValueChangeEvent(this ));
715: requestRepaint();
716: }
717:
718: /* Read-only status change events *************************************** */
719:
720: private static final Method READ_ONLY_STATUS_CHANGE_METHOD;
721:
722: static {
723: try {
724: READ_ONLY_STATUS_CHANGE_METHOD = Property.ReadOnlyStatusChangeListener.class
725: .getDeclaredMethod(
726: "readOnlyStatusChange",
727: new Class[] { Property.ReadOnlyStatusChangeEvent.class });
728: } catch (java.lang.NoSuchMethodException e) {
729: // This should never happen
730: throw new java.lang.RuntimeException();
731: }
732: }
733:
734: /**
735: * An <code>Event</code> object specifying the Property whose read-only
736: * status has changed.
737: *
738: * @author IT Mill Ltd.
739: * @version
740: * 3.1.1
741: * @since 3.0
742: */
743: public class ReadOnlyStatusChangeEvent extends Component.Event
744: implements Property.ReadOnlyStatusChangeEvent {
745:
746: /**
747: * Serial generated by eclipse.
748: */
749: private static final long serialVersionUID = 3258688823264161846L;
750:
751: /**
752: * New instance of text change event
753: *
754: * @param source
755: * Source of the event.
756: */
757: public ReadOnlyStatusChangeEvent(AbstractField source) {
758: super (source);
759: }
760:
761: /**
762: * Property where the event occurred
763: *
764: * @return Source of the event.
765: */
766: public Property getProperty() {
767: return (Property) getSource();
768: }
769: }
770:
771: /*
772: * Add a read-only status change listener for the field. Don't add a JavaDoc
773: * comment here, we use the default documentation from the implemented
774: * interface.
775: */
776: public void addListener(
777: Property.ReadOnlyStatusChangeListener listener) {
778: addListener(Property.ReadOnlyStatusChangeEvent.class, listener,
779: READ_ONLY_STATUS_CHANGE_METHOD);
780: }
781:
782: /*
783: * Removes a read-only status change listener from the field. Don't add a
784: * JavaDoc comment here, we use the default documentation from the
785: * implemented interface.
786: */
787: public void removeListener(
788: Property.ReadOnlyStatusChangeListener listener) {
789: removeListener(Property.ReadOnlyStatusChangeEvent.class,
790: listener, READ_ONLY_STATUS_CHANGE_METHOD);
791: }
792:
793: /**
794: * Emit a read-only status change event. The value contained in the field is
795: * validated before the event is created.
796: */
797: protected void fireReadOnlyStatusChange() {
798: fireEvent(new AbstractField.ReadOnlyStatusChangeEvent(this ));
799: }
800:
801: /**
802: * This method listens to data source value changes and passes the changes
803: * forwards.
804: *
805: * @param event
806: * the value change event telling the data source contents have
807: * changed
808: */
809: public void valueChange(Property.ValueChangeEvent event) {
810: if (isReadThrough() || !isModified())
811: fireValueChange();
812: }
813:
814: /** Ask the terminal to place the cursor to this field. */
815: public void focus() {
816: Window w = getWindow();
817: if (w != null) {
818: w.setFocusedComponent(this );
819: } else {
820: this .delayedFocus = true;
821: }
822: }
823:
824: /**
825: * Create abstract field by the type of the property.
826: *
827: * <p>
828: * This returns most suitable field type for editing property of given type
829: * </p>
830: *
831: * @param propertyType
832: * Type of the property, that needs to be edited.
833: */
834: public static AbstractField constructField(Class propertyType) {
835:
836: // Null typed properties can not be edited
837: if (propertyType == null)
838: return null;
839:
840: // Date field
841: if (Date.class.isAssignableFrom(propertyType)) {
842: return new DateField();
843: }
844:
845: // Boolean field
846: if (Boolean.class.isAssignableFrom(propertyType)) {
847: Button button = new Button("");
848: button.setSwitchMode(true);
849: button.setImmediate(false);
850: return button;
851: }
852:
853: // Text field is used by default
854: return new TextField();
855: }
856:
857: /**
858: * Get the tab index of this field. The tab index property is used to
859: * specify the natural tab ordering of fields.
860: *
861: * @return Tab index of this field. Negative value means unspecified.
862: */
863: public int getTabIndex() {
864: return tabIndex;
865: }
866:
867: /**
868: * Get the tab index of this field. The tab index property is used to
869: * specify the natural tab ordering of fields.
870: *
871: * @param tabIndex
872: * The tab order of this component. Negative value means
873: * unspecified.
874: */
875: public void setTabIndex(int tabIndex) {
876: this .tabIndex = tabIndex;
877: }
878:
879: /**
880: * Set the internal field value. This is purely used by AbstractField to
881: * change the internal Field value. It does not trigger any events. It can
882: * be overriden by the inheriting classes to update all dependent variables.
883: *
884: * @param newValue
885: * The new value to be set.
886: */
887: protected void setInternalValue(Object newValue) {
888: this .value = newValue;
889: }
890:
891: /**
892: * @see org.millstone.base.ui.Component.Focusable#getFocusableId()
893: */
894: public long getFocusableId() {
895: return this .focusableId;
896: }
897:
898: /**
899: * @see org.millstone.base.ui.Component#attach()
900: */
901: public void attach() {
902: super .attach();
903: if (this .delayedFocus) {
904: this .delayedFocus = false;
905: this .focus();
906: }
907: }
908:
909: /**
910: * Is this field required.
911: *
912: * Required fields must filled by the user.
913: *
914: * @return true if the
915: */
916: public boolean isRequired() {
917: return required;
918: }
919:
920: /**
921: * Set the field required. Required fields must filled by the user.
922: *
923: * @param required
924: * Is the field required
925: */
926: public void setRequired(boolean required) {
927: this .required = required;
928: }
929:
930: /**
931: * Free used resources.
932: */
933: public void finalize() throws Throwable {
934: if (focusableId > -1) {
935: Window.removeFocusableId(focusableId);
936: }
937: super.finalize();
938: }
939: }
|