alvinalexander.com | career | drupal | java | mac | mysql | perl | scala | uml | unix  

What this is

This file is included in the DevDaily.com "Java Source Code Warehouse" project. The intent of this project is to help you "Learn Java by Example" TM.

Other links

The source code

/*
 *                 Sun Public License Notice
 * 
 * The contents of this file are subject to the Sun Public License
 * Version 1.0 (the "License"). You may not use this file except in
 * compliance with the License. A copy of the License is available at
 * http://www.sun.com/
 * 
 * The Original Code is NetBeans. The Initial Developer of the Original
 * Code is Sun Microsystems, Inc. Portions Copyright 1997-2003 Sun
 * Microsystems, Inc. All Rights Reserved.
 */

package org.openide.util.lookup;

import java.util.*;
import java.lang.ref.Reference;

import javax.swing.event.EventListenerList;

import org.openide.util.Lookup;
import org.openide.util.LookupListener;
import org.openide.util.LookupEvent;

/** Implementation of lookup that can delegate to others.
 *
 * @author  Jaroslav Tulach
 * @since 1.9
 */
public class ProxyLookup extends Lookup {
    /** lookups to delegate to */
    private Lookup[] lookups;
    /** map of templates to currently active results */
    private HashMap results;
    
    /** Create a proxy to some other lookups.
     * @param lookups the initial delegates
     */
    public ProxyLookup (Lookup[] lookups) {
        this.lookups = lookups;
    }
    
    /**
     * Create a lookup initially proxying to no others.
     * Permits serializable subclasses.
     * @since 3.27
     */
    protected ProxyLookup() {
        this(new Lookup[0]);
    }
    
    public String toString() {
        return "ProxyLookup(class=" + getClass() + ")->" + Arrays.asList(lookups); // NOI18N
    }

    /** Getter for the delegates.
    * @return the array of lookups we delegate to
    * @since 1.19
    */
    protected final Lookup[] getLookups () {
        return lookups;
    }
    
    /** Change the delegates. To forbid anybody else then the creator
     * of the lookup to change the delegates, this method is protected.
     * 
     * @param lookups the new lookups to delegate to
     * @since 1.19 protected
     */
    protected final void setLookups (Lookup[] lookups) {
        Reference[] arr;
        HashSet newL;
        HashSet current;
        Lookup[] old;
        
        synchronized (this) {
            current = new HashSet (Arrays.asList (this.lookups));
            newL = new HashSet (Arrays.asList (lookups));

            old = this.lookups;
            this.lookups = lookups;
            
            if (results == null || results.isEmpty ()) {
                // no affected results => exit
                return;
            }
	    arr = (Reference[])results.values ().toArray( new Reference[0] );
        
            HashSet removed = new HashSet (current);
            removed.removeAll (newL); // current contains just those lookups that have disappeared
            newL.removeAll (current); // really new lookups

            if (removed.isEmpty () && newL.isEmpty ()) {
                // no need to notify changes
                return;
            }

            for (int i = 0; i < arr.length; i++) {
                R r = (R)arr[i].get ();
                if (r != null) {
                    r.lookupChange (newL, removed, old, lookups);
                }
            }
        }
        // this cannot be done from the synchronized block
        for (int i = 0; i < arr.length; i++) {
            R r = (R)arr[i].get ();
            if (r != null) {
                r.resultChanged(null);
            }
        }
    }
    
    /** Notifies subclasses that a query is about to be processed.
     * Subclasses can update its state before the actual processing
     * begins. It is allowed to call setLookups method
     * to change/update the set of objects the proxy delegates to.
     *
     * @param template the template of the query
     * @since 1.31
     */
    protected void beforeLookup (Template template) {
    }
    
    
    /* Lookups an object of given interface. This is the simplest method
     * for the lookuping, if more registered objects implement the given
     * class any of them can be returned. 
     *
     * @param clazz class of the object we are searching for
     * @return the object implementing given class or null if no such 
     *    has been found
     */
    public final Object lookup (Class clazz) {
        beforeLookup (new Template (clazz));
        
        Lookup[] lookups = this.lookups;

        for (int i = 0; i < lookups.length; i++) {
            Object o = lookups[i].lookup (clazz);
            if (o != null) {
                return o;
            }
        }
        
        return null;
    }
    
