1 /*
2 * Copyright (c) 2003-2008 by Cosylab d. d.
3 *
4 * This file is part of CosyBeans.
5 *
6 * CosyBeans is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * CosyBeans is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with CosyBeans. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20 package com.cosylab.gui.adapters;
21
22 import java.beans.PropertyVetoException;
23 import java.util.HashMap;
24 import java.util.Map;
25
26 import com.cosylab.gui.displayers.DataConsumer;
27 import com.cosylab.gui.displayers.DataSource;
28 import com.cosylab.gui.displayers.DataState;
29 import com.cosylab.gui.displayers.DoubleConsumer;
30 import com.cosylab.gui.displayers.LongConsumer;
31 import com.cosylab.gui.displayers.NonblockingNumberConsumer;
32 import com.cosylab.util.CommonException;
33
34 public class OnApplyDispatcher extends DataConsumerDispatcher
35 implements DoubleConsumer, LongConsumer
36 {
37 private static final long serialVersionUID = 1L;
38
39 // flag indicating if user has called cache characteristics
40 private boolean userCache = false;
41
42 // last dispatched characteristics, used to be redispatched
43 // when multiplication factor is changed or new consumer added
44 private Map lastCharacteristics;
45
46 // last dispatched data state, used to be redispatched when
47 // new consumer added
48 private DataState lastDataState;
49
50 //private Map lastCharacteristics;
51 //private DataState lastDataState;
52 protected Number valueToSet = null;
53 private boolean onApply = true;
54 // dispatcher which intercepts feedback value from peer data consumer and dispatch it to peer data sources
55 private Dispatcher dispatcher;
56
57 class Dispatcher extends NonblockingNumberDispatcher
58 {
59 /*
60 * @see com.cosylab.gui.adapters.NonblockingNumberDispatcher#updateNonblocking(java.lang.Number)
61 */
62 public void updateNonblocking(Number value)
63 {
64 valueToSet = value;
65
66 if (!onApply) {
67 super.updateNonblocking(valueToSet);
68 }
69 }
70
71 /**
72 * DOCUMENT ME!
73 */
74 public void delegateUpdateNonblocking()
75 {
76 super.updateNonblocking(valueToSet);
77 }
78 }
79
80 /**
81 * Creates a new OnApplyDispatcher object.
82 */
83 public OnApplyDispatcher()
84 {
85 // we dalagate to super suppport class only the consumer type/types we want to suppport
86 super(new Class[]{ DoubleConsumer.class, LongConsumer.class });
87
88 // the name of this converter when it acts as a consumer
89 name = "OnApplyDispatcher";
90 }
91
92 protected NonblockingNumberDispatcher getNonblDisplatecher()
93 {
94 if (dispatcher == null) {
95 dispatcher = new Dispatcher();
96 }
97
98 return dispatcher;
99 }
100
101 /**
102 * DOCUMENT ME!
103 */
104 public void applyValue()
105 {
106 if (dispatcher != null) {
107 dispatcher.delegateUpdateNonblocking();
108 }
109 }
110
111 /**
112 * DOCUMENT ME!
113 *
114 * @return DOCUMENT ME!
115 */
116 public boolean isOnApply()
117 {
118 return onApply;
119 }
120
121 /**
122 * DOCUMENT ME!
123 *
124 * @param onApply DOCUMENT ME!
125 */
126 public void setOnApply(boolean onApply)
127 {
128 this.onApply = onApply;
129 }
130 /**
131 * In this method we investigate the following cases:
132 *
133 * <ul>
134 * <li>
135 * if consumer is <code>NonblockingNumberConsumer</code>, the it comes from
136 * peer data source and wants to receive notifications from peer cosnumer
137 * about user change requests.
138 * </li>
139 * <li>
140 * if consumer is peer consumer and perhatps implements
141 * <code>DataSource</code> interface. It it does, we must notify listener
142 * for user data change requests.
143 * </li>
144 * </ul>
145 *
146 *
147 * @see com.cosylab.gui.displayers.DataSource#addConsumer(com.cosylab.gui.displayers.DataConsumer)
148 */
149 public void addConsumer(DataConsumer consumer) throws PropertyVetoException
150 {
151 if (consumer instanceof NonblockingNumberConsumer) {
152 // this one comes from peer data source, to this one we delegate calls from peer data source
153 getNonblDisplatecher().addConsumer(consumer);
154
155 return;
156 }
157
158 super.addConsumer(consumer);
159
160 // new consumer did not receive characteristics or state, if converter
161 // already have already received them, they must be delegated
162 if (lastCharacteristics != null) {
163 consumer.setCharacteristics(lastCharacteristics);
164 }
165
166 if (lastDataState != null) {
167 consumer.updateDataState(lastDataState);
168 }
169
170 if (consumer instanceof DataSource) {
171 Class[] cl = ((DataSource)consumer).getAcceptableConsumerTypes();
172
173 for (int i = 0; i < cl.length; i++) {
174 if (cl[i].equals(NonblockingNumberConsumer.class)) {
175 /* here we reqister our nonblocking dispatcher, which dispatches
176 * nonblocking requests to peer data source.
177 */
178 ((DataSource)consumer).addConsumer(getNonblDisplatecher());
179
180 return;
181 }
182 }
183 }
184 }
185 /**
186 * If converter implementation decides to change characteristics it is very
187 * advisable to cache unmodified characteristics. This chached
188 * characteristics are used to initialized new consumers. They can be also
189 * used by implementation to reinitialize consumers if transformation
190 * function has changed.
191 *
192 * <p>
193 * If implementation does not change characteristic, than this method
194 * doesen't have to be used.
195 * </p>
196 *
197 * @param characteristics the unmodified characcteristics before
198 * transformation
199 */
200 protected void cacheLastCharacteristics(Map characteristics)
201 {
202 lastCharacteristics = new HashMap(characteristics);
203 userCache = true;
204 }
205 public void clear()
206 {
207 getNonblDisplatecher().clear();
208
209 DataConsumer[] c = getConsumers();
210
211 for (int i = 0; i < c.length; i++) {
212 if (c[i] instanceof DataSource) {
213 ((DataSource)c[i]).removeConsumer(getNonblDisplatecher());
214 }
215 }
216
217 valueToSet = null;
218
219 super.clear();
220 }
221 /**
222 * Returnes characcteristics whic was last time used on this data
223 * converter. Use <code>cacheLastCharacteristics(Map)</code> to store
224 * them.
225 *
226 * @return last used characcteristics from internal chache
227 *
228 * @see #cacheLastCharacteristics(Map)
229 */
230 protected Map getLastCharacteristics()
231 {
232 return lastCharacteristics;
233 }
234 /**
235 * This is called by peer data source. Call is delegated to contained
236 * consumers. In the process it is modified with transformation function.
237 *
238 * @see com.cosylab.gui.displayers.DataConsumer#setCharacteristics(java.util.Map)
239 */
240 public void setCharacteristics(Map characteristics)
241 {
242 if (!userCache) {
243 // We store in chache characteristic if implementation does not.
244 // We are assuming that user implementation did no changed them.
245 lastCharacteristics = new HashMap(characteristics);
246 }
247
248 super.setCharacteristics(characteristics);
249 }
250 /* (non-Javadoc)
251 * @see com.cosylab.gui.displayers.DataConsumer#updateDataState(com.cosylab.gui.displayers.DataState)
252 */
253 public void updateDataState(DataState state)
254 {
255 lastDataState = state;
256 super.updateDataState(state);
257 }
258 /**
259 * This is called by peer data source. Call is delegated to contained
260 * consumers.
261 *
262 * @see com.cosylab.gui.displayers.DoubleConsumer#updateValue(long, double)
263 */
264 public void updateValue(long timestamp, double value)
265 throws CommonException
266 {
267
268 DataConsumer[] delegates = getConsumers();
269
270 // transform value
271 for (int i = 0; i < delegates.length; i++) {
272 try {
273 ((DoubleConsumer)delegates[i]).updateValue(timestamp, value);
274 } catch (Exception e) {
275 e.printStackTrace();
276 }
277 }
278 }
279 /**
280 * This is called by peer data source. Call is delegated to contained
281 * consumers.
282 *
283 * @see com.cosylab.gui.displayers.DoubleConsumer#updateValue(long, double)
284 */
285 public void updateValue(long timestamp, long value)
286 throws CommonException
287 {
288
289 DataConsumer[] delegates = getConsumers();
290
291 // transform value
292 for (int i = 0; i < delegates.length; i++) {
293 try {
294 ((LongConsumer)delegates[i]).updateValue(timestamp, value);
295 } catch (Exception e) {
296 e.printStackTrace();
297 }
298 }
299 }
300 }
301
302 /* __oOo__ */