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.NonblockingNumberConsumer;
30
31
32 /**
33 * <code>AbstractConverter</code> is implementation of
34 * <code>Converter</code> interface intended for convenience when implementing new converter.
35 *
36 * @author <a href="mailto:igor.kriznar@cosylab.com">Igor Kriznar</a>
37 * @version $Id: AbstractConverter.java,v 1.6 2008-04-22 12:31:02 jbobnar Exp $
38 *
39 * @see com.cosylab.gui.adapters.Converter
40 */
41 public abstract class AbstractConverter extends DataConsumerDispatcher
42 implements Converter, DataConsumer
43 {
44 // flag indicating if user has called cache characteristics
45 private boolean userCache = false;
46
47 // dispatcher which intercepts feedback value from peer data consumer and dispatch it to peer data sources
48
49 /** DOCUMENT ME! */
50 transient protected NonblockingNumberDispatcher nonblDispatcher;
51
52 // last dispatched characteristics, used to be redispatched
53 // when multiplication factor is changed or new consumer added
54 transient private Map lastCharacteristics;
55
56 // last dispatched data state, used to be redispatched when
57 // new consumer added
58 private DataState lastDataState;
59
60 /**
61 * Creates new converter.
62 */
63 public AbstractConverter(Class[] types)
64 {
65 // we dalagate to super suppport class only the consumer type/types we want to suppport
66 super(types);
67
68 // the name of this converter when it acts as a consumer
69 name = "AbstractConverter";
70 }
71
72 /**
73 * This is called by peer data source. Call is delegated to contained
74 * consumers. In the process it is modified with transformation function.
75 *
76 * @see com.cosylab.gui.displayers.DataConsumer#setCharacteristics(java.util.Map)
77 */
78 public void setCharacteristics(Map characteristics)
79 {
80 if (!userCache) {
81 // We store in chache characteristic if implementation does not.
82 // We are assuming that user implementation did no changed them.
83 lastCharacteristics = new HashMap(characteristics);
84 }
85
86 super.setCharacteristics(characteristics);
87 }
88
89 /**
90 * Override this method to provide own dispatcher, which forwards revers data flow (from consumer to source).
91 * @return nonblocking reverse dispatcher
92 */
93 protected NonblockingNumberDispatcher getNonblDisplatecher()
94 {
95 if (nonblDispatcher == null) {
96 nonblDispatcher = new NonblockingNumberDispatcher();
97 }
98
99 return nonblDispatcher;
100 }
101
102 /**
103 * In this method we investigate the following cases:
104 *
105 * <ul>
106 * <li>
107 * if consumer is <code>NonblockingNumberConsumer</code>, the it comes from
108 * peer data source and wants to receive notifications from peer cosnumer
109 * about user change requests.
110 * </li>
111 * <li>
112 * if consumer is peer consumer and perhatps implements
113 * <code>DataSource</code> interface. It it does, we must notify listener
114 * for user data change requests.
115 * </li>
116 * </ul>
117 *
118 *
119 * @see com.cosylab.gui.displayers.DataSource#addConsumer(com.cosylab.gui.displayers.DataConsumer)
120 */
121 public void addConsumer(DataConsumer consumer) throws PropertyVetoException
122 {
123 if (consumer instanceof NonblockingNumberConsumer) {
124 // this one comes from peer data source, to this one we delegate calls from peer data source
125 getNonblDisplatecher().addConsumer(consumer);
126
127 return;
128 }
129
130 super.addConsumer(consumer);
131 consumerAdded(consumer);
132
133 // new consumer did not receive characteristics or state, if converter
134 // already have already received them, they must be delegated
135 if (lastCharacteristics != null) {
136 consumer.setCharacteristics(lastCharacteristics);
137 }
138
139 if (lastDataState != null) {
140 consumer.updateDataState(lastDataState);
141 }
142
143 if (consumer instanceof DataSource) {
144 Class[] cl = ((DataSource)consumer).getAcceptableConsumerTypes();
145
146 for (int i = 0; i < cl.length; i++) {
147 if (cl[i].equals(NonblockingNumberConsumer.class)) {
148 /* here we reqister our nonblocking dispatcher, which dispatches
149 * nonblocking requests to peer data source.
150 */
151 ((DataSource)consumer).addConsumer(getNonblDisplatecher());
152
153 return;
154 }
155 }
156 }
157 }
158
159 /**
160 * This method is called when consumer has been added to this converter. You may
161 * override this method to implement own handling of added consumer.
162 * @param consumer the consumer which was added
163 */
164 protected void consumerAdded(DataConsumer consumer) {
165 //
166 }
167
168 /* (non-Javadoc)
169 * @see com.cosylab.gui.displayers.DataConsumer#updateDataState(com.cosylab.gui.displayers.DataState)
170 */
171 public void updateDataState(DataState state)
172 {
173 lastDataState = state;
174 super.updateDataState(state);
175 }
176
177 /**
178 * If converter implementation decides to change characteristics it is very
179 * advisable to cache unmodified characteristics. This chached
180 * characteristics are used to initialized new consumers. They can be also
181 * used by implementation to reinitialize consumers if transformation
182 * function has changed.
183 *
184 * <p>
185 * If implementation does not change characteristic, than this method
186 * doesen't have to be used.
187 * </p>
188 *
189 * @param characteristics the unmodified characcteristics before
190 * transformation
191 */
192 protected void cacheLastCharacteristics(Map characteristics)
193 {
194 lastCharacteristics = new HashMap(characteristics);
195 userCache = true;
196 }
197
198 /**
199 * Returnes characcteristics whic was last time used on this data
200 * converter. Use <code>cacheLastCharacteristics(Map)</code> to store
201 * them.
202 *
203 * @return last used characcteristics from internal chache
204 *
205 * @see #cacheLastCharacteristics(Map)
206 */
207 protected Map getLastCharacteristics()
208 {
209 return lastCharacteristics;
210 }
211
212 /**
213 * Clears converter to initial state.
214 */
215 public void clear()
216 {
217 getNonblDisplatecher().clear();
218
219 DataConsumer[] c = getConsumers();
220
221 for (int i = 0; i < c.length; i++) {
222 if (c[i] instanceof DataSource) {
223 ((DataSource)c[i]).removeConsumer(getNonblDisplatecher());
224 }
225 }
226
227 super.clear();
228 }
229
230 /* (non-Javadoc)
231 * @see com.cosylab.gui.displayers.DataSourceSupport#clone()
232 */
233 @Override
234 public Object clone() throws CloneNotSupportedException {
235 AbstractConverter c= (AbstractConverter)super.clone();
236 c.lastCharacteristics=null;
237 c.lastDataState=null;
238 c.nonblDispatcher=null;
239 return c;
240 }
241 }
242
243 /* __oOo__ */