    /* Lookups the first item that matches given template.
     * @param template the template to check
     * @return item or null
     */
    public final Item lookupItem(Template template) {
        beforeLookup (template);
        
        Lookup[] lookups = this.lookups;

        for (int i = 0; i < lookups.length; i++) {
            Item o = lookups[i].lookupItem (template);
            if (o != null) {
                return o;
            }
        }
        
        return null;
    }
    
    /* The general lookup method.
     * @param template the template describing the services we are looking for
     * @return object containing the results
     */
    public final synchronized Result lookup (Lookup.Template template) {
        R r;
        if (results != null) {
            Reference ref = (Reference)results.get (template);
            r = ref == null ? null : (R)ref.get ();
            if (r != null ) {
                return r;
            }
        } else {
            results = new HashMap ();
        }
        r = new R (template);
        results.put (template, new java.lang.ref.SoftReference (r));
        
        return r;
    }
    
    /** Unregisters a template from the has map.
     */
    private final synchronized void unregisterTemplate (Template template) {
        if (results == null) return;
        Reference ref = (Reference)results.remove (template);
        if (ref != null && ref.get () != null) {
            // seems like there is a reference to a result for this template
            // thta is still alive
            results.put (template, ref);
        }
    }

    /** Result of a lookup request. Allows access to single object
     * that was found (not too useful) and also to all objects found
     * (more useful).
     */
    private final class R extends WaitableResult implements LookupListener {
        /** list of listeners added */
        private javax.swing.event.EventListenerList listeners;
        
        /** template for this result */
        private Lookup.Template template;
        /** all results */
        private Lookup.Result[] results;
        /** collection of Objects */
        private Collection[] cache;
        
        
        /** Constructor.
         */
        public R (Lookup.Template t) {
            template = t;
        }
        
        /** When garbage collected, remove the template from the has map.
         */
        protected void finalize () {
            unregisterTemplate (template);
        }

        /** initializes the results 
         */
        private Result[] initResults () {
            synchronized (this) {
                if (results != null) return results;
            }
            
            Result[] arr = new Result[lookups.length];
            for (int i = 0; i < arr.length; i++) {
                arr[i] = lookups[i].lookup (template);
            }
            
            synchronized (this) {
                // some other thread might compute the result mean while. 
                // if not finish the computation yourself
                if (results != null) return results;
                
                cache = new Collection[3];
                for (int i = 0; i < arr.length; i++) {
                    arr[i].addLookupListener (this);
                }
                results = arr;
                return arr;
            }
        }
        
        /** Called when there is a change in the list of proxied lookups.
         * @param added set of added lookups
         * @param remove set of removed lookups
         * @param current array of current lookups
         */
        protected void lookupChange (
            Set added, Set removed, Lookup[] old, Lookup[] current
        ) {
            synchronized (this) {
                if (results == null) {
                    // not computed yet, do not need to do anything
                    return;
                }

                // map (Lookup, Lookup.Result)
                HashMap map = new HashMap (old.length * 2);


                for (int i = 0; i < old.length; i++) {
                    if (removed.contains (old[i])) {
                        // removed lookup
                        results[i].removeLookupListener (this);
                    } else {
                        // remember the association
                        map.put (old[i], results[i]);
                    }
                }

                Lookup.Result[] arr = new Lookup.Result[current.length];
                for (int i = 0; i < current.length; i++) {
                    if (added.contains (current[i])) {
                        // new lookup
                        arr[i] = current[i].lookup (template);
                        arr[i].addLookupListener (this);
                    } else {
                        // old lookup
                        arr[i] = (Lookup.Result)map.get (current[i]);
                        if (arr[i] == null) {
                            // assert
                            throw new IllegalStateException ();
                        }
                    }
                }

                // remember the new results
                results = arr;
            }
        }
        
