This Bugzilla instance is a read-only archive of historic NetBeans bug reports. To report a bug in NetBeans please follow the project's instructions for reporting issues.

View | Details | Raw Unified | Return to bug 134341
Collapse All | Expand All

(-)a/java.j2seproject/src/org/netbeans/modules/java/j2seproject/ClassPathProviderMerger.java (-82 lines)
Removed Link Here
1
/*
2
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
3
 *
4
 * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
5
 *
6
 * The contents of this file are subject to the terms of either the GNU
7
 * General Public License Version 2 only ("GPL") or the Common
8
 * Development and Distribution License("CDDL") (collectively, the
9
 * "License"). You may not use this file except in compliance with the
10
 * License. You can obtain a copy of the License at
11
 * http://www.netbeans.org/cddl-gplv2.html
12
 * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
13
 * specific language governing permissions and limitations under the
14
 * License.  When distributing the software, include this License Header
15
 * Notice in each file and include the License file at
16
 * nbbuild/licenses/CDDL-GPL-2-CP.  Sun designates this
17
 * particular file as subject to the "Classpath" exception as provided
18
 * by Sun in the GPL Version 2 section of the License file that
19
 * accompanied this code. If applicable, add the following below the
20
 * License Header, with the fields enclosed by brackets [] replaced by
21
 * your own identifying information:
22
 * "Portions Copyrighted [year] [name of copyright owner]"
23
 *
24
 * Contributor(s):
25
 *
26
 * Portions Copyrighted 2007 Sun Microsystems, Inc.
27
 */
28
package org.netbeans.modules.java.j2seproject;
29
30
import org.netbeans.api.java.classpath.ClassPath;
31
import org.netbeans.spi.java.classpath.ClassPathProvider;
32
import org.netbeans.spi.project.LookupMerger;
33
import org.openide.filesystems.FileObject;
34
import org.openide.util.Lookup;
35
36
/**
37
 *
38
 * @author Tomas Zezula
39
 */
40
public final class ClassPathProviderMerger implements LookupMerger<ClassPathProvider> {
41
    
42
    private final ClassPathProvider defaultProvider;
43
    
44
    public ClassPathProviderMerger (final ClassPathProvider defaultProvider) {
45
        assert defaultProvider != null;
46
        this.defaultProvider = defaultProvider;
47
    }
48
49
    public Class<ClassPathProvider> getMergeableClass() {
50
        return ClassPathProvider.class;
51
    }
52
53
    public ClassPathProvider merge(Lookup lookup) {
54
        return new CPProvider (lookup);
55
    }
56
    
57
    
58
    private class CPProvider implements ClassPathProvider {
59
        
60
        private final Lookup lookup;
61
62
        public CPProvider(final Lookup lookup) {
63
            assert lookup != null;
64
            this.lookup = lookup;
65
        }                
66
67
        public ClassPath findClassPath(FileObject file, String type) {
68
            ClassPath result = defaultProvider.findClassPath(file, type);
69
            if (result != null) {
70
                return result;
71
            }
72
            for (ClassPathProvider cpProvider : lookup.lookupAll(ClassPathProvider.class)) {
73
                result = cpProvider.findClassPath(file, type);
74
                if (result != null) {
75
                    return result;
76
                }
77
            }
78
            return null;
79
        }        
80
    }
81
82
}
(-)a/java.j2seproject/src/org/netbeans/modules/java/j2seproject/J2SEProject.java (-1 / +1 lines)
Lines 265-271 Link Here
265
            new J2SELogicalViewProvider(this, this.updateHelper, evaluator(), spp, refHelper),
265
            new J2SELogicalViewProvider(this, this.updateHelper, evaluator(), spp, refHelper),
266
            // new J2SECustomizerProvider(this, this.updateHelper, evaluator(), refHelper),
266
            // new J2SECustomizerProvider(this, this.updateHelper, evaluator(), refHelper),
267
            new CustomizerProviderImpl(this, this.updateHelper, evaluator(), refHelper, this.genFilesHelper),        
267
            new CustomizerProviderImpl(this, this.updateHelper, evaluator(), refHelper, this.genFilesHelper),        
268
            new ClassPathProviderMerger(cpProvider),
268
            LookupMergerSupport.createClassPathProviderMerger(cpProvider),
269
            QuerySupport.createCompiledSourceForBinaryQuery(helper, evaluator(), getSourceRoots(), getTestSourceRoots()),
269
            QuerySupport.createCompiledSourceForBinaryQuery(helper, evaluator(), getSourceRoots(), getTestSourceRoots()),
270
            QuerySupport.createJavadocForBinaryQuery(helper, evaluator()),
270
            QuerySupport.createJavadocForBinaryQuery(helper, evaluator()),
271
            new AntArtifactProviderImpl(),
271
            new AntArtifactProviderImpl(),
(-)a/java.project/apichanges.xml (+18 lines)
Lines 106-111 Link Here
106
    <!-- ACTUAL CHANGES BEGIN HERE: -->
106
    <!-- ACTUAL CHANGES BEGIN HERE: -->
107
107
108
    <changes>
108
    <changes>
109
        
110
        <change id="LookupMerger">
111
            <api name="classpath"/>
112
            <summary>Create LookupMerger implementation for ClassPathProvider</summary>
113
            <version major="1" minor="18"/>
114
            <date day="19" month="5" year="2008"/>
115
            <author login="mkleint"/>
116
            <compatibility addition="yes"/>
117
            <description>
