0
# Value Models and Data Models
1
2
Observable data containers and specialized models that support property change notifications and data binding integration.
3
4
## Capabilities
5
6
### Core Value Model Interface
7
8
The foundation interface for all observable data containers.
9
10
```groovy { .api }
11
/**
12
* Interface for observable values with change notification support
13
*/
14
interface ValueModel {
15
/** Get the current value */
16
Object getValue()
17
18
/** Set the value and notify listeners */
19
void setValue(Object value)
20
21
/** Get the expected type of values */
22
Class getType()
23
24
/** Check if the model allows value changes */
25
boolean isEditable()
26
27
/** Add property change listener */
28
void addPropertyChangeListener(PropertyChangeListener listener)
29
30
/** Remove property change listener */
31
void removePropertyChangeListener(PropertyChangeListener listener)
32
}
33
```
34
35
### Basic Value Models
36
37
Simple value containers for common use cases.
38
39
```groovy { .api }
40
/**
41
* Basic value container with property change support
42
*/
43
class ValueHolder implements ValueModel {
44
/** Create with initial value */
45
ValueHolder(Object value = null)
46
47
/** Create with initial value and type constraint */
48
ValueHolder(Object value, Class type)
49
50
/** Create with initial value, type, and editability */
51
ValueHolder(Object value, Class type, boolean editable)
52
}
53
54
/**
55
* Property-based value model for bean properties
56
*/
57
class PropertyModel implements ValueModel {
58
/** Create model for bean property */
59
PropertyModel(Object bean, String property)
60
61
/** Get the source bean */
62
Object getBean()
63
64
/** Get the property name */
65
String getProperty()
66
}
67
68
/**
69
* Nested property model for deep property access
70
*/
71
class NestedValueModel implements ValueModel {
72
/** Create model for nested property path */
73
NestedValueModel(Object bean, String propertyPath)
74
75
/** Get the source bean */
76
Object getBean()
77
78
/** Get the property path (e.g., "address.street") */
79
String getPropertyPath()
80
}
81
82
/**
83
* Closure-based computed value model
84
*/
85
class ClosureModel implements ValueModel {
86
/** Create read-only computed model */
87
ClosureModel(Closure readClosure)
88
89
/** Create read-write computed model */
90
ClosureModel(Closure readClosure, Closure writeClosure)
91
92
/** Create with type constraint */
93
ClosureModel(Closure readClosure, Class type)
94
95
/** Create full-featured computed model */
96
ClosureModel(Closure readClosure, Closure writeClosure, Class type)
97
}
98
```
99
100
### Form Models
101
102
Specialized models for form data management.
103
104
```groovy { .api }
105
/**
106
* Model for managing form data with validation and buffering
107
*/
108
class FormModel implements ValueModel {
109
/** Create form model with initial data */
110
FormModel(Map initialData = [:])
111
112
/** Get value for specific field */
113
Object getFieldValue(String fieldName)
114
115
/** Set value for specific field */
116
void setFieldValue(String fieldName, Object value)
117
118
/** Check if form has changes from initial state */
119
boolean isDirty()
120
121
/** Check if specific field has changes */
122
boolean isFieldDirty(String fieldName)
123
124
/** Reset form to initial state */
125
void reset()
126
127
/** Commit current values as new initial state */
128
void commit()
129
130
/** Get all field names */
131
Set<String> getFieldNames()
132
133
/** Add field validator */
134
void addFieldValidator(String fieldName, Closure validator)
135
136
/** Remove field validator */
137
void removeFieldValidator(String fieldName, Closure validator)
138
139
/** Validate all fields */
140
Map<String, List<String>> validate()
141
142
/** Validate specific field */
143
List<String> validateField(String fieldName)
144
}
145
```
146
147
### Table Models
148
149
Enhanced table models with Groovy-specific features.
150
151
```groovy { .api }
152
/**
153
* Extended DefaultTableModel with enhanced Groovy support
154
*/
155
class DefaultTableModel extends javax.swing.table.DefaultTableModel {
156
/** Create with column names */
157
DefaultTableModel(List columnNames)
158
159
/** Create with data and column names */
160
DefaultTableModel(List<List> data, List columnNames)
161
162
/** Create with data map and column names */
163
DefaultTableModel(List<Map> data, List columnNames)
164
165
/** Add row using Map */
166
void addRow(Map rowData)
167
168
/** Insert row using Map */
169
void insertRow(int index, Map rowData)
170
171
/** Get row as Map */
172
Map getRowAsMap(int rowIndex)
173
174
/** Set row from Map */
175
void setRowFromMap(int rowIndex, Map rowData)
176
177
/** Find rows matching criteria */
178
List<Integer> findRows(Closure criteria)
179
180
/** Filter rows by criteria */
181
List<Map> filterRows(Closure criteria)
182
}
183
184
/**
185
* Extended table column with enhanced features
186
*/
187
class DefaultTableColumn extends TableColumn {
188
/** Create with identifier and display name */
189
DefaultTableColumn(Object identifier, String displayName)
190
191
/** Set column renderer from closure */
192
void setRendererFromClosure(Closure rendererClosure)
193
194
/** Set column editor from closure */
195
void setEditorFromClosure(Closure editorClosure)
196
197
/** Set column validator */
198
void setValidator(Closure validator)
199
200
/** Get column validator */
201
Closure getValidator()
202
}
203
```
204
205
### Model Utilities
206
207
Utility classes for working with models.
208
209
```groovy { .api }
210
/**
211
* Utility for wrapping lists as ListModel
212
*/
213
class ListWrapperListModel implements ListModel {
214
/** Create wrapper for existing list */
215
ListWrapperListModel(List wrappedList)
216
217
/** Get the wrapped list */
218
List getList()
219
220
/** Refresh model after list changes */
221
void refresh()
222
}
223
224
/**
225
* Base class for sortable table models
226
*/
227
class TableSorter extends AbstractTableModel implements TableModelListener {
228
/** Create sorter for existing table model */
229
TableSorter(TableModel model)
230
231
/** Sort by column */
232
void sortByColumn(int column)
233
234
/** Sort by column with custom comparator */
235
void sortByColumn(int column, Comparator comparator)
236
237
/** Check if column is sortable */
238
boolean isSortable(int column)
239
240
/** Set column sortable state */
241
void setSortable(int column, boolean sortable)
242
}
243
```
244
245
## Usage Examples
246
247
### Basic Value Models
248
249
```groovy
250
import groovy.model.*
251
252
// Simple value holder
253
def nameModel = new ValueHolder('John Doe')
254
nameModel.addPropertyChangeListener { evt ->
255
println "Name changed from '${evt.oldValue}' to '${evt.newValue}'"
256
}
257
nameModel.value = 'Jane Smith' // Triggers listener
258
259
// Property model
260
class Person {
261
String name
262
int age
263
}
264
def person = new Person(name: 'Alice', age: 30)
265
def nameProperty = new PropertyModel(person, 'name')
266
def ageProperty = new PropertyModel(person, 'age')
267
268
// Changes to person will notify model listeners
269
person.name = 'Bob'
270
```
271
272
### Nested Property Models
273
274
```groovy
275
class Address {
276
String street
277
String city
278
}
279
280
class Customer {
281
String name
282
Address address
283
}
284
285
def customer = new Customer(
286
name: 'John',
287
address: new Address(street: '123 Main St', city: 'Anytown')
288
)
289
290
// Access nested properties
291
def streetModel = new NestedValueModel(customer, 'address.street')
292
def cityModel = new NestedValueModel(customer, 'address.city')
293
294
println streetModel.value // "123 Main St"
295
streetModel.value = '456 Oak Ave'
296
println customer.address.street // "456 Oak Ave"
297
```
298
299
### Computed Models
300
301
```groovy
302
def firstNameModel = new ValueHolder('John')
303
def lastNameModel = new ValueHolder('Doe')
304
305
// Read-only computed model
306
def fullNameModel = new ClosureModel {
307
"${firstNameModel.value} ${lastNameModel.value}"
308
}
309
310
println fullNameModel.value // "John Doe"
311
312
// Read-write computed model
313
def temperatureF = new ValueHolder(32.0)
314
def temperatureC = new ClosureModel(
315
{ (temperatureF.value - 32) * 5 / 9 }, // Read closure
316
{ temperatureF.value = it * 9 / 5 + 32 } // Write closure
317
)
318
319
temperatureC.value = 0.0
320
println temperatureF.value // 32.0
321
```
322
323
### Form Models
324
325
```groovy
326
def formModel = new FormModel([
327
firstName: 'John',
328
lastName: 'Doe',
329
email: 'john@example.com',
330
age: 30
331
])
332
333
// Add validators
334
formModel.addFieldValidator('email') { value ->
335
if (!value?.contains('@')) {
336
return 'Invalid email format'
337
}
338
return null
339
}
340
341
formModel.addFieldValidator('age') { value ->
342
if (value < 18) {
343
return 'Must be 18 or older'
344
}
345
return null
346
}
347
348
// Make changes
349
formModel.setFieldValue('email', 'invalid-email')
350
formModel.setFieldValue('age', 16)
351
352
// Validate
353
def errors = formModel.validate()
354
errors.each { field, messages ->
355
println "$field: ${messages.join(', ')}"
356
}
357
358
// Check dirty state
359
println "Form is dirty: ${formModel.dirty}"
360
println "Email field is dirty: ${formModel.isFieldDirty('email')}"
361
```
362
363
### Enhanced Table Models
364
365
```groovy
366
def columns = ['Name', 'Age', 'Email']
367
def tableModel = new DefaultTableModel(columns)
368
369
// Add rows using Maps
370
tableModel.addRow([name: 'Alice', age: 25, email: 'alice@example.com'])
371
tableModel.addRow([name: 'Bob', age: 30, email: 'bob@example.com'])
372
tableModel.addRow([name: 'Charlie', age: 35, email: 'charlie@example.com'])
373
374
// Find rows by criteria
375
def youngUsers = tableModel.findRows { row ->
376
tableModel.getRowAsMap(row).age < 30
377
}
378
379
// Filter data
380
def filteredData = tableModel.filterRows { rowMap ->
381
rowMap.name.startsWith('A')
382
}
383
384
println "Young users: $youngUsers"
385
println "Names starting with A: $filteredData"
386
```