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 150194
Collapse All | Expand All

(-)a/openide.util/src/org/openide/util/lookup/ExcludingLookup.java (+4 lines)
Lines 47-52 Link Here
47
47
48
import java.util.*;
48
import java.util.*;
49
import org.openide.util.LookupEvent;
49
import org.openide.util.LookupEvent;
50
import org.openide.util.Parameters;
50
51
51
52
52
/** Allows exclusion of certain instances from lookup.
53
/** Allows exclusion of certain instances from lookup.
Lines 67-72 Link Here
67
    ExcludingLookup(Lookup delegate, Class[] classes) {
68
    ExcludingLookup(Lookup delegate, Class[] classes) {
68
        this.delegate = delegate;
69
        this.delegate = delegate;
69
70
71
        for (Class c : classes) {
72
            Parameters.notNull("classes[x]", c);
73
        }
70
        if (classes.length == 1) {
74
        if (classes.length == 1) {
71
            this.classes = classes[0];
75
            this.classes = classes[0];
72
        } else {
76
        } else {
(-)a/projectapi/src/org/netbeans/modules/projectapi/LazyLookupProviders.java (+137 lines)
Line 0 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.modules.projectapi;
41
42
import java.lang.reflect.Constructor;
43
import java.util.ArrayList;
44
import java.util.List;
45
import java.util.Map;
46
import org.netbeans.api.project.Project;
47
import org.netbeans.spi.project.LookupMerger;
48
import org.netbeans.spi.project.LookupProvider;
49
import org.netbeans.spi.project.ProjectServiceProvider;
50
import org.openide.util.Exceptions;
51
import org.openide.util.Lookup;
52
import org.openide.util.Lookup.Template;
53
import org.openide.util.lookup.Lookups;
54
import org.openide.util.lookup.ProxyLookup;
55
56
/**
57
 * Factory methods for lazy {@link LookupProvider} registration.
58
 */