118
                <p>
119
                    <code>LookupMergerSupport.createClassPathProviderMerger(ClassPathProvider)</code>
120
                    can be used to allow composing the project's classpath from multiple sources (modules).
121
                </p>
122
            </description>
123
            <class package="org.netbeans.spi.java.project.support" name="LookupMergerSupport"/>
124
            <issue number="134341"/>
125
        </change>
126
        
109
        <change id="use-uri-for-relative-paths">
127
        <change id="use-uri-for-relative-paths">
110
            <api name="general"/>
128
            <api name="general"/>
111
            <summary>Support for adding/removing relative classpath entries</summary>
129
            <summary>Support for adding/removing relative classpath entries</summary>
(-)a/java.project/manifest.mf (-1 / +1 lines)
Lines 3-8 Link Here
3
OpenIDE-Module-Layer: org/netbeans/modules/java/project/layer.xml
3
OpenIDE-Module-Layer: org/netbeans/modules/java/project/layer.xml
4
OpenIDE-Module-Localizing-Bundle: org/netbeans/modules/java/project/Bundle.properties
4
OpenIDE-Module-Localizing-Bundle: org/netbeans/modules/java/project/Bundle.properties
5
OpenIDE-Module-Needs: javax.script.ScriptEngine.freemarker
5
OpenIDE-Module-Needs: javax.script.ScriptEngine.freemarker
6
OpenIDE-Module-Specification-Version: 1.17
6
OpenIDE-Module-Specification-Version: 1.18
7
AutoUpdate-Show-In-Client: false
7
AutoUpdate-Show-In-Client: false
8
8
(-)a5d6c99ca801 (+282 lines)
Added Link Here
1
/*
2
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
3
 * 
4
 * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
5
 * 
6
 * The contents of this file are subject to the terms of either the GNU
7
 * General Public License Version 2 only ("GPL") or the Common
8
 * Development and Distribution License("CDDL") (collectively, the
9
 * "License"). You may not use this file except in compliance with the
10
 * License. You can obtain a copy of the License at
11
 * http://www.netbeans.org/cddl-gplv2.html
12
 * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
13
 * specific language governing permissions and limitations under the
14
 * License.  When distributing the software, include this License Header
15
 * Notice in each file and include the License file at
16
 * nbbuild/licenses/CDDL-GPL-2-CP.  Sun designates this
17
 * particular file as subject to the "Classpath" exception as provided
18
 * by Sun in the GPL Version 2 section of the License file that
19
 * accompanied this code. If applicable, add the following below the
20
 * License Header, with the fields enclosed by brackets [] replaced by
21
 * your own identifying information:
22
 * "Portions Copyrighted [year] [name of copyright owner]"
23
 * 
24
 * If you wish your version of this file to be governed by only the CDDL
25
 * or only the GPL Version 2, indicate your decision by adding
26
 * "[Contributor] elects to include this software in this distribution
27
 * under the [CDDL or GPL Version 2] license." If you do not indicate a
28
 * single choice of license, a recipient has the option to distribute
29
 * your version of this file under either the CDDL, the GPL Version 2 or
30
 * to extend the choice of license to its licensees as provided above.
31
 * However, if you add GPL Version 2 code and therefore, elected the GPL
32
 * Version 2 license, then the option applies only if the new code is
33
 * made subject to such option by the copyright holder.
34
 * 
35
 * Contributor(s):
36
 * 
37
 * Portions Copyrighted 2008 Sun Microsystems, Inc.
38
 */
39
package org.netbeans.spi.java.project.support;
40
41
import java.beans.PropertyChangeEvent;
42
import java.beans.PropertyChangeListener;
43
import java.lang.reflect.Field;
44
import java.util.ArrayList;
45
import java.util.Collections;
46
import java.util.HashMap;
47
import java.util.List;
48
import java.util.Map;
49
import org.netbeans.api.java.classpath.ClassPath;
50
import org.netbeans.spi.java.classpath.ClassPathFactory;
51
import org.netbeans.spi.java.classpath.ClassPathImplementation;
52
import org.netbeans.spi.java.classpath.ClassPathProvider;
53
import org.netbeans.spi.java.classpath.PathResourceImplementation;
54
import org.netbeans.spi.project.LookupMerger;
55
import org.openide.filesystems.FileObject;
56
import org.openide.util.Exceptions;
57
import org.openide.util.Lookup;
58
import org.openide.util.LookupEvent;
59
import org.openide.util.LookupListener;
60
import org.openide.util.WeakListeners;
61
62
/**
63
 * Lookup Merger implementation for ClassPathProvider
64
 * 
65
 * @author Tomas Zezula, Milos Kleint
66
 */
