aboutsummaryrefslogtreecommitdiffstats
path: root/documentation/datamodel/datamodel-properties.asciidoc
blob: d5d0d3e39dc333869097c3992d681217bf68f0ae (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
---
title: Properties
order: 2
layout: page
---

[[datamodel.properties]]
= Properties

The [interfacename]#Property# interface is the base of the Vaadin Data Model. It
provides a standardized API for a single data value object that can be read
(get) and written (set). A property is always typed, but can optionally support
data type conversions. The type of a property can be any Java class. Optionally,
properties can provide value change events for following their changes.

You can set the value of a property with [methodname]#setValue()# and read with
[methodname]#getValue()#.

In the following, we set and read the property value from a
[classname]#TextField# component, which implements the [interfacename]#Property#
interface to allow accessing the field value.


----
final TextField tf = new TextField("Name");
        
// Set the value
tf.setValue("The text field value");
        
// When the field value is edited by the user
tf.addValueChangeListener(
    new Property.ValueChangeListener() {
    public void valueChange(ValueChangeEvent event) {
        // Do something with the new value
        layout.addComponent(new Label(tf.getValue()));
    }
});
----
See the http://demo.vaadin.com/book-examples-vaadin7/book#datamodel.properties.basic[on-line example, window="_blank"].

Changes in the property value usually fire a [classname]#ValueChangeEvent#,
which can be handled with a [classname]#ValueChangeListener#. The event object
provides reference to the property with [methodname]#getProperty()#. Note that
its [methodname]#getValue()# method returns the value with [classname]#Object#
type, so you need to cast it to the proper type.

Properties are in themselves unnamed. They are collected in __items__, which
associate the properties with names: the __Property Identifiers__ or __PID__s.
Items can be further contained in containers and are identified with __Item
Identifiers__ or __IID__s. In the spreadsheet analogy, __Property Identifiers__
would correspond to column names and __Item Identifiers__ to row names. The
identifiers can be arbitrary objects, but must implement the
[methodname]#equals(Object)# and [methodname]#hashCode()# methods so that they
can be used in any standard Java [classname]#Collection#.

The [classname]#Property# interface can be utilized either by implementing the
interface or by using some of the built-in property implementations. Vaadin
includes a [classname]#Property# interface implementation for arbitrary function
pairs and bean properties, with the [classname]#MethodProperty# class, and for
simple object properties, with the [classname]#ObjectProperty# class, as
described later.

In addition to the simple components, selection components provide their current
selection as the property value. In single selection mode, the property is a
single item identifier, while in multiple selection mode it is a set of item
identifiers. See the documentation of the selection components for further
details.

Components that can be bound to a property have an internal default data source
object, typically a [classname]#ObjectProperty#, which is described later. As
all such components are viewers or editors, also described later, so you can
rebind a component to any data source with
[methodname]#setPropertyDataSource()#.

[[datamodel.properties.viewers]]
== Property Viewers and Editors

The most important function of the [classname]#Property# as well as of the other
data model interfaces is to connect classes implementing the interface directly
to editor and viewer classes. This means connecting a data source (model) to a
user interface component (views) to allow editing or viewing the data model.

A property can be bound to a component implementing the [classname]#Viewer#
interface with [methodname]#setPropertyDataSource()#.


----
// Have a data model
ObjectProperty property =
    new ObjectProperty("Hello", String.class);
        
// Have a component that implements Viewer
Label viewer = new Label();
        
// Bind it to the data
viewer.setPropertyDataSource(property);
----

You can use the same method in the [classname]#Editor# interface to bind a
component that allows editing a particular property type to a property.


----
// Have a data model
ObjectProperty property =
    new ObjectProperty("Hello", String.class);
        
// Have a component that implements Viewer
TextField editor = new TextField("Edit Greeting");
        
// Bind it to the data
editor.setPropertyDataSource(property);

----

As all field components implement the [classname]#Property# interface, you can
bind any component implementing the [classname]#Viewer# interface to any field,
assuming that the viewer is able the view the object type of the field.
Continuing from the above example, we can bind a [classname]#Label# to the
[classname]#TextField# value:


----
Label viewer = new Label();
viewer.setPropertyDataSource(editor);

// The value shown in the viewer is updated immediately
// after editing the value in the editor (once it
// loses the focus)
editor.setImmediate(true);
----

If a field has validators, as described in
<<dummy/../../../framework/components/components-fields#components.fields.validation,"Field
Validation">>, the validators are executed before writing the value to the
property data source, or by calling the [methodname]#validate()# or
[methodname]#commit()# for the field.


[[datamodel.properties.objectproperty]]
== [classname]#ObjectProperty# Implementation

The [classname]#ObjectProperty# class is a simple implementation of the
[classname]#Property# interface that allows storing an arbitrary Java object.


----
// Have a component that implements Viewer interface
final TextField tf = new TextField("Name");
        
// Have a data model with some data
String myObject = "Hello";
        
// Wrap it in an ObjectProperty
ObjectProperty property =
    new ObjectProperty(myObject, String.class);
        
// Bind the property to the component
tf.setPropertyDataSource(property);
----


[[datamodel.properties.converter]]
== Converting Between Property Type and Representation

Fields allow editing a certain type, such as a [classname]#String# or
[classname]#Date#. The bound property, on the other hand, could have some
entirely different type. Conversion between a representation edited by the field
and the model defined in the property is handler with a converter that
implements the [interfacename]#Converter# interface.

Most common type conversions, such as between string and integer, are handled by
the default converters. They are created in a converter factory global in the
application.

[[datamodel.properties.converter.basic]]
=== Basic Use of Converters

The [methodname]#setConverter([interfacename]#Converter#)# method sets the
converter for a field. The method is defined in [classname]#AbstractField#.


----
// Have an integer property
final ObjectProperty<Integer> property =
        new ObjectProperty<Integer>(42); 
        
// Create a TextField, which edits Strings
final TextField tf = new TextField("Name");

// Use a converter between String and Integer
tf.setConverter(new StringToIntegerConverter());

// And bind the field
tf.setPropertyDataSource(property);
----

The built-in converters are the following:

[[datamodel.properties.converter.basic.built-in]]
.Built-in Converters
[options="header"]
|===============
|Converter|Representation|Model
|[classname]#StringToIntegerConverter#|[classname]#String#|[classname]#Integer#
|[classname]#StringToDoubleConverter#|[classname]#String#|[classname]#Double#
|[classname]#StringToNumberConverter#|[classname]#String#|[classname]#Number#
|[classname]#StringToBooleanConverter#|[classname]#String#|[classname]#Boolean#
|[classname]#StringToDateConverter#|[classname]#String#|[classname]#Date#
|[classname]#DateToLongConverter#|[classname]#Date#|[classname]#Long#

|===============



In addition, there is a [classname]#ReverseConverter# that takes a converter as
a parameter and reverses the conversion direction.

If a converter already exists for a type, the
[methodname]#setConverter([interfacename]#Class#)# retrieves the converter for
the given type from the converter factory, and then sets it for the field. This
method is used implicitly when binding field to a property data source.


[[datamodel.properties.converter.custom]]
=== Implementing a Converter

A conversion always occurs between a __representation type__, edited by the
field component, and a __model type__, that is, the type of the property data
source. Converters implement the [interfacename]#Converter# interface defined in
the [package]#com.vaadin.data.util.converter# package.

For example, let us assume that we have a simple [classname]#Complex# type for
storing complex values.


----
public class ComplexConverter
       implements Converter<String, Complex> {
    @Override
    public Complex convertToModel(String value, Locale locale)
            throws ConversionException {
        String parts[] =
            value.replaceAll("[\\(\\)]", "").split(",");
        if (parts.length != 2)
            throw new ConversionException(
                    "Unable to parse String to Complex");
        return new Complex(Double.parseDouble(parts[0]),
                           Double.parseDouble(parts[1]));
    }

    @Override
    public String convertToPresentation(Complex value,
                                        Locale locale)
            throws ConversionException {
        return "("+value.getReal()+","+value.getImag()+")";
    }

    @Override
    public Class<Complex> getModelType() {
        return Complex.class;
    }

    @Override
    public Class<String> getPresentationType() {
        return String.class;
    }
}
----

The conversion methods get the locale for the conversion as a parameter.


[[datamodel.properties.converter.converterfactory]]
=== Converter Factory

If a field does not directly allow editing a property type, a default converter
is attempted to create using an application-global converter factory. If you
define your own converters that you wish to include in the converter factory,
you need to implement one yourself. While you could implement the
[interfacename]#ConverterFactory# interface, it is usually easier to just extend
[classname]#DefaultConverterFactory#.


----
class MyConverterFactory extends DefaultConverterFactory {
    @Override
    public <PRESENTATION, MODEL> Converter<PRESENTATION, MODEL>
            createConverter(Class<PRESENTATION> presentationType,
                            Class<MODEL> modelType) {
        // Handle one particular type conversion
        if (String.class == presentationType &&
            Complex.class == modelType)
            return (Converter<PRESENTATION, MODEL>)
                   new ComplexConverter();

        // Default to the supertype
        return super.createConverter(presentationType,
                                     modelType);
    }
}

// Use the factory globally in the application
Application.getCurrentApplication().setConverterFactory(
        new MyConverterFactory());
----



ifdef::web[]
[[datamodel.properties.implementing]]
== Implementing the [classname]#Property# Interface

Implementation of the [classname]#Property# interface requires defining setters
and getters for the value and the __read-only__ mode. Only a getter is needed
for the property type, as the type is often fixed in property implementations.

The following example shows a simple implementation of the [classname]#Property#
interface:


----
class MyProperty implements Property {
    Integer data     = 0;
    boolean readOnly = false;
    
    // Return the data type of the model
    public Class<?> getType() {
        return Integer.class;
    }

    public Object getValue() {
        return data;
    }
    
    // Override the default implementation in Object
    @Override
    public String toString() {
        return Integer.toHexString(data);
    }

    public boolean isReadOnly() {
        return readOnly;
    }

    public void setReadOnly(boolean newStatus) {
        readOnly = newStatus;
    }

    public void setValue(Object newValue)
            throws ReadOnlyException, ConversionException {
        if (readOnly)
            throw new ReadOnlyException();
            
        // Already the same type as the internal representation
        if (newValue instanceof Integer)
            data = (Integer) newValue;
        
        // Conversion from a string is required
        else if (newValue instanceof String)
            try {
                data = Integer.parseInt((String) newValue, 16);
            } catch (NumberFormatException e) {
                throw new ConversionException();
            }
        else
             // Don't know how to convert any other types
            throw new ConversionException();

        // Reverse decode the hexadecimal value
    }
}
        
// Instantiate the property and set its data
MyProperty property = new MyProperty();
property.setValue(42);
        
// Bind it to a component
final TextField tf = new TextField("Name", property);
----

The components get the displayed value by the [methodname]#toString()# method,
so it is necessary to override it. To allow editing the value, value returned in
the [methodname]#toString()# must be in a format that is accepted by the
[methodname]#setValue()# method, unless the property is read-only. The
[methodname]#toString()# can perform any type conversion necessary to make the
internal type a string, and the [methodname]#setValue()# must be able to make a
reverse conversion.

The implementation example does not notify about changes in the property value
or in the read-only mode. You should normally also implement at least the
[classname]#Property.ValueChangeNotifier# and
[classname]#Property.ReadOnlyStatusChangeNotifier#. See the
[classname]#ObjectProperty# class for an example of the implementation.

endif::web[]