59
public class LazyLookupProviders {
60
61
    private LazyLookupProviders() {}
62
63
    /**
64
     * @see ProjectServiceProvider
65
     */
66
    public static LookupProvider forProjectServiceProvider(Map<String,Object> attrs) throws ClassNotFoundException {
67
        final String serviceName = (String) attrs.get("service");
68
        final String implName = (String) attrs.get("class");
69
        return new LookupProvider() {
70
            public Lookup createAdditionalLookup(final Lookup lkp) {
71
                return new ProxyLookup() {
72
                    Class<?> service;
73
                    protected @Override void beforeLookup(Template<?> template) {
74
                        if (service == null && template.getType().getName().equals(serviceName)) {
75
                            service = template.getType();
76
                            try {
77
                                Class<?> impl = Thread.currentThread().getContextClassLoader().loadClass(implName);
78
                                CONSTRUCTOR: for (Constructor c : impl.getConstructors()) {
79
                                    Class[] params = c.getParameterTypes();
80
                                    if (params.length > 2) {
81
                                        continue;
82
                                    }
83
                                    List<Object> values = new ArrayList<Object>();
84
                                    for (Class param : params) {
85
                                        if (param == Lookup.class) {
86
                                            values.add(lkp);
87
                                        } else if (param == Project.class) {
88
                                            values.add(lkp.lookup(Project.class));
89
                                        } else {
90
                                            continue CONSTRUCTOR;
91
                                        }
92
                                    }
93
                                    Object instance = c.newInstance(values.toArray());
94
                                    service.cast(instance);
95
                                    setLookups(Lookups.singleton(instance));
96
                                }
97
                            } catch (Exception x) {
98
                                Exceptions.printStackTrace(x);
99
                            }
100
                        }
101
                    }
102
                };
103
            }
104
        };
105
    }
106
107
    /**
108
     * @see org.netbeans.spi.project.LookupMerger.Registration
109
     */
110
    public static MetaLookupMerger forLookupMerger(Map<String,Object> attrs) throws ClassNotFoundException {
111
        final String serviceName = (String) attrs.get("service");
112
        final String implName = (String) attrs.get("class");
113
        return new MetaLookupMerger() {
114
            private LookupMerger<?> delegate;
115
            public boolean canNowMerge(Class<?> service) {
116
                if (delegate == null && service.getName().equals(serviceName)) {
117
                    try {
118
                        Class<?> impl = Thread.currentThread().getContextClassLoader().loadClass(implName);
119
                        LookupMerger<?> m = (LookupMerger<?>) impl.newInstance();
120
                        if (service != m.getMergeableClass()) {
121
                            throw new ClassCastException(service + " vs. " + m.getMergeableClass());
122
                        }
123
                        delegate = m;
124
                        return true;
125
                    } catch (Exception x) {
126
                        Exceptions.printStackTrace(x);
127
                    }
128
                }
129
                return false;
130
            }
131
            public LookupMerger merger() {
132
                return delegate;
133
            }
134
        };
135
    }
136
137
}
(-)a/projectapi/src/org/netbeans/modules/projectapi/LookupProviderAnnotationProcessor.java (-1 / +101 lines)
Lines 39-44 Link Here
39
39
40
package org.netbeans.modules.projectapi;
40
package org.netbeans.modules.projectapi;
41
41
42
import java.util.List;
42
import java.util.Set;
43
import java.util.Set;
43
import javax.annotation.processing.Processor;
44
import javax.annotation.processing.Processor;
44
import javax.annotation.processing.RoundEnvironment;
45
import javax.annotation.processing.RoundEnvironment;
Lines 46-55 Link Here
46
import javax.annotation.processing.SupportedSourceVersion;
47
import javax.annotation.processing.SupportedSourceVersion;
47
import javax.lang.model.SourceVersion;
48
import javax.lang.model.SourceVersion;
48
import javax.lang.model.element.Element;
49
import javax.lang.model.element.Element;
50
import javax.lang.model.element.ExecutableElement;
51
import javax.lang.model.element.Modifier;
49
import javax.lang.model.element.TypeElement;
52
import javax.lang.model.element.TypeElement;
53
import javax.lang.model.element.VariableElement;
54
import javax.lang.model.type.DeclaredType;
55
import javax.lang.model.type.MirroredTypeException;
56
import javax.lang.model.type.TypeMirror;
57
import javax.lang.model.util.ElementFilter;
58
import org.netbeans.api.project.Project;
59
import org.netbeans.spi.project.LookupMerger;
50
import org.netbeans.spi.project.LookupProvider;
60
import org.netbeans.spi.project.LookupProvider;
61
import org.netbeans.spi.project.ProjectServiceProvider;
51
import org.openide.filesystems.annotations.LayerGeneratingProcessor;
62
import org.openide.filesystems.annotations.LayerGeneratingProcessor;
52
import org.openide.filesystems.annotations.LayerGenerationException;
63
import org.openide.filesystems.annotations.LayerGenerationException;
64
import org.openide.util.Lookup;
53
import org.openide.util.lookup.ServiceProvider;
65
import org.openide.util.lookup.ServiceProvider;
54
66
55
/**
67
/**
Lines 58-64 Link Here
58
 */
70
 */
59
@ServiceProvider(service=Processor.class)
71
@ServiceProvider(service=Processor.class)
60
@SupportedSourceVersion(SourceVersion.RELEASE_6)
72
@SupportedSourceVersion(SourceVersion.RELEASE_6)
61
@SupportedAnnotationTypes("org.netbeans.spi.project.LookupProvider.Registration")
73
@SupportedAnnotationTypes({
74
    "org.netbeans.spi.project.LookupProvider.Registration",
75
    "org.netbeans.spi.project.ProjectServiceProvider",
76
    "org.netbeans.spi.project.LookupMerger.Registration"
77
})
62
public class LookupProviderAnnotationProcessor extends LayerGeneratingProcessor {
78
public class LookupProviderAnnotationProcessor extends LayerGeneratingProcessor {
63
79
64
    @Override
80
    @Override
Lines 72-78 Link Here
72
                layer(e).instanceFile("Projects/" + type + "/Lookup", null, LookupProvider.class).write();
88
                layer(e).instanceFile("Projects/" + type + "/Lookup", null, LookupProvider.class).write();
73
            }
89
            }
74
        }
90
        }
