KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > netbeans > spi > looks > Look


1 /*
2  * The contents of this file are subject to the terms of the Common Development
3  * and Distribution License (the License). You may not use this file except in
4  * compliance with the License.
5  *
6  * You can obtain a copy of the License at http://www.netbeans.org/cddl.html
7  * or http://www.netbeans.org/cddl.txt.
8  *
9  * When distributing Covered Code, include this CDDL Header Notice in each file
10  * and include the License file at http://www.netbeans.org/cddl.txt.
11  * If applicable, add the following below the CDDL Header, with the fields
12  * enclosed by brackets [] replaced by your own identifying information:
13  * "Portions Copyrighted [year] [name of copyright owner]"
14  *
15  * The Original Software is NetBeans. The Initial Developer of the Original
16  * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
17  * Microsystems, Inc. All Rights Reserved.
18  */

19
20 package org.netbeans.spi.looks;
21
22 import java.awt.Component JavaDoc;
23 import java.awt.datatransfer.Transferable JavaDoc;
24 import java.io.IOException JavaDoc;
25 import java.util.ArrayList JavaDoc;
26 import java.util.Collection JavaDoc;
27 import java.util.HashSet JavaDoc;
28 import java.util.IdentityHashMap JavaDoc;
29 import java.util.Iterator JavaDoc;
30 import java.util.List JavaDoc;
31 import java.util.Map JavaDoc;
32 import java.util.Set JavaDoc;
33 import javax.swing.Action JavaDoc;
34 import org.netbeans.modules.looks.LookListener;
35 import org.netbeans.modules.looks.LookEvent;
36 import org.openide.nodes.Node;
37 import org.openide.util.HelpCtx;
38 import org.openide.util.Lookup;
39 import org.openide.util.datatransfer.NewType;
40 import org.openide.util.datatransfer.PasteType;
41
42 /**
43  * Base class for all Looks. All methods in the class provide
44  * neutral behavior (i.e. do nothing, rerurn null or false).<BR>
45  * Extending this class in order to implement
46  * a Look requires overriding all methods which should provide some
47  * functionality. You may also consider subclassing
48  * the adaptor class {@link org.netbeans.spi.looks.DefaultLook
49  * DefaultLook class}, which provides basic implementations for
50  * finding Icons and Actions.
51  * <P>
52  * Most methods take a Lookup as parameter. This lookup represents environement
53  * associated with given represented object. This environement usually
54  * contains data from the {@link #getLookupItems} method. However if more
55  * looks cooperate on the same represented object it should contain union
56  * of items provided by all cooperating looks.
57  *
58  * @author Petr Hrebejk
59  * @see org.netbeans.spi.looks.DefaultLook
60  */