67
final class ClassPathProviderMerger implements LookupMerger<ClassPathProvider> {
68
69
    private final ClassPathProvider defaultProvider;
70
71
    ClassPathProviderMerger(final ClassPathProvider defaultProvider) {
72
        assert defaultProvider != null;
73
        this.defaultProvider = defaultProvider;
74
    }
75
76
    public Class<ClassPathProvider> getMergeableClass() {
77
        return ClassPathProvider.class;
78
    }
79
80
    public ClassPathProvider merge(Lookup lookup) {
81
        return new CPProvider(lookup);
82
    }
83
84
    private class CPProvider implements ClassPathProvider {
85
86
        private final Lookup lookup;
87
        private final Map<FileObject, Map<String, ClassPath>> cpCache = new HashMap<FileObject, Map<String, ClassPath>>();
88
89
        public CPProvider(final Lookup lookup) {
90
            assert lookup != null;
91
            this.lookup = lookup;
92
        }
93
94
        public ClassPath findClassPath(FileObject file, String type) {
95
            synchronized (cpCache) {
96
                Map<String, ClassPath> cptype = cpCache.get(file);
97
                if (cptype != null) {
98
                    ClassPath path = cptype.get(type);
99
                    if (path != null) {
100
                        return path;
101
                    }
102
                }
103
            }
104
            ProxyClassPathImplementation result = new ProxyClassPathImplementation(defaultProvider, lookup, file, type);
105
            ClassPath cp = ClassPathFactory.createClassPath(result);
106
            synchronized (cpCache) {
107
                Map<String, ClassPath> cptype = cpCache.get(file);
108
                if (cptype == null) {
109
                    cptype = new HashMap<String, ClassPath>();
110
                    cpCache.put(file, cptype);
111
                }
112
                cptype.put(type, cp);
113
            }
114
            return cp;
115
        }
116
    }
117
    
118
    /** ProxyClassPathImplementation provides read only proxy for ClassPathImplementations.
119
     *  The order of the resources is given by the order of its delegates.
120
     *  The proxy is designed to be used as a union of class paths.
121
     *  E.g. to be able to easily iterate or listen on all design resources = sources + compile resources
122
     */
123
    public class ProxyClassPathImplementation implements ClassPathImplementation {
124
125
        private ClassPathImplementation[] classPaths;
126
        private List<PathResourceImplementation> resourcesCache;
127
        private ArrayList<PropertyChangeListener> listeners;
128
        private LookupListener lookupList;
129
        private Lookup.Result<ClassPathProvider> providers;
130
        private ClassPathProvider mainProvider;
131
        private PropertyChangeListener classPathsListener;
132
        private FileObject file;
133
        private String type;
134
135
        public ProxyClassPathImplementation(ClassPathProvider dominant, Lookup context, FileObject fo, String type) {
136
            assert dominant != null;
137
            this.type = type;
138
            this.file = fo;
139
            mainProvider = dominant;
140
            providers = context.lookupResult(ClassPathProvider.class);
141
            classPathsListener = new DelegatesListener();
142
            
143
            checkProviders();
144
            lookupList = new LookupListener() {
145
                public void resultChanged(LookupEvent ev) {
146
                    checkProviders();
147
                }
148
            };
149
            providers.addLookupListener(lookupList);
150
            
151
        }
152
        
153
        private void checkProviders() {
154
            List<ClassPathImplementation> impls = new ArrayList<ClassPathImplementation>();
155
            ClassPath mainResult = mainProvider.findClassPath(file, type);
156
            if (mainResult != null) {
157
                impls.add(getClassPathImplementation(mainResult));
158
            }
159
            
160
            for (ClassPathProvider prvd : providers.allInstances()) {
161
                ClassPath path = prvd.findClassPath(file, type);
162
                if (path != null) {
163
                    impls.add(getClassPathImplementation(path));
164
                }
165
            }
166
            for (ClassPathImplementation cpImpl : impls) {
167
                if (cpImpl == null) {
168
                    continue;
169
                }
170
                cpImpl.addPropertyChangeListener(WeakListeners.propertyChange(classPathsListener, cpImpl));
171
            }
172
            synchronized (this) {
173
                if (classPaths != null) {
174
                    //TODO how to remove weak listeners
175
                }
176
                this.classPaths = impls.toArray(new ClassPathImplementation[impls.size()]);
177
            }
178
            PropertyChangeEvent ev = new PropertyChangeEvent(this, ClassPathImplementation.PROP_RESOURCES, null, null);
179
            firePropertyChange(ev);
180
        }
181
182
        public List<? extends PathResourceImplementation> getResources() {
183
            synchronized (this) {
184
                if (this.resourcesCache != null) {
185
                    return this.resourcesCache;
186
                }
187
            }
188
189
            ArrayList<PathResourceImplementation> result = new ArrayList<PathResourceImplementation>(classPaths.length * 10);
190
            for (ClassPathImplementation cpImpl : classPaths) {
191
                List<? extends PathResourceImplementation> subPath = cpImpl.getResources();
192
                assert subPath != null : "ClassPathImplementation.getResources() returned null. ClassPathImplementation.class: " + cpImpl.getClass().toString() + " ClassPathImplementation: " + cpImpl.toString();
193
                result.addAll(subPath);
194
            }
195
196
            synchronized (this) {
197
                if (this.resourcesCache == null) {
198
                    resourcesCache = Collections.unmodifiableList(result);
199
                }
200
                return this.resourcesCache;
201
            }
202
        }
203
204
        public synchronized void addPropertyChangeListener(PropertyChangeListener listener) {
205
            if (this.listeners == null) {
206
                this.listeners = new ArrayList<PropertyChangeListener>();
207
            }
208
            this.listeners.add(listener);
209
        }
210
211
        public synchronized void removePropertyChangeListener(PropertyChangeListener listener) {
212
            if (this.listeners == null) {
213
                return;
214
            }
215
            this.listeners.remove(listener);
216
        }
217
218
        @Override
219
        public String toString() {
220
            StringBuilder builder = new StringBuilder("[");   //NOI18N
221
222
            for (ClassPathImplementation cpImpl : this.classPaths) {
223
                builder.append(cpImpl.toString());
224
                builder.append(", ");   //NOI18N
225
226
            }
227
            builder.append("]");   //NOI18N
228
229
            return builder.toString();
230
        }
231
232
        private void firePropertyChange(PropertyChangeEvent event) {
233
                PropertyChangeListener[] _listeners;
234
                synchronized (this) {
235
                    resourcesCache = null;    //Clean the cache
236
                    if (listeners == null) {
237
                        return;
238
                    }
239
                    _listeners = listeners.toArray(new PropertyChangeListener[ProxyClassPathImplementation.this.listeners.size()]);
240
                }
241
                for (PropertyChangeListener l : _listeners) {
242
                    l.propertyChange(event);
243
                }
244
        }
245
246
        private class DelegatesListener implements PropertyChangeListener {
247
248
            public void propertyChange(PropertyChangeEvent evt) {
249
                PropertyChangeEvent event = new PropertyChangeEvent(ProxyClassPathImplementation.this, evt.getPropertyName(), null, null);
250
                firePropertyChange(event);
251
            }
252
        }
253
    }
254
    
255
    static Field implField;
256
    static {
257
        try {
258
            implField = ClassPath.class.getDeclaredField("impl");
259
            implField.setAccessible(true);
260
        } catch (Exception x) {
261
            Exceptions.printStackTrace(x);
262
        }
263
    }
264
    /**
265
     * sort of hack. JavaSupport APIs are meant to stay project free (thus the LookupMerger cannot be placed there).
266
     * But the Java project Support module has no access to the ClassPathAccessor.DEFAULT instance
267
     *  to be able to get the ClassPathImplementation out of the ClassPath instance. 
268
     * (as the ProxyClassPathImplemntation in Java Support API module does).
269
     */
270
    static ClassPathImplementation getClassPathImplementation(ClassPath path) {
271
        assert implField != null : "ClassPath.impl field is gone.";
272
        Object toRet = null;
273
        try {
274
            toRet = implField.get(path);
275
        } catch (IllegalArgumentException ex) {
276
            Exceptions.printStackTrace(ex);
277
        } catch (IllegalAccessException ex) {
278
            Exceptions.printStackTrace(ex);
279
        }
280
        return (ClassPathImplementation)toRet;
281
    }
282
}
(-)a/java.project/src/org/netbeans/spi/java/project/support/LookupMergerSupport.java (+14 lines)
Lines 43-48 Link Here
43
import java.util.Collection;
43
import java.util.Collection;
44
import org.netbeans.api.java.queries.JavadocForBinaryQuery;
44
import org.netbeans.api.java.queries.JavadocForBinaryQuery;
45
import org.netbeans.api.java.queries.SourceForBinaryQuery;
45
import org.netbeans.api.java.queries.SourceForBinaryQuery;
46
import org.netbeans.spi.java.classpath.ClassPathProvider;
46
import org.netbeans.spi.java.queries.JavadocForBinaryQueryImplementation;
47
import org.netbeans.spi.java.queries.JavadocForBinaryQueryImplementation;
47
import org.netbeans.spi.java.queries.SourceForBinaryQueryImplementation;
48
import org.netbeans.spi.java.queries.SourceForBinaryQueryImplementation;
48
import org.netbeans.spi.java.queries.SourceForBinaryQueryImplementation2;
49
import org.netbeans.spi.java.queries.SourceForBinaryQueryImplementation2;
Lines 77-82 Link Here
77
    public static LookupMerger<JavadocForBinaryQueryImplementation> createJFBLookupMerger() {
78
    public static LookupMerger<JavadocForBinaryQueryImplementation> createJFBLookupMerger() {
78
        return new JFBLookupMerger();
79
        return new JFBLookupMerger();
79
    }
80
    }
