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