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

data-binding.mddocs/

0

# Data Binding System

1

2

Comprehensive data binding framework for connecting GUI components to data models with validation, conversion, and automatic synchronization.

3

4

## Capabilities

5

6

### Core Binding Interfaces

7

8

The foundation of the binding system providing contracts for data synchronization.

9

10

```groovy { .api }

11

/**

12

* Complete bidirectional binding interface with converter/validator support

13

*/

14

interface FullBinding extends BindingUpdatable {

15

SourceBinding getSourceBinding()

16

TargetBinding getTargetBinding()

17

void setSourceBinding(SourceBinding source)

18

void setTargetBinding(TargetBinding target)

19

void setValidator(Closure validator)

20

Closure getValidator()

21

void setConverter(Closure converter)

22

Closure getConverter()

23

void setReverseConverter(Closure reverseConverter)

24

Closure getReverseConverter()

25

}

26

27

/**

28

* Base interface for all binding update operations

29

*/

30

interface BindingUpdatable {

31

void bind()

32

void unbind()

33

void rebind()

34

void update()

35

void reverseUpdate()

36

}

37

38

/**

39

* Source endpoint of a binding relationship

40

*/

41

interface SourceBinding extends BindingUpdatable {

42

Object getSourceValue()

43

void setSourceBinding(PropertyChangeListener listener)

44

}

45

46

/**

47

* Target endpoint of a binding relationship

48

*/

49

interface TargetBinding extends BindingUpdatable {

50

void setTargetValue(Object value)

51

}

52

53

/**

54

* Event-triggered binding interface

55

*/

56

interface TriggerBinding {

57

FullBinding getFullBinding()

58

void setFullBinding(FullBinding fullBinding)

59

void bind()

60

void unbind()

61

}

62

```

63

64

### Binding Factory

65

66

The primary factory for creating and configuring data bindings through the builder DSL.

67

68

```groovy { .api }

69

/**

70

* Primary factory for creating data bindings

71

*/

72

class BindFactory {

73

/** Create binding between source and target */

74

Object newInstance(FactoryBuilderSupport builder, Object name, Object value, Map attributes)

75

76

/** Handle binding attributes in component definitions */

77

boolean bindingAttributeDelegate(FactoryBuilderSupport builder, Object node, Map attributes)

78

}

79

80

// Binding DSL usage

81

bind(source: sourceObject, sourceProperty: 'propertyName',

82

target: targetComponent, targetProperty: 'text',

83

converter: { value -> value.toString() },

84

validator: { value -> value != null })

85

```

86

87

**Usage Examples:**

88

89

```groovy

90

import groovy.swing.SwingBuilder

91

import groovy.model.ValueHolder

92

93

def swing = new SwingBuilder()

94

def model = new ValueHolder('Initial Text')

95

96

swing.frame(title: 'Binding Example') {

97

panel {

98

// Bind text field to model

99

textField(id: 'textInput', columns: 20)

100

bind(source: model, sourceProperty: 'value',

101

target: textInput, targetProperty: 'text')

102

103

// Two-way binding with validation

104

textField(id: 'numberInput', columns: 10)

105

bind(source: model, sourceProperty: 'value',

106

target: numberInput, targetProperty: 'text',

107

converter: { it.toString() },

108

reverseConverter: {

109

try { Integer.parseInt(it) }

110

catch (NumberFormatException e) { 0 }

111

},

112

validator: { it instanceof Number && it >= 0 })

113

}

114

}

115

```

116

117

### Value Models

118

119

Observable data containers that support property change notifications.

120

121

```groovy { .api }

122

/**

123

* Interface for observable values with change notification

124

*/

125

interface ValueModel {

126

Object getValue()

127

void setValue(Object value)

128

Class getType()

129

boolean isEditable()

130

}

131

132

/**

133

* Basic implementation of ValueModel

134

*/

135

class ValueHolder implements ValueModel {

136

ValueHolder(Object value = null, Class type = Object, boolean editable = true)

137

Object getValue()

138

void setValue(Object value)

139

Class getType()

140

boolean isEditable()

141

void addPropertyChangeListener(PropertyChangeListener listener)

142

void removePropertyChangeListener(PropertyChangeListener listener)

143

}

144

145

/**

146

* Property-based value model with reflection support

147

*/

148

class PropertyModel implements ValueModel {

149

PropertyModel(Object bean, String propertyName)

150

Object getValue()

151

void setValue(Object value)

152

Class getType()

153

boolean isEditable()

154

void addPropertyChangeListener(PropertyChangeListener listener)

155

void removePropertyChangeListener(PropertyChangeListener listener)

156

}

157

158

/**

159

* Nested property access model for complex object graphs

160

*/

161

class NestedValueModel implements ValueModel {

162

NestedValueModel(Object root, String propertyPath)

163

Object getValue()

164

void setValue(Object value)

165

Class getType()

166

boolean isEditable()

167

void addPropertyChangeListener(PropertyChangeListener listener)

168

void removePropertyChangeListener(PropertyChangeListener listener)

169

}

170

171

/**

172

* Closure-based computed model

173

*/

174

class ClosureModel implements ValueModel {

175

ClosureModel(Closure closure)

176

Object getValue()

177

void setValue(Object value)

178

Class getType()

179

boolean isEditable()

180

void addPropertyChangeListener(PropertyChangeListener listener)

181

void removePropertyChangeListener(PropertyChangeListener listener)

182

}

183

```