81
    
82
    /**
83
     * Creates a LookupMerger for ClassPathProviders, allowing multiple instances of ClassPathProviders to reside
84
     * in project's lookup. The merger makes sure the classpaths are merged together. 
85
     * When ClassPathProviders appear or disappear in project's lookup, the classpath is updated accordingly.
86
     * @param defaultProvider the default project ClassPathProvider that will always be asked first for classpath.
87
     * @return LookupMerger instance to be put in project's lookup.
88
     * @since org.netbeans.modules.java.project 1.18
89
     * @see LookupMerger
90
     */
91
    public static LookupMerger<ClassPathProvider> createClassPathProviderMerger(ClassPathProvider defaultProvider) {
92
        return new ClassPathProviderMerger(defaultProvider);
93
    }    
80
    
94
    
81
    private static class SFBLookupMerger implements LookupMerger<SourceForBinaryQueryImplementation> {
95
    private static class SFBLookupMerger implements LookupMerger<SourceForBinaryQueryImplementation> {
82
96
(-)a5d6c99ca801 (+179 lines)
Added Link Here
1
/*
2
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
3
 * 
4
 * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
5
 * 
6
 * The contents of this file are subject to the terms of either the GNU
7
 * General Public License Version 2 only ("GPL") or the Common
8
 * Development and Distribution License("CDDL") (collectively, the
9
 * "License"). You may not use this file except in compliance with the
10
 * License. You can obtain a copy of the License at
11
 * http://www.netbeans.org/cddl-gplv2.html
12
 * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
13
 * specific language governing permissions and limitations under the
14
 * License.  When distributing the software, include this License Header
15
 * Notice in each file and include the License file at
16
 * nbbuild/licenses/CDDL-GPL-2-CP.  Sun designates this
17
 * particular file as subject to the "Classpath" exception as provided
18
 * by Sun in the GPL Version 2 section of the License file that
19
 * accompanied this code. If applicable, add the following below the
20
 * License Header, with the fields enclosed by brackets [] replaced by
21
 * your own identifying information:
22
 * "Portions Copyrighted [year] [name of copyright owner]"
23
 * 
24
 * If you wish your version of this file to be governed by only the CDDL
25
 * or only the GPL Version 2, indicate your decision by adding
26
 * "[Contributor] elects to include this software in this distribution
27
 * under the [CDDL or GPL Version 2] license." If you do not indicate a
28
 * single choice of license, a recipient has the option to distribute
29
 * your version of this file under either the CDDL, the GPL Version 2 or
30
 * to extend the choice of license to its licensees as provided above.
31
 * However, if you add GPL Version 2 code and therefore, elected the GPL
32
 * Version 2 license, then the option applies only if the new code is
33
 * made subject to such option by the copyright holder.
34
 * 
35
 * Contributor(s):
36
 * 
37
 * Portions Copyrighted 2008 Sun Microsystems, Inc.
38
 */
39
40
package org.netbeans.spi.java.project.support;
41
42
import java.beans.PropertyChangeEvent;
43
import java.beans.PropertyChangeListener;
44
import java.net.URL;
45
import java.util.ArrayList;
46
import java.util.HashMap;
47
import java.util.List;
48
import java.util.Map;
49
import org.netbeans.api.java.classpath.ClassPath;
50
import org.netbeans.junit.NbTestCase;
51
import org.netbeans.spi.java.classpath.ClassPathProvider;
52
import org.netbeans.spi.java.classpath.support.ClassPathSupport;
53
import org.openide.filesystems.FileObject;
54
import org.openide.util.Lookup;
55
import org.openide.util.lookup.AbstractLookup;
56
import org.openide.util.lookup.InstanceContent;
57
58
/**
59
 *
60
 * @author mkleint
61
 */
62
public class ClassPathProviderMergerTest extends NbTestCase {
63
    
64
    public ClassPathProviderMergerTest(String testName) {
65
        super(testName);
66
    }            
67
68
    @Override
69
    protected void setUp() throws Exception {
70
        super.setUp();
71
    }
72
73
    @Override
74
    protected void tearDown() throws Exception {
75
        super.tearDown();
76
    }
77
78
   /**
79
     * Test of merge method, of class ClassPathProviderMerger.
80
     */
81
    public void testMerge() {
82
        System.out.println("merge");
83
        InstanceContent ic = new InstanceContent();
84
        Lookup lookup = new AbstractLookup(ic);
85
        ProviderImpl defaultCP = new ProviderImpl();
86
        //for some weird reason the specific path doesn't work in this module.
87
        // it worked fine in Java Support APIs module before moving here
88
//        URL url = createURLReference("org/netbeans/modules/java/project/");
89
        URL url = createURLReference("");
90
        defaultCP.paths.put(ClassPath.COMPILE, ClassPathSupport.createClassPath(url));
91
        ClassPathProviderMerger instance = new ClassPathProviderMerger(defaultCP);
92
        ClassPathProvider result = instance.merge(lookup);
93
        ClassPath cp = result.findClassPath(null, ClassPath.BOOT);
94
        assertNotNull(cp);
95
        
96
        ClassPath compile = result.findClassPath(null, ClassPath.COMPILE);
97
        assertNotNull(compile);
98
        FileObject[] fos = compile.getRoots();
99
        assertNotNull(fos);
100
        assertEquals(1, fos.length);
101
        
102
        final List semaphor = new ArrayList();
103
        compile.addPropertyChangeListener(new PropertyChangeListener() {
104
105
            public void propertyChange(PropertyChangeEvent evt) {
106
                semaphor.add(new Object());
107
            }
108
        });
109
        
110
        ProviderImpl additional = new ProviderImpl();
111
        //for some weird reason the specific path doesn't work in this module.
112
        // it worked fine in Java Support APIs module before moving here
113
//        additional.paths.put(ClassPath.COMPILE, ClassPathSupport.createClassPath(createURLReference("org/netbeans/spi/java/project/classpath/")));
114
//        additional.paths.put(ClassPath.BOOT, ClassPathSupport.createClassPath(createURLReference("org/netbeans/spi/java/project/support/")));
115
        additional.paths.put(ClassPath.COMPILE, ClassPathSupport.createClassPath(createURLReference("")));
116
        additional.paths.put(ClassPath.BOOT, ClassPathSupport.createClassPath(createURLReference("")));
117
        
118
        ic.add(additional);
119
        
120
        fos = compile.getRoots();
121
        assertNotNull(fos);
122
        assertEquals(2, fos.length);
123
        assertEquals(2, semaphor.size()); // why 2 changes are fired?
124
        
125
        cp = result.findClassPath(null, ClassPath.COMPILE);
126
        assertEquals(cp, compile);
127
        
128
        cp = result.findClassPath(null, ClassPath.BOOT);
129
        assertNotNull(cp);
130
        fos = cp.getRoots();
131
        assertNotNull(fos);
132
        assertEquals(fos.length, 1);
133
        
134
        
135
        ic.remove(additional);
136
        
137
        fos = compile.getRoots();
138
        assertNotNull(fos);
139
        assertEquals(1, fos.length);
140
        assertEquals(4, semaphor.size()); // why 2 changes are fired?
141
        
142
143
    }
144
    
145
    public void testReflection() throws Exception {
146
        ProviderImpl defaultCP = new ProviderImpl();
147
        URL url = createURLReference("org/netbeans/modules/java/project/");
148
        defaultCP.paths.put(ClassPath.COMPILE, ClassPathSupport.createClassPath(url));
149
        ClassPathProviderMerger instance = new ClassPathProviderMerger(defaultCP);
150
        assertNotNull(instance.implField);
151
        
152
        InstanceContent ic = new InstanceContent();
153
        Lookup lookup = new AbstractLookup(ic);
154
        ClassPathProvider prov = instance.merge(lookup);
155
        ClassPath cp = prov.findClassPath(null, ClassPath.COMPILE);
156
        assertNotNull(ClassPathProviderMerger.getClassPathImplementation(cp));
157
        
158
    }
159
    
160
    
161
    private static URL createURLReference(String path) {
162
        URL url = ClassPathProviderMergerTest.class.getClassLoader().getResource(path);
163
164
        return url;
165
    }
166
    
167
    private class ProviderImpl implements ClassPathProvider {
168
        
169
        public Map<String, ClassPath> paths = new HashMap<String, ClassPath>();
170
        
171
        public ClassPath findClassPath(FileObject file, String type) {
172
            return paths.get(type);
173
        }
174
        
175
    }
176
            
177
178
}
179
(-)a/java.project/test/unit/src/org/netbeans/spi/java/project/support/ClassPathProviderMergerTest.java (-4 / +5 lines)
Lines 46-51 Link Here
46
import java.util.HashMap;
46
import java.util.HashMap;
47
import java.util.List;
47
import java.util.List;
48
import java.util.Map;
48
import java.util.Map;
49
import java.util.concurrent.atomic.AtomicInteger;
49
import org.netbeans.api.java.classpath.ClassPath;
50
import org.netbeans.api.java.classpath.ClassPath;
50
import org.netbeans.junit.NbTestCase;
51
import org.netbeans.junit.NbTestCase;
51
import org.netbeans.spi.java.classpath.ClassPathProvider;
52
import org.netbeans.spi.java.classpath.ClassPathProvider;
Lines 99-109 Link Here
99
        assertNotNull(fos);
100
        assertNotNull(fos);
100
        assertEquals(1, fos.length);
101
        assertEquals(1, fos.length);
101
        
102
        
102
        final List semaphor = new ArrayList();
103
        final AtomicInteger count = new AtomicInteger();
103
        compile.addPropertyChangeListener(new PropertyChangeListener() {
104
        compile.addPropertyChangeListener(new PropertyChangeListener() {
104
105
105
            public void propertyChange(PropertyChangeEvent evt) {
106
            public void propertyChange(PropertyChangeEvent evt) {
106
                semaphor.add(new Object());
107
                count.incrementAndGet();
107
            }
108
            }
108
        });
109
        });
109
        
110
        
Lines 120-126 Link Here
120
        fos = compile.getRoots();
121
        fos = compile.getRoots();
121
        assertNotNull(fos);
122
        assertNotNull(fos);
122
        assertEquals(2, fos.length);
123
        assertEquals(2, fos.length);
123
        assertEquals(2, semaphor.size()); // why 2 changes are fired?
124
        assertEquals(2, count.get()); // why 2 changes are fired?
124
        
125
        
125
        cp = result.findClassPath(null, ClassPath.COMPILE);
126
        cp = result.findClassPath(null, ClassPath.COMPILE);
126
        assertEquals(cp, compile);
127
        assertEquals(cp, compile);
Lines 137-143 Link Here
137
        fos = compile.getRoots();
138
        fos = compile.getRoots();
138
        assertNotNull(fos);
139
        assertNotNull(fos);
139
        assertEquals(1, fos.length);
140
        assertEquals(1, fos.length);
140
        assertEquals(4, semaphor.size()); // why 2 changes are fired?
141
        assertEquals(4, count.get()); // why 2 changes are fired?
141
        
142
        
142
143
143
    }
144
    }