91
        for (Element e : roundEnv.getElementsAnnotatedWith(ProjectServiceProvider.class)) {
92
            ProjectServiceProvider psp = e.getAnnotation(ProjectServiceProvider.class);
93
            TypeElement clazz = (TypeElement) e;
94
            TypeMirror service;
95
            try {
96
                psp.service();
97
                assert false;
98
                continue;
99
            } catch (MirroredTypeException x) {
100
                service = x.getTypeMirror();
101
            }
102
            if (!processingEnv.getTypeUtils().isAssignable(clazz.asType(), service)) {
103
                throw new LayerGenerationException("Not assignable to " + service, e);
104
            }
105
            int constructorCount = 0;
106
            CONSTRUCTOR: for (ExecutableElement constructor : ElementFilter.constructorsIn(clazz.getEnclosedElements())) {
107
                if (!constructor.getModifiers().contains(Modifier.PUBLIC)) {
108
                    continue;
109
                }
110
                List<? extends VariableElement> params = constructor.getParameters();
111
                if (params.size() > 2) {
112
                    continue;
113
                }
114
                for (VariableElement param : params) {
115
                    if (!param.asType().equals(processingEnv.getElementUtils().getTypeElement(Project.class.getCanonicalName()).asType()) &&
116
                            !param.asType().equals(processingEnv.getElementUtils().getTypeElement(Lookup.class.getCanonicalName()).asType())) {
117
                        continue CONSTRUCTOR;
118
                    }
119
                }
120
                constructorCount++;
121
            }
122
            if (constructorCount != 1) {
123
                throw new LayerGenerationException("Must have exactly one public constructor optionally taking Project and/or Lookup", e);
124
            }
125
            String binName = processingEnv.getElementUtils().getBinaryName(clazz).toString();
126
            String serviceBinName = processingEnv.getElementUtils().getBinaryName((TypeElement) processingEnv.getTypeUtils().asElement(service)).toString();
127
            if (serviceBinName.equals(LookupMerger.class.getName())) {
128
                throw new LayerGenerationException("@ProjectServiceProvider should not be used on LookupMerger; use @LookupMerger.Registration instead", e);
129
            }
130
            for (String type : psp.projectType()) {
131
                layer(e).file("Projects/" + type + "/Lookup/" + binName.replace('.', '-') + ".instance").
132
                        methodvalue("instanceCreate", LazyLookupProviders.class.getName(), "forProjectServiceProvider").
133
                        stringvalue("class", binName).
134
                        stringvalue("service", serviceBinName).
135
                        write();
136
            }
137
        }
138
        for (Element e : roundEnv.getElementsAnnotatedWith(LookupMerger.Registration.class)) {
139
            LookupMerger.Registration lmr = e.getAnnotation(LookupMerger.Registration.class);
140
            TypeElement clazz = (TypeElement) e;
141
            String binName = processingEnv.getElementUtils().getBinaryName(clazz).toString();
142
            DeclaredType service = findLookupMergerType((DeclaredType) clazz.asType());
143
            if (service == null) {
144
                throw new LayerGenerationException("Not assignable to LookupMerger<T> for some T", e);
145
            }
146
            String serviceBinName = processingEnv.getElementUtils().getBinaryName((TypeElement) service.asElement()).toString();
147
            for (String type : lmr.projectType()) {
148
                layer(e).file("Projects/" + type + "/Lookup/" + binName.replace('.', '-') + ".instance").
149
                        methodvalue("instanceCreate", LazyLookupProviders.class.getName(), "forLookupMerger").
150
                        // XXX if supporting also factory methods, could use instanceAttribute here so that attr value is actually a LookupMerger
151
                        stringvalue("class", binName).
152
                        stringvalue("service", serviceBinName).
153
                        write();
154
            }
155
        }
75
        return true;
156
        return true;
76
    }
157
    }
77
158
159
    private DeclaredType findLookupMergerType(DeclaredType t) {
160
        String rawName = processingEnv.getTypeUtils().erasure(t).toString();
161
        if (rawName.equals(LookupMerger.class.getName())) {
162
            List<? extends TypeMirror> args = t.getTypeArguments();
163
            if (args.size() == 1) {
164
                return (DeclaredType) args.get(0);
165
            } else {
166
                return null;
167
            }
168
        }
169
        for (TypeMirror supe : processingEnv.getTypeUtils().directSupertypes(t)) {
170
            DeclaredType result = findLookupMergerType((DeclaredType) supe);
171
            if (result != null) {
172
                return result;
173
            }
174
        }
175
        return null;
176
    }
