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.application;
21
22 import java.util.ArrayList;
23 import java.util.HashSet;
24
25 import com.cosylab.util.ObjectList;
26
27
28 /**
29 * Deafultimplementation of a <code>PlugInManager</code> interface.
30 *
31 * @author Igor Kriznar (<a
32 * href="mailto:igor.kriznar@cosylab.com">igor.kriznar&x40;cosylab.com</a>)
33 * @version $$id$$
34 */
35 public class DefaultPlugInManager implements PlugInManager
36 {
37 private String name= "DefaultPlugInManager";
38 private ApplicationPanel parent;
39 private boolean destroyed = false;
40 private ArrayList<PlugIn> plugins = new ArrayList<PlugIn>();
41 private HashSet<Class> cyclicPlugIn = new HashSet<Class>();
42 private ObjectList listeners = new ObjectList(PlugInListener.class);
43
44 /**
45 * A default constructor for the plug-in manager.
46 */
47 public DefaultPlugInManager()
48 {
49 }
50
51 /**
52 * Returns the parent of this manager.
53 *
54 * @return the logical parent of this
55 */
56 public ApplicationPanel getOwnerPanel()
57 {
58 return parent;
59 }
60
61 /**
62 * Returns <code>true</code> iff this panel has been destroyed.
63 *
64 * @return boolean <code>true</code> if destroyed
65 */
66 public final synchronized boolean isDestroyed()
67 {
68 return destroyed;
69 }
70
71 /**
72 * Destroys this cosy panel. If already destroyed, this method returns NOP.
73 * If plugin has visual component, first is from parent removed plugins
74 * visual component, tha is plugin destroyed.
75 *
76 * <p>
77 * All plug-ins are destroyed before managers <code>destroyed</code> flag
78 * is set to <code>true</code> and destruction completes.
79 * </p>
80 */
81 public synchronized final void destroy()
82 {
83 if (destroyed) {
84 return;
85 }
86
87 PlugIn[] pl = getPlugIns();
88
89 for (int i = 0; i < pl.length; i++) {
90 try {
91 if (pl[i] instanceof VisiblePlugIn) {
92 getOwnerPanel().removePlugInGUI((VisiblePlugIn)pl[i]);
93 }
94 } catch (Exception e) {
95 // notify
96 }
97
98 try {
99 pl[i].destroy();
100 } catch (Exception e) {
101 // notify
102 }
103 plugins.clear();
104 }
105
106 destroyed = true;
107 }
108
109 /**
110 * Installs a new plugin. Checks if the <code>plugType</code> parameter is
111 * valid, otherwise a <code>PlugInException</code> is thrown; checks if
112 * the state of this object is valid; checks if the cyclic installation
113 * is occuring; checks if the plug has already been installed. If all
114 * these checks are passed, a new instance of the plugin is created, added
115 * as a child of this cosy panel, and the <code>installPlugIn</code>
116 * method of the plugin is invoked. If it completes successfully, the cosy
117 * listeners are notified about the new plugin. If it fails, the plugin
118 * is removed from the cosy component children of this panel.
119 *
120 * @param plugType the class representing the plug, which must implement
121 * <code>PlugIn</code> interface
122 *
123 * @exception PlugInException if the plug installation fails
124 * @throws NullPointerException if input is null
125 * @throws IllegalStateException if no parent is set
126 *
127 * @see PlugInManager#installPlugIn(Class)
128 */
129 public final synchronized void installPlugIn(Class plugType)
130 throws PlugInException
131 {
132 if (plugType == null) {
133 throw new NullPointerException("plugType");
134 }
135
136 if (isDestroyed()) {
137 return;
138 }
139
140 if (parent == null) {
141 throw new IllegalStateException("parent == null");
142 }
143
144 // check plug type
145 if (!PlugIn.class.isAssignableFrom(plugType)) {
146 throw new PlugInException(this,
147 "Class '" + plugType.getName() + "' does not implement '"
148 + PlugIn.class.getName() + "'.", plugType, null);
149 }
150
151 // check if already installed
152 if (getPlugIn(plugType) != null) {
153 return;
154 }
155
156 // check cyclic resolver
157 if (cyclicPlugIn.contains(plugType)) {
158 throw new PlugInException(this,
159 "Cyclic plugin dependency: cannot instantiate without causing endless loop",
160 plugType, null);
161 }
162
163 cyclicPlugIn.add(plugType);
164
165 // instantiate plug
166 PlugIn plugIn = null;
167
168 try {
169 plugIn = (PlugIn)plugType.newInstance();
170 } catch (Exception e) {
171 throw new PlugInException(this,
172 "Could not invoke the plugin constructor.", plugType, e);
173 }
174
175 // add to this container
176 plugins.add(plugIn);
177
178 // initialize plug
179 try {
180 plugIn.installPlugIn(this);
181 } catch (Exception e) {
182 // remove from the container
183 plugins.remove(plugIn);
184 throw new PlugInException(this,
185 "Exception while installing the plugin", plugType, e);
186 } finally {
187 // remove from cyclic resolution list
188 cyclicPlugIn.remove(plugType);
189 }
190
191 // notify listeners
192 PlugInListener[] l = (PlugInListener[])listeners.toArray();
193 PlugInEvent event = new PlugInEvent(this, plugIn);
194
195 for (int i = 0; i < l.length; i++) {
196 try {
197 l[i].plugInInstalled(event);
198 } catch (Exception e) {
199 e.printStackTrace();
200 }
201 }
202 }
203
204 /**
205 * Returns all plugins managed by this cosy component.
206 *
207 * @return PlugIn[] a list of plug ins
208 *
209 * @see PlugInManager#getPlugIns()
210 */
211 public synchronized PlugIn[] getPlugIns()
212 {
213 return plugins.toArray(new PlugIn[plugins.size()]);
214 }
215
216 /**
217 * Returns a plugin with a given type. Since plugins are installed by their
218 * type and installations with the same type are not allowed, the
219 * designation by type is unique.
220 *
221 * @param plugType the type of the plug to look up
222 *
223 * @return PlugIn the plugin instance of <code>null</code> if no plugin
224 * with the specified type was found
225 *
226 * @throws NullPointerException if input is null
227 *
228 * @see PlugInManager#getPlugIn(Class)
229 */
230 public PlugIn getPlugIn(Class plugType)
231 {
232 if (plugType == null) {
233 throw new NullPointerException("plugType");
234 }
235
236 PlugIn[] array = getPlugIns();
237
238 for (int i = 0; i < array.length; i++) {
239 if (array[i].getClass().equals(plugType)) {
240 return array[i];
241 }
242 }
243
244 return null;
245 }
246
247 /**
248 * Adds a plugin listener.
249 *
250 * @param l a listener object
251 *
252 * @see PlugInManager#addPlugInListener(PlugInListener)
253 */
254 public void addPlugInListener(PlugInListener l)
255 {
256 listeners.add(l);
257 }
258
259 /**
260 * Removes a plugin listener.
261 *
262 * @param l a listener object
263 *
264 * @see PlugInManager#removePlugInListener(PlugInListener)
265 */
266 public void removePlugInListener(PlugInListener l)
267 {
268 listeners.remove(l);
269 }
270
271 /**
272 * Returns a list of all plugin listeners, as required by Java 1.4 Beans
273 * specs.
274 *
275 * @return PlugInListener[] an array of listeners
276 *
277 * @see PlugInManager#getPlugInListeners()
278 */
279 public PlugInListener[] getPlugInListeners()
280 {
281 return (PlugInListener[])listeners.toArray();
282 }
283
284 /**
285 * Removes plugin from meneger. First removes plugin visual component from
286 * parent, then calls destroy() and then sets parent to null.
287 *
288 * @see com.cosylab.gui.core.PlugInManager#removePlugIn(java.lang.Class)
289 */
290 public void removePlugIn(Class plugType)
291 {
292 if (plugType == null) {
293 throw new NullPointerException("plugType");
294 }
295
296 if (isDestroyed()) {
297 return;
298 }
299
300 if (parent == null) {
301 throw new IllegalStateException("parent == null");
302 }
303
304 PlugIn plugIn = getPlugIn(plugType);
305
306 if (plugIn == null) {
307 return;
308 }
309
310 plugins.remove(plugIn);
311
312 // remove plug
313 try {
314 if (plugIn instanceof VisiblePlugIn) {
315 getOwnerPanel().removePlugInGUI((VisiblePlugIn)plugIn);
316 }
317
318 plugIn.destroy();
319 } catch (Exception e) {
320 // we should not throw further
321 e.printStackTrace();
322 }
323
324 // notify listeners
325 PlugInListener[] l = (PlugInListener[])listeners.toArray();
326 PlugInEvent event = new PlugInEvent(this, plugIn);
327
328 for (int i = 0; i < l.length; i++) {
329 try {
330 l[i].plugInRemoved(event);
331 } catch (Exception e) {
332 e.printStackTrace();
333 }
334 }
335 }
336
337 /**
338 * Sets logical parent to this manager.
339 *
340 * @see com.cosylab.gui.core.CosyComponent#setCosyPanelParent(com.cosylab.gui.core.CosyApplicationPanel)
341 */
342 public void setOwnerPanel(ApplicationPanel panel)
343 {
344 this.parent = panel;
345 }
346
347 /* (non-Javadoc)
348 * @see com.cosylab.gui.core.CosyComponent#getName()
349 */
350 public String getName()
351 {
352 return name;
353 }
354 /* (non-Javadoc)
355 * @see com.cosylab.gui.core.PlugInManager#acquirePlugIn(java.lang.Class)
356 */
357 public PlugIn acquirePlugIn(Class plugType) throws PlugInException {
358 PlugIn pl= getPlugIn(plugType);
359 if (pl==null) installPlugIn(plugType);
360 return getPlugIn(plugType);
361 }
362
363 }
364
365 /* __oOo__ */