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-2004 Sun
 * Microsystems, Inc. All Rights Reserved.
 */
package org.netbeans.modules.javacore.classpath;

import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import java.beans.PropertyChangeEvent;
import java.net.URL;
import java.util.*;
import java.lang.ref.WeakReference;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;

import org.openide.ErrorManager;
import org.openide.filesystems.FileObject;
import org.openide.filesystems.FileStateInvalidException;
import org.openide.util.WeakListeners;
import org.openide.util.Utilities;
import org.netbeans.api.java.classpath.ClassPath;
import org.netbeans.spi.java.classpath.PathResourceImplementation;
import org.netbeans.api.java.classpath.GlobalPathRegistry;
import org.netbeans.api.java.classpath.GlobalPathRegistryListener;
import org.netbeans.api.java.classpath.GlobalPathRegistryEvent;
import org.netbeans.api.java.queries.SourceForBinaryQuery;
import org.netbeans.spi.java.classpath.ClassPathImplementation;
import org.netbeans.spi.java.classpath.support.ClassPathSupport;



public class MergedClassPathImplementation implements ClassPathImplementation {

    public static final String PROP_UNRESOLVED_ROOTS = "unresolvedRoots";       //NOI18N

    private PropertyChangeSupport support;
    private GlobalPathRegistry reg;
    private List cachedResources;
    private List unresolvedRoots;
    private ClassPathMap resourceMap;
    private GlobalPathRegistryListener gprListener;
    private PropertyChangeListener pcListener = new PropertyChangeListener() {
                public void propertyChange(PropertyChangeEvent event) {
                    assert event != null : "event == null";                     //NOI18N
                    if (ClassPath.PROP_ENTRIES.equals(event.getPropertyName())) {
                        MergedClassPathImplementation.this.updateEntries ((ClassPath)event.getSource());
                    }
                }
            };
    
    private WeakHashMap/**/ sfbResultListeners = new WeakHashMap(100);
    
    private static MergedClassPathImplementation instance;

    private MergedClassPathImplementation () {
        this.support = new PropertyChangeSupport(this);
        this.cachedResources = new ArrayList ();
        this.reg = GlobalPathRegistry.getDefault();
        this.gprListener = new GlobalPathRegistryListener() {
            public void pathsAdded(GlobalPathRegistryEvent event) {
                assert event != null : "event == null"; // NOI18N
                MergedClassPathImplementation.this.updateEntries(event);
                MergedClassPathImplementation.this.firePropertyChange (PROP_UNRESOLVED_ROOTS);
            }

            public void pathsRemoved(GlobalPathRegistryEvent event) {
            }
        };
        this.reg.addGlobalPathRegistryListener ((GlobalPathRegistryListener)
                WeakListeners.create(GlobalPathRegistryListener.class,this.gprListener,this.reg));
        assert this.reg != null : "GloabalPathRegistry.getDefault()==null";              //NOI18N
    }


    public synchronized List getResources() {
        return Collections.unmodifiableList(this.cachedResources);
    }

    public void addPropertyChangeListener(PropertyChangeListener listener) {
        assert listener != null : "gprListener == null";                               //NOI18N
        this.support.addPropertyChangeListener(listener);
    }

    public void removePropertyChangeListener(PropertyChangeListener listener) {
        assert listener != null : "gprListener == null";                               //NOI18N
        this.support.removePropertyChangeListener(listener);
    }


    public void addClassPaths (ClassPath[] cps) {
        assert cps != null : "addClassPath called with null";                         //NOI18N
        for (int i = 0; i < cps.length; i++) {
            if (cps[i]!=null) {
                this.addClassPath(cps[i]);
            }
        }
        this.firePropertyChange(PROP_UNRESOLVED_ROOTS);
    }

    public synchronized PathResourceImplementation[] getUnresolvedRoots () {
        if (this.unresolvedRoots == null) {
            this.initEntries();
        }
        return (PathResourceImplementation[]) this.unresolvedRoots.toArray (new PathResourceImplementation[this.unresolvedRoots.size()]);
    }

