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.

PropertyFormatter.java 11KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380
  1. package com.vaadin.data.util;
  2. import java.util.LinkedList;
  3. import com.vaadin.data.Property;
  4. /**
  5. * Formatting proxy for a property.
  6. *
  7. * <p>
  8. * This class can be used to implement formatting for any type of Property
  9. * datasources. The idea is to connect this as proxy between UI component and
  10. * the original datasource.
  11. * </p>
  12. *
  13. * <p>
  14. * For example <code>
  15. * textfield.setPropertyDataSource(new PropertyFormatter(property) {
  16. public String format(Object value) {
  17. return ((Double) value).toString() + "000000000";
  18. }
  19. public Object parse(String formattedValue) throws Exception {
  20. return Double.parseDouble(formattedValue);
  21. }
  22. });</code> adds formatter for Double-typed property that extends standard
  23. * "1.0" notation with more zeroes.
  24. * </p>
  25. *
  26. * @author IT Mill Ltd.
  27. * @since 5.3.0
  28. */
  29. @SuppressWarnings("serial")
  30. public abstract class PropertyFormatter implements Property,
  31. Property.ValueChangeNotifier, Property.ValueChangeListener,
  32. Property.ReadOnlyStatusChangeListener,
  33. Property.ReadOnlyStatusChangeNotifier {
  34. /**
  35. * Internal list of registered value change listeners.
  36. */
  37. private LinkedList valueChangeListeners = null;
  38. /**
  39. * Internal list of registered read-only status change listeners.
  40. */
  41. private LinkedList readOnlyStatusChangeListeners = null;
  42. /** Datasource that stores the actual value. */
  43. Property dataSource;
  44. /**
  45. * Construct a new formatter that is connected to given datasource.
  46. *
  47. * @param propertyDataSource
  48. * to connect this property to.
  49. */
  50. public PropertyFormatter(Property propertyDataSource) {
  51. setPropertyDataSource(propertyDataSource);
  52. }
  53. /**
  54. * Gets the current data source of the formatter, if any.
  55. *
  56. * @return the current data source as a Property, or <code>null</code> if
  57. * none defined.
  58. */
  59. public Property getPropertyDataSource() {
  60. return dataSource;
  61. }
  62. /**
  63. * Sets the specified Property as the data source for the formatter.
  64. *
  65. *
  66. * <p>
  67. * Remember that new data sources getValue() must return objects that are
  68. * compatible with parse() and format() methods.
  69. * </p>
  70. *
  71. * @param newDataSource
  72. * the new data source Property.
  73. */
  74. public void setPropertyDataSource(Property newDataSource) {
  75. boolean readOnly = false;
  76. String prevValue = null;
  77. if (dataSource != null) {
  78. if (dataSource instanceof Property.ValueChangeNotifier) {
  79. ((Property.ValueChangeNotifier) dataSource)
  80. .removeListener(this);
  81. }
  82. if (dataSource instanceof Property.ReadOnlyStatusChangeListener) {
  83. ((Property.ReadOnlyStatusChangeNotifier) dataSource)
  84. .removeListener(this);
  85. }
  86. readOnly = isReadOnly();
  87. prevValue = toString();
  88. }
  89. dataSource = newDataSource;
  90. if (dataSource != null) {
  91. if (dataSource instanceof Property.ValueChangeNotifier) {
  92. ((Property.ValueChangeNotifier) dataSource).addListener(this);
  93. }
  94. if (dataSource instanceof Property.ReadOnlyStatusChangeListener) {
  95. ((Property.ReadOnlyStatusChangeNotifier) dataSource)
  96. .addListener(this);
  97. }
  98. }
  99. if (isReadOnly() != readOnly) {
  100. fireReadOnlyStatusChange();
  101. }
  102. String newVal = toString();
  103. if ((prevValue == null && newVal != null)
  104. || !prevValue.equals(prevValue)) {
  105. fireValueChange();
  106. }
  107. }
  108. /* Documented in the interface */
  109. public Class getType() {
  110. return String.class;
  111. }
  112. /**
  113. * Get the formatted value.
  114. *
  115. * @return If the datasource returns null, this is null. Otherwise this is
  116. * String given by format().
  117. */
  118. public Object getValue() {
  119. return toString();
  120. }
  121. /**
  122. * Get the formatted value.
  123. *
  124. * @return If the datasource returns null, this is null. Otherwise this is
  125. * String given by format().
  126. */
  127. @Override
  128. public String toString() {
  129. Object value = dataSource == null ? false : dataSource.getValue();
  130. if (value == null) {
  131. return null;
  132. }
  133. return format(value);
  134. }
  135. /** Reflects the read-only status of the datasource. */
  136. public boolean isReadOnly() {
  137. return dataSource == null ? false : dataSource.isReadOnly();
  138. }
  139. /**
  140. * This method must be implemented to format the values received from
  141. * DataSource.
  142. *
  143. * @param value
  144. * Value object got from the datasource. This is guaranteed to be
  145. * non-null and of the type compatible with getType() of the
  146. * datasource.
  147. * @return
  148. */
  149. abstract public String format(Object value);
  150. /**
  151. * Parse string and convert it to format compatible with datasource.
  152. *
  153. * The method is required to assure that parse(format(x)) equals x.
  154. *
  155. * @param formattedValue
  156. * This is guaranteed to be non-null string.
  157. * @return Non-null value compatible with datasource.
  158. * @throws Exception
  159. * Any type of exception can be thrown to indicate that the
  160. * conversion was not succesful.
  161. */
  162. abstract public Object parse(String formattedValue) throws Exception;
  163. /**
  164. * Sets the Property's read-only mode to the specified status.
  165. *
  166. * @param newStatus
  167. * the new read-only status of the Property.
  168. */
  169. public void setReadOnly(boolean newStatus) {
  170. if (dataSource != null) {
  171. dataSource.setReadOnly(newStatus);
  172. }
  173. }
  174. public void setValue(Object newValue) throws ReadOnlyException,
  175. ConversionException {
  176. if (dataSource == null) {
  177. return;
  178. }
  179. if (newValue == null) {
  180. dataSource.setValue(null);
  181. }
  182. try {
  183. dataSource.setValue(parse((String) newValue));
  184. if (!newValue.equals(toString())) {
  185. fireValueChange();
  186. }
  187. } catch (Exception e) {
  188. if (e instanceof ConversionException) {
  189. throw (ConversionException) e;
  190. } else {
  191. throw new ConversionException(e);
  192. }
  193. }
  194. }
  195. /**
  196. * An <code>Event</code> object specifying the ObjectProperty whose value
  197. * has changed.
  198. *
  199. * @author IT Mill Ltd.
  200. * @since 5.3.0
  201. */
  202. private class ValueChangeEvent extends java.util.EventObject implements
  203. Property.ValueChangeEvent {
  204. /**
  205. * Constructs a new value change event for this object.
  206. *
  207. * @param source
  208. * the source object of the event.
  209. */
  210. protected ValueChangeEvent(PropertyFormatter source) {
  211. super(source);
  212. }
  213. /**
  214. * Gets the Property whose read-only state has changed.
  215. *
  216. * @return source the Property of the event.
  217. */
  218. public Property getProperty() {
  219. return (Property) getSource();
  220. }
  221. }
  222. /**
  223. * An <code>Event</code> object specifying the Property whose read-only
  224. * status has been changed.
  225. *
  226. * @author IT Mill Ltd.
  227. * @since 5.3.0
  228. */
  229. private class ReadOnlyStatusChangeEvent extends java.util.EventObject
  230. implements Property.ReadOnlyStatusChangeEvent {
  231. /**
  232. * Constructs a new read-only status change event for this object.
  233. *
  234. * @param source
  235. * source object of the event
  236. */
  237. protected ReadOnlyStatusChangeEvent(PropertyFormatter source) {
  238. super(source);
  239. }
  240. /**
  241. * Gets the Property whose read-only state has changed.
  242. *
  243. * @return source Property of the event.
  244. */
  245. public Property getProperty() {
  246. return (Property) getSource();
  247. }
  248. }
  249. /**
  250. * Removes a previously registered value change listener.
  251. *
  252. * @param listener
  253. * the listener to be removed.
  254. */
  255. public void removeListener(Property.ValueChangeListener listener) {
  256. if (valueChangeListeners != null) {
  257. valueChangeListeners.remove(listener);
  258. }
  259. }
  260. /**
  261. * Registers a new value change listener for this ObjectProperty.
  262. *
  263. * @param listener
  264. * the new Listener to be registered
  265. */
  266. public void addListener(Property.ValueChangeListener listener) {
  267. if (valueChangeListeners == null) {
  268. valueChangeListeners = new LinkedList();
  269. }
  270. valueChangeListeners.add(listener);
  271. }
  272. /**
  273. * Registers a new read-only status change listener for this Property.
  274. *
  275. * @param listener
  276. * the new Listener to be registered
  277. */
  278. public void addListener(Property.ReadOnlyStatusChangeListener listener) {
  279. if (readOnlyStatusChangeListeners == null) {
  280. readOnlyStatusChangeListeners = new LinkedList();
  281. }
  282. readOnlyStatusChangeListeners.add(listener);
  283. }
  284. /**
  285. * Removes a previously registered read-only status change listener.
  286. *
  287. * @param listener
  288. * the listener to be removed.
  289. */
  290. public void removeListener(Property.ReadOnlyStatusChangeListener listener) {
  291. if (readOnlyStatusChangeListeners != null) {
  292. readOnlyStatusChangeListeners.remove(listener);
  293. }
  294. }
  295. /**
  296. * Sends a value change event to all registered listeners.
  297. */
  298. private void fireValueChange() {
  299. if (valueChangeListeners != null) {
  300. final Object[] l = valueChangeListeners.toArray();
  301. final Property.ValueChangeEvent event = new ValueChangeEvent(this);
  302. for (int i = 0; i < l.length; i++) {
  303. ((Property.ValueChangeListener) l[i]).valueChange(event);
  304. }
  305. }
  306. }
  307. /**
  308. * Sends a read only status change event to all registered listeners.
  309. */
  310. private void fireReadOnlyStatusChange() {
  311. if (readOnlyStatusChangeListeners != null) {
  312. final Object[] l = readOnlyStatusChangeListeners.toArray();
  313. final Property.ReadOnlyStatusChangeEvent event = new ReadOnlyStatusChangeEvent(
  314. this);
  315. for (int i = 0; i < l.length; i++) {
  316. ((Property.ReadOnlyStatusChangeListener) l[i])
  317. .readOnlyStatusChange(event);
  318. }
  319. }
  320. }
  321. /**
  322. * Listens for changes in the datasource.
  323. *
  324. * This should not be called directly.
  325. */
  326. public void valueChange(
  327. com.vaadin.data.Property.ValueChangeEvent event) {
  328. fireValueChange();
  329. }
  330. /**
  331. * Listens for changes in the datasource.
  332. *
  333. * This should not be called directly.
  334. */
  335. public void readOnlyStatusChange(
  336. com.vaadin.data.Property.ReadOnlyStatusChangeEvent event) {
  337. fireReadOnlyStatusChange();
  338. }
  339. }