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

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