(-)a/java.project/src/org/netbeans/spi/java/project/support/ClassPathProviderMerger.java (-7 / +9 lines)
Lines 163-177 Link Here
163
                    impls.add(getClassPathImplementation(path));
163
                    impls.add(getClassPathImplementation(path));
164
                }
164
                }
165
            }
165
            }
166
            for (ClassPathImplementation cpImpl : impls) {
167
                if (cpImpl == null) {
168
                    continue;
169
                }
170
                cpImpl.addPropertyChangeListener(WeakListeners.propertyChange(classPathsListener, cpImpl));
171
            }
172
            synchronized (this) {
166
            synchronized (this) {
173
                if (classPaths != null) {
167
                if (classPaths != null) {
174
                    //TODO how to remove weak listeners
168
                    for (ClassPathImplementation old : classPaths) {
169
                        old.removePropertyChangeListener(classPathsListener);
170
                    }
171
                }
172
                for (ClassPathImplementation cpImpl : impls) {
173
                    if (cpImpl == null) {
174
                        continue;
175
                    }
176
                    cpImpl.addPropertyChangeListener(classPathsListener);
175
                }
177
                }
176
                this.classPaths = impls.toArray(new ClassPathImplementation[impls.size()]);
178
                this.classPaths = impls.toArray(new ClassPathImplementation[impls.size()]);
177
            }
179
            }
