View Javadoc

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__ */