184

185

**Usage Examples:**

186

187

```groovy

188

import groovy.model.*

189

190

// Basic value holder

191

def nameModel = new ValueHolder('John Doe', String)

192

nameModel.addValueChangeListener { evt ->

193

println "Name changed from ${evt.oldValue} to ${evt.newValue}"

194

}

195

196

// Property model for existing bean

197

class Person {

198

String name

199

int age

200

}

201

def person = new Person(name: 'Alice', age: 30)

202

def nameProperty = new PropertyModel(person, 'name')

203

def ageProperty = new PropertyModel(person, 'age')

204

205

// Nested property model

206

def addressProperty = new NestedValueModel(person, 'address.street')

207

208

// Computed model

209

def fullNameModel = new ClosureModel {

210

"${person.firstName} ${person.lastName}"

211

}

212

```

213

214

### Component Property Bindings

215

216

Specialized binding classes for different Swing components.

217

218

```groovy { .api }

219

/**

220

* Text component binding with document listeners

221

*/

222

class JTextComponentProperties {

223

static Object getText(JTextComponent self)

224

static void setText(JTextComponent self, Object value)

225

}

226

227

/**

228

* Button selection state binding

229

*/

230

class AbstractButtonProperties {

231

static Object getSelected(AbstractButton self)

232

static void setSelected(AbstractButton self, Object value)

233

}

234

235

/**

236

* Slider value binding

237

*/

238

class JSliderProperties {

239

static Object getValue(JSlider self)

240

static void setValue(JSlider self, Object value)

241

}

242

243

/**

244

* Combo box selection and item binding

245

*/

246

class JComboBoxProperties {

247

static Object getSelectedItem(JComboBox self)

248

static void setSelectedItem(JComboBox self, Object value)

249

static Object getSelectedIndex(JComboBox self)

250

static void setSelectedIndex(JComboBox self, Object value)

251

}

252

253

/**

254

* List selection binding

255

*/

256

class JListProperties {

257

static Object getSelectedElement(JList self)

258

static Object getSelectedElements(JList self)

259

static Object getSelectedIndex(JList self)

260

static Object getSelectedIndices(JList self)

261

static Object getSelectedValue(JList self)

262

static Object getSelectedValues(JList self)

263

}

264

265

/**

266

* Table selection and data binding

267

*/

268

class JTableProperties {

269

static Object getSelectedElement(JTable self)

270

static Object getSelectedElements(JTable self)

271

static Object getSelectedRow(JTable self)

272

static Object getSelectedRows(JTable self)

273

static Object getSelectedColumn(JTable self)

274

static Object getSelectedColumns(JTable self)

275

}

276

277

/**

278

* Spinner value binding

279

*/

280

class JSpinnerProperties {

281

static Object getValue(JSpinner self)

282

static void setValue(JSpinner self, Object value)

283

}

284

```

285

286

### Binding Implementations

287

288

Concrete implementations of the binding interfaces for various scenarios.

289

290

```groovy { .api }

291

/**

292

* Base implementation of FullBinding

293

*/

294

abstract class AbstractFullBinding implements FullBinding {

295

SourceBinding sourceBinding

296

TargetBinding targetBinding

297

Closure validator

298

Closure converter

299

Closure reverseConverter

300

301

void bind()

302

void unbind()

303

void rebind()

304

void update()

305

}

306

307

/**

308

* Property-based binding implementation

309

*/

310

class PropertyBinding extends AbstractFullBinding {

311

PropertyBinding(Object source, String sourceProperty,

312

Object target, String targetProperty)

313

}

314

315

/**

316

* Property path binding for nested properties

317

*/

318

class PropertyPathFullBinding extends AbstractFullBinding {

319

PropertyPathFullBinding(Object source, String sourcePath,

320

Object target, String targetPath)

321

}

322

323

/**

324

* Closure-based source binding

325

*/

326

class ClosureSourceBinding implements SourceBinding {

327

ClosureSourceBinding(Closure closure)

328

Object getSourceValue()

329

void setSourceBinding(PropertyChangeListener listener)

330

void bind()

331

void unbind()

332

void rebind()

333

void update()

334

}

335

336

/**

337

* Event-triggered binding

338

*/

339

class EventTriggerBinding implements TriggerBinding {

340

EventTriggerBinding(Object source, String eventName,

341

FullBinding fullBinding = null, Closure closure = null)

342

}

343

344

/**

345

* Timer-based binding updates

346

*/

347

class SwingTimerTriggerBinding implements TriggerBinding {

348

SwingTimerTriggerBinding(int delay, FullBinding fullBinding = null)

349

}

350

351

/**

352

* Property-based binding with configurable update strategies

353

*/

354

class PropertyBinding extends AbstractFullBinding {

355

PropertyBinding(Object sourceBean, String sourceProperty,

356

Object targetBean, String targetProperty,

357

UpdateStrategy updateStrategy = UpdateStrategy.MIXED)

358

359

static enum UpdateStrategy {

360

/** EDT if on EDT, otherwise invokeLater */

361

MIXED,

362

/** Always use invokeLater */

363

ASYNC,

364

/** invokeAndWait if not on EDT, direct if on EDT */

365

SYNC,

366

/** Execute in same thread */

367

SAME,

368

/** Execute outside EDT */

369

OUTSIDE,

370

/** Always execute in background thread */

371

DEFER

372

}

373

}

374

375

/**

376

* Bidirectional property binding between two properties

377

*/

378

class MutualPropertyBinding extends PropertyBinding {

379

MutualPropertyBinding(Object sourceBean, String sourceProperty,

380

Object targetBean, String targetProperty,

381

UpdateStrategy updateStrategy = UpdateStrategy.MIXED)

382

}

383

384

/**

385

* Aggregate multiple bindings for group operations

386

*/

387

class AggregateBinding implements FullBinding {

388

void addBinding(FullBinding binding)

389

void removeBinding(FullBinding binding)

390

List<FullBinding> getBindings()

391

}

392

```