    public void classPathRootResolved (PathResourceImplementation impl) {
        synchronized (this) {
            if (this.unresolvedRoots.remove(impl)) {
                this.cachedResources.add(impl);
            }
        }
        this.firePropertyChange (PROP_RESOURCES);
    }

    public void removeRoot(PathResourceImplementation impl) {
        synchronized (this) {
            this.unresolvedRoots.remove(impl);
        }
    }

    /**
     * Slower version of classPathRootResolved, use classPathRootResolved(PathResourceImplementation)
     * where possible
     * @param url
     */
    public void classPathRootResolved (URL url) {
        synchronized (this) {
            for (Iterator it = unresolvedRoots.iterator(); it.hasNext();) {
                PathResourceImplementation resource = (PathResourceImplementation) it.next ();
                if (resource.getRoots()[0].equals(url)) {
                    it.remove();
                    this.cachedResources.add(resource);
                    break;
                }
            }
        }
        this.firePropertyChange (PROP_RESOURCES);
    }

    private synchronized void initEntries () {
        Set classPaths = new HashSet ();
        this.unresolvedRoots = new ArrayList ();
        this.resourceMap = new ClassPathMap();
        classPaths.addAll(this.reg.getPaths(ClassPath.SOURCE));
        classPaths.addAll(this.reg.getPaths(ClassPath.COMPILE));
        classPaths.addAll(this.reg.getPaths(ClassPath.BOOT));
        for(Iterator it = classPaths.iterator();it.hasNext();) {
            ClassPath cp = (ClassPath) it.next ();
            this.addClassPath (cp);
        }
    }

    private synchronized void updateEntries (GlobalPathRegistryEvent event) {
        if (this.cachedResources == null)
            return;
        for (Iterator it = event.getChangedPaths().iterator(); it.hasNext();) {
            ClassPath cp = (ClassPath) it.next();
            this.addClassPath (cp);
        }
    }

    private void updateEntries (ClassPath cp) {
        boolean fire = false;
        synchronized (this) {
            List c = (List) this.resourceMap.remove (cp);
            assert c != null : "Change in unknown classpath"; // NOI18N
            for (Iterator it = c.iterator(); it.hasNext();) {
                PathResourceImplementation resource = (PathResourceImplementation) it.next();
                if (!this.cachedResources.remove(resource)) {
                    this.unresolvedRoots.remove (resource);
                }
                else {
                    fire = true;
                }
            }
            c = addClassPathResources (cp);
            this.resourceMap.put (cp,c);
            this.unresolvedRoots.addAll(c);
        }
        if (fire) {
            this.firePropertyChange(PROP_RESOURCES);
        }
        this.firePropertyChange(PROP_UNRESOLVED_ROOTS);
    }

    private synchronized void addClassPath (ClassPath cp) {
        if (this.resourceMap == null) {
            initEntries();
        }
        if (this.resourceMap.containsKey(cp)) {
            return;
        }
        List c = addClassPathResources (cp);
        this.resourceMap.put (cp,c);
        this.unresolvedRoots.addAll(c);
        cp.addPropertyChangeListener((PropertyChangeListener)WeakListeners.create(PropertyChangeListener.class,this.pcListener,cp));
    }
    
    private void firePropertyChange (String propName) {
        this.support.firePropertyChange(propName,null,null);
    }
    
    private class SFBQListener implements ChangeListener {
        private final WeakReference cp;
        private Map results;
        
        public SFBQListener(ClassPath cp) {
            this.cp = new WeakReference(cp);
        }
        
        public SourceForBinaryQuery.Result getResult(URL url) {
            if (results == null) {
                results = new HashMap();
            }
            SourceForBinaryQuery.Result result = (SourceForBinaryQuery.Result) results.get(url);
            if (result == null) {
                result = SourceForBinaryQuery.findSourceRoots(url);
                results.put(url, result);
                result.addChangeListener(this);
            }
            return result;
        }

        public void stateChanged(ChangeEvent e) {
            //System.err.println("SourceForBinaryQuery.Result changed");
            ClassPath classPath = (ClassPath) cp.get();
            if (classPath != null) {
                updateEntries(classPath);
            }
        }
    }