(-)a/java.project/src/org/netbeans/spi/java/project/support/ClassPathProviderMerger.java (-40 / +53 lines)
Lines 40-46 Link Here
40
40
41
import java.beans.PropertyChangeEvent;
41
import java.beans.PropertyChangeEvent;
42
import java.beans.PropertyChangeListener;
42
import java.beans.PropertyChangeListener;
43
import java.lang.reflect.Field;
43
import java.net.URL;
44
import java.util.ArrayList;
44
import java.util.ArrayList;
45
import java.util.Collections;
45
import java.util.Collections;
46
import java.util.HashMap;
46
import java.util.HashMap;
Lines 50-63 Link Here
50
import org.netbeans.spi.java.classpath.ClassPathFactory;
50
import org.netbeans.spi.java.classpath.ClassPathFactory;
51
import org.netbeans.spi.java.classpath.ClassPathImplementation;
51
import org.netbeans.spi.java.classpath.ClassPathImplementation;
52
import org.netbeans.spi.java.classpath.ClassPathProvider;
52
import org.netbeans.spi.java.classpath.ClassPathProvider;
53
import org.netbeans.spi.java.classpath.FilteringPathResourceImplementation;
53
import org.netbeans.spi.java.classpath.PathResourceImplementation;
54
import org.netbeans.spi.java.classpath.PathResourceImplementation;
54
import org.netbeans.spi.project.LookupMerger;
55
import org.netbeans.spi.project.LookupMerger;
55
import org.openide.filesystems.FileObject;
56
import org.openide.filesystems.FileObject;
56
import org.openide.util.Exceptions;
57
import org.openide.util.Lookup;
57
import org.openide.util.Lookup;
58
import org.openide.util.LookupEvent;
58
import org.openide.util.LookupEvent;
59
import org.openide.util.LookupListener;
59
import org.openide.util.LookupListener;
60
import org.openide.util.WeakListeners;
61
60
62
/**
61
/**
63
 * Lookup Merger implementation for ClassPathProvider
62
 * Lookup Merger implementation for ClassPathProvider
Lines 115-128 Link Here
115
        }
114
        }
116
    }
115
    }
117
    
116
    
118
    /** ProxyClassPathImplementation provides read only proxy for ClassPathImplementations.
117
    /** ProxyClassPathImplementation provides read only proxy for PathResourceImplementations based on original ClassPath items.
119
     *  The order of the resources is given by the order of its delegates.
118
     *  The order of the resources is given by the order of its delegates.
120
     *  The proxy is designed to be used as a union of class paths.
119
     *  The proxy is designed to be used as a union of class paths.
121
     *  E.g. to be able to easily iterate or listen on all design resources = sources + compile resources
120
     *  E.g. to be able to easily iterate or listen on all design resources = sources + compile resources
122
     */
