platform-ui-home/components-proposal/ComponentFrameworkProposal.html

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.2 - (view) (download) (as text)

1 : sxenos 1.1 <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
2 :     <html>
3 :     <head>
4 :     <meta content="text/html; charset=ISO-8859-1"
5 :     http-equiv="content-type">
6 :     <title>Component Framework proposal</title>
7 :     </head>
8 :     <body>
9 :     <span style="font-weight: bold;"></span>
10 :     <br>
11 :     <div style="text-align: center;"><font size="+3"><span
12 :     style="font-weight: bold;">Component Framework Proposal</span></font><br>
13 :     </div>
14 :     <br>
15 : sxenos 1.2 <div style="text-align: center;">Draft<br>
16 :     Revision 1.0.4<br>
17 : sxenos 1.1 By Stefan Xenos and Nick Edgar<br>
18 : sxenos 1.2 Last modified 2004/11/05<br>
19 : sxenos 1.1 </div>
20 :     <br>
21 :     <br>
22 :     <br>
23 :     <font size="+3"><span style="font-weight: bold;">Table of Contents</span></font><br>
24 :     <ul id="mozToc">
25 :     <!--mozToc h1 1 h2 2 h3 3 h4 4 h5 5 h6 6--><li><a href="#mozTocId511305">1.0
26 :     Introduction
27 :     </a>
28 :     <ul>
29 : sxenos 1.2 <li><a href="#mozTocId619855">1.1 Understanding the problem</a></li>
30 :     <li><a href="#mozTocId340323">1.2 Requirements
31 :     </a>
32 :     <ul>
33 :     <li><a href="#mozTocId29093">1.2.1 Robustness /
34 :     leak
35 :     proofing
36 :     </a></li>
37 :     <li><a href="#mozTocId223982">1.2.2 Nesting of
38 :     components</a></li>
39 :     <li><a href="#mozTocId634024">1.2.3 Creating parts
40 :     outside the workbench</a></li>
41 :     <li><a href="#mozTocId800379">1.2.4 Scalability</a></li>
42 :     <li><a href="#mozTocId866479">1.2.5 Ease of use
43 :     </a></li>
44 :     </ul>
45 :     </li>
46 : sxenos 1.1 </ul>
47 :     </li>
48 : sxenos 1.2 <li><a href="#mozTocId416167">2.0 Component
49 :     Framework
50 : sxenos 1.1 </a>
51 :     <ul>
52 : sxenos 1.2 <li><a href="#mozTocId489102">2.1 Instantiating a
53 :     view
54 : sxenos 1.1 </a></li>
55 : sxenos 1.2 <li><a href="#mozTocId717633">2.2 Instantiating
56 :     components in general
57 : sxenos 1.1 </a></li>
58 : sxenos 1.2 <li><a href="#mozTocId450231">2.3 Creating
59 :     dependent components on demand </a></li>
60 :     <li><a href="#mozTocId928619">2.4 Declaring Component Interfaces</a></li>
61 :     <li><a href="#mozTocId749133">2.5 Using Component Interfaces</a></li>
62 :     <li><a href="#mozTocId705695">2.6 Lifecycle</a></li>
63 :     <li><a href="#mozTocId543223">2.7 Dynamic Interfaces</a></li>
64 :     <li><a href="#mozTocId886834">2.8 Optional Interface
65 : sxenos 1.1 </a></li>
66 : sxenos 1.2 <li><a href="#mozTocId578639">2.8 Component Scopes</a></li>
67 :     <li><a href="#mozTocId762751">2.9 Shared Adapters</a></li>
68 :     </ul>
69 :     </li>
70 :     <li><a href="#mozTocId707022">3.0 Views and Editors
71 :     as Components</a>
72 :     <ul>
73 :     <li><a href="#mozTocId125361">3.1 Interfaces offered by the
74 :     workbench</a>
75 :     <ul>
76 :     <li><a href="#mozTocId163729">3.1.1 IMultiPart: Redirecting
77 :     adapters from the active child
78 :     </a></li>
79 :     <li><a href="#mozTocId483376">3.1.2 INameable: Changing the
80 :     name of a part</a></li>
81 :     <li><a href="#mozTocId969580">3.1.3 IPartFactory: Creating
82 :     child parts</a></li>
83 :     </ul>
84 :     </li>
85 : sxenos 1.1 </ul>
86 :     </li>
87 :     </ul>
88 :     <br>
89 :     <h1><a class="mozTocH1" name="mozTocId511305"></a>1.0 Introduction<br>
90 :     </h1>
91 :     This proposal outlines a framework for managing complex executable
92 :     extensions. This framework is a first step toward allowing views and
93 :     editors to be combined recursively, and created inside arbitrary SWT
94 :     composites. Although it is intended for creating reusable UI
95 :     components, the framework is useful for any extension point that
96 :     creates complicated Java objects. For this reason, we will refer to the
97 :     objects being created as "components" even though our components are
98 :     typically views and editors.<br>
99 :     <br>
100 :     This proposal is broken into three parts. The introduction describes
101 :     the motivation for creating the component framework and its
102 :     requirements. The second section describes the component framework in
103 :     itself, which could be used for any executable extension point. The
104 :     third section describes how the workbench will use the component
105 :     framework for views and editors.<br>
106 :     <br>
107 : sxenos 1.2 <h2><a class="mozTocH2" name="mozTocId619855"></a>1.1 Understanding the
108 :     problem</h2>
109 :     This section explains why the component framework is needed to support
110 :     nested parts. We will show the problems involved in designing an
111 :     extensible Site interface, and will show how they can be solved using
112 :     the patterns in the component framework. Skip to the next sections if
113 :     you don't need convincing and just want to see the specification.
114 :     IMPORTANT NOTE:
115 :     the code examples in this section are only intended to illustrate
116 :     patterns and do not reflect the actual implementation<br>
117 :     <br>
118 :     Currently, parts communicate with their parent through their site. The
119 :     site provides the view with access to the outside world. In general, it
120 :     is better to provide new API on the part's site than have the part
121 :     reach to some global object, since the site can provide some context
122 :     and can manage resources allocated to the part.<br>
123 :     <br>
124 :     Here is a simplified version of the View and Site interface. We've
125 :     omitted most of the details. The important part is that the View is
126 :     given a Site when it is initialized, it uses various methods on the
127 :     Site throughout its life, and the Site has a dispose() method that can
128 :     clean up after the View.<br>
129 :     <code><br>
130 :     </code>
131 :     <div style="margin-left: 40px;"><code>/**<br>
132 :     &nbsp;* A view's main interface to the world.<br>
133 :     &nbsp;*/<br>
134 :     public class Site {<br>
135 :     &nbsp;&nbsp;&nbsp; /**<br>
136 :     &nbsp;&nbsp;&nbsp;&nbsp; * Create a view site, given the plugin that
137 :     contains the implementation for the view<br>
138 :     &nbsp;&nbsp;&nbsp;&nbsp; */<br>
139 :     &nbsp;&nbsp;&nbsp; public Site(Plugin viewPlugin) {<br>
140 :     &nbsp;&nbsp;&nbsp; }<br>
141 :     <br>
142 :     &nbsp;&nbsp;&nbsp; /**<br>
143 :     &nbsp;&nbsp;&nbsp;&nbsp; * Return the view's ID<br>
144 :     &nbsp;&nbsp;&nbsp;&nbsp; */<br>
145 :     &nbsp;&nbsp;&nbsp; public String getId() {...};<br>
146 :     <br>
147 :     &nbsp;&nbsp;&nbsp; public IActionBars getActionBars() {...};<br>
148 :     <br>
149 :     &nbsp;&nbsp;&nbsp; /**<br>
150 :     &nbsp;&nbsp;&nbsp;&nbsp; * Clean up any leftover resources allocated by
151 :     the site<br>
152 :     &nbsp;&nbsp;&nbsp;&nbsp; */<br>
153 :     &nbsp;&nbsp;&nbsp; public void dispose() {<br>
154 :     &nbsp;&nbsp;&nbsp; };<br>
155 :     }<br>
156 :     <br>
157 :     /**<br>
158 :     &nbsp;* Base class for all views.<br>
159 :     &nbsp;*/<br>
160 :     public abstract class View {<br>
161 :     &nbsp;&nbsp;&nbsp; private Site mySite;<br>
162 :     <br>
163 :     &nbsp;&nbsp;&nbsp; public View(Site viewSite) {<br>
164 :     &nbsp;&nbsp;&nbsp; &nbsp;&nbsp; mySite = viewSite;<br>
165 :     &nbsp;&nbsp;&nbsp; }<br>
166 :     <br>
167 :     &nbsp;&nbsp;&nbsp; public Site getSite() {<br>
168 :     &nbsp;&nbsp;&nbsp; &nbsp;&nbsp; return mySite;<br>
169 :     &nbsp;&nbsp;&nbsp; }<br>
170 :     <br>
171 :     &nbsp;&nbsp;&nbsp; public abstract String getTitle();<br>
172 :     <br>
173 :     &nbsp;&nbsp;&nbsp; public void dispose() {};<br>
174 :     }<br>
175 :     <br>
176 :     /**<br>
177 :     &nbsp;* A factory that can create views by ID.<br>
178 :     &nbsp;*/<br>
179 :     public ViewFactory {<br>
180 :     &nbsp;&nbsp;&nbsp; public static View createView(String
181 :     viewExtensionId, Site viewSite) {<br>
182 :     &nbsp;&nbsp;&nbsp; &nbsp;&nbsp; //... create a view and return it<br>
183 :     &nbsp;&nbsp;&nbsp; }<br>
184 :     }<br>
185 :     </code></div>
186 :     <br>
187 :     Anyone that wants to instantiate a view would need to create an
188 :     instance of Site and create the view by calling ViewFactory.createView.
189 :     It would be possible to subclass Site in order to provide the part with
190 :     additional context. For example, a subclass of Site could overload
191 :     getActionBars() to substitute a different implementation of IActionBars
192 :     to be used by the view. <br>
193 :     <br>
194 :     One of the problems with using the Site as the main interface to a view
195 :     is that we continually need to add new methods to the Site base class.
196 :     For example, we might decide to add a logError method that allows views
197 :     to log exceptions to their plugin log without needing to reach to their
198 :     Plugin object or worry about constructing IStatus instances with the
199 :     correct plugin ID.<br>
200 :     <br>
201 :     <div style="margin-left: 40px;"><code>/**</code><br>
202 :     <code>&nbsp;* A view's main interface to the world.</code><br>
203 :     <code>&nbsp;*/</code><br>
204 :     <code>public class Site {<br>
205 :     &nbsp;&nbsp;&nbsp; Plugin plugin;<br>
206 :     <br>
207 :     </code><code></code><code>&nbsp;&nbsp;&nbsp; public Site(Plugin
208 :     viewPlugin) {<br>
209 :     &nbsp;&nbsp;&nbsp; &nbsp;&nbsp; plugin = viewPlugin;<br>
210 :     &nbsp;&nbsp;&nbsp; }<br>
211 :     <br>
212 :     &nbsp;&nbsp;&nbsp; // ...all other Site methods omitted...<br>
213 :     <br>
214 :     </code><code>&nbsp;&nbsp;&nbsp; public void logError(Throwable t) {<br>
215 :     &nbsp;&nbsp;&nbsp; &nbsp;&nbsp; plugin.getLog().log(new Status(..., t));<br>
216 :     &nbsp;&nbsp;&nbsp; };<br>
217 :     </code><code>}</code><br>
218 :     </div>
219 :     <br>
220 :     The site could also provide the view with localized ways to allocate
221 :     resources. For example, if the view could allocate Images through its
222 :     site, the Site could guarantee that the images would be cleaned up
223 :     properly when the view is destroyed.<br>
224 :     <div style="margin-left: 40px;"><code><br>
225 :     /**</code><br>
226 :     <code>
227 :     &nbsp;* A view's main interface to the world.</code><br>
228 :     <code>
229 :     &nbsp;*/</code><br>
230 :     <code>
231 :     public class Site {<br>
232 :     <br>
233 :     &nbsp;&nbsp;&nbsp; // .... everything in the above version of Site plus
234 :     this:<br>
235 :     <br>
236 :     &nbsp;&nbsp;&nbsp; Map allocatedImages = new HashMap();<br>
237 :     <br>
238 :     &nbsp;&nbsp;&nbsp; public Image createImage(ImageDescriptor toCreate) {<br>
239 :     &nbsp;&nbsp;&nbsp; &nbsp;&nbsp; Image image =
240 :     toCreate.createImage(true);<br>
241 :     &nbsp;&nbsp;&nbsp; &nbsp;&nbsp; allocatedImages.put(toCreate, image);<br>
242 :     &nbsp;&nbsp;&nbsp; &nbsp;&nbsp; return image;<br>
243 :     &nbsp;&nbsp;&nbsp; }<br>
244 :     <br>
245 :     &nbsp;&nbsp;&nbsp; public void destroyImage(ImageDescriptor toDestroy) {<br>
246 :     &nbsp;&nbsp;&nbsp; &nbsp;&nbsp; Image image =
247 :     allocatedImages.get(toDestroy);<br>
248 :     &nbsp;&nbsp;&nbsp; &nbsp;&nbsp; if (image != null) {<br>
249 :     &nbsp;&nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;
250 :     allocatedImages.remove(toDestroy);<br>
251 :     &nbsp;&nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; image.dispose();<br>
252 :     &nbsp;&nbsp;&nbsp; &nbsp;&nbsp; }<br>
253 :     &nbsp;&nbsp;&nbsp; }<br>
254 :     <br>
255 :     &nbsp;&nbsp;&nbsp; public void dispose() {<br>
256 :     &nbsp;&nbsp;&nbsp; &nbsp;&nbsp; Collection values =
257 :     allocatedImages.getValueSet();<br>
258 :     &nbsp;&nbsp;&nbsp; &nbsp;&nbsp; Iterator iter = values.iterator();<br>
259 :     &nbsp;&nbsp;&nbsp; &nbsp;&nbsp; while (iter.hasNext()) {<br>
260 :     &nbsp;&nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; Image img =
261 :     (Image)iter.next();<br>
262 :     &nbsp;&nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; img.dispose();<br>
263 :     &nbsp;&nbsp;&nbsp; &nbsp;&nbsp; }<br>
264 :     &nbsp;&nbsp;&nbsp; }</code><code><br>
265 :     </code><code>
266 :     }<br>
267 :     <br>
268 :     </code></div>
269 :     In order to provide the best protection against leaks, new API should
270 :     use this pattern as much as possible for views. The obvious problems
271 :     with this are:<br>
272 :     <ol>
273 :     <li>Whoever implements the Site class can't possibly know about every
274 :     type of resource that might be allocated by every view</li>
275 :     <li>The Site interface would become ridiculously large and
276 :     complicated.<br>
277 :     </li>
278 :     </ol>
279 :     It would be possible to work around this by writing new subclasses of
280 :     Site that add new methods, but then Views that use the new APIs would
281 :     not function properly when given a different Site implementation. The
282 :     ideal solution would allow plugins to contribute new default behavior
283 :     to the Site base class, and only require the Site class to be
284 :     subclassed when this behavior needs to be specialized. This can be
285 :     achieved by making Site adaptable. Rather than always adding new
286 :     methods to Site, plugins couldcontribute adapters. Subclasses of site
287 :     would still be able to override the default adapter behavior by
288 :     explicitly implementing the adapter interface, but parts that use the
289 :     adapter would no longer be tied to a particular Site implementation.<br>
290 :     <br>
291 :     Here is an example that demonstrates the pattern:<br>
292 :     <br>
293 :     <div style="margin-left: 40px;"><code>/**</code><br>
294 :     <code>
295 :     &nbsp;* Adapter for a view site that allows allocation/deallocation of
296 :     images</code><br>
297 :     <code>
298 :     &nbsp;*/</code><br>
299 :     <code>
300 :     public interface IImageAllocator {<br>
301 :     &nbsp;&nbsp;&nbsp; public Image createImage(ImageDescriptor toCreate);<br>
302 :     &nbsp;&nbsp;&nbsp; public void destroyImage(ImageDescriptor toDestroy);<br>
303 :     </code><code>}<br>
304 :     <br>
305 :     /**<br>
306 :     &nbsp;* Implementation of the image allocator adapter provided via XML<br>
307 :     &nbsp;* by some plugin.<br>
308 :     &nbsp;*/<br>
309 :     public class ImageAllocator implements IImageAllocator, IDisposable {<br>
310 :     &nbsp;&nbsp;&nbsp; </code><code>public Image
311 :     createImage(ImageDescriptor toCreate) {<br>
312 :     &nbsp;&nbsp;&nbsp; &nbsp;&nbsp; ...<br>
313 :     &nbsp;&nbsp;&nbsp; };<br>
314 :     <br>
315 :     &nbsp;&nbsp;&nbsp; public void destroyImage(ImageDescriptor toDestroy) {<br>
316 :     &nbsp;&nbsp;&nbsp; &nbsp;&nbsp; ...<br>
317 :     &nbsp;&nbsp;&nbsp; };<br>
318 :     <br>
319 :     &nbsp;&nbsp;&nbsp; public void dispose() {<br>
320 :     &nbsp;&nbsp;&nbsp; &nbsp;&nbsp; // Dispose any images leaked through
321 :     createImage<br>
322 :     &nbsp;&nbsp;&nbsp; };<br>
323 :     </code><code>}<br>
324 :     <br>
325 :     /**<br>
326 :     &nbsp;* Next attempt at a site interface<br>
327 :     &nbsp;*/<br>
328 :     public class Site implements IAdaptable {<br>
329 :     <br>
330 :     &nbsp;&nbsp;&nbsp; List disposableAdapters = new ArrayList();<br>
331 :     <br>
332 :     &nbsp;&nbsp;&nbsp; public Object getAdapter(Class adapterType) {<br>
333 :     </code><code>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp; Object result = //</code><code>...some
334 :     magic that looks through the adapter extension point and<br>
335 :     &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;
336 :     &nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //creates an instance of ImageAllocator<br>
337 :     <br>
338 :     &nbsp;&nbsp;&nbsp; &nbsp;&nbsp; if (adapter instanceof IDisposable) {<br>
339 :     &nbsp;&nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;
340 :     disposableAdapters.add(adapter);<br>
341 :     &nbsp;&nbsp;&nbsp; &nbsp;&nbsp; }<br>
342 :     <br>
343 :     &nbsp;&nbsp;&nbsp; &nbsp;&nbsp; return result;<br>
344 :     &nbsp;&nbsp;&nbsp; }<br>
345 :     <br>
346 :     &nbsp;&nbsp;&nbsp; /**<br>
347 :     &nbsp;&nbsp;&nbsp;&nbsp; * Allow all the adapters to clean up anything
348 :     they allocated.<br>
349 :     &nbsp;&nbsp;&nbsp;&nbsp; */<br>
350 :     &nbsp;&nbsp;&nbsp; public void dispose() {<br>
351 :     &nbsp;&nbsp;&nbsp; &nbsp;&nbsp; Iterator iter =
352 :     disposableAdapters.iterator();<br>
353 :     &nbsp;&nbsp;&nbsp; &nbsp;&nbsp; while(iter.hasNext()) {<br>
354 :     &nbsp;&nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; IDisposable next =
355 :     (IDisposable)iter.next();<br>
356 :     <br>
357 :     &nbsp;&nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; next.dispose();<br>
358 :     &nbsp;&nbsp;&nbsp; &nbsp;&nbsp; }<br>
359 :     &nbsp;&nbsp;&nbsp; }<br>
360 :     }<br>
361 :     <br>
362 :     </code><code>/**<br>
363 :     &nbsp;* Base class for all views.<br>
364 :     &nbsp;*/<br>
365 :     public abstract class View {<br>
366 :     &nbsp;&nbsp;&nbsp; private IAdaptable mySite;<br>
367 :     <br>
368 :     &nbsp;&nbsp;&nbsp; public View(IAdaptable viewSite) {<br>
369 :     &nbsp;&nbsp;&nbsp; &nbsp;&nbsp; mySite = viewSite;<br>
370 :     &nbsp;&nbsp;&nbsp; }<br>
371 :     <br>
372 :     &nbsp;&nbsp;&nbsp; public IAdaptable getSite() {<br>
373 :     &nbsp;&nbsp;&nbsp; &nbsp;&nbsp; return mySite;<br>
374 :     &nbsp;&nbsp;&nbsp; }<br>
375 :     <br>
376 :     &nbsp;&nbsp;&nbsp; public abstract String getTitle();<br>
377 :     <br>
378 :     &nbsp;&nbsp;&nbsp; public void dispose() {};<br>
379 :     }</code><br>
380 :     </div>
381 :     <br>
382 :     Notice that this type of adapter is slightly different from most other
383 :     adapters in Eclipse since it keeps some state and need to be notified
384 :     when the Site is disposed. <br>
385 :     <br>
386 :     A particular view would look like this:<br>
387 :     <br>
388 :     <div style="margin-left: 40px;"><code>/**<br>
389 :     &nbsp;* My implementation of a view<br>
390 :     &nbsp;*/<br>
391 :     public class MyView extends View {<br>
392 :     &nbsp;&nbsp;&nbsp; IImageAllocator allocator;<br>
393 :     <br>
394 :     &nbsp;&nbsp;&nbsp; public MyView(IAdaptable viewSite) {<br>
395 :     &nbsp;&nbsp;&nbsp; &nbsp;&nbsp; allocator =
396 :     (IImageAllocator)viewSite.getAdapter(IImageAllocator.class);<br>
397 :     <br>
398 :     &nbsp;&nbsp;&nbsp; &nbsp;&nbsp; if (allocator == null) {<br>
399 :     &nbsp;&nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; throw new Exception("I
400 :     can't exist without images!");<br>
401 :     &nbsp;&nbsp;&nbsp; &nbsp;&nbsp; }<br>
402 :     &nbsp;&nbsp;&nbsp; }<br>
403 :     <br>
404 :     &nbsp;&nbsp;&nbsp; //...<br>
405 :     }<br>
406 :     <br>
407 :     </code></div>
408 :     Most views will contain similar code in their constructor that requests
409 :     a set of adapters from their site and check that they aren't null. We
410 :     can add some syntactic sugar to remove this repetition. Rather than
411 :     having the view explicitly request every adapter it needs, it can take
412 :     them as arguments to its constructor and the factory that creates the
413 :     view can use reflection to ensure that it gets all the adapters it
414 :     needs. Using this pattern, the same view would look like this:<br>
415 :     <br>
416 :     <div style="margin-left: 40px;"><code>/**</code><br>
417 :     <code>&nbsp;* My implementation of a view</code><br>
418 :     <code>&nbsp;*/</code><br>
419 :     <code>public class MyView extends View {</code><br>
420 :     <code>&nbsp;&nbsp;&nbsp; IImageAllocator allocator;</code><br>
421 :     <code></code><br>
422 :     <code>&nbsp;&nbsp;&nbsp; public MyView(IImageAllocator imageAllocator) {<br>
423 :     &nbsp;&nbsp;&nbsp; &nbsp;&nbsp; allocator = imageAllocator;<br>
424 :     </code><code></code><code>&nbsp;&nbsp;&nbsp; }</code><br>
425 :     <code></code><br>
426 :     <code>&nbsp;&nbsp;&nbsp; //...</code><br>
427 :     <code>}<br>
428 :     <br>
429 :     </code></div>
430 :     Since the entire Site API would be provided by adapters, the Site class
431 :     could be reduced to this:<br>
432 :     <br>
433 :     <div style="margin-left: 40px;"><code>/**</code><br>
434 :     <code>&nbsp;* My implementation of a view</code><br>
435 :     <code>&nbsp;*/</code><br>
436 :     <code>public abstract class Site implements IAdaptable {<br>
437 :     &nbsp;&nbsp;&nbsp; public abstract Object getAdapter(Class adapter);<br>
438 :     &nbsp;&nbsp;&nbsp; public abstract void dispose();<br>
439 :     </code><code></code><code>}</code><br>
440 :     </div>
441 :     <br>
442 :     In the component framework, this simplified Site interface is called
443 :     IContainer. We now have a highly extensible Site interface, however the
444 :     same versioning problems apply to the view as well. The parent of the
445 :     view may want to access methods on the View, but we don't want to be
446 :     continually adding things to the View base class. For this reason, we
447 :     communicate with the view through adapters as well.<br>
448 :     <br>
449 :     This is the core pattern behind the component framework. It consists of:<br>
450 :     <ol>
451 :     <li>An extension point that allows 3rd party plugins to contribute
452 :     interfaces to components (views and their sites)<br>
453 :     </li>
454 :     <li>A factory that allows components to be created through
455 :     constructor injection</li>
456 :     <li>A container interface that manages the lifecycle of components
457 :     and their adapters </li>
458 :     </ol>
459 :     <div style="margin-left: 40px;"></div>
460 :     <h2><a class="mozTocH2" name="mozTocId340323"></a>1.2 Requirements<br>
461 :     </h2>
462 : sxenos 1.1 Primary goals:<br>
463 :     <ul>
464 :     <li>Robustness / leak proofing</li>
465 :     <li>Nesting of components</li>
466 :     <li>Allow components to be reused outside the workbench</li>
467 :     <li>Scalability (allow an open-ended set of components)</li>
468 :     <li>API versioning<br>
469 :     </li>
470 :     <li>Ease of use</li>
471 :     </ul>
472 : sxenos 1.2 <h3><a class="mozTocH2" name="mozTocId29093"></a>1.2.1 Robustness /
473 :     leak
474 : sxenos 1.1 proofing<br>
475 : sxenos 1.2 </h3>
476 : sxenos 1.1 Much of the Eclipse API is currently accessed through singleton
477 :     objects. This makes it easy for a view or editor to leak listeners,
478 :     fail to clean up reference counts, leak OS resources, etc. since there
479 :     is no way of tracking which resources were allocated by a particular
480 :     view. This problem would be reduced if views and editors were more like
481 :     mini-applications. The view or editor would access the rest of the
482 : sxenos 1.2 world through a set of local interfaces. When the view or editor is
483 :     destroyed, all the interfaces would be notified giving them a chance to
484 :     clean up any leaked resources.<br>
485 : sxenos 1.1 <br>
486 :     For example, instead of reaching into a global
487 :     preference store, a view or editor could access all preferences through
488 : sxenos 1.2 a local preference adapter. A unique instance of the local preference
489 :     adapter would be
490 : sxenos 1.1 created for each view, and would be disposed with the view. When the
491 : sxenos 1.2 preference adapter is destroyed it would clear its listener list,
492 : sxenos 1.1 ensuring that the view will not leak any global listeners. This example
493 :     shows what we mean by a component. Essentially, a
494 :     component is an object that communicates with the rest of the
495 :     application through a set of interfaces given to it in its constructor.<br>
496 :     <span style="font-weight: bold;"></span>
497 : sxenos 1.2 <h3><a class="mozTocH2" name="mozTocId223982"></a>1.2.2 Nesting of
498 :     components</h3>
499 : sxenos 1.1 There is demand for the ability to embed views and editors inside one
500 :     another. Some examples:<br>
501 :     <ul>
502 :     <li>An XML editor might embed the properties view</li>
503 :     <li>A refactoring wizard might include a source editor</li>
504 :     <li>A plugin may wish to create a set of pluggable UI components that
505 :     are not views or editors themselves, but can be used inside any view or
506 :     editor</li>
507 :     <li>Various workbench objects (like the PartSashContainer that
508 :     handles the layout of docked parts within the workbench) could be
509 :     exposed as API<br>
510 :     </li>
511 :     </ul>
512 :     Many downstream plugins have solved these problems by creating their
513 : sxenos 1.2 own frameworks for reusable UI components. Unfortunately, this only
514 :     works
515 : sxenos 1.1 for specific views and editors, does not encourage interoperability
516 :     between plugins that have adopted different frameworks, and requires a
517 :     lot of work. The goal here is to adopt a framework in the workbench
518 :     itself that allows all editors, views, and other workbench objects to
519 :     be easily nested.<br>
520 : sxenos 1.2 <h3><a class="mozTocH2" name="mozTocId634024"></a>1.2.3 Creating parts
521 :     outside the workbench</h3>
522 : sxenos 1.1 It should be possible to instantiate views and editors outside the
523 :     workbench. Some examples:<br>
524 :     <ul>
525 :     <li>Unit-test editors and views by instantiating them within a JUnit
526 :     test suite.</li>
527 :     <li>Create an RCP application that does not depend on the workbench
528 :     but includes view-like pluggable parts
529 :     that could also be used as views within Eclipse</li>
530 :     </ul>
531 : sxenos 1.2 <h3><a class="mozTocH2" name="mozTocId800379"></a>1.2.4 Scalability</h3>
532 :     The workbench currently offers a closed interface to parts. This
533 : sxenos 1.1 forces parts to reach to global objects whenever they need something
534 :     that isn't available from their site. It should be possible for any
535 :     plugin to contribute to the set of local API available to a part, and
536 :     it
537 :     should be possible to instantiate a part even if its parent doesn't
538 :     know about all of a part's dependencies.<br>
539 : sxenos 1.2 <h3><a class="mozTocH2" name="mozTocId866479"></a>1.2.5 Ease of use<br>
540 :     </h3>
541 : sxenos 1.1 Views and editors currently have a complicated lifecycle that must be
542 :     managed by the workbench. This complexity should not be exposed to
543 :     client code. <br>
544 :     <ul>
545 :     <li>It should only require one method call to create a component and
546 :     one method call to destroy it.</li>
547 :     <li>There should not be unnecessary duplication between XML and java
548 :     code.</li>
549 :     <li>Components should not
550 :     be responsible for dealing with error conditions (such as missing
551 :     dependencies) that can be detected by the framework.</li>
552 :     <li>Components should not need to implement interfaces they don't
553 :     care about.</li>
554 :     <li>A parent context should not need to provide child components with
555 :     interfaces it doesn't care about.<span style="font-weight: bold;"></span></li>
556 :     </ul>
557 :     <h1><a class="mozTocH1" name="mozTocId416167"></a>2.0 Component
558 :     Framework<br>
559 :     </h1>
560 : sxenos 1.2 First, some terminology. The pluggable objects that clients write are
561 :     called "components". For example, the class that contains the
562 :     implementation of a view itself would be called a component. A
563 :     component exists inside an IContainer. In the case of editors and
564 :     views, IContainer replaces the existing IWorkbenchSite. Unlike
565 :     IWorkbenchSite, the component never needs to directly access the
566 :     IContainer since all of its useful interface is provided through
567 :     adapters. Instead, the component supplies a constructor that takes all
568 :     the interfaces it needs, and it communicates with the outside world
569 :     through these interfaces.<br>
570 :     <br>
571 :     Components are built using constructor
572 : sxenos 1.1 injection. Constructor injection means that the framework looks at the
573 :     object's constructor to determine what it needs to do to build that
574 :     object.<br>
575 :     <br>
576 :     Components:<br>
577 :     <ul>
578 :     <li>Have exactly one constructor</li>
579 :     <li>Take zero or more other components as arguments to their
580 :     constructor</li>
581 :     <li>Are fully initialized by their constructor</li>
582 :     </ul>
583 :     Components do not:<br>
584 :     <ul>
585 :     <li>Take arrays or primitives as arguments to their constructor</li>
586 :     <li>Take more than one argument of the same type in their constructor<br>
587 :     </li>
588 :     <li>Need to support any particular base class or interface</li>
589 :     </ul>
590 :     Any class with these properties can be used as a component. Since
591 :     components are plain-old-java-objects, they do not need to depend on
592 :     the component framework. Components are fully initialized by
593 :     their constructor, meaning it is never necessary
594 :     to call an initialize method or any combination of set methods after
595 :     constructing the object. Components never accept null as an argument
596 :     to their constructor.<br>
597 :     <br>
598 :     For example, if a view could be created as a component it might look
599 :     like this:<br>
600 :     <br>
601 :     <div style="margin-left: 40px;"><code>/**</code><br>
602 : sxenos 1.2 <code>&nbsp;* "Hello world" view using the component framework</code><br>
603 : sxenos 1.1 <code>&nbsp;*/</code><br>
604 :     <code>public class HelloWorldView {</code><br>
605 :     <code>&nbsp;&nbsp;&nbsp; public HelloWorldView(Composite parent) {</code><br>
606 :     <code>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Label helloWorld = new
607 :     Label(parent, SWT.NONE);</code><br>
608 :     <code>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
609 :     helloWorld.setText("Hello
610 :     world");</code><br>
611 :     <code>&nbsp;&nbsp;&nbsp; }</code><br>
612 :     <code>}</code><br>
613 :     <code></code></div>
614 :     <code><br>
615 : sxenos 1.2 </code>The HelloWorldView component depends on one other object: a
616 :     Composite
617 : sxenos 1.1 created for it by whoever instantiated the view. Compare this with the
618 :     same view written using the existing API:<br>
619 :     <br>
620 :     <div style="margin-left: 40px;"><code>/**</code><br>
621 :     <code>&nbsp;* "hello world" view using the Eclipse 3.0 API.</code><br>
622 :     <code>&nbsp;*/</code><br>
623 :     <code>public class HelloWorldView extends ViewPart {</code><br>
624 :     <code>&nbsp;&nbsp;&nbsp; public void createPartControl(Composite
625 :     parent) {</code><br>
626 :     <code></code><code>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Label
627 :     helloWorld = new Label(parent, SWT.NONE);</code><br>
628 :     <code>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
629 :     helloWorld.setText("Hello
630 :     world");</code><code>&nbsp;&nbsp; </code><br>
631 :     <code>&nbsp;&nbsp;&nbsp; }</code><br>
632 :     <code></code><br>
633 :     <code>&nbsp;&nbsp;&nbsp; public void setFocus() {</code><br>
634 :     <code>&nbsp;&nbsp;&nbsp; }</code><br>
635 :     <code>}</code><br>
636 :     </div>
637 :     <br>
638 :     The main difference is that the component version does not require the
639 :     ViewPart base class and is fully initialized after construction. In
640 :     both cases, the extension point markup would look like this:<br>
641 :     <br>
642 :     <div style="margin-left: 40px;"><code>&lt;extension
643 :     point="org.eclipse.ui.views"&gt;</code><br>
644 :     <code>&nbsp;&nbsp;&nbsp; &lt;view</code><br>
645 :     <code>&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; name="Title Test View"</code><br>
646 :     <code>&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; icon="icons\view.gif"</code><br>
647 :     <code>&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;
648 :     class="org.eclipse.ui.mytest.HelloWorldView"</code><br>
649 :     <code>&nbsp;&nbsp;&nbsp;&nbsp; &nbsp; id="</code><code>org.eclipse.ui.mytest.HelloWorldView</code><code>ID"&gt;</code><br>
650 :     <code>&nbsp;&nbsp;&nbsp; &lt;/view&gt;</code><br>
651 :     <code>&lt;/extension&gt;</code><br>
652 :     </div>
653 :     <br>
654 :     <h2><a class="mozTocH2" name="mozTocId489102"></a>2.1 Instantiating a
655 :     view<br>
656 :     </h2>
657 :     Components are instantiated using factories. For the moment, let's
658 :     focus on views before we explore components in general. Views could be
659 :     created using a method on IWorkbenchPage that would look something like
660 :     this. Note that this example is only intended to illustrate the general
661 :     idea -- the actual protocol for creating views is likely to change.<br>
662 :     <br>
663 :     <div style="margin-left: 40px;"><code>/**</code><br>
664 :     <code>&nbsp;* Creates a view of the given type inside the given
665 :     composite.
666 :     The caller must dispose the container once they are done with it.</code><br>
667 :     <code>&nbsp;*</code><br>
668 :     <code>&nbsp;* @param viewId id of the view extension to use</code><br>
669 :     <code>&nbsp;* @param parentComposite parent composite for the view</code><br>
670 :     <code>&nbsp;* @return an IContainer that contains all components needed
671 :     for the view</code><br>
672 :     <code>&nbsp;*/</code><br>
673 :     <code>IContainer createView(String viewId, Composite parentComposite);</code><br>
674 :     <code></code></div>
675 :     <code></code><br>
676 :     <br>
677 :     This creates a new instance of the view in the given composite. Notice
678 :     that the factory method doesn't return an instance of the view itself
679 :     but an instance of IContainer, which looks something like this:<br>
680 :     <br>
681 :     <div style="margin-left: 40px;"><code>/**</code><br>
682 :     <code>&nbsp;* Main interface to a component.</code><br>
683 :     <code>&nbsp;*/</code><br>
684 :     <code>public interface IContainer extends IAdaptable {</code><br>
685 :     <code></code>&nbsp; &nbsp;&nbsp; public void dispose();<br>
686 :     }<br>
687 :     </div>
688 :     <br>
689 :     This interface wraps the component and all of its dependencies. Once
690 :     we've obtained an IContainer handle, we are obligated to dispose it
691 :     when we are done with it. All access to the component is done through
692 :     adapters, which insulates
693 :     the application from changes in individual views. For example, if a
694 :     view that previously implemented the IViewInterface1 migrates to using
695 :     the newer IViewInterface2, its container will continue to work as long
696 :     as someone has provided an adapter between the two versions of
697 : sxenos 1.2 IViewInterface. The getAdapter() method on IContainer searches for
698 : sxenos 1.1 adapters in the following order:<br>
699 :     <br>
700 :     1. If the component itself implements the adapter type, it returns the
701 :     component.<br>
702 :     2. If the component itself implements IAdapter, and the component's
703 :     getAdapter(...) method returns non-null, we return that adapter.<br>
704 :     3. If the adapter manager has an adapter between the component and the
705 :     adapter type, we return that adapter.<br>
706 :     4. If the container's factory can construct a component of the
707 :     requested type, we
708 :     create that component, add it as a local dependency to the IContainer,
709 :     and
710 :     return it.<br>
711 :     5. Return null<br>
712 :     <br>
713 :     For example, it would be possible to create our HelloWorldView (above)
714 :     in a modal dialog like this:<br>
715 :     <br>
716 :     <div style="margin-left: 40px;"><code>void
717 :     createHelloWorldViewInADialog(IWorkbenchPage page) {</code><br>
718 :     <code>&nbsp;&nbsp;&nbsp; Display display = Display.getDefault()</code><br>
719 :     <code>&nbsp;&nbsp;&nbsp; Shell shell = new Shell(Display.getDefault());</code><br>
720 :     <code></code><br>
721 :     <code>&nbsp;&nbsp;&nbsp; shell.setLayout(new FillLayout());</code><br>
722 :     <code></code><br>
723 :     <code>&nbsp;&nbsp;&nbsp; // Create the view and its widgets</code><br>
724 :     <code>&nbsp;&nbsp;&nbsp; IContainer myView =
725 :     workbenchPage.createView("org.eclipse.ui.mytest.HelloWorldViewID",
726 :     shell);</code><br>
727 :     <code></code><br>
728 :     <code>&nbsp;&nbsp;&nbsp; shell.open();</code><br>
729 :     <code></code><br>
730 :     <code>&nbsp;&nbsp;&nbsp; while (!shell.isDisposed()) {</code><br>
731 :     <code>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp; if (!display.readAndDispatch ())
732 :     display.sleep ();</code><br>
733 :     <code>&nbsp;&nbsp;&nbsp; }</code><br>
734 :     <code></code><br>
735 :     <code>&nbsp;&nbsp;&nbsp; // Dispose the view</code><br>
736 :     <code>&nbsp;&nbsp;&nbsp; myView.dispose();</code><br>
737 :     <code>}</code><br>
738 :     <code></code></div>
739 :     <h2><a class="mozTocH2" name="mozTocId717633"></a>2.2 Instantiating
740 :     components in general<br>
741 :     </h2>
742 :     All components are constructed using an IContainerFactory, however most
743 :     component-based extension points will provide some
744 :     sort of convenience method to wrap their IContainerFactory. The
745 :     IWorkbenchPage.createView method in the
746 :     previous section is a convenience method for the <span
747 :     style="font-style: italic;">org.eclipse.ui.views </span>extension
748 :     point.<br style="font-style: italic;">
749 :     &nbsp;<br>
750 :     IContainerFactory looks like this:<br>
751 :     <br>
752 :     <div style="margin-left: 40px;"><code>/**<br>
753 :     &nbsp;* Factory for IContainer instances. The default factory is
754 :     returned by<br>
755 :     &nbsp;* Components.getFactory(). Clients wishing to implement their own
756 :     specialized <br>
757 :     &nbsp;* factories should call createDerivedFactory() to get access to a
758 :     factory<br>
759 :     &nbsp;* whose behavior can be modified programmatically. Not intended
760 :     to be <br>
761 :     &nbsp;* implemented by clients. <br>
762 :     &nbsp;* <br>
763 :     &nbsp;* @since 3.1<br>
764 :     &nbsp;*/<br>
765 :     public interface IContainerFactory {<br>
766 :     &nbsp;&nbsp;&nbsp; <br>
767 :     &nbsp;&nbsp;&nbsp; /**<br>
768 :     &nbsp;&nbsp;&nbsp;&nbsp; * Creates and returns a new IContainer
769 :     instance, given the <br>
770 :     &nbsp;&nbsp;&nbsp;&nbsp; * implementation class for its component. The
771 :     caller MUST call IContainer.dispose() <br>
772 :     &nbsp;&nbsp;&nbsp;&nbsp; * when it is done with the component. The
773 :     factory does not need any prior<br>
774 : sxenos 1.2 &nbsp;&nbsp;&nbsp;&nbsp; * knowledge of the component class.<br>
775 : sxenos 1.1 &nbsp;&nbsp;&nbsp;&nbsp; * <br>
776 :     &nbsp;&nbsp;&nbsp;&nbsp; * @param componentImplementation concrete
777 :     class to be instantiated by the factory. The class<br>
778 :     &nbsp;&nbsp;&nbsp;&nbsp; * &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;
779 :     must be a valid component (it must have exactly one constructor which
780 :     only<br>
781 :     &nbsp;&nbsp;&nbsp;&nbsp; * &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;
782 :     references other component interfaces known to this factory).<br>
783 :     &nbsp;&nbsp;&nbsp;&nbsp; * @return a newly constructed
784 :     &lt;code&gt;IContainer&lt;/code&gt; instance<br>
785 : sxenos 1.2 &nbsp;&nbsp;&nbsp;&nbsp; * @throws CoreException if unable to create
786 :     the component<br>
787 : sxenos 1.1 &nbsp;&nbsp;&nbsp;&nbsp; */<br>
788 :     &nbsp;&nbsp;&nbsp; public IContainer createContainer(Class
789 : sxenos 1.2 componentImplementation) throws CoreException;<br>
790 : sxenos 1.1 &nbsp;&nbsp;&nbsp; <br>
791 :     &nbsp;&nbsp;&nbsp; /**<br>
792 :     &nbsp;&nbsp;&nbsp;&nbsp; * Creates a specialization of this factory. By
793 :     default, the specialized<br>
794 :     &nbsp;&nbsp;&nbsp;&nbsp; * factory will have the same behavior as its
795 :     parent. However, the derived<br>
796 :     &nbsp;&nbsp;&nbsp;&nbsp; * factory can add, or override the
797 :     implementation for any components.<br>
798 :     &nbsp;&nbsp;&nbsp;&nbsp; * <br>
799 :     &nbsp;&nbsp;&nbsp;&nbsp; * @return new factory instance that allows
800 :     individual components to be overridden.<br>
801 :     &nbsp;&nbsp;&nbsp;&nbsp; * By default, the derived factory will
802 :     delegate all of its behavior to the receiver.<br>
803 :     &nbsp;&nbsp;&nbsp;&nbsp; * Changes in the receiver will affect the
804 :     derived factory.<br>
805 :     &nbsp;&nbsp;&nbsp;&nbsp; */<br>
806 :     &nbsp;&nbsp;&nbsp; public IMutableContainerFactory
807 :     createDerivedFactory();<br>
808 :     }</code><code></code><br>
809 :     <code></code></div>
810 :     <code></code><br>
811 :     All arguments to a component's constructor are provided by its factory.
812 :     Different
813 :     factories know how to create different types of components or will
814 :     provide
815 :     different implementations for the same components. All factories are
816 :     derived from the root factory, which is returned by
817 :     Components.getFactory(). A plugin can create derived factories to
818 : sxenos 1.2 supply alternative adapter implementations to the components it creates.<br>
819 :     <br>
820 :     In
821 : sxenos 1.1 our case, we will create a container factory to pass a specific
822 :     Composite into the constructor of HelloWorldView. The code looks like
823 :     this:<br>
824 :     <code></code><code><br>
825 :     </code>
826 :     <div style="margin-left: 40px;"><code>// Get the global factory</code><br>
827 :     <code>IContainerFactory viewFactory =
828 :     Components.getFactory();</code><br>
829 :     <code></code><br>
830 :     <code>// Create the Composite for the view</code><br>
831 :     <code></code><code>Composite viewComposite = new
832 :     Composite(parentComposite, SWT.NONE);</code><br>
833 :     <code>viewComposite.setLayout(new FillLayout();</code><br>
834 :     <code></code><br>
835 :     <code>// Create a specialized factory that knows about the view's
836 :     composite and plugin bundle</code><br>
837 :     <code>IMutableContainerFactory derivedFactory =
838 :     viewFactory.createDerivedFactory();</code><br>
839 :     <code></code><br>
840 : sxenos 1.2 <code>// Add the view's composite to the factory so that it can be seen
841 :     by the view constructor<br>
842 :     </code><code>derivedFactory.addComponentInstance(viewComposite);</code><br>
843 : sxenos 1.1 <code></code><br>
844 : sxenos 1.2 <code>// Add the view's plugin bundle to the factory (not
845 : sxenos 1.1 required in this example, but this</code><br>
846 :     <code>// is recommended practise for any component created from an
847 :     extension point).</code><br>
848 :     <code>derivedFactory.addComponentInstance(pluginBundle);</code><br>
849 :     <code></code><br>
850 :     <code>// Create the view. Provide its constructor and some
851 :     context (the page that created it).</code><br>
852 :     <code>IContainer view =
853 :     derivedFactory.createContainer(HelloWorldView.class);</code><br>
854 :     <code></code><br>
855 :     <code>// Do something with the view</code><br>
856 :     <code>// ...</code><br>
857 :     <code></code><br>
858 :     <code>// Now clean up</code><br>
859 :     <code>view.dispose();</code><br>
860 :     <code>viewComposite.dispose();</code><br>
861 :     <code></code></div>
862 :     <code><br>
863 :     </code><br>
864 :     This code will work, but it has two problems:<br>
865 :     <br>
866 :     1. Even if HelloWorldView didn't require a Composite, we would still
867 :     have created one (which is wasteful).<br>
868 :     2. We need to manually dispose the view's composite after we're done
869 : sxenos 1.2 with it. This defeats the point of IContainer.dispose(), which is
870 : sxenos 1.1 supposed to clean up all of the component's dependencies automatically.<br>
871 :     <h2><a class="mozTocH2" name="mozTocId450231"></a>2.3 Creating
872 :     dependent components on demand </h2>
873 :     Rather than managing the view's Composite ourselves, we could supply a
874 :     factory that knows how to create and destroy Composites as needed. Such
875 :     a factory would look like this:<br>
876 :     <br>
877 :     <div style="margin-left: 40px;"><code>/**</code><br>
878 : sxenos 1.2 <code>&nbsp;* ComponentAdapter for creating and managing SWT Composites.</code><br>
879 : sxenos 1.1 <code>&nbsp;*/</code><br>
880 :     <code>public class CompositeFactory extends ComponentAdapter {</code><br>
881 :     <code></code><br>
882 :     <code>&nbsp;&nbsp;&nbsp; private Composite parent;</code><br>
883 :     <code>&nbsp;&nbsp;&nbsp; </code><br>
884 :     <code>&nbsp;&nbsp;&nbsp; /**</code><br>
885 :     <code>&nbsp;&nbsp;&nbsp;&nbsp; * The SWT composites created by this
886 :     factory will all be children of the given</code><br>
887 :     <code>&nbsp;&nbsp;&nbsp;&nbsp; * composite.</code><br>
888 :     <code>&nbsp;&nbsp;&nbsp;&nbsp; */</code><br>
889 :     <code>&nbsp;&nbsp;&nbsp; public CompositeFactory(Composite parent) {</code><br>
890 :     <code>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; this.parent = parent;</code><br>
891 :     <code>&nbsp;&nbsp;&nbsp; }</code><br>
892 :     <code>&nbsp;&nbsp;&nbsp; </code><br>
893 :     <code>&nbsp;&nbsp;&nbsp; protected Object create(IAdaptable
894 : sxenos 1.2 availableAdapters) throws CoreException {</code><br>
895 :     <code>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; // Create a new Composite</code><code></code><br>
896 : sxenos 1.1 <code>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Composite newChild =
897 :     new Composite(parent, SWT.NONE);</code><br>
898 :     <code>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; newChild.setLayout(new
899 :     FillLayout());</code><br>
900 :     <code>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return newChild;</code><br>
901 :     <code>&nbsp;&nbsp;&nbsp; }</code><br>
902 :     <code></code><br>
903 : sxenos 1.2 <code>&nbsp;&nbsp;&nbsp; protected void dispose(Object toDispose) {</code><br>
904 : sxenos 1.1 <code>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
905 : sxenos 1.2 ((Composite)toDispose).dispose();</code><br>
906 : sxenos 1.1 <code>&nbsp;&nbsp;&nbsp; }</code><br>
907 :     <code></code><br>
908 :     <code>&nbsp;&nbsp;&nbsp; public String getInterfaceName() {</code><br>
909 :     <code>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; // Return the fully
910 :     qualified class name of the Composite class. The framework will call</code><br>
911 :     <code>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; // this method to determine
912 : sxenos 1.2 what type of interface will be implemented by the objects <br>
913 :     &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; // constructed by this factory.</code><br>
914 : sxenos 1.1 <code>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return
915 :     Composite.class.getName();</code><br>
916 :     <code>&nbsp;&nbsp;&nbsp; }</code><br>
917 :     <code>}</code><br>
918 :     <code></code></div>
919 :     <br>
920 :     Using CompositeFactory, we could construct HelloWorldView like this:<br>
921 :     <br>
922 :     <div style="margin-left: 40px;"><code>// Get the global factory</code><br>
923 :     <code>IContainerFactory viewFactory =
924 :     Components.getFactory();</code><code></code><br>
925 :     <code></code><br>
926 :     <code>// Create a specialized factory that knows about the view's
927 :     composite and plugin bundle</code><br>
928 :     <code>IMutableContainerFactory derivedFactory =
929 :     viewFactory.createDerivedFactory();</code><br>
930 :     <code></code><br>
931 : sxenos 1.2 <code>// Add a factory that can create the view's composite</code><br>
932 : sxenos 1.1 <code>derivedFactory.addComponentFactory(new
933 :     CompositeFactory(parentComposite));</code><br>
934 :     <code></code><br>
935 : sxenos 1.2 <code>// Add the view's plugin bundle to the factory (not
936 : sxenos 1.1 required in this example, but this</code><br>
937 :     <code>// is recommended practise for any component created from an
938 :     extension point).</code><br>
939 :     <code>derivedFactory.addComponentInstance(pluginBundle);</code><br>
940 :     <code></code><br>
941 :     <code>// Create the view. Provide its constructor and some
942 :     context (the page that created it).</code><br>
943 :     <code>IContainer view =
944 :     derivedFactory.createComponent(HelloWorldView.class);</code><br>
945 :     <code></code><br>
946 :     <code>// Do something with the view</code><br>
947 :     <code>// ...</code><br>
948 :     <code></code><br>
949 :     <code>// Now clean up</code><br>
950 :     <code>view.dispose();</code><br>
951 :     <code></code></div>
952 :     <code>
953 :     </code><br>
954 :     The composite will now be allocated as needed and disposed inside the
955 :     call to view.dispose().<br>
956 :     <br>
957 :     <code></code>
958 : sxenos 1.2 <h2><a class="mozTocH2" name="mozTocId928619"></a>2.4 Declaring
959 :     Component Interfaces</h2>
960 :     The types that a component receives in its constructor are called
961 :     component interfaces. Component interfaces are registered through the <span
962 :     style="font-style: italic;">org.eclipse.core.component.interface</span>
963 :     extension point, and each has a default implementation. When a
964 :     component is
965 :     created, its parent may provide an implementation of any service
966 :     it knows about. If the component requests an interface that isn't
967 :     provided explicitly by the parent, the default implementation is used.<br>
968 : sxenos 1.1 <br>
969 : sxenos 1.2 Here is an
970 :     example component interface:<br>
971 : sxenos 1.1 <code><br>
972 :     </code>
973 :     <div style="margin-left: 40px;"><code>/**</code><br>
974 : sxenos 1.2 <code>&nbsp;* Component interface: </code><br>
975 : sxenos 1.1 <code>&nbsp;*</code><br>
976 :     <code>&nbsp;* Provides a context for reporting and logging exceptions.</code><br>
977 :     <code>&nbsp;*/</code><br>
978 :     <code>public interface IErrorContext {</code><br>
979 :     <code>&nbsp;&nbsp;&nbsp; /**</code><br>
980 :     <code>&nbsp;&nbsp;&nbsp;&nbsp; * Create an IStatus error describing the
981 :     given
982 :     throwable</code><br>
983 :     <code>&nbsp;&nbsp;&nbsp;&nbsp; */</code><br>
984 :     <code>&nbsp;&nbsp;&nbsp; public IStatus createStatus(Throwable t);</code><br>
985 :     <code></code><br>
986 :     <code>&nbsp;&nbsp;&nbsp; /**</code><br>
987 :     <code>&nbsp;&nbsp;&nbsp;&nbsp; * Create an IStatus message with the
988 :     given
989 :     severity, message, and (optional) throwable</code><br>
990 :     <code>&nbsp;&nbsp;&nbsp;&nbsp; */</code><br>
991 :     <code>&nbsp;&nbsp;&nbsp; public IStatus createStatus(int severity,
992 :     String
993 :     message, Throwable t);</code><br>
994 :     <code></code><br>
995 :     <code>&nbsp;&nbsp;&nbsp; /**</code><br>
996 :     <code>&nbsp;&nbsp;&nbsp;&nbsp; * Logs an IStatus message</code><br>
997 :     <code>&nbsp;&nbsp;&nbsp;&nbsp; */</code><br>
998 :     <code>&nbsp;&nbsp;&nbsp; public void log(IStatus status);</code><br>
999 :     <code></code><br>
1000 :     <code>&nbsp;&nbsp;&nbsp; /**</code><br>
1001 :     <code>&nbsp; &nbsp;&nbsp; * Logs an exception to the system log</code><br>
1002 :     <code>&nbsp;&nbsp;&nbsp;&nbsp; */</code><br>
1003 :     <code>&nbsp;&nbsp;&nbsp; public void log(Throwable t);</code><br>
1004 :     <code>}</code><br>
1005 :     <code></code></div>
1006 :     <code><br>
1007 :     <br>
1008 : sxenos 1.2 </code>Here is the associated default implementation:<br>
1009 : sxenos 1.1 <code><br>
1010 :     </code>
1011 :     <div style="margin-left: 40px;"><code>/**</code><br>
1012 : sxenos 1.2 <code>&nbsp;* Default implementation of the IErrorContext interface:</code><br>
1013 : sxenos 1.1 <code>&nbsp;*</code><br>
1014 :     <code>&nbsp;* Creates and logs errors in the context of a plugin bundle</code><br>
1015 :     <code>&nbsp;*/</code><br>
1016 :     <code>public class ErrorContext implements IErrorContext {</code><br>
1017 :     <code>&nbsp;&nbsp;&nbsp; private Bundle pluginBundle;</code><br>
1018 :     <code></code><br>
1019 : sxenos 1.2 <code>&nbsp;&nbsp;&nbsp; public ErrorContext(Bundle
1020 : sxenos 1.1 pluginBundle) {</code><br>
1021 :     <code>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; this.pluginBundle =
1022 :     pluginBundle;</code><br>
1023 :     <code>&nbsp;&nbsp;&nbsp; }</code><br>
1024 :     <code></code><br>
1025 :     <code>&nbsp;&nbsp;&nbsp; public IStatus createStatus(Throwable t) {</code><br>
1026 :     <code>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp; String message = t.getMessage();</code><br>
1027 :     <code>&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; if (message == null) {</code><br>
1028 :     <code>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; message =
1029 :     t.getString();</code><br>
1030 :     <code>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</code><br>
1031 :     <code>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp; return createStatus(Status.ERROR,
1032 :     message, t);</code><br>
1033 :     <code>&nbsp;&nbsp;&nbsp; }</code><br>
1034 :     <code></code><br>
1035 :     <code>&nbsp;&nbsp;&nbsp; public IStatus createStatus(int severity,
1036 :     String
1037 :     message, Throwable t) {</code><br>
1038 :     <code>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp; return new Status(severity,
1039 :     pluginBundle.getSymbolicName(), Status.OK, message, t);</code><br>
1040 :     <code>&nbsp;&nbsp;&nbsp; }</code><br>
1041 :     <code></code><br>
1042 :     <code>&nbsp;&nbsp;&nbsp; public void log(Throwable t) {</code><br>
1043 :     <code>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp; log(createStatus(t));</code><br>
1044 :     <code>&nbsp;&nbsp;&nbsp; }</code><br>
1045 :     <code></code><br>
1046 :     <code>&nbsp;&nbsp;&nbsp; public void log(IStatus status) {</code><br>
1047 :     <code>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp; Plugin plugin =
1048 :     Platform.getPlugin(pluginBundle.getSymbolicName());</code><br>
1049 :     <code>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp; plugin.getLog().log(status);</code><br>
1050 :     <code>&nbsp;&nbsp;&nbsp; }</code><br>
1051 :     <code>}</code><br>
1052 :     <code></code></div>
1053 :     <code></code><br>
1054 :     Finally, here is the extension markup:<br>
1055 :     <br>
1056 :     <div style="margin-left: 40px;"><code>&lt;extension
1057 : sxenos 1.2 point="org.eclipse.core.component.interface"&gt;</code><br>
1058 :     <code>&nbsp;&nbsp; &lt;interface
1059 : sxenos 1.1 </code><br>
1060 :     <code>&nbsp;&nbsp;&nbsp;&nbsp;
1061 :     class="org.eclipse.ui.workbench.ErrorContext"</code><br>
1062 : sxenos 1.2 <code>&nbsp;&nbsp;&nbsp;&nbsp; </code><code>interface="</code><code>org.eclipse.ui.workbench.IErrorContext</code><code>"<br>
1063 :     &nbsp;&nbsp;&nbsp;&nbsp; childadapter="false"<br>
1064 :     </code><code>&nbsp;&nbsp;&nbsp;&nbsp; scope="plugin"&gt;</code><br>
1065 :     <code>&nbsp;&nbsp; &lt;/interface&gt;</code><br>
1066 : sxenos 1.1 <code>&lt;/extension&gt;</code><br>
1067 :     </div>
1068 :     <br>
1069 :     Notice that the extension XML needs to indicate which service interface
1070 :     is being implemented by the service, but not its dependencies. When it
1071 :     comes time to create the service, the framework detects that the
1072 :     ErrorContext needs to be associated with a plugin Bundle by examining
1073 :     its constructor. This means that if plugin A defines the ErrorContext
1074 :     service and plugin B defines a view that uses the service, then all of
1075 :     the status messages constructed by the view will be associated with the
1076 :     view's own plugin - not the plugin that defined the ErrorContext
1077 :     service.<br>
1078 : sxenos 1.2 <br>
1079 :     The <span style="font-style: italic;">scope</span> attribute is a path
1080 :     indicating how much context is needed to create the service. The <span
1081 :     style="font-style: italic;">plugin </span>scope indicates that this
1082 :     service is associated with a plugin, which is why it is allowed to take
1083 :     a Bundle in its constructor. More on scopes later.<br>
1084 :     <br>
1085 :     The <span style="font-style: italic;">childadapter="false" </span>tag
1086 :     is optional. The default value, false, indicates that a component can
1087 :     take the interface in its constructor and/or supply an alternative
1088 :     implementation to its children. If <span style="font-style: italic;">childadapter="true"</span>
1089 :     were specified, the component would be allowed to implement the
1090 :     interface or request it from its children. Essentially, this determines
1091 :     if the interface is intended for top-down or bottom-up communication
1092 :     between a component and its parent.<br>
1093 :     <br>
1094 :     Plugins are not allowed to declare services using interfaces defined in
1095 :     another plugin. This prevents two plugins from defining a service using
1096 :     the same interface, and ensures that circular dependencies can only
1097 :     occur within the same plugin.<br>
1098 :     <h2><a class="mozTocH2" name="mozTocId749133"></a>2.5 Using Component
1099 :     Interfaces<br>
1100 :     </h2>
1101 :     This example shows how a component can use an interface supplied by its
1102 :     parent.<br>
1103 : sxenos 1.1 <br>
1104 :     <div style="margin-left: 40px;"><code>public class MyView {</code><br>
1105 :     <code>&nbsp;&nbsp;&nbsp; private IErrorContext errorContext;</code><br>
1106 :     <code></code><br>
1107 :     <code>&nbsp;&nbsp;&nbsp; public MyView(</code><code>Composite parent, </code><code>IErrorContext
1108 :     errorContext) {</code><br>
1109 :     <code>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp; this.errorContext = errorContext;</code><br>
1110 :     <code></code><br>
1111 :     <code></code><code>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp; Button errorButton =
1112 :     new
1113 :     Button(parent, SWT.PUSH);</code><br>
1114 :     <code>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; errorButton.setText("Log an
1115 :     exception");</code><br>
1116 :     <code>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp; </code><br>
1117 :     <code>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;
1118 :     errorButton.addSelectionListener(new
1119 :     SelectionAdapter() {</code><br>
1120 :     <code>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp; public void
1121 :     widgetSelected(SelectionEvent e) {</code><br>
1122 :     <code>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;
1123 :     &nbsp;&nbsp;
1124 :     try {</code><br>
1125 :     <code>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;
1126 :     &nbsp;&nbsp;
1127 :     &nbsp;&nbsp; // Throw a NPE</code><br>
1128 :     <code>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;
1129 :     &nbsp;&nbsp;
1130 :     &nbsp;&nbsp; String myString = null;</code><br>
1131 :     <code>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;
1132 :     &nbsp;&nbsp;
1133 :     &nbsp;&nbsp; String bogusCode = myString.substring(10, 30);</code><br>
1134 :     <code>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;
1135 :     &nbsp;&nbsp;
1136 :     } catch (Exception e) {</code><br>
1137 :     <code>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;
1138 :     &nbsp;&nbsp;
1139 : sxenos 1.2 &nbsp;&nbsp; // Log the NPE using the error context</code><br>
1140 : sxenos 1.1 <code>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;
1141 :     &nbsp;&nbsp; &nbsp; errorContext.log(e);</code><br>
1142 :     <code>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;
1143 :     &nbsp;&nbsp; }</code><br>
1144 :     <code>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; }</code><br>
1145 :     <code>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp; });</code><br>
1146 :     <code></code><code>&nbsp;&nbsp;&nbsp; }</code><br>
1147 :     <code></code><code>}</code><br>
1148 :     <code></code></div>
1149 :     <code><br>
1150 :     </code>This view creates a button which, when pressed, will throw a NPE
1151 :     and log it. The status message will be constructed and logged using the
1152 : sxenos 1.2 IErrorContext interface we defined above. The default implementation of
1153 :     the interface is associated with a
1154 :     unique component instance, so if we were to create two instances of
1155 :     MyView we would also end up with two instances of ErrorContext. <br>
1156 :     <br>
1157 :     There
1158 :     is never any ambiguity about which implementation of IErrorContext to
1159 :     use. If the parent of MyView explicitly supplies an IErrorContext
1160 :     service, that implementation is used. Otherwise, the unique default
1161 :     implementation is used. There can never be two global implementations.<br>
1162 : sxenos 1.1 <br>
1163 :     <h2><a class="mozTocH2" name="mozTocId705695"></a>2.6 Lifecycle</h2>
1164 :     Many components need to do explicit cleanup. Components
1165 :     that need to perform cleanup will implement the
1166 :     IDisposable interface. For example, the following component attaches a
1167 :     listener to a global preference store and detaches it when done.<br>
1168 :     <br>
1169 :     <div style="margin-left: 40px;"><code>/**</code><br>
1170 :     <code>&nbsp;* Provides access to a plugin's preference store in a
1171 :     manner that
1172 :     prevents listener leaks.</code><br>
1173 :     <code>&nbsp;*/</code><br>
1174 :     <code>public interface IPreferences {</code><br>
1175 :     <code>&nbsp;&nbsp;&nbsp; public String getString(String prefId);</code><br>
1176 :     <code>&nbsp;&nbsp;&nbsp; public void setString(String prefId, String
1177 :     value);</code><br>
1178 :     <code>&nbsp;&nbsp;&nbsp; public void
1179 :     addListener(IPropertyChangeListener l);</code><br>
1180 :     <code>&nbsp;&nbsp;&nbsp; public void
1181 :     removeListener(IPropertyChangeListener
1182 :     l);</code><br>
1183 :     <code>}</code><br>
1184 :     <code></code><br>
1185 :     <code>/**</code><br>
1186 :     <code>&nbsp;* Concrete implementation of the IPreferences interface</code><br>
1187 :     <code>&nbsp;*/</code><br>
1188 :     <code>public LocalPreferenceStore implements IPreferences, IDisposable {</code><br>
1189 :     <code>&nbsp;&nbsp;&nbsp; ListenerList listeners = new ListenerList();</code><br>
1190 :     <code>&nbsp;&nbsp;&nbsp; Preferences prefs;</code><br>
1191 :     <code>&nbsp;&nbsp;&nbsp; </code><br>
1192 :     <code>&nbsp;&nbsp;&nbsp; private class PropertyChangeListener
1193 :     implements
1194 :     Preferences.IPropertyChangeListener {</code><br>
1195 :     <code>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; /*</code><br>
1196 :     <code>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;* @see
1197 :     org.eclipse.core.runtime.Preferences.IPropertyChangeListener#propertyChange(org.eclipse.core.runtime.Preferences.PropertyChangeEvent)</code><br>
1198 :     <code>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;*/</code><br>
1199 :     <code>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; public void
1200 :     propertyChange(Preferences.PropertyChangeEvent event) {</code><br>
1201 :     <code>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;
1202 :     firePropertyChangeEvent(event.getProperty(), event.getOldValue(),
1203 :     event.getNewValue());</code><br>
1204 :     <code>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; }</code><br>
1205 :     <code>&nbsp;&nbsp;&nbsp; }</code><br>
1206 :     <code></code><br>
1207 :     <code>&nbsp;&nbsp;&nbsp; PropertyChangeListener listener = new
1208 :     PropertyChangeListener();</code><br>
1209 :     <code></code><br>
1210 :     <code>&nbsp;&nbsp;&nbsp; /**</code><br>
1211 :     <code>&nbsp;&nbsp;&nbsp;&nbsp; * Create a wrapper around the given
1212 :     bundle's
1213 :     preference store.</code><br>
1214 :     <code>&nbsp;&nbsp;&nbsp;&nbsp; */</code><br>
1215 :     <code>&nbsp;&nbsp;&nbsp; public LocalPreferenceStore(Bundle
1216 :     pluginBundle) {</code><br>
1217 :     <code>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp; Plugin plugin =
1218 :     Platform.getPlugin(pluginBundle.getSymbolicName());</code><br>
1219 :     <code>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp; prefs =
1220 :     plugin.getPluginPreferences();</code><br>
1221 :     <code>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp; prefs.addListener(listener);</code><br>
1222 :     <code>&nbsp;&nbsp;&nbsp; }</code><br>
1223 :     <code></code><br>
1224 :     <code>&nbsp;&nbsp;&nbsp; private void firePropertyChange(String name,
1225 :     Object
1226 :     oldValue, Object newValue) {</code><br>
1227 :     <code>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; PropertyChangeEvent event =
1228 :     new
1229 :     PropertyChangeEvent(this, name, oldValue, newValue);</code><br>
1230 :     <code>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; Object[] listeners =
1231 :     this.listeners.getListeners();</code><br>
1232 :     <code>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; for (int i= 0; i &lt;
1233 :     listeners.length; i++)</code><br>
1234 :     <code>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
1235 :     ((IPropertyChangeListener) listeners[i]).propertyChange(event);</code><br>
1236 :     <code>&nbsp;&nbsp;&nbsp; }</code><br>
1237 :     <code></code><br>
1238 :     <code>&nbsp;&nbsp;&nbsp; /**</code><br>
1239 :     <code>&nbsp;&nbsp;&nbsp;&nbsp; * This method will automatically be
1240 :     called
1241 : sxenos 1.2 when the component that uses this adapter is disposed.</code><br>
1242 : sxenos 1.1 <code>&nbsp;&nbsp;&nbsp;&nbsp; * It should clean up anything that has
1243 :     been
1244 :     allocated by the service.</code><br>
1245 :     <code>&nbsp;&nbsp;&nbsp;&nbsp; */</code><br>
1246 :     <code>&nbsp;&nbsp;&nbsp; public void dispose() {</code><br>
1247 :     <code>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp; prefs.removeListener(listener);</code><br>
1248 :     <code>&nbsp;&nbsp;&nbsp; }</code><br>
1249 :     <code></code><br>
1250 :     <code>&nbsp;&nbsp;&nbsp; public String getString(String prefId) {</code><br>
1251 :     <code>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp; return prefs.getString(prefId);</code><br>
1252 :     <code>&nbsp;&nbsp;&nbsp; }</code><br>
1253 :     <code></code><br>
1254 :     <code>&nbsp;&nbsp;&nbsp; public void setString(String prefId, String
1255 :     value) {</code><br>
1256 :     <code>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp; prefs.setValue(prefId, value);</code><br>
1257 :     <code>&nbsp;&nbsp;&nbsp; }</code><br>
1258 :     <code>}</code><br>
1259 :     <code></code></div>
1260 :     <code></code><br>
1261 :     Similarly, components may implement the IDisposable interface. A
1262 : sxenos 1.2 component that implements IDisposable will be disposed before the
1263 :     objects it depends on, as shown by the following example:<br>
1264 : sxenos 1.1 <br>
1265 :     <div style="margin-left: 40px;"><code>public LifecycleService
1266 :     implements IDisposable {</code><br>
1267 :     <code>&nbsp;&nbsp;&nbsp; public LifecycleService() {</code><br>
1268 :     <code>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;
1269 :     System.out.println("LifecycleService
1270 :     created");</code><br>
1271 :     <code>&nbsp;&nbsp;&nbsp; }</code><br>
1272 :     <code></code><br>
1273 :     <code>&nbsp;&nbsp;&nbsp; public void dispose() {</code><br>
1274 :     <code>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;
1275 :     System.out.println("LifecycleService
1276 :     destroyed");</code><br>
1277 :     <code>&nbsp;&nbsp;&nbsp; }</code><br>
1278 :     <code>}</code><br>
1279 :     <code></code><br>
1280 :     <code>public LifecycleView implements IDisposable {</code><br>
1281 :     <code>&nbsp;&nbsp;&nbsp; public LifecycleView(LifecycleService service)
1282 :     {</code><br>
1283 :     <code>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp; System.out.println("LifecycleView
1284 :     created");</code><br>
1285 :     <code>&nbsp;&nbsp;&nbsp; }</code><br>
1286 :     <code></code><br>
1287 :     <code>&nbsp;&nbsp;&nbsp; public void dispose() {</code><br>
1288 :     <code>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp; System.out.println("LifecycleView
1289 :     destroyed");</code><br>
1290 :     <code>&nbsp;&nbsp;&nbsp; }</code><br>
1291 :     <code>}</code><br>
1292 :     <code></code><br>
1293 :     </div>
1294 : sxenos 1.2 Finally, we could execute the following code to trace when the
1295 :     LifecycleService is created and destroyed:<br>
1296 : sxenos 1.1 <div style="margin-left: 40px;"><br>
1297 :     IMutableContainerFactory factory =
1298 :     Components.getFactory().createDerivedFactory();<br>
1299 :     factory.addComponentImplementation(LifecycleService.class);<br>
1300 :     <br>
1301 :     System.out.println("Creating a LifecycleView");<br>
1302 :     <br>
1303 :     IContainer viewComponent = factory.createComponent(LifecycleView.class);<br>
1304 :     viewComponent.dispose();<br>
1305 :     <br>
1306 :     System.out.println("Creating two LifecycleViews");<br>
1307 :     <br>
1308 :     IContainer viewComponent2 =
1309 :     factory.createComponent(LifecycleView.class);<br>
1310 :     IContainer viewComponent3 =
1311 :     factory.createComponent(LifecycleView.class);<br>
1312 :     viewComponent3.dispose();<br>
1313 :     viewComponent2.dispose();<br>
1314 :     <br>
1315 :     </div>
1316 :     The code will generate the following output:<br>
1317 :     <br>
1318 :     <div style="margin-left: 40px;">Creating a LifecycleView<br>
1319 :     LifecycleService created<br>
1320 :     LifecycleView created<br>
1321 :     LifecycleView destroyed<br>
1322 :     LifecycleService destroyed<br>
1323 :     <br>
1324 :     Creating two LifecycleViews<br>
1325 :     LifecycleService created<br>
1326 :     LifecycleView created<br>
1327 :     LifecycleService created<br>
1328 :     LifecycleView created<br>
1329 :     LifecycleView destroyed<br>
1330 :     LifecycleService destroyed<br>
1331 :     LifecycleView destroyed<br>
1332 :     LifecycleService destroyed<br>
1333 :     <br>
1334 :     </div>
1335 : sxenos 1.2 <h2><a class="mozTocH2" name="mozTocId543223"></a>2.7 Dynamic Interfaces<br>
1336 :     </h2>
1337 :     Component interfaces are dynamic in the sense that they may be
1338 :     registered or unregistered as their plugins are installed or
1339 :     uninstalled. If a plugin providing component interface is uninstalled,
1340 :     that interface will become unavailable and it will no longer be
1341 :     possible to construct objects that depend on the interface.<br>
1342 :     <br>
1343 :     The objects instances themselves are not dynamic. Once an adapter is
1344 :     created to supply an interface, it will exist for as long as the
1345 :     component it was created for.
1346 :     Even if the a plugin stops providing an interface, all instances of
1347 :     that
1348 :     interface will continue to exist until disposed. All interfaces used by
1349 :     a
1350 : sxenos 1.1 component will be created before that component, so components do not
1351 :     need to query for the existence of services or to wait for services
1352 :     that will be created after-the-fact. <br>
1353 : sxenos 1.2 <h2><a class="mozTocH2" name="mozTocId886834"></a>2.8 Optional Interface<br>
1354 :     </h2>
1355 :     Sometimes a component does not require an interface but can make use of
1356 :     it if it exists. This is normally not necessary for any interface
1357 :     registered through the <code>org.eclipse.core.component.interface</code>
1358 :     extension point since the default implementation will guarantee that
1359 :     the interface always exists even if the parent context doesn't know
1360 :     about it. However, this situation can occur if a parent and child are
1361 :     communicating with an interface that wasn't originally intended for use
1362 :     as a component interface, or if the child requires a variable set of
1363 :     interfaces.<br>
1364 :     <br>
1365 :     Note: this pattern doesn't actually allow the component to be used in a
1366 :     wider context, but it does allow the component to request interfaces
1367 :     that aren't registered by the normal means.<br>
1368 : sxenos 1.1 <br>
1369 : sxenos 1.2 This can be done by taking an argument of type
1370 : sxenos 1.1 IAdaptable, like this:<br>
1371 :     <br>
1372 : sxenos 1.2 <div style="margin-left: 40px;"><code>public interface INameService {<br>
1373 :     &nbsp;&nbsp;&nbsp; public String getName();<br>
1374 :     }<br>
1375 :     <br>
1376 :     <br>
1377 :     /**<br>
1378 : sxenos 1.1 &nbsp;* A component with no dependencies, but which can optionally
1379 :     accept an IMemento for initialization.<br>
1380 :     &nbsp;*/<br>
1381 :     public class MyComponent {<br>
1382 :     <br>
1383 :     &nbsp;&nbsp;&nbsp; String myName = "default name";<br>
1384 :     <br>
1385 : sxenos 1.2 &nbsp;&nbsp;&nbsp; public MyComponent(IAdaptable optionalInterfaces) {<br>
1386 : sxenos 1.1 <br>
1387 : sxenos 1.2 </code><code>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp; // Check if an
1388 :     INameService
1389 : sxenos 1.1 service exists.</code><br>
1390 : sxenos 1.2 <code>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp; INameService nameService =
1391 :     (INameService)optionalInterfaces.getAdapter(INameService.class);<br>
1392 : sxenos 1.1 &nbsp;&nbsp;&nbsp; &nbsp;&nbsp; <br>
1393 :     &nbsp;&nbsp;&nbsp; &nbsp;&nbsp; if (memento != null) {<br>
1394 : sxenos 1.2 &nbsp;&nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; myName =
1395 :     nameService.getName();<br>
1396 : sxenos 1.1 &nbsp;&nbsp;&nbsp; &nbsp;&nbsp; }<br>
1397 :     <br>
1398 :     &nbsp;&nbsp;&nbsp; &nbsp;&nbsp; System.out.println("created component
1399 :     with name = " + myName);<br>
1400 :     &nbsp;&nbsp;&nbsp; }<br>
1401 :     }<br>
1402 :     <br>
1403 :     </code></div>
1404 :     We could create the component like this:<br>
1405 :     <br>
1406 :     <div style="margin-left: 40px;"><code>// Create a memento with the name
1407 :     "custom name"<br>
1408 : sxenos 1.2 INameService nameService = new INameService() {<br>
1409 :     &nbsp;&nbsp;&nbsp; public String getName() {<br>
1410 :     &nbsp;&nbsp;&nbsp; &nbsp;&nbsp; return "custom name";<br>
1411 :     &nbsp;&nbsp;&nbsp; }<br>
1412 :     }<br>
1413 : sxenos 1.1 <br>
1414 :     // Create a factory which knows about our memento<br>
1415 :     IMutableContainerFactory context =
1416 :     Components.getFactory().createDerivedFactory();<br>
1417 : sxenos 1.2 context.addComponentInstance(nameService);<br>
1418 : sxenos 1.1 <br>
1419 :     // Use the factory to instantiate MyComponent<br>
1420 :     IContainer component = context.createContainer(MyComponent.class);<br>
1421 :     // Will print the message "created component with name = custom name"<br>
1422 :     component.dispose();</code><br>
1423 :     <br>
1424 :     </div>
1425 : sxenos 1.2 We can also create the component without the optional INameService:<br>
1426 : sxenos 1.1 <br>
1427 :     <div style="margin-left: 40px;"><code>IContainer component =
1428 :     Components.getFactory().createContainer(MyComponent.class);<br>
1429 :     // Will print the message "create component with name = default name"<br>
1430 :     component.dispose();</code><code></code><br>
1431 :     </div>
1432 :     <br>
1433 :     <br>
1434 : sxenos 1.2 <span style="font-weight: bold;">WARNING: </span>This pattern should
1435 :     be used with care since it adds special
1436 :     cases to the component code. It is mainly inteded for advanced
1437 :     situations where a component needs to request an interface that isn't
1438 :     normally visible in its scope.<br>
1439 :     <br>
1440 :     The preferred method of dealing with missing interfaces is to provide a
1441 :     default implementation, like this:<br>
1442 :     <br>
1443 :     <div style="margin-left: 40px;"><code>&lt;extension
1444 :     point="</code><code>org.eclipse.core.component.interface</code><code>"&gt;</code><br>
1445 :     <code>&nbsp;&nbsp; &lt;interface
1446 :     </code><br>
1447 :     <code>&nbsp;&nbsp;&nbsp;&nbsp;
1448 :     class="org.eclipse.ui.DefaultNameService"</code><br>
1449 :     <code>&nbsp;&nbsp;&nbsp;&nbsp; </code><code>interface="</code><code>org.eclipse.ui.INameService</code><code>"</code><code>&gt;</code><br>
1450 :     <code>&nbsp;&nbsp; &lt;/interface&gt;</code><br>
1451 :     <code>&lt;/extension&gt;<br>
1452 :     <br>
1453 :     <br>
1454 :     /**<br>
1455 :     &nbsp;* Default implementation of INameService that will be used if the
1456 :     parent doesn't explicitly provide one<br>
1457 :     &nbsp;*/<br>
1458 :     class NameService implements INameService {<br>
1459 :     </code><code>&nbsp;&nbsp;&nbsp; public String getName() {<br>
1460 :     &nbsp;&nbsp;&nbsp; &nbsp;&nbsp; return "default name";<br>
1461 :     &nbsp;&nbsp;&nbsp; }<br>
1462 :     </code><code>}<br>
1463 :     <br>
1464 :     </code><code>/**<br>
1465 :     &nbsp;* A component with no dependencies, but which can optionally
1466 :     accept an INameService for initialization.<br>
1467 :     &nbsp;*/<br>
1468 :     public class MyComponent {<br>
1469 :     <br>
1470 :     &nbsp;&nbsp;&nbsp; String myName;<br>
1471 :     <br>
1472 :     &nbsp;&nbsp;&nbsp; public MyComponent(INameService nameService) {<br>
1473 :     &nbsp;&nbsp;&nbsp; &nbsp;&nbsp; myName = nameService.getName();<br>
1474 :     </code><code></code><code><br>
1475 :     &nbsp;&nbsp;&nbsp; &nbsp;&nbsp; System.out.println("created component
1476 :     with name = " + myName);<br>
1477 :     &nbsp;&nbsp;&nbsp; }<br>
1478 :     }</code><code><br>
1479 :     </code></div>
1480 :     <br>
1481 :     With this approach, it is still possible to create a MyComponent with
1482 :     or without an INameService, but the special-case code for handling
1483 :     missing interfaces is written once inside the default implementation
1484 :     rather than many times inside each component that uses
1485 :     the interface.<br>
1486 :     <h2><a class="mozTocH2" name="mozTocId578639"></a>2.8 Component Scopes</h2>
1487 :     Scopes are a way to determine what interfaces can be used in the
1488 :     constructor of a component. At some point, a programmer is going to ask
1489 :     "What interfaces am I allowed to pass into the constructor of my view?"
1490 :     Ultimately, this could be determined by examining the dependencies
1491 :     between each service, but it is not reasonable to expect a programmer
1492 :     to have this level of knowledge of the dependencies between services.<br>
1493 :     <br>
1494 :     Scopes limit the dependencies between services in order to make this
1495 :     question easier to answer. For example, views are allowed to use any
1496 :     service in the "/plugin/part/view" scope. This makes it easy to write a
1497 :     PDE extension which displays the list of interfaces available to a
1498 :     views. <br>
1499 :     <br>
1500 :     The only difference between scopes is how much context the components
1501 :     are allowed to expect. For example, components in the <span
1502 :     style="font-style: italic;">plugin</span> scope will be given a plugin
1503 :     Bundle and components in the <span style="font-style: italic;">plugin/part</span>
1504 :     scope need to be associated with a particular SWT Composite. Components
1505 :     may depend on services in the same scope or in a
1506 :     more general scope. It is still possible for Components to reference
1507 :     services in a more specific scope, but they must do so as an optional
1508 :     interface (as described in section 2.7).<span
1509 :     style="font-style: italic;"></span><br>
1510 :     <br>
1511 :     Eclipse defines the following standard scopes.<br>
1512 :     <br>
1513 :     <table style="width: 100%; text-align: left;" border="1" cellpadding="2"
1514 :     cellspacing="2">
1515 :     <tbody>
1516 :     <tr>
1517 :     <td style="vertical-align: top;">Scope path<br>
1518 :     </td>
1519 :     <td style="vertical-align: top;">Required context<br>
1520 :     </td>
1521 :     <td style="vertical-align: top;">Description<br>
1522 :     </td>
1523 :     </tr>
1524 :     <tr>
1525 :     <td style="vertical-align: top;">/<br>
1526 :     </td>
1527 :     <td style="vertical-align: top;">None<br>
1528 :     </td>
1529 :     <td style="vertical-align: top;">Any service that omits the <span
1530 :     style="font-style: italic;">scope</span> attribute automatically
1531 :     belongs to the global scope. Services in the global scope can be used
1532 :     by any other component, but may only depend on other global services.
1533 :     Global services are not given any context from the application.<br>
1534 :     </td>
1535 :     </tr>
1536 :     <tr>
1537 :     <td style="vertical-align: top;">/plugin<br>
1538 :     </td>
1539 :     <td style="vertical-align: top;">Bundle<br>
1540 :     </td>
1541 :     <td style="vertical-align: top;">Components in the /plugin scope
1542 :     are created in the context of a plugin bundle. All executable
1543 :     extensions (any component created using an extension point) belong to
1544 :     the plugin scope. Components in this scope can reference their own
1545 :     plugin Bundle in their constructor. Services can reference the Bundle
1546 :     associated with the component that requested them.<br>
1547 :     </td>
1548 :     </tr>
1549 :     <tr>
1550 :     <td style="vertical-align: top;">/plugin/part<br>
1551 :     </td>
1552 :     <td style="vertical-align: top;">Bundle, Composite<br>
1553 :     </td>
1554 :     <td style="vertical-align: top;">Components in the /plugin/part
1555 :     scope are associated with an SWT Composite. Components in this scope
1556 :     are given their
1557 :     own SWT Composite. They may change the layout on the given Composite,
1558 :     but may not change its layout data. The composite will be managed as a
1559 :     service, so the component does not need to dispose it. Components in
1560 :     this scope may be used as editors, views, or both.<br>
1561 :     </td>
1562 :     </tr>
1563 :     <tr>
1564 :     <td style="vertical-align: top;">/plugin/part/view<br>
1565 :     </td>
1566 :     <td style="vertical-align: top;">Bundle,<br>
1567 :     Composite,<br>
1568 :     IWorkbenchPage<br>
1569 :     </td>
1570 :     <td style="vertical-align: top;">All views belong to this scope.
1571 :     They are given the IConfigurationElement containing their extension
1572 :     markup, and an IWorkbenchPage.<br>
1573 :     </td>
1574 :     </tr>
1575 :     <tr>
1576 :     <td style="vertical-align: top;">/plugin/part/editor<br>
1577 :     </td>
1578 :     <td style="vertical-align: top;">Bundle,<br>
1579 :     Composite,<br>
1580 :     IWorkbenchPage,<br>
1581 :     IEditorInput<br>
1582 :     </td>
1583 :     <td style="vertical-align: top;">All editors belong to this
1584 :     scope. They are given the IConfigurationElement containing their
1585 :     extension markup, an IWorkbenchPage, and the IEditorInput containing
1586 :     their input.<br>
1587 :     </td>
1588 :     </tr>
1589 :     </tbody>
1590 :     </table>
1591 :     <br>
1592 :     These scopes should be sufficient for most situations. However, some
1593 :     plugins may wish to create new scopes in order to pass additional
1594 :     context to the objects in an extension point they provided.<br>
1595 :     <br>
1596 :     Any plugin that defines its own scopes must include its fully qualified
1597 :     plugin ID as part of the scope path. For example, if the plugin
1598 :     org.eclipse.myplugin extends the /plugin/part/editor scope, the new
1599 :     scope name would look like
1600 :     /plugin/part/editor/org.eclipse.myplugin.scopename. All scope paths
1601 :     that do not contain a period (.) are reserved by the framework.<br>
1602 :     <br>
1603 :     It is never a breaking change to move a service into a more general
1604 :     scope. However, it is always a breaking change to move a service into a
1605 :     more specific scope. A service interface may only exist in one scope.<br>
1606 :     <h2><a class="mozTocH2" name="mozTocId762751"></a>2.9 Shared Adapters</h2>
1607 :     Normally, a new instance of a default interface implementation is
1608 :     created for each component that requests it. This allows the adapter to
1609 :     keep some state associated with the component and to clean up resources
1610 :     once they are no longer needed by the component. However, some
1611 :     interface do not need any state or expose any API that could be used to
1612 :     create a leak. For example, the IErrorContext interface in section 2.4
1613 :     could safely share one instance between all components in the same
1614 :     plugin. In order to reduce memory consumption, the component API should
1615 :     support the notion of a shared adapter. A shared adapter will be reused
1616 :     in the broadest possible context permitted by its scope. For example,
1617 :     an adapter for an interface in the global scope would become a
1618 :     singleton, shared adapters in the plugin scope will have at most one
1619 :     instance per plugin, and the shared flag will be ignored in the
1620 :     /plugin/part scope.<br>
1621 :     <br>
1622 :     This is intended as a future optimization, and will not be included in
1623 :     the initial version of the component framework<br>
1624 :     <br>
1625 :     <h1><a class="mozTocH1" name="mozTocId707022"></a>3.0 Views and Editors
1626 :     as Components</h1>
1627 :     This section describes how the workbench uses the component framework
1628 :     to create editors and views.<br>
1629 :     <br>
1630 :     <h2><a class="mozTocH2" name="mozTocId125361"></a>3.1 Interfaces
1631 :     offered by the workbench</h2>
1632 :     Parent interfaces: (Interfaces given to a component as arguments in its
1633 :     constructor)<br>
1634 :     <br>
1635 :     <table style="width: 100%; text-align: left;" border="1" cellpadding="2"
1636 :     cellspacing="2">
1637 :     <tbody>
1638 :     <tr>
1639 :     <td style="vertical-align: top;">Scope<br>
1640 :     </td>
1641 :     <td style="vertical-align: top;">Interface<br>
1642 :     </td>
1643 :     <td style="vertical-align: top;">Description<br>
1644 :     </td>
1645 :     </tr>
1646 :     <tr>
1647 :     <td style="vertical-align: top;">/plugin<br>
1648 :     </td>
1649 :     <td style="vertical-align: top;">IErrorContext<br>
1650 :     </td>
1651 :     <td style="vertical-align: top;">Provides facilities for logging
1652 :     exceptions<br>
1653 :     </td>
1654 :     </tr>
1655 :     <tr>
1656 :     <td style="vertical-align: top;">/plugin<br>
1657 :     </td>
1658 :     <td style="vertical-align: top;">ISwtResources<br>
1659 :     </td>
1660 :     <td style="vertical-align: top;">Provides facilities for
1661 :     allocating SWT resources such as Fonts, Images, and Colors<br>
1662 :     </td>
1663 :     </tr>
1664 :     <tr>
1665 :     <td style="vertical-align: top;">/<br>
1666 :     </td>
1667 :     <td style="vertical-align: top;">INameable<br>
1668 :     </td>
1669 :     <td style="vertical-align: top;">Parts can use this service to
1670 :     change their name, content description, title image, and tooltip. The
1671 :     default implementation ignores all method calls.<br>
1672 :     </td>
1673 :     </tr>
1674 :     <tr>
1675 :     <td style="vertical-align: top;">/<br>
1676 :     </td>
1677 :     <td style="vertical-align: top;">IMemento<br>
1678 :     </td>
1679 :     <td style="vertical-align: top;">Parts will receive a memento
1680 :     which they can use to load previously-saved state<br>
1681 :     </td>
1682 :     </tr>
1683 :     <tr>
1684 :     <td style="vertical-align: top;">/plugin<br>
1685 :     </td>
1686 :     <td style="vertical-align: top;">IPartFactory<br>
1687 :     </td>
1688 :     <td style="vertical-align: top;">Interface that can be used to
1689 :     create child views, editors, and other UI parts<br>
1690 :     </td>
1691 :     </tr>
1692 :     <tr>
1693 :     <td style="vertical-align: top;">/plugin/part<br>
1694 :     </td>
1695 :     <td style="vertical-align: top;">IActionBars2<br>
1696 :     </td>
1697 :     <td style="vertical-align: top;">Interface used to add to the
1698 :     toolbar, cool bar, etc. Currently exposed on the view site.<br>
1699 :     </td>
1700 :     </tr>
1701 :     <tr>
1702 :     <td style="vertical-align: top;"><br>
1703 :     </td>
1704 :     <td style="vertical-align: top;"><br>
1705 :     </td>
1706 :     <td style="vertical-align: top;"><br>
1707 :     </td>
1708 :     </tr>
1709 :     </tbody>
1710 :     </table>
1711 :     <br>
1712 :     Child interfaces: (Services that a component offers to its parent -
1713 :     either by implementing directly or as an adapter)<br>
1714 :     <br>
1715 :     <table style="text-align: left; width: 1550px; height: 88px;" border="1"
1716 :     cellpadding="2" cellspacing="2">
1717 :     <tbody>
1718 :     <tr>
1719 :     <td style="vertical-align: top;">Scope<br>
1720 :     </td>
1721 :     <td style="vertical-align: top;">Interface<br>
1722 :     </td>
1723 :     <td style="vertical-align: top;">Description<br>
1724 :     </td>
1725 :     </tr>
1726 :     <tr>
1727 :     <td style="vertical-align: top;">/plugin/part<br>
1728 :     </td>
1729 :     <td style="vertical-align: top;">IFocusable<br>
1730 :     </td>
1731 :     <td style="vertical-align: top;">Parts can implement this service
1732 :     to allow their parent<br>
1733 :     </td>
1734 :     </tr>
1735 :     <tr>
1736 :     <td style="vertical-align: top;">/<br>
1737 :     </td>
1738 :     <td style="vertical-align: top;">IMultiPart<br>
1739 :     </td>
1740 :     <td style="vertical-align: top;">Parts should implement this if
1741 :     they contain other parts and have the notion of an "active" child.
1742 :     Other services can use this service if their default implementation
1743 :     should redirect to the active child.<br>
1744 :     </td>
1745 :     </tr>
1746 :     <tr>
1747 :     <td style="vertical-align: top;">/<br>
1748 :     </td>
1749 :     <td style="vertical-align: top;">IPersistable<br>
1750 :     </td>
1751 :     <td style="vertical-align: top;">Parts may implement this
1752 :     interface if they wish to save their state between sessions.<br>
1753 :     </td>
1754 :     </tr>
1755 :     <tr>
1756 :     <td style="vertical-align: top;"><br>
1757 :     </td>
1758 :     <td style="vertical-align: top;"><br>
1759 :     </td>
1760 :     <td style="vertical-align: top;"><br>
1761 :     </td>
1762 :     </tr>
1763 :     </tbody>
1764 :     </table>
1765 :     <br>
1766 :     <br>
1767 :     <h3><a class="mozTocH2" name="mozTocId163729"></a>3.1.1 IMultiPart:
1768 :     Redirecting
1769 :     adapters from the active child<br>
1770 :     </h3>
1771 : sxenos 1.1 In UI code, it is common to create aggregate components that have an
1772 : sxenos 1.2 "active" child. This section suggests a general pattern for redirecting
1773 :     components in this way. Many of the adapters that the aggregate offers
1774 :     its
1775 :     parent will redirect their implementation to the active child. The
1776 :     aggregate itself will know
1777 : sxenos 1.1 how to select its active child, but it may not know about all the
1778 : sxenos 1.2 interfaces that need to be redirected in this manner. The default
1779 :     implementation of an interface determines how it should be redirected.<br>
1780 : sxenos 1.1 <br>
1781 : sxenos 1.2 This can be shown using a concrete example. Workbench parts
1782 : sxenos 1.1 offer an IFocusable adapter to their parent that allows their parent to
1783 :     give them focus. <br>
1784 :     <br>
1785 :     <code></code>
1786 :     <div style="margin-left: 40px;"><code>/**<br>
1787 :     &nbsp;* Parts can implement this interface if they wish to overload the
1788 :     default<br>
1789 :     &nbsp;* setFocus behavior.<br>
1790 :     &nbsp;*/<br>
1791 :     public interface IFocusable {<br>
1792 :     &nbsp;&nbsp;&nbsp; /**<br>
1793 :     &nbsp;&nbsp;&nbsp; &nbsp;* Gives focus to the part<br>
1794 :     &nbsp;&nbsp;&nbsp; &nbsp;*/<br>
1795 :     &nbsp;&nbsp;&nbsp; public void setFocus();<br>
1796 :     }</code><br>
1797 :     <br>
1798 :     </div>
1799 :     This interface should be multiplexed by default. For example, if we
1800 :     call setFocus() on a multi-page editor that doesn't explicitly
1801 :     implement the IFocusable interface, it should redirect focus to its
1802 :     active child. If the part doesn't have an active child, focus should go
1803 :     directly to the part's main control. To supply this default behavior,
1804 : sxenos 1.2 we provide a default implementation of IFocusable:<br>
1805 : sxenos 1.1 <br>
1806 :     <code></code>
1807 :     <div style="margin-left: 40px;"><code>/**<br>
1808 :     &nbsp;* Default implementation of the IFocusable service. If a part
1809 :     doesn't explicitly<br>
1810 :     &nbsp;* provide an adapter for IFocusable, this implementation will be
1811 :     used.<br>
1812 :     &nbsp;*/<br>
1813 :     public class DefaultFocusable implements IFocusable {<br>
1814 :     <br>
1815 :     &nbsp;&nbsp;&nbsp; private Composite control;<br>
1816 : sxenos 1.2 &nbsp;&nbsp;&nbsp; private IMultiPart activePart;<br>
1817 : sxenos 1.1 &nbsp;&nbsp;&nbsp; <br>
1818 :     &nbsp;&nbsp;&nbsp; /**<br>
1819 :     &nbsp;&nbsp;&nbsp; &nbsp;* Creates the default implementation of
1820 :     IFocusable, given the main control<br>
1821 : sxenos 1.2 &nbsp;&nbsp;&nbsp; &nbsp;* of the part and an IMultiPart that can be
1822 : sxenos 1.1 queried for the active child.<br>
1823 :     &nbsp;&nbsp;&nbsp; &nbsp;* <br>
1824 :     &nbsp;&nbsp;&nbsp; &nbsp;* @param toGiveFocus main control of the pane<br>
1825 :     &nbsp;&nbsp;&nbsp; &nbsp;* @param activePartProvider multiplexer that
1826 :     returns the active part<br>
1827 :     &nbsp;&nbsp;&nbsp; &nbsp;*/<br>
1828 :     &nbsp;&nbsp;&nbsp; public DefaultFocusable(Composite toGiveFocus,
1829 : sxenos 1.2 IMultiPart activePartProvider) {<br>
1830 : sxenos 1.1 &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; control = toGiveFocus;<br>
1831 :     &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; activePart = activePartProvider;<br>
1832 :     &nbsp;&nbsp;&nbsp; }<br>
1833 :     &nbsp;&nbsp;&nbsp; <br>
1834 :     &nbsp;&nbsp;&nbsp; /**<br>
1835 :     &nbsp;&nbsp;&nbsp;&nbsp; * First, try to give focus to the active
1836 :     child. If there is no active child, give focus to<br>
1837 :     &nbsp;&nbsp;&nbsp;&nbsp; * the main control.<br>
1838 :     &nbsp;&nbsp;&nbsp;&nbsp; */<br>
1839 :     &nbsp;&nbsp;&nbsp; public void setFocus() {<br>
1840 :     &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; // If this part has the notion of
1841 :     an active child, give that child focus<br>
1842 :     &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; Object current =
1843 :     activePart.getCurrent();<br>
1844 :     &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; if (current != null) {<br>
1845 :     &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; IFocusable
1846 :     focusable = (IFocusable)Components.getAdapter(current,
1847 :     IFocusable.class);<br>
1848 :     &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; if (focusable
1849 :     != null) {<br>
1850 :     &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;
1851 :     &nbsp;&nbsp;&nbsp; focusable.setFocus();<br>
1852 :     &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;
1853 :     &nbsp;&nbsp;&nbsp; return;<br>
1854 :     &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; }<br>
1855 :     &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; }<br>
1856 :     &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; <br>
1857 :     &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; // If the part has no children,
1858 :     then give focus to the part itself.&nbsp;&nbsp;&nbsp;
1859 :     &nbsp;&nbsp;&nbsp; <br>
1860 :     &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; control.setFocus();<br>
1861 :     &nbsp;&nbsp;&nbsp; }<br>
1862 :     }</code><br>
1863 :     <br>
1864 :     </div>
1865 : sxenos 1.2 Here is the XML markup to register the interface:<br>
1866 : sxenos 1.1 <br>
1867 :     <div style="margin-left: 40px;"><code>&lt;service<br>
1868 :     &nbsp;&nbsp;&nbsp;
1869 :     interface="org.eclipse.ui.workbench.services.IFocusable"<br>
1870 :     &nbsp;&nbsp;&nbsp;
1871 : sxenos 1.2 class="org.eclipse.ui.internal.part.serviceimplementation.DefaultFocusable<br>
1872 :     &nbsp;&nbsp;&nbsp; childadapter="true"<br>
1873 :     &nbsp;&nbsp;&nbsp; scope="plugin/part"/&gt;</code><code></code><br>
1874 : sxenos 1.1 </div>
1875 :     <br>
1876 : sxenos 1.2 All of the interfaces we have looked at so far have been offered by a
1877 :     parent for use in a child's constructor. In this case, the interface is
1878 : sxenos 1.1 offered as an adapter on the child to be used by the parent. In both
1879 : sxenos 1.2 cases, we support the notion of a default implementation that is
1880 :     declared through XML. Any service that should be redirected based on
1881 :     the active child will take IMultiPart in its constructor. Here is an
1882 :     example part that supports
1883 : sxenos 1.1 multiplexing:<br>
1884 :     <code><br>
1885 :     </code>
1886 :     <div style="margin-left: 40px;"><code>/**<br>
1887 :     &nbsp;* Part that explicitly implements IFocusable.<br>
1888 :     &nbsp;*/<br>
1889 :     public class Page implements IFocusable {<br>
1890 :     &nbsp;&nbsp;&nbsp; Text textField;<br>
1891 :     <br>
1892 :     &nbsp;&nbsp;&nbsp; public Page(Composite control) {<br>
1893 :     &nbsp;&nbsp;&nbsp; &nbsp;&nbsp; textField = new Text(control, SWT.NONE);<br>
1894 :     &nbsp;&nbsp;&nbsp; }<br>
1895 :     <br>
1896 :     &nbsp;&nbsp;&nbsp; public void setFocus() {<br>
1897 :     &nbsp;&nbsp;&nbsp; &nbsp;&nbsp; textField.setFocus();<br>
1898 :     &nbsp;&nbsp;&nbsp; }<br>
1899 :     }<br>
1900 :     <br>
1901 :     /**<br>
1902 :     &nbsp;* Non-multiplexing part that relies on the DefaultFocusable
1903 :     service<br>
1904 :     &nbsp;* to give focus to its composite.<br>
1905 :     &nbsp;*/<br>
1906 :     public class Page2 {<br>
1907 :     &nbsp;&nbsp;&nbsp; Text textField;<br>
1908 :     &nbsp;&nbsp;&nbsp; <br>
1909 :     &nbsp;&nbsp;&nbsp; public Page(Composite control) {<br>
1910 :     &nbsp;&nbsp;&nbsp; &nbsp;&nbsp; textField = new Text(control, SWT.NONE);<br>
1911 :     &nbsp;&nbsp;&nbsp; }<br>
1912 :     }<br>
1913 :     <br>
1914 :     /**<br>
1915 :     &nbsp;* Multiplexing part that contains a Page, a Page2, and a checkbox.<br>
1916 :     &nbsp;* When the checkbox is selected, the first page will be active.
1917 :     When the<br>
1918 :     &nbsp;* checkbox is deselected, the second page will be active. Calling
1919 :     setFocus<br>
1920 :     &nbsp;* on the MultiplexingView will always give focus to the active
1921 :     page.<br>
1922 :     &nbsp;*/<br>
1923 :     public class MultiplexingView implements IDisposable, IAdaptable {<br>
1924 :     &nbsp;&nbsp;&nbsp; private IContainer page1;<br>
1925 :     &nbsp;&nbsp;&nbsp; private IContainer page2;<br>
1926 :     <br>
1927 : sxenos 1.2 &nbsp;&nbsp;&nbsp; // Helper class that implements the IMultiPart
1928 : sxenos 1.1 interface<br>
1929 :     &nbsp;&nbsp;&nbsp; private Multiplexer multiplexer = new Multiplexer();<br>
1930 :     &nbsp;&nbsp;&nbsp; private Button checkBox;<br>
1931 :     <br>
1932 :     &nbsp;&nbsp;&nbsp; public MultiplexingView(Composite myControl) {<br>
1933 :     &nbsp;&nbsp;&nbsp; &nbsp;&nbsp; myControl.setLayout(new RowLayout());<br>
1934 :     <br>
1935 :     &nbsp;&nbsp;&nbsp; &nbsp;&nbsp; checkBox = new Button(myControl,
1936 :     SWT.CHECK);<br>
1937 :     &nbsp;&nbsp;&nbsp; &nbsp;&nbsp; checkBox.addSelectionListener(new
1938 :     SelectionAdapter() {<br>
1939 :     &nbsp;&nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp; public void
1940 :     widgetSelected(SelectionEvent e) {<br>
1941 :     &nbsp;&nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp;
1942 :     updateActivePart();<br>
1943 :     &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; }<br>
1944 :     &nbsp;&nbsp;&nbsp; &nbsp;&nbsp; });<br>
1945 :     <br>
1946 :     &nbsp;&nbsp;&nbsp; &nbsp;&nbsp; checkBox.setText("Activate part 1");<br>
1947 :     <br>
1948 :     &nbsp;&nbsp;&nbsp; &nbsp;&nbsp; // Create the child controls<br>
1949 :     &nbsp;&nbsp;&nbsp; &nbsp;&nbsp; IMutableComponentFactory childFactory =
1950 :     Components.getFactory().createDerivedFactory();<br>
1951 :     <br>
1952 :     &nbsp;&nbsp;&nbsp; &nbsp;&nbsp; // Use the CompositeAdapter class we
1953 :     created earlier in order to provide each page<br>
1954 :     &nbsp;&nbsp;&nbsp; &nbsp;&nbsp; // with its own control.<br>
1955 :     &nbsp;&nbsp;&nbsp; &nbsp;&nbsp; childFactory.addComponentInstance(new
1956 :     CompositeAdapter(myControl));<br>
1957 :     <br>
1958 :     &nbsp;&nbsp;&nbsp; &nbsp;&nbsp; page1 =
1959 :     childFactory.createContainer(Page.class);<br>
1960 :     &nbsp;&nbsp;&nbsp; &nbsp;&nbsp; page2 =
1961 :     childFactory.createContainer(Page2.class);<br>
1962 :     <br>
1963 :     &nbsp;&nbsp;&nbsp; &nbsp;&nbsp; // Activate the initial part<br>
1964 :     </code><code>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp; updateActivePart();</code><br>
1965 :     <code>&nbsp;&nbsp;&nbsp; }<br>
1966 :     <br>
1967 :     &nbsp;&nbsp;&nbsp; private final void updateActivePart() {<br>
1968 :     </code><code>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; if
1969 :     (checkBox.getSelection()) {<br>
1970 :     &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;
1971 :     multiplexer.setCurrent(page1);<br>
1972 :     &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; } else {<br>
1973 :     &nbsp;&nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; &nbsp;
1974 :     multiplexer.setCurrent(page2);<br>
1975 :     &nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; }</code><br>
1976 :     <code>&nbsp;&nbsp;&nbsp; }<br>
1977 :     <br>
1978 :     &nbsp;&nbsp;&nbsp; /**<br>
1979 :     &nbsp;&nbsp;&nbsp;&nbsp; * In order to indicate that this is a
1980 :     multiplexing part, we need to implement<br>
1981 :     &nbsp;&nbsp;&nbsp;&nbsp; * the IMultiplexer adapter. We simply redirect
1982 :     to the multiplexer object.<br>
1983 :     &nbsp;&nbsp;&nbsp;&nbsp; */<br>
1984 :     &nbsp;&nbsp;&nbsp; public Object getAdapter(Class adapterType) {<br>
1985 :     &nbsp;&nbsp;&nbsp; &nbsp;&nbsp; if (adapterType == IMultiplexer.class) {<br>
1986 :     &nbsp;&nbsp;&nbsp; &nbsp;&nbsp; &nbsp;&nbsp; return multiplexer;<br>
1987 :     &nbsp;&nbsp;&nbsp; &nbsp;&nbsp; }<br>
1988 :     &nbsp;&nbsp;&nbsp; }<br>
1989 :     <br>
1990 :     &nbsp;&nbsp;&nbsp; /**<br>
1991 :     &nbsp;&nbsp;&nbsp;&nbsp; * Release any resources allocated in the
1992 :     constructor<br>
1993 :     &nbsp;&nbsp;&nbsp;&nbsp; */<br>
1994 :     &nbsp;&nbsp;&nbsp; public void dispose() {<br>
1995 :     &nbsp;&nbsp;&nbsp; &nbsp;&nbsp; page1.dispose();<br>
1996 :     &nbsp;&nbsp;&nbsp; &nbsp;&nbsp; page2.dispose();<br>
1997 :     &nbsp;&nbsp;&nbsp; }<br>
1998 :     }</code><br>
1999 :     </div>
2000 :     <br>
2001 :     Notice that MultiplexingView itself doesn't know about the IFocusable
2002 :     interface, however if we were to create a MultiplexingView and ask its
2003 :     container for an IFocusable interface, we would get an interface that
2004 :     behaves as expected. <br>
2005 :     <br>
2006 :     <div style="margin-left: 40px;"><code></code><code>// Stupid example
2007 :     that creates a MultiplexingView, gives it focus, then destroys it<br>
2008 :     <br>
2009 :     // Create a MultiplexingView</code><br>
2010 :     <code>IMutableComponentFactory childFactory =
2011 :     Components.getFactory().createDerivedFactory();<br>
2012 :     </code><code>childFactory.addComponentInstance(new
2013 :     CompositeAdapter(myControl));<br>
2014 :     <br>
2015 :     IContainer viewContainer =
2016 :     childFactory.createContainer(MultiplexingView.class);<br>
2017 :     <br>
2018 :     // Give focus to the active part<br>
2019 :     IFocusable focusable = viewContainer.getAdapter(IFocusable.class);<br>
2020 :     focusable.setFocus();<br>
2021 :     </code><code></code><code><br>
2022 :     // Destroy the view<br>
2023 :     viewContainer.dispose();<br>
2024 :     </code><code></code></div>
2025 :     <code>&nbsp;&nbsp;&nbsp; <br>
2026 : sxenos 1.2 </code>This example also shows how the same default implementation can
2027 :     work for both
2028 : sxenos 1.1 multiplexing and non-multiplexing parts. The Page2 class doesn't
2029 : sxenos 1.2 implement IFocusable or IMultiPart, but we are still able to
2030 : sxenos 1.1 ask for an IFocusable interface and use it to give focus to the part.<br>
2031 : sxenos 1.2 <span style="font-weight: bold;"><br>
2032 :     </span><span style="font-weight: bold;"></span>
2033 :     <h3><a class="mozTocH3" name="mozTocId483376"></a>3.1.2 INameable:
2034 :     Changing the name of a part</h3>
2035 :     In order to change their name, existing parts need to implement a set
2036 :     of get methods, maintain a listener list, and send notifications when
2037 :     their name changes. Component-based parts will change their name by
2038 :     using an INameable interface provided by their parent. INameable looks
2039 :     like this:<br>
2040 :     <br>
2041 :     <div style="margin-left: 40px;"><code>public interface INameable {<br>
2042 :     &nbsp;&nbsp;&nbsp; public void setName(String newName);<br>
2043 :     &nbsp;&nbsp;&nbsp; public void setContentDescription(String
2044 :     contentDescription);<br>
2045 :     &nbsp;&nbsp;&nbsp; public void setImage(Image theImage);<br>
2046 :     &nbsp;&nbsp;&nbsp; public void setTooltip(String toolTip);<br>
2047 :     }<br>
2048 :     <br>
2049 :     </code></div>
2050 :     For example, a view could set its name like this:<br>
2051 :     <br>
2052 :     <div style="margin-left: 40px;"><code>public class MyView {<br>
2053 :     &nbsp;&nbsp;&nbsp; public MyView(Composite parent, INameable name) {<br>
2054 :     &nbsp;&nbsp;&nbsp; &nbsp;&nbsp; name.setName("Some tab text");<br>
2055 :     &nbsp;&nbsp;&nbsp; }<br>
2056 :     }<br>
2057 :     <br>
2058 :     </code></div>
2059 :     If a parent cares about the names of its children, it should provide an
2060 :     implementation of INameable that reacts when the child changes its
2061 :     name. Otherwise, the child will be given a default implementation of
2062 :     INameable that ignores all method calls. This eliminates the need to
2063 :     attach listeners to views.<br>
2064 :     <h3><a class="mozTocH3" name="mozTocId969580"></a>3.1.3 IPartFactory:
2065 :     Creating child parts<br>
2066 :     </h3>
2067 :     Any part that wants to create other nested parts would use an
2068 :     IPartFactory interface.<br>
2069 :     <br>
2070 :     <div style="margin-left: 40px;"><code>public interface IPartFactory {<br>
2071 :     &nbsp;&nbsp;&nbsp; public IContainer createView(String viewId,
2072 :     Composite parentComposite, IWorkbenchPage page, IContainerFactory
2073 :     services);<br>
2074 :     &nbsp;&nbsp;&nbsp; public IContainer createEditor(String editorId,
2075 :     Composite parentComposite, IWorkbenchPage page, </code><code>IEditorInput
2076 :     input, </code><code>IContainerFactory services);<br>
2077 :     &nbsp;&nbsp;&nbsp; public IContainer createPart(String partId,
2078 :     Composite parentComposite, IContainerFactory services);<br>
2079 :     }</code><br>
2080 : sxenos 1.1 <br>
2081 :     <br>
2082 : sxenos 1.2 </div>
2083 :     <div style="margin-left: 40px;"><code></code><code></code></div>
2084 :     <span style="font-weight: bold;"></span>
2085 : sxenos 1.1 </body>
2086 :     </html>