Search

Dark theme | Light theme

September 27, 2009

Groovy Goodness: Observable Map and List

Groovy provides the ObservableMap and ObservableList classes. These classes send out PropertyChangeEvent objects when we add, remove or change the contents of the map or list. For several actions we get different event types and we can use a PropertyChangeListener object to subscribe to these events. For example if we add an element we can subscribe to the property change event and get an PropertyAddedEvent.

 
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
import java.beans.*
 
def event
// Listener will assign event to global event variable.
def listener = {
    event = it
} as PropertyChangeListener
 
/* ObservableList */
def list = ['Groovy', 'rocks', 'the world', true] as ObservableList
list.addPropertyChangeListener(listener)
 
list << 'More text'
assert event instanceof ObservableList.ElementAddedEvent
assert 4 == event.index
assert 'More text' == event.newValue
 
list.remove(3)
assert event instanceof ObservableList.ElementRemovedEvent
assert 3 == event.index
 
list[0] = 'Grails'
assert event instanceof ObservableList.ElementUpdatedEvent
assert 0 == event.index
assert 'Groovy' == event.oldValue
assert 'Grails' == event.newValue
 
list.addAll([42, 101])
assert event instanceof ObservableList.MultiElementAddedEvent
assert [42, 101] == event.values
 
list.removeAll([true, 'More text', 42, 101])
assert event instanceof ObservableList.MultiElementRemovedEvent
assert 3 == list.size()
 
list.clear()
assert event instanceof ObservableList.ElementClearedEvent
assert ['Grails', 'rocks', 'the world'] == event.values
 
event = null
 
// We can define a closure as a filter. The closure is
// executed for each element and if it returns true,
// the property change event is fired.
def strict = new ObservableList({ it.size() > 2 })
strict.addPropertyChangeListener(listener)
strict.addAll(['a', 'ab', 'abc', 'abcd'])
assert ['abc', 'abcd'] == event.values
 
/* ObservableMap */
event = null
 
// Extra property change listener to assign to a specific
// property instead of the whole map.
def propEvent
def propListener = { propEvent = it } as PropertyChangeListener
 
def map = [username: 'mrhaki', email: 'email@host.com', active: true] as ObservableMap
map.addPropertyChangeListener(listener)
map.addPropertyChangeListener("active", propListener)
 
map.location = "@work"
assert event instanceof ObservableMap.PropertyAddedEvent
assert 'location' == event.propertyName
assert '@work' == event.newValue
assert !propEvent
 
map.active = false
assert event instanceof ObservableMap.PropertyUpdatedEvent
assert propEvent instanceof ObservableMap.PropertyUpdatedEvent
assert true == propEvent.oldValue
assert false == propEvent.newValue
assert 'active' == event.propertyName
 
map.remove('active')
assert propEvent instanceof ObservableMap.PropertyRemovedEvent
assert 3 == map.size()
 
map.putAll([car: true, phone: '555-1234'])
assert event instanceof ObservableMap.MultiPropertyEvent
assert event.events[0] instanceof ObservableMap.PropertyAddedEvent
assert 'car' == event.events[0].propertyName
assert true == event.events[0].newValue
assert event.events[1] instanceof ObservableMap.PropertyAddedEvent
assert 'phone' == event.events[1].propertyName
assert '555-1234' == event.events[1].newValue
 
map.clear()
assert event instanceof ObservableMap.PropertyClearedEvent
assert [username: 'mrhaki', car: true, phone: '555-1234', location: '@work', email: 'email@host.com'] == event.values
 
def strictMap = new ObservableMap({ name, value -> name ==~ /^a.*/ })
strictMap.addPropertyChangeListener(listener)
strictMap.putAll([a: 1, b: 2, c: 3])
assert 1 == event.events.size()
assert 'a' == event.events[0].propertyName
assert 1 == event.events[0].newValue