# HG changeset patch # User Milos Kleint # Date 1210593472 -7200 # Node ID 0578533e5301f2956b7393bb746271fb23cd6a0a # Parent a5d6c99ca8019c7f72a75360740084f5780c84bf #134341 additional api factory method to create a LookupMerger for ClassPathProvider implementations. Make use of it in j2se project. diff -r a5d6c99ca801 -r 0578533e5301 java.j2seproject/src/org/netbeans/modules/java/j2seproject/ClassPathProviderMerger.java --- a/java.j2seproject/src/org/netbeans/modules/java/j2seproject/ClassPathProviderMerger.java Mon May 12 15:47:10 2008 +0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,82 +0,0 @@ -/* - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. - * - * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved. - * - * The contents of this file are subject to the terms of either the GNU - * General Public License Version 2 only ("GPL") or the Common - * Development and Distribution License("CDDL") (collectively, the - * "License"). You may not use this file except in compliance with the - * License. You can obtain a copy of the License at - * http://www.netbeans.org/cddl-gplv2.html - * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the - * specific language governing permissions and limitations under the - * License. When distributing the software, include this License Header - * Notice in each file and include the License file at - * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this - * particular file as subject to the "Classpath" exception as provided - * by Sun in the GPL Version 2 section of the License file that - * accompanied this code. If applicable, add the following below the - * License Header, with the fields enclosed by brackets [] replaced by - * your own identifying information: - * "Portions Copyrighted [year] [name of copyright owner]" - * - * Contributor(s): - * - * Portions Copyrighted 2007 Sun Microsystems, Inc. - */ -package org.netbeans.modules.java.j2seproject; - -import org.netbeans.api.java.classpath.ClassPath; -import org.netbeans.spi.java.classpath.ClassPathProvider; -import org.netbeans.spi.project.LookupMerger; -import org.openide.filesystems.FileObject; -import org.openide.util.Lookup; - -/** - * - * @author Tomas Zezula - */ -public final class ClassPathProviderMerger implements LookupMerger { - - private final ClassPathProvider defaultProvider; - - public ClassPathProviderMerger (final ClassPathProvider defaultProvider) { - assert defaultProvider != null; - this.defaultProvider = defaultProvider; - } - - public Class getMergeableClass() { - return ClassPathProvider.class; - } - - public ClassPathProvider merge(Lookup lookup) { - return new CPProvider (lookup); - } - - - private class CPProvider implements ClassPathProvider { - - private final Lookup lookup; - - public CPProvider(final Lookup lookup) { - assert lookup != null; - this.lookup = lookup; - } - - public ClassPath findClassPath(FileObject file, String type) { - ClassPath result = defaultProvider.findClassPath(file, type); - if (result != null) { - return result; - } - for (ClassPathProvider cpProvider : lookup.lookupAll(ClassPathProvider.class)) { - result = cpProvider.findClassPath(file, type); - if (result != null) { - return result; - } - } - return null; - } - } - -} diff -r a5d6c99ca801 -r 0578533e5301 java.j2seproject/src/org/netbeans/modules/java/j2seproject/J2SEProject.java --- a/java.j2seproject/src/org/netbeans/modules/java/j2seproject/J2SEProject.java Mon May 12 15:47:10 2008 +0400 +++ b/java.j2seproject/src/org/netbeans/modules/java/j2seproject/J2SEProject.java Mon May 12 13:57:52 2008 +0200 @@ -265,7 +265,7 @@ new J2SELogicalViewProvider(this, this.updateHelper, evaluator(), spp, refHelper), // new J2SECustomizerProvider(this, this.updateHelper, evaluator(), refHelper), new CustomizerProviderImpl(this, this.updateHelper, evaluator(), refHelper, this.genFilesHelper), - new ClassPathProviderMerger(cpProvider), + LookupMergerSupport.createClassPathProviderMerger(cpProvider), QuerySupport.createCompiledSourceForBinaryQuery(helper, evaluator(), getSourceRoots(), getTestSourceRoots()), QuerySupport.createJavadocForBinaryQuery(helper, evaluator()), new AntArtifactProviderImpl(), diff -r a5d6c99ca801 -r 0578533e5301 java.project/apichanges.xml --- a/java.project/apichanges.xml Mon May 12 15:47:10 2008 +0400 +++ b/java.project/apichanges.xml Mon May 12 13:57:52 2008 +0200 @@ -106,6 +106,24 @@ + + + + Create LookupMerger implementation for ClassPathProvider + + + + + +

