or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

data-binding.mdextensions.mdgui-building.mdindex.mdlayout-management.mdlook-and-feel.mdmodels.md

models.mddocs/

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

```