121
     */
123
    public class ProxyClassPathImplementation implements ClassPathImplementation {
122
    class ProxyClassPathImplementation implements ClassPathImplementation {
124
123
125
        private ClassPathImplementation[] classPaths;
124
        private PathResourceImplementation[] classPaths;
126
        private List<PathResourceImplementation> resourcesCache;
125
        private List<PathResourceImplementation> resourcesCache;
127
        private ArrayList<PropertyChangeListener> listeners;
126
        private ArrayList<PropertyChangeListener> listeners;
128
        private LookupListener lookupList;
127
        private LookupListener lookupList;
Lines 151-157 Link Here
151
        }
150
        }
152
        
151
        
153
        private void checkProviders() {
152
        private void checkProviders() {
154
            List<ClassPathImplementation> impls = new ArrayList<ClassPathImplementation>();
153
            List<PathResourceImplementation> impls = new ArrayList<PathResourceImplementation>();
155
            ClassPath mainResult = mainProvider.findClassPath(file, type);
154
            ClassPath mainResult = mainProvider.findClassPath(file, type);
156
            if (mainResult != null) {
155
            if (mainResult != null) {
157
                impls.add(getClassPathImplementation(mainResult));
156
                impls.add(getClassPathImplementation(mainResult));
Lines 165-181 Link Here
165
            }
164
            }
166
            synchronized (this) {
165
            synchronized (this) {
167
                if (classPaths != null) {
166
                if (classPaths != null) {
168
                    for (ClassPathImplementation old : classPaths) {
167
                    for (PathResourceImplementation old : classPaths) {
169
                        old.removePropertyChangeListener(classPathsListener);
168
                        old.removePropertyChangeListener(classPathsListener);
170
                    }
169
                    }
171
                }
170
                }
172
                for (ClassPathImplementation cpImpl : impls) {
171
                for (PathResourceImplementation cpImpl : impls) {
173
                    if (cpImpl == null) {
172
                    if (cpImpl == null) {
174
                        continue;
173
                        continue;
175
                    }
174
                    }
176
                    cpImpl.addPropertyChangeListener(classPathsListener);
175
                    cpImpl.addPropertyChangeListener(classPathsListener);
177
                }
176
                }
178
                this.classPaths = impls.toArray(new ClassPathImplementation[impls.size()]);
177
                this.classPaths = impls.toArray(new PathResourceImplementation[impls.size()]);
179
            }
178
            }
180
            PropertyChangeEvent ev = new PropertyChangeEvent(this, ClassPathImplementation.PROP_RESOURCES, null, null);
179
            PropertyChangeEvent ev = new PropertyChangeEvent(this, ClassPathImplementation.PROP_RESOURCES, null, null);
181
            firePropertyChange(ev);
180
            firePropertyChange(ev);
Lines 189-198 Link Here
189
            }
188
            }