177
78
}
178
}
(-)a/projectapi/src/org/netbeans/modules/projectapi/MetaLookupMerger.java (+54 lines)
Line 0 Link Here
1
/*
2
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
3
 *
4
 * Copyright 2009 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 2009 Sun Microsystems, Inc.
38
 */
39
40
package org.netbeans.modules.projectapi;
41
42
import org.netbeans.spi.project.LookupMerger;
43
import org.openide.util.Lookup;
44
45
/**
46
 * @see LazyLookupProviders#forLookupMerger
47
 */
48
public interface MetaLookupMerger {
49
50
    boolean canNowMerge(Class<?> service);
51
52
    LookupMerger/*|null*/ merger();
53
54
}
(-)a/projectapi/src/org/netbeans/spi/project/LookupMerger.java (+20 lines)
Lines 41-46 Link Here
41
41
42
package org.netbeans.spi.project;
42
package org.netbeans.spi.project;
43
43
44
import java.lang.annotation.ElementType;
45
import java.lang.annotation.Retention;
46
import java.lang.annotation.RetentionPolicy;
47
import java.lang.annotation.Target;
44
import org.openide.util.Lookup;
48
import org.openide.util.Lookup;
45
49
46
/**
50
/**
Lines 72-75 Link Here
72
     */
76
     */
73
    T merge(Lookup lookup);
77
    T merge(Lookup lookup);
74
78
79
    /**
80
     * Registers a lookup merger for some project types.
81
     * The annotated class must be assignable to {@link LookupMerger} with a type parameter.
82
     * @since XXX
83
     */
84
    @Retention(RetentionPolicy.SOURCE)
85
    @Target(ElementType.TYPE) // XXX support static factory methods too
86
    @interface Registration {
87
88
        /**
89
         * Token(s) denoting one or more project types, e.g. {@code "org-netbeans-modules-java-j2seproject"}
90
         */
91
        String[] projectType();
92
93
    }
94
75
}
95
}
(-)a/projectapi/src/org/netbeans/spi/project/LookupProvider.java (-1 / +4 lines)
Lines 66-72 Link Here
66
    Lookup createAdditionalLookup(Lookup baseContext);
66
    Lookup createAdditionalLookup(Lookup baseContext);
67
67
68
    /**
68
    /**
69
     * annotation to register LookupProvider instances.
69
     * Annotation to register {@link LookupProvider} instances.
70
     * <p>If you wish to unconditionally register one or more objects,
71
     * it will be more efficient and may be easier to use
72
     * {@link ProjectServiceProvider} (and/or {@link LookupMerger.Registration}).
70
     * @since org.netbeans.modules.projectapi 1.21
73
     * @since org.netbeans.modules.projectapi 1.21
71
     */
74
     */
