1 package cz.cuni.amis.utils.flag;
2
3 import java.util.ArrayList;
4 import java.util.Collection;
5 import java.util.List;
6 import java.util.concurrent.CountDownLatch;
7 import java.util.concurrent.TimeUnit;
8
9 import cz.cuni.amis.utils.collections.MyCollections;
10 import cz.cuni.amis.utils.exception.PogamutInterruptedException;
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27 public class WaitForFlagChange<TYPE> {
28
29 public static interface IAccept<TYPE> {
30
31 public boolean accept(TYPE flagValue);
32
33 }
34
35 private class ListAccept implements IAccept<TYPE> {
36
37 private List<TYPE> waitingFor;
38
39 public ListAccept(Collection<TYPE> list) {
40 waitingFor = new ArrayList<TYPE>(list.size());
41 waitingFor.addAll(list);
42 }
43
44
45
46
47 public ListAccept() {
48 this.waitingFor = null;
49 }
50
51 public ListAccept(TYPE... accept) {
52 waitingFor = new ArrayList(accept.length);
53 waitingFor.addAll(MyCollections.toList(accept));
54 }
55
56 @Override
57 public boolean accept(TYPE flagValue) {
58 if (waitingFor == null) return true;
59 return waitingFor.contains(flagValue);
60 }
61
62 }
63
64 private IAccept<TYPE> accept;
65 private IFlag<TYPE> flag;
66
67 private Object latchAccessMutex = new Object();
68 private Object mutex = new Object();
69
70 private CountDownLatch latch = null;
71
72 private boolean isResult = false;
73 private TYPE result = null;
74
75 private class Listener implements FlagListener<TYPE> {
76
77 public Listener(IFlag<TYPE> flag) {
78 WaitForFlagChange.this.result = flag.getFlag();
79 if (!(isResult = isDesiredValue(WaitForFlagChange.this.result))) {
80 flag.addListener(this);
81 }
82
83 if (!isResult) {
84 TYPE result = flag.getFlag();
85 if (isDesiredValue(result)) {
86 isResult = true;
87 WaitForFlagChange.this.result = result;
88 flag.removeListener(this);
89 synchronized(latchAccessMutex) {
90 if (latch != null) {
91 latch.countDown();
92 }
93 }
94 }
95 }
96 }
97
98 @Override
99 public void flagChanged(TYPE changedValue) {
100 if (!isResult && isDesiredValue(changedValue)) {
101 flag.removeListener(this);
102 synchronized(latchAccessMutex) {
103 isResult = true;
104 result = changedValue;
105 if (latch != null) {
106 latch.countDown();
107 }
108 }
109 }
110 }
111
112 }
113
114
115
116
117
118 public WaitForFlagChange(IFlag<TYPE> flag) {
119 this.flag = flag;
120 this.accept = new ListAccept();
121 }
122
123 public WaitForFlagChange(IFlag<TYPE> flag, IAccept<TYPE> waitingFor) {
124 this.flag = flag;
125 this.accept = waitingFor;
126 }
127
128 public WaitForFlagChange(IFlag<TYPE> flag, TYPE waitingFor) {
129 this.flag = flag;
130 this.accept = new ListAccept(waitingFor);
131 }
132
133 public WaitForFlagChange(IFlag<TYPE> flag, TYPE[] waitingFor) {
134 this.flag = flag;
135 this.accept = new ListAccept(waitingFor);
136 }
137
138 public WaitForFlagChange(IFlag<TYPE> flag, Collection<TYPE> waitingFor) {
139 this.flag = flag;
140 this.accept = new ListAccept(waitingFor);
141 }
142
143 private boolean isDesiredValue(TYPE value) {
144 return accept.accept(value);
145 }
146
147
148
149
150
151
152
153
154 public TYPE await() throws PogamutInterruptedException {
155 synchronized(mutex) {
156 synchronized(latchAccessMutex) {
157 latch = new CountDownLatch(1);
158 }
159
160
161 Listener listener = new Listener(flag);
162 if (isResult) return result;
163 try {
164 latch.await();
165 } catch (InterruptedException e) {
166 throw new PogamutInterruptedException(e, this);
167 }
168 synchronized(latchAccessMutex) {
169 flag.removeListener(listener);
170 latch = null;
171 }
172 return result;
173 }
174 }
175
176
177
178
179
180
181
182
183
184
185
186
187 public TYPE await(long timeout, TimeUnit timeUnit) throws PogamutInterruptedException {
188 synchronized(mutex) {
189 synchronized(latchAccessMutex) {
190 latch = new CountDownLatch(1);
191 }
192
193
194 Listener listener = new Listener(flag);
195 if (isResult) return result;
196 try {
197 latch.await(timeout, timeUnit);
198 } catch (InterruptedException e) {
199 throw new PogamutInterruptedException(e, this);
200 }
201 synchronized(latchAccessMutex) {
202 flag.removeListener(listener);
203 latch = null;
204 }
205 return result;
206 }
207 }
208
209 }