    private List addClassPathResources (final ClassPath cp) {
        List c = new ArrayList ();
        SFBQListener listener = (SFBQListener) sfbResultListeners.get(cp);
        if (listener == null) {
            listener = new SFBQListener(cp);
            sfbResultListeners.put(cp, listener);
        }
        for (Iterator et = cp.entries().iterator(); et.hasNext();) {
            ClassPath.Entry entry = (ClassPath.Entry)et.next();
            URL url = entry.getURL();
            assert url != null : "ClassPath.Entry.getURL() returned null";      //NOI18N
            addResources (url, listener, c);
        }
        return c;
    }

    private static void addResources (URL url, SFBQListener sfbResultListener, List resourceList) {
        PathResourceImplementation resource = ClassPathSupport.createResource(url);
        assert resource != null : "ClassPathSupport.createResource() returned null";    //NOI18N
        resourceList.add (resource);
        FileObject[] sources = sfbResultListener.getResult(url).getRoots();
        for (int i=0; i< sources.length; i++) {
            try {
                url = sources[i].getURL();
                resource = ClassPathSupport.createResource(url);
                assert resource != null : "ClassPathSupport.createResource() returned null";    //NOI18N
                resourceList.add (resource);
            } catch (FileStateInvalidException e) {
                ErrorManager.getDefault().notify (e);
            }
        }
    }


    public synchronized static MergedClassPathImplementation getDefault () {
        if (instance == null ) {
            instance = new MergedClassPathImplementation();
        }
        return instance;
    }


    private class ClassPathMap {

        private LinkedList data;

        public ClassPathMap () {
            this.data = new LinkedList ();
        }

        public void put (Object key, Object value ) {
            synchronized (MergedClassPathImplementation.this) {
                WeakPair wp = new WeakPair (key, value);
                data.add (wp);
            }
        }

        public Object remove (Object key) {
            if (key == null) {
                return null;
            }
            synchronized (MergedClassPathImplementation.this) {
                for (Iterator it = this.data.iterator(); it.hasNext();) {
                    WeakPair pair = (WeakPair) it.next ();
                    Object wpk = pair.getKey();
                    if (key.equals(wpk)) {
                        it.remove();
                        return pair.getValue();
                    }
                }
            }
            return null;
        }

        public boolean containsKey (Object key) {
            if (key == null) {
                return false;
            }
            Iterator it;
            synchronized (MergedClassPathImplementation.this) {
                it = ((LinkedList)this.data.clone()).iterator();
            }
            while (it.hasNext()) {
                WeakPair pair = (WeakPair) it.next();
                Object pk = pair.getKey();
                if (key.equals(pk)) {
                    return true;
                }
            }
            return false;
        }

        private void cleanUp (WeakPair toClean) {
            byte fire = 0;
            synchronized (MergedClassPathImplementation.this) {
                for (Iterator it = this.data.iterator(); it.hasNext();) {
                    WeakPair pair = (WeakPair) it.next ();
                    if (pair == toClean) {
                        it.remove();
                        for (Iterator  resIt= ((Collection)pair.getValue()).iterator(); resIt.hasNext();) {
                            PathResourceImplementation resource = (PathResourceImplementation) resIt.next();
                            if (!MergedClassPathImplementation.this.cachedResources.remove (resource)) {
                                MergedClassPathImplementation.this.unresolvedRoots.remove (resource);
                                fire|=2;
                            }
                            else {
                                fire|=1;
                            }
                        }
                        break;
                    }
                }
            }
            if ((fire&1)==1) {
                firePropertyChange(PROP_RESOURCES);
            }
            if ((fire&2)==2) {
                firePropertyChange(PROP_UNRESOLVED_ROOTS);
            }
        }

        private class WeakPair extends WeakReference implements Runnable {

            private Object value;

            public WeakPair (Object key, Object value) {
                super (key, Utilities.activeReferenceQueue());
                this.value = value;
            }

            public Object getKey () {
                return get ();
            }

            public Object getValue () {
                return value;
            }

            public void run() {
                cleanUp(this);
            }
        }

    }
}
... 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.