393

394

### Binding Groups and Proxies

395

396

Advanced binding constructs for complex scenarios.

397

398

```groovy { .api }

399

/**

400

* Grouped bindings for batch operations

401

*/

402

class BindGroupFactory {

403

Object newInstance(FactoryBuilderSupport builder, Object name, Object value, Map attributes)

404

}

405

406

/**

407

* Binding proxy for complex binding scenarios

408

*/

409

class BindingProxy {

410

BindingProxy(Object target, String property, Map args = [:])

411

void bind(Object source, String sourceProperty)

412

void unbind()

413

}

414

415

/**

416

* Multiple binding aggregation

417

*/

418

class AggregateBinding extends AbstractFullBinding {

419

AggregateBinding(FullBinding... bindings)

420

void addBinding(FullBinding binding)

421

void removeBinding(FullBinding binding)

422

}

423

```

424

425

**Usage Examples:**

426

427

```groovy

428

// Binding group for form validation

429

bindGroup {

430

bind(source: model.name, target: nameField, targetProperty: 'text')

431

bind(source: model.email, target: emailField, targetProperty: 'text')

432

bind(source: model.age, target: ageSpinner, targetProperty: 'value')

433

}

434

435

// Binding proxy for complex property chains

436

def proxy = new BindingProxy(someComplexObject, 'deeply.nested.property')

437

proxy.bind(textField, 'text')

438

439

// Timer-based binding for real-time updates

440

def timerBinding = new SwingTimerTriggerBinding(1000) // Update every second

441

timerBinding.fullBinding = bind(source: statusModel, target: statusLabel)

442

```

443

444

### Validation and Conversion

445

446

Built-in support for data validation and type conversion in bindings.

447

448

```groovy { .api }

449

// Binding with validation

450

bind(source: model, sourceProperty: 'value',

451

target: textField, targetProperty: 'text',

452

validator: { value ->

453

if (value == null || value.toString().trim().isEmpty()) {

454

throw new IllegalArgumentException('Value cannot be empty')

455

}

456

return true

457

})

458

459

// Binding with bi-directional conversion

460

bind(source: model, sourceProperty: 'temperature',

461

target: textField, targetProperty: 'text',

462

converter: { celsius ->

463

String.format('%.1f°C', celsius)

464

},

465

reverseConverter: { text ->

466

def match = text =~ /(\d+\.?\d*)°?C?/

467

match ? Double.parseDouble(match[0][1]) : 0.0

468

})

469

```

470

471

### Table Model Integration

472

473

Enhanced table models with binding support for complex data display.

474

475

```groovy { .api }

476

/**

477

* Enhanced table model with PropertyModel columns

478

*/

479

class DefaultTableModel extends AbstractTableModel {

480

DefaultTableModel(List rowData = [], List columnNames = [])

481

void addColumn(String name, Closure valueGetter)

482

void addPropertyColumn(String name, String property)

483

void fireTableDataChanged()

484

}

485

486

/**

487

* Table column with value model support

488

*/

489

class DefaultTableColumn extends TableColumn {

490

DefaultTableColumn(ValueModel valueModel, String header)

491

ValueModel getValueModel()

492

void setValueModel(ValueModel model)

493

}

494

```

495

496

**Usage Examples:**

497

498

```groovy

499

// Table with bound data model

500

table {

501

tableModel(list: peopleList) {

502

propertyColumn(header: 'Name', propertyName: 'name', editable: true)

503

propertyColumn(header: 'Age', propertyName: 'age', type: Integer)

504

closureColumn(header: 'Status') { person ->

505

person.age >= 18 ? 'Adult' : 'Minor'

506

}

507

}

508

}

509

510

// Bind table selection to detail view

511

bind(source: table, sourceProperty: 'selectedElement',

512

target: detailPanel, targetProperty: 'person')

513

```