61 public abstract class Look<T> extends Object JavaDoc {
62     
63     /** Internal mask for property change */
64     private static final long PROPERTY_CHANGE = -1;
65     
66     /** Mask for enabling (unmasking) all methods */
67     public static final long ALL_METHODS = Long.MAX_VALUE;
68     /** Mask for disabling (masking) all methods */
69     public static final long NO_METHODS = 0;
70     
71     
72     /** Mask for the method {@link org.netbeans.spi.looks.Look#destroy}. */
73     public static final long DESTROY = 1;
74     /** Mask for the method {@link org.netbeans.spi.looks.Look#rename}. */
75     public static final long RENAME = DESTROY << 1;
76     /** Mask for firing Cookie events and providing lookup items */
77     public static final long GET_LOOKUP_ITEMS = RENAME << 1;
78     /** Mask for the method {@link org.netbeans.spi.looks.Look#getDisplayName}. */
79     public static final long GET_DISPLAY_NAME = GET_LOOKUP_ITEMS << 1;
80     /** Mask for the method {@link org.netbeans.spi.looks.Look#getName}. */
81     public static final long GET_NAME = GET_DISPLAY_NAME << 1;
82     /** Mask for the method {@link org.netbeans.spi.looks.Look#getShortDescription}. */
83     public static final long GET_SHORT_DESCRIPTION = GET_NAME << 1;
84     /** Mask for the method {@link org.netbeans.spi.looks.Look#getIcon}. */
85     public static final long GET_ICON = GET_SHORT_DESCRIPTION << 1;
86     /** Mask for the method {@link org.netbeans.spi.looks.Look#getOpenedIcon}. */
87     public static final long GET_OPENED_ICON = GET_ICON << 1;
88     /** Mask for the method {@link org.netbeans.spi.looks.Look#getHelpCtx}. */
89     public static final long GET_HELP_CTX = GET_OPENED_ICON << 1;
90     /** Mask for the method {@link org.netbeans.spi.looks.Look#getChildObjects}. */
91     public static final long GET_CHILD_OBJECTS = GET_HELP_CTX << 1;
92     /** Mask for the method {@link org.netbeans.spi.looks.Look#getNewTypes}. */
93     public static final long GET_NEW_TYPES = GET_CHILD_OBJECTS << 1;
94     /** Mask for the method {@link org.netbeans.spi.looks.Look#getActions}. */
95     public static final long GET_ACTIONS = GET_NEW_TYPES << 1;
96     /** Mask for the method {@link org.netbeans.spi.looks.Look#getContextActions}. */
97     public static final long GET_CONTEXT_ACTIONS = GET_ACTIONS << 1;
98     /** Mask for the method {@link org.netbeans.spi.looks.Look#getDefaultAction}. */
99     public static final long GET_DEFAULT_ACTION = GET_CONTEXT_ACTIONS << 1;
100     /** Mask for the method {@link org.netbeans.spi.looks.Look#getPropertySets}. */
101     public static final long GET_PROPERTY_SETS = GET_DEFAULT_ACTION << 1;
102     /** Mask for the method {@link org.netbeans.spi.looks.Look#getCustomizer}. */
103     public static final long GET_CUSTOMIZER = GET_PROPERTY_SETS << 1;
104     /** Mask for the method {@link org.netbeans.spi.looks.Look#canRename}. */
105     public static final long CAN_RENAME = GET_CUSTOMIZER << 1;
106     /** Mask for the method {@link org.netbeans.spi.looks.Look#canDestroy}. */
107     public static final long CAN_DESTROY = CAN_RENAME << 1;
108     /** Mask for the method {@link org.netbeans.spi.looks.Look#canCopy}. */
109     public static final long CAN_COPY = CAN_DESTROY << 1;
110     /** Mask for the method {@link org.netbeans.spi.looks.Look#canCut}. */
111     public static final long CAN_CUT = CAN_COPY << 1;
112     /** Mask for the method {@link org.netbeans.spi.looks.Look#getPasteTypes}. */
113     public static final long GET_PASTE_TYPES = CAN_CUT << 1;
114     /** Mask for the method {@link org.netbeans.spi.looks.Look#getDropType}. */
115     public static final long GET_DROP_TYPE = GET_PASTE_TYPES << 1;
116     /** Mask for the method {@link org.netbeans.spi.looks.Look#clipboardCopy}. */
117     public static final long CLIPBOARD_COPY = GET_DROP_TYPE << 1;
118     /** Mask for the method {@link org.netbeans.spi.looks.Look#clipboardCut}. */
119     public static final long CLIPBOARD_CUT = CLIPBOARD_COPY << 1;
120     /** Mask for the method {@link org.netbeans.spi.looks.Look#drag}. */
121     public static final long DRAG = CLIPBOARD_CUT << 1;
122     /** Mask for the method {@link org.netbeans.spi.looks.Look#hasCustomizer}. */
123     public static final long HAS_CUSTOMIZER = DRAG << 1;
124     
125     
126     private ListenerCache<T> listenerCache;
127
128     // Programmatic name of the look
129

130     private String JavaDoc name;
131
132     static {
133         // initialize module private access to this package
134
org.netbeans.modules.looks.Accessor.DEFAULT = new AccessorImpl ();
135     }
136
137     // Constructors ------------------------------------------------------------
138

139     /** Creates new instance of look does no work.
140      * @param name the name to assign to the look.
141      */

142     protected Look( String JavaDoc name ) {
143         this.name = name;
144     }
145
146     // Identification methods --------------------------------------------------
147

148     /** Returns name of the look. This name should identify the look.
149      * @return Name of the look.
150      */

151     public final String JavaDoc getName() {
152         return name;
153     }
154
155     /** The human presentable name of the look. Default implementation
156      * calls getName();
157      * @return human presentable name
158      */

159     public String JavaDoc getDisplayName() {
160         return getName();
161     }
162
163     public String JavaDoc toString() {
164         return getName();
165     }
166
167     // General methods ---------------------------------------------------------
168

169     /**
170      * Overriding this method permits registering listeners on represented objects.
171      * <p>
172      * If given instance is used in many places (e.g. multiple views are using
173      * the look for representing the given object or when the look is used as a sublook
174      * in multiple composite looks) <code>attachTo</code> will only be called once per
175      * represented object.
176      * <P>
177      * Implementors may not wait for any other threads at it may be potentially called
178      * from internal lock.
179      *
180      * @param representedObject Represented object the look should work with.
181      * @throws ClassCastException When the represented object is unacceptable
182      * for the look due to wrong class
183      * @throws IllegalArgumentException When the represented object is
184      * unacceptable for other reason than class cast.
185      *
186      */

187     protected void attachTo( T representedObject ) {
188     }
189
190
191     /**
192      * This method is called when listening on represeted object is no
193      * longer needed. Make sure to deregister all listeners registered
194      * in {@link #attachTo} method.<BR>
195      * If given instance is used in many places (e.g. more views are using
196      * the look for representing given object or when the look is used as a sublook
197      * in more composite look) the detachFrom method will only be called once per
198      * reprsented object.
199      * <P>
200      * Implementors may not wait for any other threads at it may be potentially called
201      * from internal lock.
202      * @param representedObject Represented object to detach from.
203      */

204     protected void detachFrom( T representedObject ) {
205     }
206
207     // Methods for FUNCTIONALITY EXTENSIONS ------------------------------------
208

209     /** Allowes for adding new object into the object's environement passed
210      * to other methods as the env parameter.
211      * @param representedObject Parameter is ignored.
212      * @param oldEnv Content of previous environement when called after a change
213      * of environemnt or is empty.
214      * @return <CODE>null</CODE>
215      */

216     public java.util.Collection JavaDoc getLookupItems( T representedObject, Lookup oldEnv ) {
217         return null;
218     }
219
220     // Methods for STYLE -------------------------------------------------------
221

222     /** Gets the programmatic name of the object. This name shouldn't be
223      * localized.<BR>
224      * Notice that the env parameter may be empty in some cases (e.g. when
225      * call to this methods is performed during serialization)
226      * @param representedObject Represented object the look should work with.
227      * @param env Environment for the represented object.
228      * @return Programmatic name of the object.
229      */

230     public String JavaDoc getName( T representedObject, Lookup env ) {
231         return null;
232     }
233
234     /** Gets localized name of the object.
235      * @param representedObject Represented object the look should work with.
236      * @param env Environment for the represented object.
237      * @return Localized name of the object.
238      */

239     public String JavaDoc getDisplayName( T representedObject, Lookup env ) {
240         return null;
241     }
242
243     /** This method is called when the user renames the object.
244      * @param representedObject Represented object to be renamed.
245      * @param newName The new name set by the user.
246      * @param env Environment for the represented object.
247      */

248     public void rename( T representedObject, String JavaDoc newName, Lookup env ) throws IOException JavaDoc {
249     }
250
251     /** Gets short description for given object. The short description is usually
252      * visualized as a tooltip, but may have another forms as well.
253      * @param representedObject Represented object the look should work with.
254      * @param env Environment for the represented object.
255      * @return A localized short description of the object.
256      */

257     public String JavaDoc getShortDescription( T representedObject, Lookup env ) {
258         return null;
259     }
260
261     /** Find an icon for this object (in the closed state).
262      * @param representedObject Represented object the look should work with.
263      * @param type Constant from {@link java.beans.BeanInfo}
264      * @param env Environment for the represented object.
265      * @return Icon to use to represent the object in the closed state.
266      */

267     public java.awt.Image JavaDoc getIcon( T representedObject, int type, Lookup env ) {
268         return null;
269     }
270
271
272
273     /** Find an icon for this object (in the open state).
274      * This icon is used when the object may have children and is expanded.
275      * @param representedObject Represented object the look should work with.
276      * @param type Constant from {@link java.beans.BeanInfo}
277      * @param env Environment for the represented object.
278      * @return Icon to use to represent the object in the open state.
279      */

280     public java.awt.Image JavaDoc getOpenedIcon( T representedObject, int type, Lookup env ) {
281         return null;
282     }
283
284     /** Get context help associated with this object.
285      * @param representedObject Represented object the look should work with.
286      * @param env Environment for the represented object.
287      * @return The context help object (could be <code>null</code> or
288      * {@link org.openide.util.HelpCtx#DEFAULT_HELP})
289      */

290     public HelpCtx getHelpCtx( T representedObject, Lookup env ) {
291         return null;
292     }
293
294     // Methods for CHILDREN ----------------------------------------------------
295

296     /** Gets objects which are children of the represented object in the
297      * hierarchy represented by this Look.
298      * @param representedObject Represented object the look should work with.
299      * @param env Environment for the represented object.
300      * @return List of objects which should be represented as node children of
301      * the node, can return <code>null</code>.
302      */

303     public List JavaDoc<?> getChildObjects( T representedObject, Lookup env ) {
304         return null;
305     }
306
307     /** Decides whether given object should be a leaf.
308      * I.e. if the visual
309      * representation of the object should be expandable.
310      * <P>
311      * <B>Notice :</B> Implementation of
312      * this method must be consistent with implementation of
313      * {@link #getChildObjects}.
314      * Notice that you can switch the form LEAF to nonLEAF node and vice versa
315      * by calling {@link #fireChange} with parameter {@link #GET_CHILD_OBJECTS},
316      * which in turn will call
317      * <CODE>isLeaf(...)</CODE> and eventually <CODE>getChildObjects(...)</CODE>
318      * where you can return new value.
319      * @param representedObject Represented object the look should work with.
320      * @param env Environment for the represented object.
321      * @return <CODE>true</CODE> if the object should be unexpandable
322      * <CODE>false</CODE> otherwise.
323      */

324     public boolean isLeaf( T representedObject, Lookup env ) {
325         return false;
326     }
327
328
329     // Methods for ACTIONS & NEW TYPES -----------------------------------------
330

331     /** Get the new types that user can create from given object.
332      * For example, a a Java package will permit classes to be added.
333      * @param representedObject Represented object the look should work with.
334      * @param env Environment for the represented object.
335      * @return Array of new type operations that are allowed,
336      * can return <code>null</code> that is equivalent to empty array
337      */

338     public NewType[] getNewTypes( T representedObject, Lookup env ) {
339         return null;
340     }
341
342     /** Get the set of actions associated with the object.
343      * This may be used for in constructing popup menus etc.
344      * <P>
345      * @param representedObject Represented object the look should work with.
346      * @param env Environment for the represented object.
347      * @return Array of the Actions applicable to the node or <CODE>null</CODE>
348      * if actions in {@link org.openide.nodes.NodeOp#getDefaultActions()} should be used.
349      */

350     public Action JavaDoc[] getActions( T representedObject, Lookup env ) {
351         return null;
352     }
353
354
355     /** Get a special set of actions for situations when this object is displayed
356      * as a context. (E.g. right clicking in the empty area of a tree)
357      * @param representedObject Represented object the look should work with.
358      * @param env Environment for the represented object.
359      * @return Actions for a context.
360      */

361     public Action JavaDoc[] getContextActions( T representedObject, Lookup env ) {
362         return null;
363     }
364
365     /** Get the default action for this object.
366      * This action can but need not be one from the list returned
367      * from {@link #getActions}.
368      * @param representedObject Represented object the look should work with.
369      * @param env Environment for the represented object.
370      * @return Default action, or <code>null</code> if there should be none.
371      */

372     public Action JavaDoc getDefaultAction( T representedObject, Lookup env ) {
373         return null;
374     }
375
376     // Methods for PROPERTIES AND CUSTOMIZER -----------------------------------
377

378     /** Get the list of property sets for object. E.g. typically there
379      * may be one for normal Bean properties, one for expert
380      * properties, and one for hidden properties.
381      * @param representedObject Represented object the look should work with.
382      * @param env Environment for the represented object.
383      * @return Property sets for the object, can return <code>null</code> that
384      * is equivalent to empty array
385      */

386     public Node.PropertySet[] getPropertySets( T representedObject, Lookup env ) {
387         return null;
388     }
389
390     /** Check whether the customizer for the represented object is available.
391      * If so, the method
392      * getCustomizer should return non-null value.
393      *
394      * @param representedObject Represented object the look should work with.
395      * @param env Environment for the represented object.
396      * @return true if the customizer is available, false otherwise
397      */

398     public boolean hasCustomizer( T representedObject, Lookup env ) {
399         return false;
400     }
401
402     /** Get the customizer for represented object if available.
403      * @param representedObject Represented object the look should work with.
404      * @param env Environment for the represented object.
405      * @return The component, or <CODE>null</CODE> if there is no customizer.
406      */

407     public Component JavaDoc getCustomizer( T representedObject, Lookup env ) {
408         return null;
409     }
410
411
412     // Methods for CLIPBOARD OPERATIONS ----------------------------------------
413

414     /** Test whether this object can be renamed.
415      * If true, {@link #rename} will be called when the user changes the name
416      * of the node.
417      * @param representedObject Represented object the look should work with.
418      * @param env Environment for the represented object.
419      * @return <code>true</code> if the node object be renamed.
420      */

421     public boolean canRename( T representedObject, Lookup env ) {
422         return false;
423     }
424
425     /** Test whether this object can be deleted.
426      * @param representedObject Represented object the look should work with.
427      * @param env Environment for the represented object.
428      * @return <CODE>True</CODE> if can be deleted.
429      */

430     public boolean canDestroy( T representedObject, Lookup env ) {
431         return false;
432     }
433
434     /** Test whether this object permits copying.
435      * @param representedObject Represented object the look should work with.
436      * @param env Environment for the represented object.
437      * @return <code>True</code> if so.
438      */

439     public boolean canCopy( T representedObject, Lookup env ) {
440         return false;
441     }
442
443     /** Test whether this object permits cutting.
444      * @param representedObject Represented object the look should work with.
445      * @param env Environment for the represented object.
446      * @return <code>True</code> if so.
447      */

448     public boolean canCut( T representedObject, Lookup env ) {
449         return false;
450     }
451
452     /** Determine which paste operations are allowed when a given
453      * transferable is in the clipboard. For example, a
454      * Java package will permit classes to be pasted into it.
455      * @param representedObject Represented object the look should work with.
456      * @param t The transferable in the clipboard.
457      * @param env Environment for the represented object.
458      * @return Array of operations that are allowed, can return <code>null</code>
459      * that is equivalent to empty array
460      */

461     public PasteType[] getPasteTypes( T representedObject, Transferable JavaDoc t, Lookup env ) {
462         return null;
463     }
464
465     /** Determine if there is a paste operation that can be performed
466      * on provided transferable. Used by drag'n'drop code to check
467      * whether the drop is possible.
468      * @param representedObject Represented object the look should work with.
469      * @param t The transferable.
470      * @param action The drag'n'drop action to do DnDConstants.ACTION_MOVE,
471      * ACTION_COPY, ACTION_LINK.
472      * @param index Index between children the drop occured at or -1 if not specified.
473      * @param env Environment for the represented object.
474      * @return <CODE>Null</CODE> if the transferable cannot be accepted or the paste type
475      * to execute when the drop occurs.
476      */

477     public PasteType getDropType( T representedObject, Transferable JavaDoc t, int action, int index, Lookup env ) {
478         return null;
479     }
480
481     /** Called when a object is to be copied to the clipboard.
482      * @param representedObject Represented object the look should work with.
483      * @param env Environment for the represented object.
484      * @return The transferable object representing the content of the clipboard.
485      * @throws java.io.IOException When the copy cannot be performed.
486      */

487     public Transferable JavaDoc clipboardCopy( T representedObject, Lookup env ) throws IOException JavaDoc {
488         return null;
489     }
490
491     /** Called when the object is to be cut to the clipboard.
492      * @param representedObject Represented object the look should work with.
493      * @param env Environment for the represented object.
494      * @return The transferable object representing the content of the clipboard.
495      * @throws java.io.IOException When the copy cannot be performed.
496      */

497     public Transferable JavaDoc clipboardCut( T representedObject, Lookup env ) throws IOException JavaDoc {
498         return null;
499     }
500
501     /** Called when a drag is started with this object.
502      * The object can attach a transfer listener to ExTransferable and
503      * will be then notified about progress of the drag (accept/reject).
504      * @param representedObject Represented object the look should work with.
505      * @param env Environment for the represented object.
506      * @return Transferable to represent this object during a drag.
507      * @throws java.io.IOException If a drag cannot be started.
508      */

509     public Transferable JavaDoc drag( T representedObject, Lookup env ) throws IOException JavaDoc {
510         return null;
511     }
512
513     /** Called when object was destroyed.
514      * @param representedObject Represented object the look should work with.
515      * @param env Environment for the represented object.
516      * @throws java.io.IOException If reflecting the destroy action in underlying data
517      * fails for some reason.
518      */

519     public void destroy( T representedObject, Lookup env ) throws IOException JavaDoc {
520     }
521
522     // Event firing ------------------------------------------------------------
523

524     /** Notifies all listeners attached to the representedObject that
525      * result(s) of some method(s) changed. The mask patameter contains
526      * bit mask of the methods.
527      *
528      * @param representedObject the object that has changed
529      * @param mask Bit mask of methods which's result changed. See {@link Look}
530      * for the constants
531      *
532      */

533     protected final void fireChange( T representedObject, long mask ) {
534         fireUniversal( mask, representedObject, null );
535     }
536     
537     /** Notifies all listeners attached to the representedObject that
538      * a property in the PropertySets changed it's value.
539      *
540      * @param representedObject the object that has changed
541      * @param propertyName Name of the property
542      */

543     protected final void firePropertyChange( T representedObject, String JavaDoc propertyName ) {
544         fireUniversal( PROPERTY_CHANGE, representedObject, propertyName );
545     }
546
547     // Registering look listeners ----------------------------------------------
548

549     synchronized void addLookListener( T representedObject, LookListener listener ) {
550                 
551         if ( representedObject != null &&
552             ( listenerCache == null ||
553               listenerCache.getListenersCount( representedObject ) == 0 ) ) {
554             attachTo( representedObject );
555         }
556                 
557         if ( listenerCache == null ) {
558             listenerCache = new ListenerCache<T>();
559         }
560         
561         listenerCache.addListener( representedObject, listener );
562
563     }
564     
565     synchronized void changeLookListener( T representedObject, LookListener oldListener, LookListener newListener ) {
566         if ( listenerCache != null ) {
567             listenerCache.changeListener( representedObject, oldListener, newListener );
568         }
569     }
570
571     synchronized void removeLookListener( T representedObject, LookListener listener ) {
572                 
573         if ( representedObject != null && listenerCache != null &&
574              listenerCache.getListenersCount( representedObject ) == 1 ) {
575             detachFrom( representedObject );
576         }
577                 
578         if ( listenerCache != null /* && listener != null */ ) {
579             listenerCache.removeListener( representedObject, listener );
580         }
581     }
582
583     // Package private methods -------------------------------------------------
584

585     /** Gets all objects which have some object registred. Used from look and
586      * to fire on all objects and from ProxyLook to fire when selector change
587      * occurs. It returns copy of the cache.
588      */

589     synchronized T[] getAllObjects() {
590         Collection JavaDoc<T> allObjects = listenerCache.getAllObjects();
591         @SuppressWarnings JavaDoc("unchecked")
592         T[] objects = (T[]) allObjects.toArray();
593         return objects;
594     }
595     
596     // Private methods ---------------------------------------------------------
597

598     private void fireUniversal( long mask, T representedObject, String JavaDoc propertyName ) {
599         if ( listenerCache == null ) {
600             return;
601         }
602         
603         if ( representedObject == null ) { // Fire on all objects
604
T[] objects = getAllObjects();
605             if ( objects == null ) {
606                 return;
607             }
608             for( int i = 0; i < objects.length; i++ ) {
609                 fireUniversal( mask, objects[i], name );
610             }
611         }
612         else { // Fire on one object
613
LookEvent evt = mask == PROPERTY_CHANGE ?
614                 new LookEvent( representedObject, propertyName ) :
615                 new LookEvent( representedObject, mask );
616              
617             List JavaDoc listeners;
618             synchronized( this ) {
619                 listeners = listenerCache.getListeners( representedObject );
620             }
621             for( int i = 0; i < listeners.size(); i++ ) {
622                 if ( mask == PROPERTY_CHANGE ) {
623                     ((LookListener)listeners.get( i )).propertyChange( evt );
624                 }
625                 else {
626                     ((LookListener)listeners.get( i )).change( evt );
627                 }
628             }
629         }
630                                                  
631     }
632     
633     
634     // Innerclasses ------------------------------------------------------------
635

636     private static final Object JavaDoc PLACE_HOLDER = new Object JavaDoc();
637     
638     // Cache of listeners
639
private class ListenerCache<T> {
640
641         private Set JavaDoc<LookListener> allObjectListeners; // Listeners which should listen to all objects
642
private Map JavaDoc<T,/*Reference<LookListener>|List<Reference<LookListener>>*/Object JavaDoc> obj2l;
643
644         void addListener( T object, LookListener listener ) {
645
646             if ( object == null ) { // Listener which wants to know about all objects
647

648                 if ( allObjectListeners == null ) {
649                     allObjectListeners = new HashSet JavaDoc<LookListener>();
650                 }
651                 if ( listener != null ) {
652                     allObjectListeners.add( listener );
653                 }
654             }
655             else { // Listener registered to particular object
656

657                 if ( obj2l == null ) {
658                     obj2l = new IdentityHashMap JavaDoc<T,Object JavaDoc>();
659                 }
660                                 
661                 Object JavaDoc l = obj2l.get( object );
662
663                 if ( l == null ) { // There is nothing in the cache
664
obj2l.put( object, listener == null ? PLACE_HOLDER : listener ); // just add
665
}
666                 else { // Something already registered
667
if ( l instanceof LookListener || l == PLACE_HOLDER ) { // One listener
668
if (l == listener) {
669                             return;
670                         }
671                         List JavaDoc ll = new ArrayList JavaDoc( 2 ); // PENDING make this to array in order to save some memory
672
ll.add( l );
673                         ll.add( listener == null ? PLACE_HOLDER : listener );
674                         obj2l.put( object, ll );
675                     }
676                     else { // There are already many listeners
677
List JavaDoc list = (List JavaDoc)l;
678                         if (list.contains(listener)) {
679                             return;
680                         }
681                         list.add( listener == null ? PLACE_HOLDER : listener );
682                     }
683                 }
684             }
685         }
686
687         void removeListener( T object, LookListener listener ) {
688
689             if ( object == null ) {
690                 if ( allObjectListeners != null ) {
691                     allObjectListeners.remove( listener );
692                 }
693                 return;
694             }
695
696             if ( obj2l == null ) {
697                 return;
698             }
699
700             Object JavaDoc l = obj2l.get( object );
701
702             if ( l == PLACE_HOLDER || l instanceof LookListener ) { // Removing the listener from map
703
obj2l.remove( object );
704             }
705             else if ( l != null ) { // Removing the listener from list
706
List JavaDoc ll = (List JavaDoc)l;
707                 ll.remove( listener == null ? PLACE_HOLDER : listener );
708                 if ( ll.size() == 1 ) {
709                     obj2l.put( object, ll.get( 0 ) ); // Remove list put last listener instead
710
}
711             }
712
713         }
714         
715         void changeListener( T object, LookListener oldListener, LookListener newListener ) {
716
717             if ( object == null ) {
718                 if ( allObjectListeners != null ) {
719                     allObjectListeners.remove( oldListener );
720                     allObjectListeners.add( newListener );
721                 }
722                 return;
723             }
724
725             if ( obj2l == null ) {
726                 return;
727             }
728
729             Object JavaDoc l = obj2l.get( object );
730
731             if ( l == oldListener ) {
732                 obj2l.put( object, newListener == null ? PLACE_HOLDER : newListener ); // Change the listener
733
}
734             else { // Removing the listener from list
735
List JavaDoc ll = (List JavaDoc)l;
736                 if ( ll.remove( oldListener ) ) {
737                     ll.add( newListener == null ? PLACE_HOLDER : newListener );
738                 }
739             }
740
741         }
742
743         List JavaDoc getListeners( Object JavaDoc object ) {
744
745             if ( object == null ) {
746                 throw new IllegalStateException JavaDoc( "Reperesented object is null" );
747             }
748
749             List JavaDoc<LookListener> result = new ArrayList JavaDoc<LookListener>( 4 );
750
751             if ( allObjectListeners != null ) {
752                 result.addAll( allObjectListeners );
753             }
754
755             if ( obj2l != null ) {
756                 Object JavaDoc l = obj2l.get( object );
757
758                 if ( l == null ) {
759 // System.err.println( "LOOK : " + Look.this );
760
// System.err.println( "OBJ : " + object );
761
}
762                 else if ( l instanceof LookListener ) {
763                     result.add((LookListener) l);
764                 }
765                 else if ( l == PLACE_HOLDER ) {
766                     // Cant fire on placeholder
767
}
768                 else {
769                     // We need to filter out PLACE_HOLDERs
770
for( Iterator JavaDoc it = ((List JavaDoc)l).iterator(); it.hasNext(); ) {
771                         Object JavaDoc listener = it.next();
772                         if ( listener != PLACE_HOLDER ) {
773                             result.add((LookListener) listener);
774                         }
775                     }
776                 }
777             }
778
779             return result;
780         }
781         
782         /*@ !! does not count listeners on all objects */
783         int getListenersCount( Object JavaDoc object ) {
784             
785             if ( object == null ) {
786                 throw new IllegalStateException JavaDoc( "Reperesented object is null" );
787             }
788             
789             if ( obj2l != null ) {
790                 Object JavaDoc l = obj2l.get( object );
791
792                 if ( l == null ) {
793                     return 0;
794                 }
795                 else if ( l == PLACE_HOLDER || l instanceof LookListener ) {
796                     return 1;
797                 }
798                 else {
799                     return ((List JavaDoc)l).size();
800                 }
801             }
802             
803             return 0;
804             
805         }
806         
807         Collection JavaDoc<T> getAllObjects() {
808             if ( obj2l == null ) {
809                 return null;
810             }
811             else {
812                 return obj2l.keySet();
813             }
814         }
815
816     }
817
818
819
820 }
821
Popular Tags