You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

AbstractField.java 34KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098
  1. /*
  2. @ITMillApache2LicenseForJavaFiles@
  3. */
  4. package com.itmill.toolkit.ui;
  5. import java.lang.reflect.Method;
  6. import java.util.Collection;
  7. import java.util.Collections;
  8. import java.util.Date;
  9. import java.util.Iterator;
  10. import java.util.LinkedList;
  11. import java.util.Map;
  12. import com.itmill.toolkit.Application;
  13. import com.itmill.toolkit.data.Buffered;
  14. import com.itmill.toolkit.data.Property;
  15. import com.itmill.toolkit.data.Validatable;
  16. import com.itmill.toolkit.data.Validator;
  17. import com.itmill.toolkit.terminal.CompositeErrorMessage;
  18. import com.itmill.toolkit.terminal.ErrorMessage;
  19. import com.itmill.toolkit.terminal.PaintException;
  20. import com.itmill.toolkit.terminal.PaintTarget;
  21. /**
  22. * <p>
  23. * Abstract field component for implementing buffered property editors. The
  24. * field may hold an internal value, or it may be connected to any data source
  25. * that implements the {@link com.itmill.toolkit.data.Property}interface.
  26. * <code>AbstractField</code> implements that interface itself, too, so
  27. * accessing the Property value represented by it is straightforward.
  28. * </p>
  29. *
  30. * <p>
  31. * AbstractField also provides the {@link com.itmill.toolkit.data.Buffered}
  32. * interface for buffering the data source value. By default the Field is in
  33. * write through-mode and {@link #setWriteThrough(boolean)}should be called to
  34. * enable buffering.
  35. * </p>
  36. *
  37. * <p>
  38. * The class also supports {@link com.itmill.toolkit.data.Validator validators}
  39. * to make sure the value contained in the field is valid.
  40. * </p>
  41. *
  42. * @author IT Mill Ltd.
  43. * @version
  44. * @VERSION@
  45. * @since 3.0
  46. */
  47. public abstract class AbstractField extends AbstractComponent implements Field,
  48. Property.ReadOnlyStatusChangeNotifier {
  49. /* Private members */
  50. private boolean delayedFocus;
  51. /**
  52. * Value of the abstract field.
  53. */
  54. private Object value;
  55. /**
  56. * Connected data-source.
  57. */
  58. private Property dataSource = null;
  59. /**
  60. * The list of validators.
  61. */
  62. private LinkedList validators = null;
  63. /**
  64. * Auto commit mode.
  65. */
  66. private boolean writeTroughMode = true;
  67. /**
  68. * Reads the value from data-source, when it is not modified.
  69. */
  70. private boolean readTroughMode = true;
  71. /**
  72. * Is the field modified but not committed.
  73. */
  74. private boolean modified = false;
  75. /**
  76. * Current source exception.
  77. */
  78. private Buffered.SourceException currentBufferedSourceException = null;
  79. /**
  80. * Are the invalid values allowed in fields ?
  81. */
  82. private boolean invalidAllowed = true;
  83. /**
  84. * Are the invalid values committed ?
  85. */
  86. private boolean invalidCommitted = false;
  87. /**
  88. * The tab order number of this field.
  89. */
  90. private int tabIndex = 0;
  91. /**
  92. * Required field.
  93. */
  94. private boolean required = false;
  95. /**
  96. * The error message for the exception that is thrown when the field is
  97. * required but empty.
  98. */
  99. private String requiredError = "";
  100. /**
  101. * Is automatic validation enabled.
  102. */
  103. private boolean validationVisible = true;
  104. /* Component basics */
  105. /*
  106. * Paints the field. Don't add a JavaDoc comment here, we use the default
  107. * documentation from the implemented interface.
  108. */
  109. public void paintContent(PaintTarget target) throws PaintException {
  110. // The tab ordering number
  111. if (tabIndex != 0) {
  112. target.addAttribute("tabindex", tabIndex);
  113. }
  114. // If the field is modified, but not committed, set modified attribute
  115. if (isModified()) {
  116. target.addAttribute("modified", true);
  117. }
  118. // Adds the required attribute
  119. if (isRequired()) {
  120. target.addAttribute("required", true);
  121. }
  122. }
  123. /*
  124. * Gets the field type Don't add a JavaDoc comment here, we use the default
  125. * documentation from the implemented interface.
  126. */
  127. public abstract Class getType();
  128. /**
  129. * The abstract field is read only also if the data source is in read only
  130. * mode.
  131. */
  132. public boolean isReadOnly() {
  133. return super.isReadOnly()
  134. || (dataSource != null && dataSource.isReadOnly());
  135. }
  136. /**
  137. * Changes the readonly state and throw read-only status change events.
  138. *
  139. * @see com.itmill.toolkit.ui.Component#setReadOnly(boolean)
  140. */
  141. public void setReadOnly(boolean readOnly) {
  142. super.setReadOnly(readOnly);
  143. fireReadOnlyStatusChange();
  144. }
  145. /**
  146. * Tests if the invalid data is committed to datasource.
  147. *
  148. * @see com.itmill.toolkit.data.BufferedValidatable#isInvalidCommitted()
  149. */
  150. public boolean isInvalidCommitted() {
  151. return invalidCommitted;
  152. }
  153. /**
  154. * Sets if the invalid data should be committed to datasource.
  155. *
  156. * @see com.itmill.toolkit.data.BufferedValidatable#setInvalidCommitted(boolean)
  157. */
  158. public void setInvalidCommitted(boolean isCommitted) {
  159. invalidCommitted = isCommitted;
  160. }
  161. /*
  162. * Saves the current value to the data source Don't add a JavaDoc comment
  163. * here, we use the default documentation from the implemented interface.
  164. */
  165. public void commit() throws Buffered.SourceException {
  166. if (dataSource != null && (isInvalidCommitted() || isValid())
  167. && !dataSource.isReadOnly()) {
  168. final Object newValue = getValue();
  169. try {
  170. // Commits the value to datasource.
  171. dataSource.setValue(newValue);
  172. } catch (final Throwable e) {
  173. // Sets the buffering state.
  174. currentBufferedSourceException = new Buffered.SourceException(
  175. this, e);
  176. requestRepaint();
  177. // Throws the source exception.
  178. throw currentBufferedSourceException;
  179. }
  180. }
  181. boolean repaintNeeded = false;
  182. // The abstract field is not modified anymore
  183. if (modified) {
  184. modified = false;
  185. repaintNeeded = true;
  186. }
  187. // If successful, remove set the buffering state to be ok
  188. if (currentBufferedSourceException != null) {
  189. currentBufferedSourceException = null;
  190. repaintNeeded = true;
  191. }
  192. if (repaintNeeded) {
  193. requestRepaint();
  194. }
  195. }
  196. /*
  197. * Updates the value from the data source. Don't add a JavaDoc comment here,
  198. * we use the default documentation from the implemented interface.
  199. */
  200. public void discard() throws Buffered.SourceException {
  201. if (dataSource != null) {
  202. // Gets the correct value from datasource
  203. Object newValue;
  204. try {
  205. // Discards buffer by overwriting from datasource
  206. newValue = dataSource.getValue();
  207. // If successful, remove set the buffering state to be ok
  208. if (currentBufferedSourceException != null) {
  209. currentBufferedSourceException = null;
  210. requestRepaint();
  211. }
  212. } catch (final Throwable e) {
  213. // Sets the buffering state
  214. currentBufferedSourceException = new Buffered.SourceException(
  215. this, e);
  216. requestRepaint();
  217. // Throws the source exception
  218. throw currentBufferedSourceException;
  219. }
  220. final boolean wasModified = isModified();
  221. modified = false;
  222. // If the new value differs from the previous one
  223. if ((newValue == null && value != null)
  224. || (newValue != null && !newValue.equals(value))) {
  225. setInternalValue(newValue);
  226. fireValueChange(false);
  227. }
  228. // If the value did not change, but the modification status did
  229. else if (wasModified) {
  230. requestRepaint();
  231. }
  232. }
  233. }
  234. /*
  235. * Has the field been modified since the last commit()? Don't add a JavaDoc
  236. * comment here, we use the default documentation from the implemented
  237. * interface.
  238. */
  239. public boolean isModified() {
  240. return modified;
  241. }
  242. /*
  243. * Tests if the field is in write-through mode. Don't add a JavaDoc comment
  244. * here, we use the default documentation from the implemented interface.
  245. */
  246. public boolean isWriteThrough() {
  247. return writeTroughMode;
  248. }
  249. /*
  250. * Sets the field's write-through mode to the specified status Don't add a
  251. * JavaDoc comment here, we use the default documentation from the
  252. * implemented interface.
  253. */
  254. public void setWriteThrough(boolean writeTrough)
  255. throws Buffered.SourceException {
  256. if (writeTroughMode == writeTrough) {
  257. return;
  258. }
  259. writeTroughMode = writeTrough;
  260. if (writeTroughMode) {
  261. commit();
  262. }
  263. }
  264. /*
  265. * Tests if the field is in read-through mode. Don't add a JavaDoc comment
  266. * here, we use the default documentation from the implemented interface.
  267. */
  268. public boolean isReadThrough() {
  269. return readTroughMode;
  270. }
  271. /*
  272. * Sets the field's read-through mode to the specified status Don't add a
  273. * JavaDoc comment here, we use the default documentation from the
  274. * implemented interface.
  275. */
  276. public void setReadThrough(boolean readTrough)
  277. throws Buffered.SourceException {
  278. if (readTroughMode == readTrough) {
  279. return;
  280. }
  281. readTroughMode = readTrough;
  282. if (!isModified() && readTroughMode && dataSource != null) {
  283. setInternalValue(dataSource.getValue());
  284. fireValueChange(false);
  285. }
  286. }
  287. /* Property interface implementation */
  288. /**
  289. * Returns the value of the Property in human readable textual format.
  290. *
  291. * @see java.lang.Object#toString()
  292. */
  293. public String toString() {
  294. final Object value = getValue();
  295. if (value == null) {
  296. return null;
  297. }
  298. return getValue().toString();
  299. }
  300. /**
  301. * Gets the current value of the field. This is the visible, modified and
  302. * possible invalid value the user have entered to the field. In the
  303. * read-through mode, the abstract buffer is also updated and validation is
  304. * performed.
  305. *
  306. * @return the current value of the field.
  307. */
  308. public Object getValue() {
  309. // Give the value from abstract buffers if the field if possible
  310. if (dataSource == null || !isReadThrough() || isModified()) {
  311. return value;
  312. }
  313. final Object newValue = dataSource.getValue();
  314. if ((newValue == null && value != null)
  315. || (newValue != null && !newValue.equals(value))) {
  316. setInternalValue(newValue);
  317. fireValueChange(false);
  318. }
  319. return newValue;
  320. }
  321. /**
  322. * Sets the value of the field.
  323. *
  324. * @param newValue
  325. * the New value of the field.
  326. * @throws Property.ReadOnlyException
  327. * @throws Property.ConversionException
  328. */
  329. public void setValue(Object newValue) throws Property.ReadOnlyException,
  330. Property.ConversionException {
  331. setValue(newValue, false);
  332. }
  333. /**
  334. * Sets the value of the field.
  335. *
  336. * @param newValue
  337. * the New value of the field.
  338. * @param repaintIsNotNeeded
  339. * True iff caller is sure that repaint is not needed.
  340. * @throws Property.ReadOnlyException
  341. * @throws Property.ConversionException
  342. */
  343. protected void setValue(Object newValue, boolean repaintIsNotNeeded)
  344. throws Property.ReadOnlyException, Property.ConversionException {
  345. if ((newValue == null && value != null)
  346. || (newValue != null && !newValue.equals(value))) {
  347. // Read only fields can not be changed
  348. if (isReadOnly()) {
  349. throw new Property.ReadOnlyException();
  350. }
  351. // If invalid values are not allowed, the value must be checked
  352. if (!isInvalidAllowed()) {
  353. final Collection v = getValidators();
  354. if (v != null) {
  355. for (final Iterator i = v.iterator(); i.hasNext();) {
  356. ((Validator) i.next()).validate(newValue);
  357. }
  358. }
  359. }
  360. // Changes the value
  361. setInternalValue(newValue);
  362. modified = dataSource != null;
  363. // In write trough mode , try to commit
  364. if (isWriteThrough() && dataSource != null
  365. && (isInvalidCommitted() || isValid())) {
  366. try {
  367. // Commits the value to datasource
  368. dataSource.setValue(newValue);
  369. // The buffer is now unmodified
  370. modified = false;
  371. } catch (final Throwable e) {
  372. // Sets the buffering state
  373. currentBufferedSourceException = new Buffered.SourceException(
  374. this, e);
  375. requestRepaint();
  376. // Throws the source exception
  377. throw currentBufferedSourceException;
  378. }
  379. }
  380. // If successful, remove set the buffering state to be ok
  381. if (currentBufferedSourceException != null) {
  382. currentBufferedSourceException = null;
  383. requestRepaint();
  384. }
  385. // Fires the value change
  386. fireValueChange(repaintIsNotNeeded);
  387. }
  388. }
  389. /* External data source */
  390. /**
  391. * Gets the current data source of the field, if any.
  392. *
  393. * @return the current data source as a Property, or <code>null</code> if
  394. * none defined.
  395. */
  396. public Property getPropertyDataSource() {
  397. return dataSource;
  398. }
  399. /**
  400. * <p>
  401. * Sets the specified Property as the data source for the field. All
  402. * uncommitted changes to the field are discarded and the value is refreshed
  403. * from the new data source.
  404. * </p>
  405. *
  406. * <p>
  407. * If the datasource has any validators, the same validators are added to
  408. * the field. Because the default behavior of the field is to allow invalid
  409. * values, but not to allow committing them, this only adds visual error
  410. * messages to fields and do not allow committing them as long as the value
  411. * is invalid. After the value is valid, the error message is not shown and
  412. * the commit can be done normally.
  413. * </p>
  414. *
  415. * @param newDataSource
  416. * the new data source Property.
  417. */
  418. public void setPropertyDataSource(Property newDataSource) {
  419. // Saves the old value
  420. final Object oldValue = value;
  421. // Discards all changes to old datasource
  422. try {
  423. discard();
  424. } catch (final Buffered.SourceException ignored) {
  425. }
  426. // Stops listening the old data source changes
  427. if (dataSource != null
  428. && Property.ValueChangeNotifier.class
  429. .isAssignableFrom(dataSource.getClass())) {
  430. ((Property.ValueChangeNotifier) dataSource).removeListener(this);
  431. }
  432. // Sets the new data source
  433. dataSource = newDataSource;
  434. // Gets the value from source
  435. try {
  436. if (dataSource != null) {
  437. setInternalValue(dataSource.getValue());
  438. }
  439. modified = false;
  440. } catch (final Throwable e) {
  441. currentBufferedSourceException = new Buffered.SourceException(this,
  442. e);
  443. modified = true;
  444. }
  445. // Listens the new data source if possible
  446. if (dataSource instanceof Property.ValueChangeNotifier) {
  447. ((Property.ValueChangeNotifier) dataSource).addListener(this);
  448. }
  449. // Copy the validators from the data source
  450. if (dataSource instanceof Validatable) {
  451. final Collection validators = ((Validatable) dataSource)
  452. .getValidators();
  453. if (validators != null) {
  454. for (final Iterator i = validators.iterator(); i.hasNext();) {
  455. addValidator((Validator) i.next());
  456. }
  457. }
  458. }
  459. // Fires value change if the value has changed
  460. if ((value != oldValue)
  461. && ((value != null && !value.equals(oldValue)) || value == null)) {
  462. fireValueChange(false);
  463. }
  464. }
  465. /* Validation */
  466. /**
  467. * Adds a new validator for the field's value. All validators added to a
  468. * field are checked each time the its value changes.
  469. *
  470. * @param validator
  471. * the new validator to be added.
  472. */
  473. public void addValidator(Validator validator) {
  474. if (validators == null) {
  475. validators = new LinkedList();
  476. }
  477. validators.add(validator);
  478. requestRepaint();
  479. }
  480. /**
  481. * Gets the validators of the field.
  482. *
  483. * @return the Unmodifiable collection that holds all validators for the
  484. * field.
  485. */
  486. public Collection getValidators() {
  487. if (validators == null || validators.isEmpty()) {
  488. return null;
  489. }
  490. return Collections.unmodifiableCollection(validators);
  491. }
  492. /**
  493. * Removes the validator from the field.
  494. *
  495. * @param validator
  496. * the validator to remove.
  497. */
  498. public void removeValidator(Validator validator) {
  499. if (validators != null) {
  500. validators.remove(validator);
  501. }
  502. requestRepaint();
  503. }
  504. /**
  505. * Tests the current value against all registered validators.
  506. *
  507. * @return <code>true</code> if all registered validators claim that the
  508. * current value is valid, <code>false</code> otherwise.
  509. */
  510. public boolean isValid() {
  511. if (isRequired()) {
  512. if (isEmpty()) {
  513. return false;
  514. }
  515. }
  516. if (validators == null) {
  517. return true;
  518. }
  519. final Object value = getValue();
  520. for (final Iterator i = validators.iterator(); i.hasNext();) {
  521. if (!((Validator) i.next()).isValid(value)) {
  522. return false;
  523. }
  524. }
  525. return true;
  526. }
  527. /**
  528. * Checks the validity of the Validatable by validating the field with all
  529. * attached validators.
  530. *
  531. * The "required" validation is a built-in validation feature. If the field
  532. * is required, but empty, validation will throw an EmptyValueException with
  533. * the error message set with setRequiredError().
  534. *
  535. * @see com.itmill.toolkit.data.Validatable#validate()
  536. */
  537. public void validate() throws Validator.InvalidValueException {
  538. if (isRequired()) {
  539. if (isEmpty()) {
  540. throw new Validator.EmptyValueException(requiredError);
  541. }
  542. }
  543. // If there is no validator, there can not be any errors
  544. if (validators == null) {
  545. return;
  546. }
  547. // Initialize temps
  548. Validator.InvalidValueException firstError = null;
  549. LinkedList errors = null;
  550. final Object value = getValue();
  551. // Gets all the validation errors
  552. for (final Iterator i = validators.iterator(); i.hasNext();) {
  553. try {
  554. ((Validator) i.next()).validate(value);
  555. } catch (final Validator.InvalidValueException e) {
  556. if (firstError == null) {
  557. firstError = e;
  558. } else {
  559. if (errors == null) {
  560. errors = new LinkedList();
  561. errors.add(firstError);
  562. }
  563. errors.add(e);
  564. }
  565. }
  566. }
  567. // If there were no error
  568. if (firstError == null) {
  569. return;
  570. }
  571. // If only one error occurred, throw it forwards
  572. if (errors == null) {
  573. throw firstError;
  574. }
  575. // Creates composite validator
  576. final Validator.InvalidValueException[] exceptions = new Validator.InvalidValueException[errors
  577. .size()];
  578. int index = 0;
  579. for (final Iterator i = errors.iterator(); i.hasNext();) {
  580. exceptions[index++] = (Validator.InvalidValueException) i.next();
  581. }
  582. throw new Validator.InvalidValueException(null, exceptions);
  583. }
  584. /**
  585. * Fields allow invalid values by default. In most cases this is wanted,
  586. * because the field otherwise visually forget the user input immediately.
  587. *
  588. * @return true iff the invalid values are allowed.
  589. * @see com.itmill.toolkit.data.Validatable#isInvalidAllowed()
  590. */
  591. public boolean isInvalidAllowed() {
  592. return invalidAllowed;
  593. }
  594. /**
  595. * Fields allow invalid values by default. In most cases this is wanted,
  596. * because the field otherwise visually forget the user input immediately.
  597. * <p>
  598. * In common setting where the user wants to assure the correctness of the
  599. * datasource, but allow temporarily invalid contents in the field, the user
  600. * should add the validators to datasource, that should not allow invalid
  601. * values. The validators are automatically copied to the field when the
  602. * datasource is set.
  603. * </p>
  604. *
  605. * @see com.itmill.toolkit.data.Validatable#setInvalidAllowed(boolean)
  606. */
  607. public void setInvalidAllowed(boolean invalidAllowed)
  608. throws UnsupportedOperationException {
  609. this.invalidAllowed = invalidAllowed;
  610. }
  611. /**
  612. * Error messages shown by the fields are composites of the error message
  613. * thrown by the superclasses (that is the component error message),
  614. * validation errors and buffered source errors.
  615. *
  616. * @see com.itmill.toolkit.ui.AbstractComponent#getErrorMessage()
  617. */
  618. public ErrorMessage getErrorMessage() {
  619. // Check validation errors only if automatic validation is enabled.
  620. // As an exception, no validation messages are shown for empty
  621. // required fields, as in those cases user is aware of the problem.
  622. ErrorMessage validationError = null;
  623. if (isValidationVisible()) {
  624. try {
  625. validate();
  626. } catch (Validator.InvalidValueException e) {
  627. if (!"".equals(e.getMessage())) {
  628. validationError = e;
  629. }
  630. }
  631. }
  632. // Check if there are any systems errors
  633. final ErrorMessage superError = super.getErrorMessage();
  634. // Return if there are no errors at all
  635. if (superError == null && validationError == null
  636. && currentBufferedSourceException == null) {
  637. return null;
  638. }
  639. // Throw combination of the error types
  640. return new CompositeErrorMessage(new ErrorMessage[] { superError,
  641. validationError, currentBufferedSourceException });
  642. }
  643. /* Value change events */
  644. private static final Method VALUE_CHANGE_METHOD;
  645. static {
  646. try {
  647. VALUE_CHANGE_METHOD = Property.ValueChangeListener.class
  648. .getDeclaredMethod("valueChange",
  649. new Class[] { Property.ValueChangeEvent.class });
  650. } catch (final java.lang.NoSuchMethodException e) {
  651. // This should never happen
  652. throw new java.lang.RuntimeException(
  653. "Internal error finding methods in AbstractField");
  654. }
  655. }
  656. /*
  657. * Adds a value change listener for the field. Don't add a JavaDoc comment
  658. * here, we use the default documentation from the implemented interface.
  659. */
  660. public void addListener(Property.ValueChangeListener listener) {
  661. addListener(AbstractField.ValueChangeEvent.class, listener,
  662. VALUE_CHANGE_METHOD);
  663. }
  664. /*
  665. * Removes a value change listener from the field. Don't add a JavaDoc
  666. * comment here, we use the default documentation from the implemented
  667. * interface.
  668. */
  669. public void removeListener(Property.ValueChangeListener listener) {
  670. removeListener(AbstractField.ValueChangeEvent.class, listener,
  671. VALUE_CHANGE_METHOD);
  672. }
  673. /**
  674. * Emits the value change event. The value contained in the field is
  675. * validated before the event is created.
  676. */
  677. protected void fireValueChange(boolean repaintIsNotNeeded) {
  678. fireEvent(new AbstractField.ValueChangeEvent(this));
  679. if (!repaintIsNotNeeded) {
  680. requestRepaint();
  681. }
  682. }
  683. /* Read-only status change events */
  684. private static final Method READ_ONLY_STATUS_CHANGE_METHOD;
  685. static {
  686. try {
  687. READ_ONLY_STATUS_CHANGE_METHOD = Property.ReadOnlyStatusChangeListener.class
  688. .getDeclaredMethod(
  689. "readOnlyStatusChange",
  690. new Class[] { Property.ReadOnlyStatusChangeEvent.class });
  691. } catch (final java.lang.NoSuchMethodException e) {
  692. // This should never happen
  693. throw new java.lang.RuntimeException(
  694. "Internal error finding methods in AbstractField");
  695. }
  696. }
  697. /**
  698. * An <code>Event</code> object specifying the Property whose read-only
  699. * status has changed.
  700. *
  701. * @author IT Mill Ltd.
  702. * @version
  703. * @VERSION@
  704. * @since 3.0
  705. */
  706. public class ReadOnlyStatusChangeEvent extends Component.Event implements
  707. Property.ReadOnlyStatusChangeEvent {
  708. /**
  709. * Serial generated by eclipse.
  710. */
  711. private static final long serialVersionUID = 3258688823264161846L;
  712. /**
  713. * New instance of text change event.
  714. *
  715. * @param source
  716. * the Source of the event.
  717. */
  718. public ReadOnlyStatusChangeEvent(AbstractField source) {
  719. super(source);
  720. }
  721. /**
  722. * Property where the event occurred.
  723. *
  724. * @return the Source of the event.
  725. */
  726. public Property getProperty() {
  727. return (Property) getSource();
  728. }
  729. }
  730. /*
  731. * Adds a read-only status change listener for the field. Don't add a
  732. * JavaDoc comment here, we use the default documentation from the
  733. * implemented interface.
  734. */
  735. public void addListener(Property.ReadOnlyStatusChangeListener listener) {
  736. addListener(Property.ReadOnlyStatusChangeEvent.class, listener,
  737. READ_ONLY_STATUS_CHANGE_METHOD);
  738. }
  739. /*
  740. * Removes a read-only status change listener from the field. Don't add a
  741. * JavaDoc comment here, we use the default documentation from the
  742. * implemented interface.
  743. */
  744. public void removeListener(Property.ReadOnlyStatusChangeListener listener) {
  745. removeListener(Property.ReadOnlyStatusChangeEvent.class, listener,
  746. READ_ONLY_STATUS_CHANGE_METHOD);
  747. }
  748. /**
  749. * Emits the read-only status change event. The value contained in the field
  750. * is validated before the event is created.
  751. */
  752. protected void fireReadOnlyStatusChange() {
  753. fireEvent(new AbstractField.ReadOnlyStatusChangeEvent(this));
  754. }
  755. /**
  756. * This method listens to data source value changes and passes the changes
  757. * forwards.
  758. *
  759. * @param event
  760. * the value change event telling the data source contents have
  761. * changed.
  762. */
  763. public void valueChange(Property.ValueChangeEvent event) {
  764. if (isReadThrough() || !isModified()) {
  765. fireValueChange(false);
  766. }
  767. }
  768. public void changeVariables(Object source, Map variables) {
  769. super.changeVariables(source, variables);
  770. }
  771. /**
  772. * Asks the terminal to place the cursor to this field.
  773. */
  774. public void focus() {
  775. final Application app = getApplication();
  776. if (app != null) {
  777. app.setFocusedComponent(this);
  778. } else {
  779. delayedFocus = true;
  780. }
  781. }
  782. /**
  783. * Creates abstract field by the type of the property.
  784. *
  785. * <p>
  786. * This returns most suitable field type for editing property of given type.
  787. * </p>
  788. *
  789. * @param propertyType
  790. * the Type of the property, that needs to be edited.
  791. */
  792. public static AbstractField constructField(Class propertyType) {
  793. // Null typed properties can not be edited
  794. if (propertyType == null) {
  795. return null;
  796. }
  797. // Date field
  798. if (Date.class.isAssignableFrom(propertyType)) {
  799. return new DateField();
  800. }
  801. // Boolean field
  802. if (Boolean.class.isAssignableFrom(propertyType)) {
  803. final Button button = new Button("");
  804. button.setSwitchMode(true);
  805. button.setImmediate(false);
  806. return button;
  807. }
  808. // Text field is used by default
  809. return new TextField();
  810. }
  811. /*
  812. * (non-Javadoc)
  813. *
  814. * @see com.itmill.toolkit.ui.Component.Focusable#getTabIndex()
  815. */
  816. public int getTabIndex() {
  817. return tabIndex;
  818. }
  819. /*
  820. * (non-Javadoc)
  821. *
  822. * @see com.itmill.toolkit.ui.Component.Focusable#setTabIndex(int)
  823. */
  824. public void setTabIndex(int tabIndex) {
  825. this.tabIndex = tabIndex;
  826. }
  827. /**
  828. * Sets the internal field value. This is purely used by AbstractField to
  829. * change the internal Field value. It does not trigger valuechange events.
  830. * It can be overriden by the inheriting classes to update all dependent
  831. * variables.
  832. *
  833. * @param newValue
  834. * the new value to be set.
  835. */
  836. protected void setInternalValue(Object newValue) {
  837. value = newValue;
  838. if (validators != null && !validators.isEmpty()) {
  839. requestRepaint();
  840. }
  841. }
  842. /**
  843. * Notifies the component that it is connected to an application.
  844. *
  845. * @see com.itmill.toolkit.ui.Component#attach()
  846. */
  847. public void attach() {
  848. super.attach();
  849. if (delayedFocus) {
  850. delayedFocus = false;
  851. focus();
  852. }
  853. }
  854. /**
  855. * Is this field required. Required fields must filled by the user.
  856. *
  857. * If the field is required, it is visually indicated in the user interface.
  858. * Furthermore, setting field to be required implicitly adds "non-empty"
  859. * validator and thus isValid() == false or any isEmpty() fields. In those
  860. * cases validation errors are not painted as it is obvious that the user
  861. * must fill in the required fields.
  862. *
  863. * On the other hand, for the non-required fields isValid() == true if the
  864. * field isEmpty() regardless of any attached validators.
  865. *
  866. *
  867. * @return <code>true</code> if the field is required .otherwise
  868. * <code>false</code>.
  869. */
  870. public boolean isRequired() {
  871. return required;
  872. }
  873. /**
  874. * Sets the field required. Required fields must filled by the user.
  875. *
  876. * If the field is required, it is visually indicated in the user interface.
  877. * Furthermore, setting field to be required implicitly adds "non-empty"
  878. * validator and thus isValid() == false or any isEmpty() fields. In those
  879. * cases validation errors are not painted as it is obvious that the user
  880. * must fill in the required fields.
  881. *
  882. * On the other hand, for the non-required fields isValid() == true if the
  883. * field isEmpty() regardless of any attached validators.
  884. *
  885. * @param required
  886. * Is the field required.
  887. */
  888. public void setRequired(boolean required) {
  889. this.required = required;
  890. requestRepaint();
  891. }
  892. public void setRequiredError(String requiredMessage) {
  893. requiredError = requiredMessage;
  894. requestRepaint();
  895. }
  896. public String getRequiredError() {
  897. return requiredError;
  898. }
  899. /**
  900. * Is the field empty?
  901. *
  902. * In general, "empty" state is same as null. As an exception, TextField
  903. * also treats empty string as "empty".
  904. */
  905. protected boolean isEmpty() {
  906. return (getValue() == null);
  907. }
  908. /**
  909. * Is automatic, visible validation enabled?
  910. *
  911. * If automatic validation is enabled, any validators connected to this
  912. * component are evaluated while painting the component and potential error
  913. * messages are sent to client. If the automatic validation is turned off,
  914. * isValid() and validate() methods still work, but one must show the
  915. * validation in their own code.
  916. *
  917. * @return True, if automatic validation is enabled.
  918. */
  919. public boolean isValidationVisible() {
  920. return validationVisible;
  921. }
  922. /**
  923. * Enable or disable automatic, visible validation.
  924. *
  925. * If automatic validation is enabled, any validators connected to this
  926. * component are evaluated while painting the component and potential error
  927. * messages are sent to client. If the automatic validation is turned off,
  928. * isValid() and validate() methods still work, but one must show the
  929. * validation in their own code.
  930. *
  931. * @param validateAutomatically
  932. * True, if automatic validation is enabled.
  933. */
  934. public void setValidationVisible(boolean validateAutomatically) {
  935. if (validationVisible != validateAutomatically) {
  936. requestRepaint();
  937. validationVisible = validateAutomatically;
  938. }
  939. }
  940. /**
  941. * Sets the current buffered source exception.
  942. *
  943. * @param currentBufferedSourceException
  944. */
  945. public void setCurrentBufferedSourceException(
  946. Buffered.SourceException currentBufferedSourceException) {
  947. this.currentBufferedSourceException = currentBufferedSourceException;
  948. requestRepaint();
  949. }
  950. }