72
    public @interface Registration {
75
    public @interface Registration {
(-)a/projectapi/src/org/netbeans/spi/project/ProjectServiceProvider.java (+69 lines)
Line 0 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.project;
41
42
import java.lang.annotation.ElementType;
43
import java.lang.annotation.Retention;
44
import java.lang.annotation.RetentionPolicy;
45
import java.lang.annotation.Target;
46
import org.netbeans.api.project.Project;
47
import org.openide.util.Lookup;
48
49
/**
50
 * Like {@link LookupProvider} but registers a single object into a project's lookup.
51
 * The annotated class must have one public constructor, which may take {@link Project} and/or {@link Lookup} parameters.
52
 * @since XXX
53
 */
54
@Retention(RetentionPolicy.SOURCE)
55
@Target(ElementType.TYPE) // XXX support static factory methods too
56
public @interface ProjectServiceProvider {
57
58
    /**
59
     * Service class to be registered.
60
     * The annotated class must be assignable to the service class.
61
     */
62
    Class<?> service();
63
64
    /**
65
     * Token(s) denoting one or more project types, e.g. {@code "org-netbeans-modules-java-j2seproject"}
66
     */
67
    String[] projectType();
68
69
}
(-)a/projectapi/src/org/netbeans/spi/project/support/LookupProviderSupport.java (-6 / +27 lines)
Lines 54-59 Link Here
54
import javax.swing.event.ChangeListener;
54
import javax.swing.event.ChangeListener;
55
import org.netbeans.api.project.SourceGroup;
55
import org.netbeans.api.project.SourceGroup;
56
import org.netbeans.api.project.Sources;
56
import org.netbeans.api.project.Sources;
57
import org.netbeans.modules.projectapi.MetaLookupMerger;
57
import org.netbeans.spi.project.LookupMerger;
58
import org.netbeans.spi.project.LookupMerger;
58
import org.netbeans.spi.project.LookupProvider;
59
import org.netbeans.spi.project.LookupProvider;
59
import org.openide.ErrorManager;
60
import org.openide.ErrorManager;
Lines 108-113 Link Here
108
        private List<Lookup> currentLookups;
109
        private List<Lookup> currentLookups;
109
        
110
        
110
        private Lookup.Result<LookupMerger> mergers;
111
        private Lookup.Result<LookupMerger> mergers;
112
        private final Lookup.Result<MetaLookupMerger> metaMergers;
111
        private Reference<LookupListener> listenerRef;
113
        private Reference<LookupListener> listenerRef;
112
        //#68623: the proxy lookup fires changes only if someone listens on a particular template:
114
        //#68623: the proxy lookup fires changes only if someone listens on a particular template:
113
        private final List<Lookup.Result<?>> results = new ArrayList<Lookup.Result<?>>();
115
        private final List<Lookup.Result<?>> results = new ArrayList<Lookup.Result<?>>();
Lines 121-137 Link Here
121
            assert base != null;
123
            assert base != null;
122
            baseLookup = base;
124
            baseLookup = base;
123
            providerResult = providerLookup.lookup(new Lookup.Template<LookupProvider>(LookupProvider.class));
125
            providerResult = providerLookup.lookup(new Lookup.Template<LookupProvider>(LookupProvider.class));
126
            metaMergers = providerLookup.lookupResult(MetaLookupMerger.class);
124
            assert isAllJustLookupProviders(providerLookup) : 
127
            assert isAllJustLookupProviders(providerLookup) : 
125
                "Layer content at " + path + " contains other than LookupProvider instances! See messages.log file for more details."; //NOI18N
128
                "Layer content at " + path + " contains other than LookupProvider instances! See messages.log file for more details."; //NOI18N
126
            doDelegate(providerResult.allInstances());
129
            doDelegate();
127
            providerListener = new LookupListener() {
130
            providerListener = new LookupListener() {
128
                public void resultChanged(LookupEvent ev) {
131
                public void resultChanged(LookupEvent ev) {
129
                    // XXX this may need to be run asynchronously; deadlock-prone
132
                    // XXX this may need to be run asynchronously; deadlock-prone
130
                    doDelegate(providerResult.allInstances());
133
                    doDelegate();
131
                }
134
                }
132
            };
135
            };
133
            providerResult.addLookupListener(
136
            providerResult.addLookupListener(
134
                WeakListeners.create(LookupListener.class, providerListener, providerResult));
137
                WeakListeners.create(LookupListener.class, providerListener, providerResult));
138
            metaMergers.addLookupListener(
139
                WeakListeners.create(LookupListener.class, providerListener, metaMergers));
135
        }
140
        }
136
        
141
        
137
        //just for assertion evaluation.
142
        //just for assertion evaluation.
Lines 139-145 Link Here
139
            Lookup.Result<Object> res = lkp.lookupResult(Object.class);
144
            Lookup.Result<Object> res = lkp.lookupResult(Object.class);
140
            Set<Class<?>> set = res.allClasses();
145
            Set<Class<?>> set = res.allClasses();