190
189
191
            ArrayList<PathResourceImplementation> result = new ArrayList<PathResourceImplementation>(classPaths.length * 10);
190
            ArrayList<PathResourceImplementation> result = new ArrayList<PathResourceImplementation>(classPaths.length * 10);
192
            for (ClassPathImplementation cpImpl : classPaths) {
191
            for (PathResourceImplementation cpImpl : classPaths) {
193
                List<? extends PathResourceImplementation> subPath = cpImpl.getResources();
192
                result.add(cpImpl);
194
                assert subPath != null : "ClassPathImplementation.getResources() returned null. ClassPathImplementation.class: " + cpImpl.getClass().toString() + " ClassPathImplementation: " + cpImpl.toString();
195
                result.addAll(subPath);
196
            }
193
            }
197
194
198
            synchronized (this) {
195
            synchronized (this) {
Lines 221-227 Link Here
221
        public String toString() {
218
        public String toString() {
222
            StringBuilder builder = new StringBuilder("[");   //NOI18N
219
            StringBuilder builder = new StringBuilder("[");   //NOI18N
223
220
224
            for (ClassPathImplementation cpImpl : this.classPaths) {
221
            for (PathResourceImplementation cpImpl : this.classPaths) {
225
                builder.append(cpImpl.toString());
222
                builder.append(cpImpl.toString());
226
                builder.append(", ");   //NOI18N
223
                builder.append(", ");   //NOI18N
227
224
Lines 254-284 Link Here
254
        }
251
        }
255
    }
252
    }
256
    
253
    
257
    static Field implField;
254
    static FilteringPathResourceImplementation getClassPathImplementation(ClassPath path) {
258
    static {
255
        return new ProxyFilteringCPI(path);
259
        try {
256
    }
260
            implField = ClassPath.class.getDeclaredField("impl");
257
    
261
            implField.setAccessible(true);
258
    private static class ProxyFilteringCPI implements FilteringPathResourceImplementation {
262
        } catch (Exception x) {
259
        private ClassPath classpath;
263
            Exceptions.printStackTrace(x);
260
261
        private ProxyFilteringCPI(ClassPath path) {
262
            this.classpath = path;
264
        }
263
        }
264
265
        
266
        public boolean includes(URL root, String resource) {
267
            for (ClassPath.Entry ent : classpath.entries()) {
268
                if (ent.getURL().equals(root)) {
269
                    return ent.includes(resource);
270
                }
271
            }
272
            return false;
273
        }
274
275
        public URL[] getRoots() {
276
            ArrayList<URL> urls = new ArrayList<URL>();
277
            for (ClassPath.Entry ent : classpath.entries()) {
278
                urls.add(ent.getURL());
279
            }
280
            return urls.toArray(new URL[urls.size()]);
281
        }
282
283
        public ClassPathImplementation getContent() {
284
            throw new UnsupportedOperationException("Not supported yet.");
285
        }
286
287
        public void addPropertyChangeListener(PropertyChangeListener listener) {
288
            classpath.addPropertyChangeListener(listener);
289
        }
290
291
        public void removePropertyChangeListener(PropertyChangeListener listener) {
292
            classpath.removePropertyChangeListener(listener);
293
        }
294
        
265
    }
295
    }
266
    /**
296
            
267
     * sort of hack. JavaSupport APIs are meant to stay project free (thus the LookupMerger cannot be placed there).
268
     * But the Java project Support module has no access to the ClassPathAccessor.DEFAULT instance
269
     *  to be able to get the ClassPathImplementation out of the ClassPath instance. 
270
     * (as the ProxyClassPathImplemntation in Java Support API module does).
271
     */
272
    static ClassPathImplementation getClassPathImplementation(ClassPath path) {
273
        assert implField != null : "ClassPath.impl field is gone.";
274
        Object toRet = null;
275
        try {
276
            toRet = implField.get(path);
277
        } catch (IllegalArgumentException ex) {
278
            Exceptions.printStackTrace(ex);
279
        } catch (IllegalAccessException ex) {
280
            Exceptions.printStackTrace(ex);
281
        }
282
        return (ClassPathImplementation)toRet;
283
    }
284
}
297
}
(-)a/java.project/test/unit/src/org/netbeans/spi/java/project/support/ClassPathProviderMergerTest.java (-15 lines)
Lines 143-163 Link Here
143
143
144
    }
144
    }
145
    
145
    
146
    public void testReflection() throws Exception {
147
        ProviderImpl defaultCP = new ProviderImpl();
148
        URL url = createURLReference("org/netbeans/modules/java/project/");
149
        defaultCP.paths.put(ClassPath.COMPILE, ClassPathSupport.createClassPath(url));
150
        ClassPathProviderMerger instance = new ClassPathProviderMerger(defaultCP);
151
        assertNotNull(instance.implField);
152
        
153
        InstanceContent ic = new InstanceContent();
154
        Lookup lookup = new AbstractLookup(ic);
155
        ClassPathProvider prov = instance.merge(lookup);
156
        ClassPath cp = prov.findClassPath(null, ClassPath.COMPILE);
157
        assertNotNull(ClassPathProviderMerger.getClassPathImplementation(cp));
158
        
159
    }
160
    
161
    
146
    
162
    private static URL createURLReference(String path) {
147
    private static URL createURLReference(String path) {
163
        URL url = ClassPathProviderMergerTest.class.getClassLoader().getResource(path);
148
        URL url = ClassPathProviderMergerTest.class.getClassLoader().getResource(path);

Return to bug 134341