+ LookupMergerSupport.createClassPathProviderMerger(ClassPathProvider) + can be used to allow composing the project's classpath from multiple sources (modules). +

+
+ + +
+ Support for adding/removing relative classpath entries diff -r a5d6c99ca801 -r 0578533e5301 java.project/manifest.mf --- a/java.project/manifest.mf Mon May 12 15:47:10 2008 +0400 +++ b/java.project/manifest.mf Mon May 12 13:57:52 2008 +0200 @@ -3,6 +3,6 @@ OpenIDE-Module-Layer: org/netbeans/modules/java/project/layer.xml OpenIDE-Module-Localizing-Bundle: org/netbeans/modules/java/project/Bundle.properties OpenIDE-Module-Needs: javax.script.ScriptEngine.freemarker -OpenIDE-Module-Specification-Version: 1.17 +OpenIDE-Module-Specification-Version: 1.18 AutoUpdate-Show-In-Client: false diff -r a5d6c99ca801 -r 0578533e5301 java.project/src/org/netbeans/spi/java/project/support/ClassPathProviderMerger.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/java.project/src/org/netbeans/spi/java/project/support/ClassPathProviderMerger.java Mon May 12 13:57:52 2008 +0200 @@ -0,0 +1,282 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common + * Development and Distribution License("CDDL") (collectively, the + * "License"). You may not use this file except in compliance with the + * License. You can obtain a copy of the License at + * http://www.netbeans.org/cddl-gplv2.html + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the + * specific language governing permissions and limitations under the + * License. When distributing the software, include this License Header + * Notice in each file and include the License file at + * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the GPL Version 2 section of the License file that + * accompanied this code. If applicable, add the following below the + * License Header, with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * If you wish your version of this file to be governed by only the CDDL + * or only the GPL Version 2, indicate your decision by adding + * "[Contributor] elects to include this software in this distribution + * under the [CDDL or GPL Version 2] license." If you do not indicate a + * single choice of license, a recipient has the option to distribute + * your version of this file under either the CDDL, the GPL Version 2 or + * to extend the choice of license to its licensees as provided above. + * However, if you add GPL Version 2 code and therefore, elected the GPL + * Version 2 license, then the option applies only if the new code is + * made subject to such option by the copyright holder. + * + * Contributor(s): + * + * Portions Copyrighted 2008 Sun Microsystems, Inc. + */ +package org.netbeans.spi.java.project.support; + +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; +import java.lang.reflect.Field; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import org.netbeans.api.java.classpath.ClassPath; +import org.netbeans.spi.java.classpath.ClassPathFactory; +import org.netbeans.spi.java.classpath.ClassPathImplementation; +import org.netbeans.spi.java.classpath.ClassPathProvider; +import org.netbeans.spi.java.classpath.PathResourceImplementation; +import org.netbeans.spi.project.LookupMerger; +import org.openide.filesystems.FileObject; +import org.openide.util.Exceptions; +import org.openide.util.Lookup; +import org.openide.util.LookupEvent; +import org.openide.util.LookupListener; +import org.openide.util.WeakListeners; + +/** + * Lookup Merger implementation for ClassPathProvider + * + * @author Tomas Zezula, Milos Kleint + */ +final class ClassPathProviderMerger implements LookupMerger { + + private final ClassPathProvider defaultProvider; + + ClassPathProviderMerger(final ClassPathProvider defaultProvider) { + assert defaultProvider != null; + this.defaultProvider = defaultProvider; + } + + public Class getMergeableClass() { + return ClassPathProvider.class; + } + + public ClassPathProvider merge(Lookup lookup) { + return new CPProvider(lookup); + } + + private class CPProvider implements ClassPathProvider { + + private final Lookup lookup; + private final Map> cpCache = new HashMap>(); + + public CPProvider(final Lookup lookup) { + assert lookup != null; + this.lookup = lookup; + } + + public ClassPath findClassPath(FileObject file, String type) { + synchronized (cpCache) { + Map cptype = cpCache.get(file); + if (cptype != null) { + ClassPath path = cptype.get(type); + if (path != null) { + return path; + } + } + } + ProxyClassPathImplementation result = new ProxyClassPathImplementation(defaultProvider, lookup, file, type); + ClassPath cp = ClassPathFactory.createClassPath(result); + synchronized (cpCache) { + Map cptype = cpCache.get(file); + if (cptype == null) { + cptype = new HashMap(); + cpCache.put(file, cptype); + } + cptype.put(type, cp); + } + return cp; + } + } + + /** ProxyClassPathImplementation provides read only proxy for ClassPathImplementations. + * The order of the resources is given by the order of its delegates. + * The proxy is designed to be used as a union of class paths. + * E.g. to be able to easily iterate or listen on all design resources = sources + compile resources + */ + public class ProxyClassPathImplementation implements ClassPathImplementation { + + private ClassPathImplementation[] classPaths; + private List resourcesCache; + private ArrayList listeners; + private LookupListener lookupList; + private Lookup.Result providers; + private ClassPathProvider mainProvider; + private PropertyChangeListener classPathsListener; + private FileObject file; + private String type; + + public ProxyClassPathImplementation(ClassPathProvider dominant, Lookup context, FileObject fo, String type) { + assert dominant != null; + this.type = type; + this.file = fo; + mainProvider = dominant; + providers = context.lookupResult(ClassPathProvider.class); + classPathsListener = new DelegatesListener(); + + checkProviders(); + lookupList = new LookupListener() { + public void resultChanged(LookupEvent ev) { + checkProviders(); + } + }; + providers.addLookupListener(lookupList); + + } + + private void checkProviders() { + List impls = new ArrayList(); + ClassPath mainResult = mainProvider.findClassPath(file, type); + if (mainResult != null) { + impls.add(getClassPathImplementation(mainResult)); + } + + for (ClassPathProvider prvd : providers.allInstances()) { + ClassPath path = prvd.findClassPath(file, type); + if (path != null) { + impls.add(getClassPathImplementation(path)); + } + } + for (ClassPathImplementation cpImpl : impls) { + if (cpImpl == null) { + continue; + } + cpImpl.addPropertyChangeListener(WeakListeners.propertyChange(classPathsListener, cpImpl)); + } + synchronized (this) { + if (classPaths != null) { + //TODO how to remove weak listeners + } + this.classPaths = impls.toArray(new ClassPathImplementation[impls.size()]); + } + PropertyChangeEvent ev = new PropertyChangeEvent(this, ClassPathImplementation.PROP_RESOURCES, null, null); + firePropertyChange(ev); + } + + public List getResources() { + synchronized (this) { + if (this.resourcesCache != null) { + return this.resourcesCache; + } + } + + ArrayList result = new ArrayList(classPaths.length * 10); + for (ClassPathImplementation cpImpl : classPaths) { + List subPath = cpImpl.getResources(); + assert subPath != null : "ClassPathImplementation.getResources() returned null. ClassPathImplementation.class: " + cpImpl.getClass().toString() + " ClassPathImplementation: " + cpImpl.toString(); + result.addAll(subPath); + } + + synchronized (this) { + if (this.resourcesCache == null) { + resourcesCache = Collections.unmodifiableList(result); + } + return this.resourcesCache; + } + } + + public synchronized void addPropertyChangeListener(PropertyChangeListener listener) { + if (this.listeners == null) { + this.listeners = new ArrayList(); + } + this.listeners.add(listener); + } + + public synchronized void removePropertyChangeListener(PropertyChangeListener listener) { + if (this.listeners == null) { + return; + } + this.listeners.remove(listener); + } + + @Override + public String toString() { + StringBuilder builder = new StringBuilder("["); //NOI18N + + for (ClassPathImplementation cpImpl : this.classPaths) { + builder.append(cpImpl.toString()); + builder.append(", "); //NOI18N + + } + builder.append("]"); //NOI18N + + return builder.toString(); + } + + private void firePropertyChange(PropertyChangeEvent event) { + PropertyChangeListener[] _listeners; + synchronized (this) { + resourcesCache = null; //Clean the cache + if (listeners == null) { + return; + } + _listeners = listeners.toArray(new PropertyChangeListener[ProxyClassPathImplementation.this.listeners.size()]); + } + for (PropertyChangeListener l : _listeners) { + l.propertyChange(event); + } + } + + private class DelegatesListener implements PropertyChangeListener { + + public void propertyChange(PropertyChangeEvent evt) { + PropertyChangeEvent event = new PropertyChangeEvent(ProxyClassPathImplementation.this, evt.getPropertyName(), null, null); + firePropertyChange(event); + } + } + } + + static Field implField; + static { + try { + implField = ClassPath.class.getDeclaredField("impl"); + implField.setAccessible(true); + } catch (Exception x) { + Exceptions.printStackTrace(x); + } + } + /** + * sort of hack. JavaSupport APIs are meant to stay project free (thus the LookupMerger cannot be placed there). + * But the Java project Support module has no access to the ClassPathAccessor.DEFAULT instance + * to be able to get the ClassPathImplementation out of the ClassPath instance. + * (as the ProxyClassPathImplemntation in Java Support API module does). + */ + static ClassPathImplementation getClassPathImplementation(ClassPath path) { + assert implField != null : "ClassPath.impl field is gone."; + Object toRet = null; + try { + toRet = implField.get(path); + } catch (IllegalArgumentException ex) { + Exceptions.printStackTrace(ex); + } catch (IllegalAccessException ex) { + Exceptions.printStackTrace(ex); + } + return (ClassPathImplementation)toRet; + } +} diff -r a5d6c99ca801 -r 0578533e5301 java.project/src/org/netbeans/spi/java/project/support/LookupMergerSupport.java --- a/java.project/src/org/netbeans/spi/java/project/support/LookupMergerSupport.java Mon May 12 15:47:10 2008 +0400 +++ b/java.project/src/org/netbeans/spi/java/project/support/LookupMergerSupport.java Mon May 12 13:57:52 2008 +0200 @@ -43,6 +43,7 @@ import java.util.Collection; import org.netbeans.api.java.queries.JavadocForBinaryQuery; import org.netbeans.api.java.queries.SourceForBinaryQuery; +import org.netbeans.spi.java.classpath.ClassPathProvider; import org.netbeans.spi.java.queries.JavadocForBinaryQueryImplementation; import org.netbeans.spi.java.queries.SourceForBinaryQueryImplementation; import org.netbeans.spi.java.queries.SourceForBinaryQueryImplementation2; @@ -77,6 +78,19 @@ public static LookupMerger createJFBLookupMerger() { return new JFBLookupMerger(); } + + /** + * Creates a LookupMerger for ClassPathProviders, allowing multiple instances of ClassPathProviders to reside + * in project's lookup. The merger makes sure the classpaths are merged together. + * When ClassPathProviders appear or disappear in project's lookup, the classpath is updated accordingly. + * @param defaultProvider the default project ClassPathProvider that will always be asked first for classpath. + * @return LookupMerger instance to be put in project's lookup. + * @since org.netbeans.modules.java.project 1.18 + * @see LookupMerger + */ + public static LookupMerger createClassPathProviderMerger(ClassPathProvider defaultProvider) { + return new ClassPathProviderMerger(defaultProvider); + } private static class SFBLookupMerger implements LookupMerger { diff -r a5d6c99ca801 -r 0578533e5301 java.project/test/unit/src/org/netbeans/spi/java/project/support/ClassPathProviderMergerTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/java.project/test/unit/src/org/netbeans/spi/java/project/support/ClassPathProviderMergerTest.java Mon May 12 13:57:52 2008 +0200 @@ -0,0 +1,179 @@ +/* + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. + * + * Copyright 2008 Sun Microsystems, Inc. All rights reserved. + * + * The contents of this file are subject to the terms of either the GNU + * General Public License Version 2 only ("GPL") or the Common + * Development and Distribution License("CDDL") (collectively, the + * "License"). You may not use this file except in compliance with the + * License. You can obtain a copy of the License at + * http://www.netbeans.org/cddl-gplv2.html + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the + * specific language governing permissions and limitations under the + * License. When distributing the software, include this License Header + * Notice in each file and include the License file at + * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this + * particular file as subject to the "Classpath" exception as provided + * by Sun in the GPL Version 2 section of the License file that + * accompanied this code. If applicable, add the following below the + * License Header, with the fields enclosed by brackets [] replaced by + * your own identifying information: + * "Portions Copyrighted [year] [name of copyright owner]" + * + * If you wish your version of this file to be governed by only the CDDL + * or only the GPL Version 2, indicate your decision by adding + * "[Contributor] elects to include this software in this distribution + * under the [CDDL or GPL Version 2] license." If you do not indicate a + * single choice of license, a recipient has the option to distribute + * your version of this file under either the CDDL, the GPL Version 2 or + * to extend the choice of license to its licensees as provided above. + * However, if you add GPL Version 2 code and therefore, elected the GPL + * Version 2 license, then the option applies only if the new code is + * made subject to such option by the copyright holder. + * + * Contributor(s): + * + * Portions Copyrighted 2008 Sun Microsystems, Inc. + */ + +package org.netbeans.spi.java.project.support; + +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; +import java.net.URL; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import org.netbeans.api.java.classpath.ClassPath; +import org.netbeans.junit.NbTestCase; +import org.netbeans.spi.java.classpath.ClassPathProvider; +import org.netbeans.spi.java.classpath.support.ClassPathSupport; +import org.openide.filesystems.FileObject; +import org.openide.util.Lookup; +import org.openide.util.lookup.AbstractLookup; +import org.openide.util.lookup.InstanceContent; + +/** + * + * @author mkleint + */ +public class ClassPathProviderMergerTest extends NbTestCase { + + public ClassPathProviderMergerTest(String testName) { + super(testName); + } + + @Override + protected void setUp() throws Exception { + super.setUp(); + } + + @Override + protected void tearDown() throws Exception { + super.tearDown(); + } + + /** + * Test of merge method, of class ClassPathProviderMerger. + */ + public void testMerge() { + System.out.println("merge"); + InstanceContent ic = new InstanceContent(); + Lookup lookup = new AbstractLookup(ic); + ProviderImpl defaultCP = new ProviderImpl(); + //for some weird reason the specific path doesn't work in this module. + // it worked fine in Java Support APIs module before moving here +// URL url = createURLReference("org/netbeans/modules/java/project/"); + URL url = createURLReference(""); + defaultCP.paths.put(ClassPath.COMPILE, ClassPathSupport.createClassPath(url)); + ClassPathProviderMerger instance = new ClassPathProviderMerger(defaultCP); + ClassPathProvider result = instance.merge(lookup); + ClassPath cp = result.findClassPath(null, ClassPath.BOOT); + assertNotNull(cp); + + ClassPath compile = result.findClassPath(null, ClassPath.COMPILE); + assertNotNull(compile); + FileObject[] fos = compile.getRoots(); + assertNotNull(fos); + assertEquals(1, fos.length); + + final List semaphor = new ArrayList(); + compile.addPropertyChangeListener(new PropertyChangeListener() { + + public void propertyChange(PropertyChangeEvent evt) { + semaphor.add(new Object()); + } + }); + + ProviderImpl additional = new ProviderImpl(); + //for some weird reason the specific path doesn't work in this module. + // it worked fine in Java Support APIs module before moving here +// additional.paths.put(ClassPath.COMPILE, ClassPathSupport.createClassPath(createURLReference("org/netbeans/spi/java/project/classpath/"))); +// additional.paths.put(ClassPath.BOOT, ClassPathSupport.createClassPath(createURLReference("org/netbeans/spi/java/project/support/"))); + additional.paths.put(ClassPath.COMPILE, ClassPathSupport.createClassPath(createURLReference(""))); + additional.paths.put(ClassPath.BOOT, ClassPathSupport.createClassPath(createURLReference(""))); + + ic.add(additional); + + fos = compile.getRoots(); + assertNotNull(fos); + assertEquals(2, fos.length); + assertEquals(2, semaphor.size()); // why 2 changes are fired? + + cp = result.findClassPath(null, ClassPath.COMPILE); + assertEquals(cp, compile); + + cp = result.findClassPath(null, ClassPath.BOOT); + assertNotNull(cp); + fos = cp.getRoots(); + assertNotNull(fos); + assertEquals(fos.length, 1); + + + ic.remove(additional); + + fos = compile.getRoots(); + assertNotNull(fos); + assertEquals(1, fos.length); + assertEquals(4, semaphor.size()); // why 2 changes are fired? + + + } + + public void testReflection() throws Exception { + ProviderImpl defaultCP = new ProviderImpl(); + URL url = createURLReference("org/netbeans/modules/java/project/"); + defaultCP.paths.put(ClassPath.COMPILE, ClassPathSupport.createClassPath(url)); + ClassPathProviderMerger instance = new ClassPathProviderMerger(defaultCP); + assertNotNull(instance.implField); + + InstanceContent ic = new InstanceContent(); + Lookup lookup = new AbstractLookup(ic); + ClassPathProvider prov = instance.merge(lookup); + ClassPath cp = prov.findClassPath(null, ClassPath.COMPILE); + assertNotNull(ClassPathProviderMerger.getClassPathImplementation(cp)); + + } + + + private static URL createURLReference(String path) { + URL url = ClassPathProviderMergerTest.class.getClassLoader().getResource(path); + + return url; + } + + private class ProviderImpl implements ClassPathProvider { + + public Map paths = new HashMap(); + + public ClassPath findClassPath(FileObject file, String type) { + return paths.get(type); + } + + } + + +} + # HG changeset patch # User Milos Kleint # Date 1210763129 -7200 # Node ID 76e11bd307c8f4d29b0027b53cb4dd7447a0002b # Parent 0578533e5301f2956b7393bb746271fb23cd6a0a #134341 minor changes to test diff -r 0578533e5301 -r 76e11bd307c8 java.project/test/unit/src/org/netbeans/spi/java/project/support/ClassPathProviderMergerTest.java --- a/java.project/test/unit/src/org/netbeans/spi/java/project/support/ClassPathProviderMergerTest.java Mon May 12 13:57:52 2008 +0200 +++ b/java.project/test/unit/src/org/netbeans/spi/java/project/support/ClassPathProviderMergerTest.java Wed May 14 13:05:29 2008 +0200 @@ -46,6 +46,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.concurrent.atomic.AtomicInteger; import org.netbeans.api.java.classpath.ClassPath; import org.netbeans.junit.NbTestCase; import org.netbeans.spi.java.classpath.ClassPathProvider; @@ -99,11 +100,11 @@ assertNotNull(fos); assertEquals(1, fos.length); - final List semaphor = new ArrayList(); + final AtomicInteger count = new AtomicInteger(); compile.addPropertyChangeListener(new PropertyChangeListener() { public void propertyChange(PropertyChangeEvent evt) { - semaphor.add(new Object()); + count.incrementAndGet(); } }); @@ -120,7 +121,7 @@ fos = compile.getRoots(); assertNotNull(fos); assertEquals(2, fos.length); - assertEquals(2, semaphor.size()); // why 2 changes are fired? + assertEquals(2, count.get()); // why 2 changes are fired? cp = result.findClassPath(null, ClassPath.COMPILE); assertEquals(cp, compile); @@ -137,7 +138,7 @@ fos = compile.getRoots(); assertNotNull(fos); assertEquals(1, fos.length); - assertEquals(4, semaphor.size()); // why 2 changes are fired? + assertEquals(4, count.get()); // why 2 changes are fired? } # HG changeset patch # User Milos Kleint # Date 1210764120 -7200 # Node ID 3453765319d96cca84d074e5f8af09ffa92b02cf # Parent 76e11bd307c8f4d29b0027b53cb4dd7447a0002b #134341 don't use weaklistener, the delegating provider has the same lifecycle as the originals diff -r 76e11bd307c8 -r 3453765319d9 java.project/src/org/netbeans/spi/java/project/support/ClassPathProviderMerger.java --- a/java.project/src/org/netbeans/spi/java/project/support/ClassPathProviderMerger.java Wed May 14 13:05:29 2008 +0200 +++ b/java.project/src/org/netbeans/spi/java/project/support/ClassPathProviderMerger.java Wed May 14 13:22:00 2008 +0200 @@ -163,15 +163,17 @@ impls.add(getClassPathImplementation(path)); } } - for (ClassPathImplementation cpImpl : impls) { - if (cpImpl == null) { - continue; - } - cpImpl.addPropertyChangeListener(WeakListeners.propertyChange(classPathsListener, cpImpl)); - } synchronized (this) { if (classPaths != null) { - //TODO how to remove weak listeners + for (ClassPathImplementation old : classPaths) { + old.removePropertyChangeListener(classPathsListener); + } + } + for (ClassPathImplementation cpImpl : impls) { + if (cpImpl == null) { + continue; + } + cpImpl.addPropertyChangeListener(classPathsListener); } this.classPaths = impls.toArray(new ClassPathImplementation[impls.size()]); } # HG changeset patch # User Milos Kleint # Date 1211185186 -7200 # Node ID 267cb054ef11003035967f0e92744a667c429d1a # Parent 3453765319d96cca84d074e5f8af09ffa92b02cf #134677 don't use reflection but add a FilteringPathResourceImplementation middleman. diff -r 3453765319d9 -r 267cb054ef11 java.project/src/org/netbeans/spi/java/project/support/ClassPathProviderMerger.java --- a/java.project/src/org/netbeans/spi/java/project/support/ClassPathProviderMerger.java Wed May 14 13:22:00 2008 +0200 +++ b/java.project/src/org/netbeans/spi/java/project/support/ClassPathProviderMerger.java Mon May 19 10:19:46 2008 +0200 @@ -40,7 +40,7 @@ import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; -import java.lang.reflect.Field; +import java.net.URL; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; @@ -50,14 +50,13 @@ import org.netbeans.spi.java.classpath.ClassPathFactory; import org.netbeans.spi.java.classpath.ClassPathImplementation; import org.netbeans.spi.java.classpath.ClassPathProvider; +import org.netbeans.spi.java.classpath.FilteringPathResourceImplementation; import org.netbeans.spi.java.classpath.PathResourceImplementation; import org.netbeans.spi.project.LookupMerger; import org.openide.filesystems.FileObject; -import org.openide.util.Exceptions; import org.openide.util.Lookup; import org.openide.util.LookupEvent; import org.openide.util.LookupListener; -import org.openide.util.WeakListeners; /** * Lookup Merger implementation for ClassPathProvider @@ -115,14 +114,14 @@ } } - /** ProxyClassPathImplementation provides read only proxy for ClassPathImplementations. + /** ProxyClassPathImplementation provides read only proxy for PathResourceImplementations based on original ClassPath items. * The order of the resources is given by the order of its delegates. * The proxy is designed to be used as a union of class paths. * E.g. to be able to easily iterate or listen on all design resources = sources + compile resources */ - public class ProxyClassPathImplementation implements ClassPathImplementation { + class ProxyClassPathImplementation implements ClassPathImplementation { - private ClassPathImplementation[] classPaths; + private PathResourceImplementation[] classPaths; private List resourcesCache; private ArrayList listeners; private LookupListener lookupList; @@ -151,7 +150,7 @@ } private void checkProviders() { - List impls = new ArrayList(); + List impls = new ArrayList(); ClassPath mainResult = mainProvider.findClassPath(file, type); if (mainResult != null) { impls.add(getClassPathImplementation(mainResult)); @@ -165,17 +164,17 @@ } synchronized (this) { if (classPaths != null) { - for (ClassPathImplementation old : classPaths) { + for (PathResourceImplementation old : classPaths) { old.removePropertyChangeListener(classPathsListener); } } - for (ClassPathImplementation cpImpl : impls) { + for (PathResourceImplementation cpImpl : impls) { if (cpImpl == null) { continue; } cpImpl.addPropertyChangeListener(classPathsListener); } - this.classPaths = impls.toArray(new ClassPathImplementation[impls.size()]); + this.classPaths = impls.toArray(new PathResourceImplementation[impls.size()]); } PropertyChangeEvent ev = new PropertyChangeEvent(this, ClassPathImplementation.PROP_RESOURCES, null, null); firePropertyChange(ev); @@ -189,10 +188,8 @@ } ArrayList result = new ArrayList(classPaths.length * 10); - for (ClassPathImplementation cpImpl : classPaths) { - List subPath = cpImpl.getResources(); - assert subPath != null : "ClassPathImplementation.getResources() returned null. ClassPathImplementation.class: " + cpImpl.getClass().toString() + " ClassPathImplementation: " + cpImpl.toString(); - result.addAll(subPath); + for (PathResourceImplementation cpImpl : classPaths) { + result.add(cpImpl); } synchronized (this) { @@ -221,7 +218,7 @@ public String toString() { StringBuilder builder = new StringBuilder("["); //NOI18N - for (ClassPathImplementation cpImpl : this.classPaths) { + for (PathResourceImplementation cpImpl : this.classPaths) { builder.append(cpImpl.toString()); builder.append(", "); //NOI18N @@ -254,31 +251,47 @@ } } - static Field implField; - static { - try { - implField = ClassPath.class.getDeclaredField("impl"); - implField.setAccessible(true); - } catch (Exception x) { - Exceptions.printStackTrace(x); + static FilteringPathResourceImplementation getClassPathImplementation(ClassPath path) { + return new ProxyFilteringCPI(path); + } + + private static class ProxyFilteringCPI implements FilteringPathResourceImplementation { + private ClassPath classpath; + + private ProxyFilteringCPI(ClassPath path) { + this.classpath = path; } + + + public boolean includes(URL root, String resource) { + for (ClassPath.Entry ent : classpath.entries()) { + if (ent.getURL().equals(root)) { + return ent.includes(resource); + } + } + return false; + } + + public URL[] getRoots() { + ArrayList urls = new ArrayList(); + for (ClassPath.Entry ent : classpath.entries()) { + urls.add(ent.getURL()); + } + return urls.toArray(new URL[urls.size()]); + } + + public ClassPathImplementation getContent() { + throw new UnsupportedOperationException("Not supported yet."); + } + + public void addPropertyChangeListener(PropertyChangeListener listener) { + classpath.addPropertyChangeListener(listener); + } + + public void removePropertyChangeListener(PropertyChangeListener listener) { + classpath.removePropertyChangeListener(listener); + } + } - /** - * sort of hack. JavaSupport APIs are meant to stay project free (thus the LookupMerger cannot be placed there). - * But the Java project Support module has no access to the ClassPathAccessor.DEFAULT instance - * to be able to get the ClassPathImplementation out of the ClassPath instance. - * (as the ProxyClassPathImplemntation in Java Support API module does). - */ - static ClassPathImplementation getClassPathImplementation(ClassPath path) { - assert implField != null : "ClassPath.impl field is gone."; - Object toRet = null; - try { - toRet = implField.get(path); - } catch (IllegalArgumentException ex) { - Exceptions.printStackTrace(ex); - } catch (IllegalAccessException ex) { - Exceptions.printStackTrace(ex); - } - return (ClassPathImplementation)toRet; - } + } diff -r 3453765319d9 -r 267cb054ef11 java.project/test/unit/src/org/netbeans/spi/java/project/support/ClassPathProviderMergerTest.java --- a/java.project/test/unit/src/org/netbeans/spi/java/project/support/ClassPathProviderMergerTest.java Wed May 14 13:22:00 2008 +0200 +++ b/java.project/test/unit/src/org/netbeans/spi/java/project/support/ClassPathProviderMergerTest.java Mon May 19 10:19:46 2008 +0200 @@ -143,21 +143,6 @@ } - public void testReflection() throws Exception { - ProviderImpl defaultCP = new ProviderImpl(); - URL url = createURLReference("org/netbeans/modules/java/project/"); - defaultCP.paths.put(ClassPath.COMPILE, ClassPathSupport.createClassPath(url)); - ClassPathProviderMerger instance = new ClassPathProviderMerger(defaultCP); - assertNotNull(instance.implField); - - InstanceContent ic = new InstanceContent(); - Lookup lookup = new AbstractLookup(ic); - ClassPathProvider prov = instance.merge(lookup); - ClassPath cp = prov.findClassPath(null, ClassPath.COMPILE); - assertNotNull(ClassPathProviderMerger.getClassPathImplementation(cp)); - - } - private static URL createURLReference(String path) { URL url = ClassPathProviderMergerTest.class.getClassLoader().getResource(path);