141
            for (Class clzz : set) {
146
            for (Class clzz : set) {
142
                if (!LookupProvider.class.isAssignableFrom(clzz)) {
147
                if (!LookupProvider.class.isAssignableFrom(clzz) && !MetaLookupMerger.class.isAssignableFrom(clzz)) {
143
                    Logger.getLogger(LookupProviderSupport.class.getName()).warning("" + clzz.getName() + " is not instance of LookupProvider."); //NOI18N
148
                    Logger.getLogger(LookupProviderSupport.class.getName()).warning("" + clzz.getName() + " is not instance of LookupProvider."); //NOI18N
144
                    return false;
149
                    return false;
145
                }
150
                }
Lines 149-164 Link Here
149
        
154
        
150
        
155
        
151
        public void resultChanged(LookupEvent ev) {
156
        public void resultChanged(LookupEvent ev) {
152
            doDelegate(providerResult.allInstances());
157
            doDelegate();
158
        }
159
160
        protected @Override void beforeLookup(Lookup.Template<?> template) {
161
            for (MetaLookupMerger metaMerger : metaMergers.allInstances()) {
162
                if (metaMerger.canNowMerge(template.getType())) {
163
                    doDelegate();
164
                }
165
            }
153
        }
166
        }
154
        
167
        
155
        
168
        
156
        private synchronized void doDelegate(Collection<? extends LookupProvider> providers) {
169
        private synchronized void doDelegate() {
157
            //unregister listeners from the old results:
170
            //unregister listeners from the old results:
158
            for (Lookup.Result<?> r : results) {
171
            for (Lookup.Result<?> r : results) {
159
                r.removeLookupListener(this);
172
                r.removeLookupListener(this);
160
            }
173
            }
161
            
174
            
175
            Collection<? extends LookupProvider> providers = providerResult.allInstances();
162
            List<Lookup> newLookups = new ArrayList<Lookup>();
176
            List<Lookup> newLookups = new ArrayList<Lookup>();
163
            for (LookupProvider elem : providers) {
177
            for (LookupProvider elem : providers) {
164
                if (old.contains(elem)) {
178
                if (old.contains(elem)) {
Lines 186-192 Link Here
186
            l = WeakListeners.create(LookupListener.class, this, mergers);
200
            l = WeakListeners.create(LookupListener.class, this, mergers);
187
            listenerRef = new WeakReference<LookupListener>(l);
201
            listenerRef = new WeakReference<LookupListener>(l);
188
            mergers.addLookupListener(l);
202
            mergers.addLookupListener(l);
189
            for (LookupMerger lm : mergers.allInstances()) {
203
            Collection<LookupMerger> allMergers = new ArrayList<LookupMerger>(mergers.allInstances());
204
            for (MetaLookupMerger metaMerger : metaMergers.allInstances()) {
205
                LookupMerger merger = metaMerger.merger();
206
                if (merger != null) {
207
                    allMergers.add(merger);
208
                }
209
            }
210
            for (LookupMerger lm : allMergers) {
190
                Class<?> c = lm.getMergeableClass();
211
                Class<?> c = lm.getMergeableClass();
191
                if (filteredClasses.contains(c)) {
212
                if (filteredClasses.contains(c)) {
192
                    ErrorManager.getDefault().log(ErrorManager.WARNING,
213
                    ErrorManager.getDefault().log(ErrorManager.WARNING,
(-)a/projectapi/test/unit/src/org/netbeans/spi/project/support/LookupProviderSupportTest.java (+87 lines)
Lines 42-50 Link Here
42
package org.netbeans.spi.project.support;
42
package org.netbeans.spi.project.support;
43
43
44
import java.beans.PropertyChangeListener;
44
import java.beans.PropertyChangeListener;
45
import java.net.URL;
46
import java.net.URLClassLoader;
47
import java.util.Arrays;
48
import java.util.Collection;
45
import java.util.Collections;
49
import java.util.Collections;
46
import java.util.HashMap;
50
import java.util.HashMap;
47
import java.util.HashSet;
51
import java.util.HashSet;
52
import java.util.Set;
53
import java.util.SortedSet;
54
import java.util.TreeSet;
48
import javax.swing.Icon;
55
import javax.swing.Icon;
49
import javax.swing.JButton;
56
import javax.swing.JButton;
50
import javax.swing.JCheckBox;
57
import javax.swing.JCheckBox;
Lines 58-63 Link Here
58
import org.netbeans.junit.NbTestCase;
65
import org.netbeans.junit.NbTestCase;
59
import org.netbeans.spi.project.LookupMerger;
66
import org.netbeans.spi.project.LookupMerger;
60
import org.netbeans.spi.project.LookupProvider;
67
import org.netbeans.spi.project.LookupProvider;
68
import org.netbeans.spi.project.ProjectServiceProvider;
61
import org.openide.filesystems.FileObject;
69
import org.openide.filesystems.FileObject;
62
import org.openide.util.Lookup;
70
import org.openide.util.Lookup;
63
import org.openide.util.lookup.AbstractLookup;
71
import org.openide.util.lookup.AbstractLookup;
Lines 285-289 Link Here
285
        public void removePropertyChangeListener(PropertyChangeListener listener) {
293
        public void removePropertyChangeListener(PropertyChangeListener listener) {
286
        }
294
        }
287
    }
295
    }
296
297
    public void testLazyProviders() throws Exception {
298
        // Cannot simply use static initializers to tell when classes are loaded;
299
        // these will not be run in case a service is loaded but not yet initialized.
300
        ClassLoader l = new URLClassLoader(new URL[] {
301
            LookupProviderSupportTest.class.getProtectionDomain().getCodeSource().getLocation()},
302
            LookupProviderSupportTest.class.getClassLoader()) {
303
            protected @Override synchronized Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
304
                if (name.startsWith(LookupProviderSupportTest.class.getName() + "$")) {
305
                    Class c = findLoadedClass(name);
306
                    if (c == null) {
307
                        // do not delegate to parent, i.e. be sure we have loaded it
308
                        c = findClass(name);
309
                        if (resolve) {
310
                            resolveClass(c);
311
                        }
312
                        loadedClasses.add(c);
313
                    }
314
                    return c;
315
                } else {
316
                    return super.loadClass(name, resolve);
317
                }
318
            }
319
        };
320
        Thread.currentThread().setContextClassLoader(l);
321
        assertLoadedClasses();
322
        Lookup all = LookupProviderSupport.createCompositeLookup(Lookups.fixed("hello"), "Projects/x/Lookup");
323
        assertLoadedClasses();
324
        assertEquals("hello", all.lookup(String.class));
325
        assertLoadedClasses();
326
        Collection<?> svcs2 = all.lookupAll(l.loadClass(Service2.class.getName()));
327
        assertEquals(1, svcs2.size());
328
        assertEquals(ServiceImpl2.class.getName(), svcs2.iterator().next().getClass().getName());
329
        assertLoadedClasses("Service2", "ServiceImpl2");
330
        Collection<?> svcs1 = all.lookupAll(l.loadClass(Service1.class.getName()));
331
        assertLoadedClasses("MergedServiceImpl1", "Merger", "Service1", "Service2", "ServiceImpl1a", "ServiceImpl1b", "ServiceImpl2");
332
        assertEquals(svcs1.toString(), 1, svcs1.size());
333
        assertTrue(svcs1.toString(), svcs1.toString().contains("ServiceImpl1a@"));
334
        assertTrue(svcs1.toString(), svcs1.toString().contains("ServiceImpl1b@"));
335
        assertTrue(svcs1.toString(), svcs1.toString().contains("Merge["));
336
    }
337
    private static final Set<Class<?>> loadedClasses = new HashSet<Class<?>>();
338
    private static void assertLoadedClasses(String... names) {
339
        SortedSet<String> actual = new TreeSet<String>();
340
        for (Class<?> clazz : loadedClasses) {
341
            actual.add(clazz.getName().replaceFirst("^\\Q" + LookupProviderSupportTest.class.getName() + "$\\E", ""));
342
        }
343
        assertEquals(Arrays.toString(names), actual.toString());
344
    }
345
    public interface Service1 {}
346
    public interface Service2 {}
347
    @ProjectServiceProvider(projectType="x", service=Service1.class)
348
    public static class ServiceImpl1a implements Service1 {}
349
    @ProjectServiceProvider(projectType="x", service=Service1.class)
350
    public static class ServiceImpl1b implements Service1 {}
351
    @ProjectServiceProvider(projectType="x", service=Service2.class)
352
    public static class ServiceImpl2 implements Service2 {
353
        public ServiceImpl2(Lookup base) {
354
            assertNotNull(base.lookup(String.class));
355
        }
356
    }
357
    @LookupMerger.Registration(projectType="x")
358
    public static class Merger implements LookupMerger<Service1> {
359
        public Class<Service1> getMergeableClass() {
360
            return Service1.class;
361
        }
362
        public Service1 merge(final Lookup lkp) {
363
            return new MergedServiceImpl1(lkp.lookupAll(Service1.class));
364
        }
365
    }
366
    private static class MergedServiceImpl1 implements Service1 {
367
        private final Collection<? extends Service1> delegates;
368
        MergedServiceImpl1(Collection<? extends Service1> delegates) {
369
            this.delegates = delegates;
370
        }
371
        public @Override String toString() {
372
            return "Merge" + delegates;
373
        }
374
    }
288
    
375
    
289
}
376
}

Return to bug 150194