        /** Just delegates.
         */
        public void addLookupListener (LookupListener l) {
            if (listeners == null) {
                synchronized (this) {
                    if (listeners == null) {
                        listeners = new EventListenerList ();
                    }
                }
            }
            listeners.add (LookupListener.class, l);
        }
            
        /** Just delegates.
         */
        public void removeLookupListener (LookupListener l) {
            if (listeners != null) {
                listeners.remove (LookupListener.class, l);
            }
        }
        
        /** Access to all instances in the result.
         * @return collection of all instances
         */
        public java.util.Collection allInstances () {
            return computeResult (0);
        }
            
        /** Classes of all results. Set of the most concreate classes
         * that are registered in the system.
         * @return set of Class objects
         */
        public java.util.Set allClasses () {
            return (java.util.Set)computeResult (1);
        }
        
        /** All registered items. The collection of all pairs of 
         * ii and their classes.
         * @return collection of Lookup.Item
         */
        public java.util.Collection allItems () {
            return computeResult (2);
        }
            

        /** Computes results from proxied lookups.
         * @param indexToCache 0 = allInstances, 1 = allClasses, 2 = allItems
         * @return the collection or set of the objects
         */
        private java.util.Collection computeResult (int indexToCache) {
            // results to use
            Lookup.Result[] arr = myBeforeLookup ();
            
            
            // if the call to beforeLookup resulted in deletion of caches
            synchronized (this) {
                if (cache != null && cache[indexToCache] != null) {
                    return cache[indexToCache];
                }
            }
                

            // initialize the collection to hold result
            Collection ll;
            if (indexToCache == 1) {
                ll = new HashSet ();
            } else {
                ll = new ArrayList (arr.length * 2);
            }
            
            // fill the collection
            for (int i = 0; i < arr.length; i++) {
                switch (indexToCache) {
                    case 0:
                        ll.addAll (arr[i].allInstances ());
                        break;
                    case 1:
                        ll.addAll (arr[i].allClasses ());
                        break;
                    case 2:
                        ll.addAll (arr[i].allItems ());
                        break;
                }
            }

            synchronized (this) {
                if (arr == results && cache != null) {
                    // updates the results, if the results have not been
                    // changed during the computation of allInstances
                    cache[indexToCache] = ll;
                }
            }
            
            return ll;
        }
        
        /** When the result changes, fire the event.
         */
        public void resultChanged (LookupEvent ev) {
            // clear cached instances
            synchronized (this) {
                cache = null;
                if (listeners == null) return;
            }
            
            Object[] arr = listeners.getListenerList ();
            if (arr.length == 0) {
                return;
            }
            
            ev = new LookupEvent (this);
            AbstractLookup.notifyListeners(arr, ev);
        }

        /** Implementation of my before lookup.
         * @return results to work on.
         */
        private Lookup.Result[] myBeforeLookup () {
            ProxyLookup.this.beforeLookup (template);
            
            Lookup.Result[] arr = initResults ();

            // invoke update on the results
            for (int i = 0; i < arr.length; i++) {
                if (arr[i] instanceof WaitableResult) {
                    WaitableResult w = (WaitableResult)arr[i];
                    w.beforeLookup (template);
                }
            }
            
            return arr;
        }

        
        /** Used by proxy results to synchronize before lookup.
         */
        protected void beforeLookup(Lookup.Template t) {
            if (t.getType () == template.getType ()) {
                myBeforeLookup ();
            }
        }        
        
    }
}
... this post is sponsored by my books ...

#1 New Release!

FP Best Seller

 

new blog posts

 

Copyright 1998-2021 Alvin Alexander, alvinalexander.com
All Rights Reserved.

A percentage of advertising revenue from
pages under the /java/jwarehouse URI on this website is
paid back to open source projects.