1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1.2 +++ b/openide.util.lookup/src/META-INF/services/javax.annotation.processing.Processor Sat Oct 31 16:33:02 2009 +0100
1.3 @@ -0,0 +1,1 @@
1.4 +org.netbeans.modules.openide.util.ServiceProviderProcessor
2.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
2.2 +++ b/openide.util.lookup/src/org/netbeans/modules/openide/util/ServiceProviderProcessor.java Sat Oct 31 16:33:02 2009 +0100
2.3 @@ -0,0 +1,348 @@
2.4 +/*
2.5 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
2.6 + *
2.7 + * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
2.8 + *
2.9 + * The contents of this file are subject to the terms of either the GNU
2.10 + * General Public License Version 2 only ("GPL") or the Common
2.11 + * Development and Distribution License("CDDL") (collectively, the
2.12 + * "License"). You may not use this file except in compliance with the
2.13 + * License. You can obtain a copy of the License at
2.14 + * http://www.netbeans.org/cddl-gplv2.html
2.15 + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
2.16 + * specific language governing permissions and limitations under the
2.17 + * License. When distributing the software, include this License Header
2.18 + * Notice in each file and include the License file at
2.19 + * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
2.20 + * particular file as subject to the "Classpath" exception as provided
2.21 + * by Sun in the GPL Version 2 section of the License file that
2.22 + * accompanied this code. If applicable, add the following below the
2.23 + * License Header, with the fields enclosed by brackets [] replaced by
2.24 + * your own identifying information:
2.25 + * "Portions Copyrighted [year] [name of copyright owner]"
2.26 + *
2.27 + * If you wish your version of this file to be governed by only the CDDL
2.28 + * or only the GPL Version 2, indicate your decision by adding
2.29 + * "[Contributor] elects to include this software in this distribution
2.30 + * under the [CDDL or GPL Version 2] license." If you do not indicate a
2.31 + * single choice of license, a recipient has the option to distribute
2.32 + * your version of this file under either the CDDL, the GPL Version 2 or
2.33 + * to extend the choice of license to its licensees as provided above.
2.34 + * However, if you add GPL Version 2 code and therefore, elected the GPL
2.35 + * Version 2 license, then the option applies only if the new code is
2.36 + * made subject to such option by the copyright holder.
2.37 + *
2.38 + * Contributor(s):
2.39 + *
2.40 + * Portions Copyrighted 2008 Sun Microsystems, Inc.
2.41 + */
2.42 +
2.43 +package org.netbeans.modules.openide.util;
2.44 +
2.45 +import java.io.BufferedReader;
2.46 +import java.io.FileNotFoundException;
2.47 +import java.io.IOException;
2.48 +import java.io.InputStream;
2.49 +import java.io.InputStreamReader;
2.50 +import java.io.OutputStream;
2.51 +import java.io.OutputStreamWriter;
2.52 +import java.io.PrintWriter;
2.53 +import java.lang.annotation.Annotation;
2.54 +import java.util.ArrayList;
2.55 +import java.util.Collection;
2.56 +import java.util.Collections;
2.57 +import java.util.HashMap;
2.58 +import java.util.LinkedList;
2.59 +import java.util.List;
2.60 +import java.util.Map;
2.61 +import java.util.Set;
2.62 +import javax.annotation.processing.AbstractProcessor;
2.63 +import javax.annotation.processing.Completion;
2.64 +import javax.annotation.processing.RoundEnvironment;
2.65 +import javax.annotation.processing.SupportedAnnotationTypes;
2.66 +import javax.annotation.processing.SupportedSourceVersion;
2.67 +import javax.lang.model.SourceVersion;
2.68 +import javax.lang.model.element.AnnotationMirror;
2.69 +import javax.lang.model.element.AnnotationValue;
2.70 +import javax.lang.model.element.Element;
2.71 +import javax.lang.model.element.ExecutableElement;
2.72 +import javax.lang.model.element.Modifier;
2.73 +import javax.lang.model.element.TypeElement;
2.74 +import javax.lang.model.type.MirroredTypeException;
2.75 +import javax.lang.model.type.TypeKind;
2.76 +import javax.lang.model.type.TypeMirror;
2.77 +import javax.lang.model.util.ElementFilter;
2.78 +import javax.tools.Diagnostic.Kind;
2.79 +import javax.tools.FileObject;
2.80 +import javax.tools.StandardLocation;
2.81 +import org.openide.util.lookup.ServiceProvider;
2.82 +import org.openide.util.lookup.ServiceProviders;
2.83 +
2.84 +@SupportedSourceVersion(SourceVersion.RELEASE_6)
2.85 +@SupportedAnnotationTypes({"org.openide.util.lookup.ServiceProvider", "org.openide.util.lookup.ServiceProviders"})
2.86 +public class ServiceProviderProcessor extends AbstractProcessor {
2.87 +
2.88 + /** public for ServiceLoader */
2.89 + public ServiceProviderProcessor() {}
2.90 +
2.91 + private final Map<String, List<String>> outputFiles = new HashMap<String,List<String>>();
2.92 + private final Map<String, List<Element>> originatingElements = new HashMap<String,List<Element>>();
2.93 +
2.94 + @Override
2.95 + public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
2.96 + if (roundEnv.errorRaised()) {
2.97 + return false;
2.98 + }
2.99 + if (roundEnv.processingOver()) {
2.100 + writeServices();
2.101 + return false;
2.102 + } else {
2.103 + for (Element el : roundEnv.getElementsAnnotatedWith(ServiceProvider.class)) {
2.104 + TypeElement clazz = (TypeElement) el;
2.105 + if (!verifyServiceProviderSignature(clazz)) {
2.106 + continue;
2.107 + }
2.108 + ServiceProvider sp = clazz.getAnnotation(ServiceProvider.class);
2.109 + register(clazz, sp);
2.110 + }
2.111 + for (Element el : roundEnv.getElementsAnnotatedWith(ServiceProviders.class)) {
2.112 + TypeElement clazz = (TypeElement) el;
2.113 + if (!verifyServiceProviderSignature(clazz)) {
2.114 + continue;
2.115 + }
2.116 + ServiceProviders spp = clazz.getAnnotation(ServiceProviders.class);
2.117 + for (ServiceProvider sp : spp.value()) {
2.118 + register(clazz, sp);
2.119 + }
2.120 + }
2.121 + return true;
2.122 + }
2.123 + }
2.124 +
2.125 + private void register(TypeElement clazz, ServiceProvider svc) {
2.126 + TypeMirror type;
2.127 + try {
2.128 + svc.service();
2.129 + assert false;
2.130 + return;
2.131 + } catch (MirroredTypeException e) {
2.132 + type = e.getTypeMirror();
2.133 + }
2.134 + String impl = processingEnv.getElementUtils().getBinaryName(clazz).toString();
2.135 + String xface = processingEnv.getElementUtils().getBinaryName((TypeElement) processingEnv.getTypeUtils().asElement(type)).toString();
2.136 + if (!processingEnv.getTypeUtils().isAssignable(clazz.asType(), type)) {
2.137 + AnnotationMirror ann = findAnnotationMirror(clazz, ServiceProvider.class);
2.138 + processingEnv.getMessager().printMessage(Kind.ERROR, impl + " is not assignable to " + xface,
2.139 + clazz, ann, findAnnotationValue(ann, "service"));
2.140 + return;
2.141 + }
2.142 + processingEnv.getMessager().printMessage(Kind.NOTE, impl + " to be registered as a " + xface);
2.143 + String rsrc = (svc.path().length() > 0 ? "META-INF/namedservices/" + svc.path() + "/" : "META-INF/services/") + xface;
2.144 + {
2.145 + List<Element> origEls = originatingElements.get(rsrc);
2.146 + if (origEls == null) {
2.147 + origEls = new ArrayList<Element>();
2.148 + originatingElements.put(rsrc, origEls);
2.149 + }
2.150 + origEls.add(clazz);
2.151 + }
2.152 + List<String> lines = outputFiles.get(rsrc);
2.153 + if (lines == null) {
2.154 + lines = new ArrayList<String>();
2.155 + try {
2.156 + try {
2.157 + FileObject in = processingEnv.getFiler().getResource(StandardLocation.SOURCE_PATH, "", rsrc);
2.158 + in.openInputStream().close();
2.159 + processingEnv.getMessager().printMessage(Kind.ERROR,
2.160 + "Cannot generate " + rsrc + " because it already exists in sources: " + in.toUri());
2.161 + return;
2.162 + } catch (FileNotFoundException x) {
2.163 + // Good.
2.164 + }
2.165 + try {
2.166 + FileObject in = processingEnv.getFiler().getResource(StandardLocation.CLASS_OUTPUT, "", rsrc);
2.167 + InputStream is = in.openInputStream();
2.168 + try {
2.169 + BufferedReader r = new BufferedReader(new InputStreamReader(is, "UTF-8"));
2.170 + String line;
2.171 + while ((line = r.readLine()) != null) {
2.172 + lines.add(line);
2.173 + }
2.174 + } finally {
2.175 + is.close();
2.176 + }
2.177 + } catch (FileNotFoundException x) {
2.178 + // OK, created for the first time
2.179 + }
2.180 + } catch (IOException x) {
2.181 + processingEnv.getMessager().printMessage(Kind.ERROR, x.toString());
2.182 + return;
2.183 + }
2.184 + outputFiles.put(rsrc, lines);
2.185 + }
2.186 + int idx = lines.indexOf(impl);
2.187 + if (idx != -1) {
2.188 + lines.remove(idx);
2.189 + while (lines.size() > idx && lines.get(idx).matches("#position=.+|#-.+")) {
2.190 + lines.remove(idx);
2.191 + }
2.192 + }
2.193 + lines.add(impl);
2.194 + if (svc.position() != Integer.MAX_VALUE) {
2.195 + lines.add("#position=" + svc.position());
2.196 + }
2.197 + for (String exclude : svc.supersedes()) {
2.198 + lines.add("#-" + exclude);
2.199 + }
2.200 + }
2.201 +
2.202 + private boolean verifyServiceProviderSignature(TypeElement clazz) {
2.203 + AnnotationMirror ann = findAnnotationMirror(clazz, ServiceProvider.class);
2.204 + if (!clazz.getModifiers().contains(Modifier.PUBLIC)) {
2.205 + processingEnv.getMessager().printMessage(Kind.ERROR, clazz + " must be public", clazz, ann);
2.206 + return false;
2.207 + }
2.208 + if (clazz.getModifiers().contains(Modifier.ABSTRACT)) {
2.209 + processingEnv.getMessager().printMessage(Kind.ERROR, clazz + " must not be abstract", clazz, ann);
2.210 + return false;
2.211 + }
2.212 + {
2.213 + boolean hasDefaultCtor = false;
2.214 + for (ExecutableElement constructor : ElementFilter.constructorsIn(clazz.getEnclosedElements())) {
2.215 + if (constructor.getModifiers().contains(Modifier.PUBLIC) && constructor.getParameters().isEmpty()) {
2.216 + hasDefaultCtor = true;
2.217 + break;
2.218 + }
2.219 + }
2.220 + if (!hasDefaultCtor) {
2.221 + processingEnv.getMessager().printMessage(Kind.ERROR, clazz + " must have a public no-argument constructor", clazz, ann);
2.222 + return false;
2.223 + }
2.224 + }
2.225 + return true;
2.226 + }
2.227 +
2.228 + private void writeServices() {
2.229 + for (Map.Entry<String, List<String>> entry : outputFiles.entrySet()) {
2.230 + try {
2.231 + FileObject out = processingEnv.getFiler().createResource(StandardLocation.CLASS_OUTPUT, "", entry.getKey(),
2.232 + originatingElements.get(entry.getKey()).toArray(new Element[0]));
2.233 + OutputStream os = out.openOutputStream();
2.234 + try {
2.235 + PrintWriter w = new PrintWriter(new OutputStreamWriter(os, "UTF-8"));
2.236 + for (String line : entry.getValue()) {
2.237 + w.println(line);
2.238 + }
2.239 + w.flush();
2.240 + w.close();
2.241 + } finally {
2.242 + os.close();
2.243 + }
2.244 + } catch (IOException x) {
2.245 + processingEnv.getMessager().printMessage(Kind.ERROR, "Failed to write to " + entry.getKey() + ": " + x.toString());
2.246 + }
2.247 + }
2.248 + }
2.249 +
2.250 + /**
2.251 + * @param element a source element
2.252 + * @param annotation a type of annotation
2.253 + * @return the instance of that annotation on the element, or null if not found
2.254 + */
2.255 + private AnnotationMirror findAnnotationMirror(Element element, Class<? extends Annotation> annotation) {
2.256 + for (AnnotationMirror ann : element.getAnnotationMirrors()) {
2.257 + if (processingEnv.getElementUtils().getBinaryName((TypeElement) ann.getAnnotationType().asElement()).
2.258 + contentEquals(annotation.getName())) {
2.259 + return ann;
2.260 + }
2.261 + }
2.262 + return null;
2.263 + }
2.264 +
2.265 + /**
2.266 + * @param annotation an annotation instance (null permitted)
2.267 + * @param name the name of an attribute of that annotation
2.268 + * @return the corresponding value if found
2.269 + */
2.270 + private AnnotationValue findAnnotationValue(AnnotationMirror annotation, String name) {
2.271 + if (annotation != null) {
2.272 + for (Map.Entry<? extends ExecutableElement,? extends AnnotationValue> entry : annotation.getElementValues().entrySet()) {
2.273 + if (entry.getKey().getSimpleName().contentEquals(name)) {
2.274 + return entry.getValue();
2.275 + }
2.276 + }
2.277 + }
2.278 + return null;
2.279 + }
2.280 +
2.281 + @Override
2.282 + public Iterable<? extends Completion> getCompletions(Element annotated, AnnotationMirror annotation, ExecutableElement attr, String userText) {
2.283 + if (processingEnv == null || annotated == null || !annotated.getKind().isClass()) {
2.284 + return Collections.emptyList();
2.285 + }
2.286 +
2.287 + if ( annotation == null
2.288 + || !"org.openide.util.lookup.ServiceProvider".contentEquals(((TypeElement) annotation.getAnnotationType().asElement()).getQualifiedName())) {
2.289 + return Collections.emptyList();
2.290 + }
2.291 +
2.292 + if (!"service".contentEquals(attr.getSimpleName())) {
2.293 + return Collections.emptyList();
2.294 + }
2.295 +
2.296 + TypeElement jlObject = processingEnv.getElementUtils().getTypeElement("java.lang.Object");
2.297 +
2.298 + if (jlObject == null) {
2.299 + return Collections.emptyList();
2.300 + }
2.301 +
2.302 + Collection<Completion> result = new LinkedList<Completion>();
2.303 + List<TypeElement> toProcess = new LinkedList<TypeElement>();
2.304 +
2.305 + toProcess.add((TypeElement) annotated);
2.306 +
2.307 + while (!toProcess.isEmpty()) {
2.308 + TypeElement c = toProcess.remove(0);
2.309 +
2.310 + result.add(new TypeCompletion(c.getQualifiedName().toString() + ".class"));
2.311 +
2.312 + List<TypeMirror> parents = new LinkedList<TypeMirror>();
2.313 +
2.314 + parents.add(c.getSuperclass());
2.315 + parents.addAll(c.getInterfaces());
2.316 +
2.317 + for (TypeMirror tm : parents) {
2.318 + if (tm == null || tm.getKind() != TypeKind.DECLARED) {
2.319 + continue;
2.320 + }
2.321 +
2.322 + TypeElement type = (TypeElement) processingEnv.getTypeUtils().asElement(tm);
2.323 +
2.324 + if (!jlObject.equals(type)) {
2.325 + toProcess.add(type);
2.326 + }
2.327 + }
2.328 + }
2.329 +
2.330 + return result;
2.331 + }
2.332 +
2.333 + private static final class TypeCompletion implements Completion {
2.334 +
2.335 + private final String type;
2.336 +
2.337 + public TypeCompletion(String type) {
2.338 + this.type = type;
2.339 + }
2.340 +
2.341 + public String getValue() {
2.342 + return type;
2.343 + }
2.344 +
2.345 + public String getMessage() {
2.346 + return null;
2.347 + }
2.348 +
2.349 + }
2.350 +
2.351 +}
3.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
3.2 +++ b/openide.util.lookup/test/unit/src/org/netbeans/modules/openide/util/ServiceProviderProcessorTest.java Sat Oct 31 16:33:02 2009 +0100
3.3 @@ -0,0 +1,180 @@
3.4 +/*
3.5 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
3.6 + *
3.7 + * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
3.8 + *
3.9 + * The contents of this file are subject to the terms of either the GNU
3.10 + * General Public License Version 2 only ("GPL") or the Common
3.11 + * Development and Distribution License("CDDL") (collectively, the
3.12 + * "License"). You may not use this file except in compliance with the
3.13 + * License. You can obtain a copy of the License at
3.14 + * http://www.netbeans.org/cddl-gplv2.html
3.15 + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
3.16 + * specific language governing permissions and limitations under the
3.17 + * License. When distributing the software, include this License Header
3.18 + * Notice in each file and include the License file at
3.19 + * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
3.20 + * particular file as subject to the "Classpath" exception as provided
3.21 + * by Sun in the GPL Version 2 section of the License file that
3.22 + * accompanied this code. If applicable, add the following below the
3.23 + * License Header, with the fields enclosed by brackets [] replaced by
3.24 + * your own identifying information:
3.25 + * "Portions Copyrighted [year] [name of copyright owner]"
3.26 + *
3.27 + * If you wish your version of this file to be governed by only the CDDL
3.28 + * or only the GPL Version 2, indicate your decision by adding
3.29 + * "[Contributor] elects to include this software in this distribution
3.30 + * under the [CDDL or GPL Version 2] license." If you do not indicate a
3.31 + * single choice of license, a recipient has the option to distribute
3.32 + * your version of this file under either the CDDL, the GPL Version 2 or
3.33 + * to extend the choice of license to its licensees as provided above.
3.34 + * However, if you add GPL Version 2 code and therefore, elected the GPL
3.35 + * Version 2 license, then the option applies only if the new code is
3.36 + * made subject to such option by the copyright holder.
3.37 + *
3.38 + * Contributor(s):
3.39 + *
3.40 + * Portions Copyrighted 2008 Sun Microsystems, Inc.
3.41 + */
3.42 +
3.43 +package org.netbeans.modules.openide.util;
3.44 +
3.45 +import java.io.ByteArrayOutputStream;
3.46 +import java.io.File;
3.47 +import java.util.ArrayList;
3.48 +import java.util.Arrays;
3.49 +import java.util.Collections;
3.50 +import java.util.Comparator;
3.51 +import java.util.List;
3.52 +import org.netbeans.junit.NbTestCase;
3.53 +import org.openide.util.Lookup;
3.54 +import org.openide.util.lookup.Lookups;
3.55 +import org.openide.util.lookup.ServiceProvider;
3.56 +import org.openide.util.lookup.ServiceProviders;
3.57 +import org.openide.util.test.AnnotationProcessorTestUtils;
3.58 +
3.59 +public class ServiceProviderProcessorTest extends NbTestCase {
3.60 +
3.61 + public ServiceProviderProcessorTest(String n) {
3.62 + super(n);
3.63 + }
3.64 +
3.65 + private static List<Class<?>> classesOf(Iterable<?> objects) {
3.66 + List<Class<?>> cs = new ArrayList<Class<?>>();
3.67 + for (Object o : objects) {
3.68 + cs.add(o.getClass());
3.69 + }
3.70 + return cs;
3.71 + }
3.72 +
3.73 + private static List<Class<?>> classesOfLookup(Class<?> xface) {
3.74 + return classesOf(Lookup.getDefault().lookupAll(xface));
3.75 + }
3.76 +
3.77 + private static List<Class<?>> sortClassList(List<Class<?>> classes) {
3.78 + List<Class<?>> sorted = new ArrayList<Class<?>>(classes);
3.79 + Collections.sort(sorted, new Comparator<Class<?>>() {
3.80 + public int compare(Class<?> c1, Class<?> c2) {
3.81 + return c1.getName().compareTo(c2.getName());
3.82 + }
3.83 + });
3.84 + return sorted;
3.85 + }
3.86 +
3.87 + public void testBasicUsage() throws Exception {
3.88 + assertEquals(Collections.singletonList(Implementation.class), classesOfLookup(Interface.class));
3.89 + }
3.90 + public interface Interface {}
3.91 + @ServiceProvider(service=Interface.class)
3.92 + public static class Implementation implements Interface {}
3.93 +
3.94 + public void testPosition() throws Exception {
3.95 + assertEquals(Arrays.<Class<?>>asList(OrderedImpl3.class, OrderedImpl2.class, OrderedImpl1.class), classesOfLookup(OrderedInterface.class));
3.96 + }
3.97 + public interface OrderedInterface {}
3.98 + @ServiceProvider(service=OrderedInterface.class)
3.99 + public static class OrderedImpl1 implements OrderedInterface {}
3.100 + @ServiceProvider(service=OrderedInterface.class, position=200)
3.101 + public static class OrderedImpl2 implements OrderedInterface {}
3.102 + @ServiceProvider(service=OrderedInterface.class, position=100)
3.103 + public static class OrderedImpl3 implements OrderedInterface {}
3.104 +
3.105 + public void testPath() throws Exception {
3.106 + assertEquals(Collections.singletonList(PathImplementation.class), classesOf(Lookups.forPath("some/path").lookupAll(Interface.class)));
3.107 + }
3.108 + @ServiceProvider(service=Interface.class, path="some/path")
3.109 + public static class PathImplementation implements Interface {}
3.110 +
3.111 + public void testSupersedes() throws Exception {
3.112 + assertEquals(Arrays.<Class<?>>asList(Overrider.class, Unrelated.class), sortClassList(classesOfLookup(CancellableInterface.class)));
3.113 + }
3.114 + public interface CancellableInterface {}
3.115 + @ServiceProvider(service=CancellableInterface.class)
3.116 + public static class Overridden implements CancellableInterface {}
3.117 + @ServiceProvider(service=CancellableInterface.class, supersedes="org.netbeans.modules.openide.util.ServiceProviderProcessorTest$Overridden")
3.118 + public static class Overrider implements CancellableInterface {}
3.119 + @ServiceProvider(service=CancellableInterface.class)
3.120 + public static class Unrelated implements CancellableInterface {}
3.121 +
3.122 + public void testMultipleRegistrations() throws Exception {
3.123 + assertEquals(Collections.singletonList(Multitasking.class), classesOfLookup(Interface1.class));
3.124 + assertEquals(Collections.singletonList(Multitasking.class), classesOfLookup(Interface2.class));
3.125 + }
3.126 + public interface Interface1 {}
3.127 + public interface Interface2 {}
3.128 + @ServiceProviders({@ServiceProvider(service=Interface1.class), @ServiceProvider(service=Interface2.class)})
3.129 + public static class Multitasking implements Interface1, Interface2 {}
3.130 +
3.131 + public void testErrorReporting() throws Exception {
3.132 + clearWorkDir();
3.133 + File src = new File(getWorkDir(), "src");
3.134 + File dest = new File(getWorkDir(), "classes");
3.135 + String xfaceName = Interface.class.getCanonicalName();
3.136 +
3.137 + AnnotationProcessorTestUtils.makeSource(src, "p.C1",
3.138 + "@org.openide.util.lookup.ServiceProvider(service=" + xfaceName + ".class)",
3.139 + "public class C1 implements " + xfaceName + " {}");
3.140 + ByteArrayOutputStream baos = new ByteArrayOutputStream();
3.141 + assertTrue(AnnotationProcessorTestUtils.runJavac(src, "C1", dest, null, baos));
3.142 +
3.143 + AnnotationProcessorTestUtils.makeSource(src, "p.C2",
3.144 + "@org.openide.util.lookup.ServiceProvider(service=" + xfaceName + ".class)",
3.145 + "class C2 implements " + xfaceName + " {}");
3.146 + baos = new ByteArrayOutputStream();
3.147 + assertFalse(AnnotationProcessorTestUtils.runJavac(src, "C2", dest, null, baos));
3.148 + assertTrue(baos.toString(), baos.toString().contains("public"));
3.149 +
3.150 + AnnotationProcessorTestUtils.makeSource(src, "p.C3",
3.151 + "@org.openide.util.lookup.ServiceProvider(service=" + xfaceName + ".class)",
3.152 + "public class C3 implements " + xfaceName + " {",
3.153 + "public C3(boolean x) {}",
3.154 + "}");
3.155 + baos = new ByteArrayOutputStream();
3.156 + assertFalse(AnnotationProcessorTestUtils.runJavac(src, "C3", dest, null, baos));
3.157 + assertTrue(baos.toString(), baos.toString().contains("constructor"));
3.158 +
3.159 + AnnotationProcessorTestUtils.makeSource(src, "p.C4",
3.160 + "@org.openide.util.lookup.ServiceProvider(service=" + xfaceName + ".class)",
3.161 + "public class C4 implements " + xfaceName + " {",
3.162 + "C4() {}",
3.163 + "}");
3.164 + baos = new ByteArrayOutputStream();
3.165 + assertFalse(AnnotationProcessorTestUtils.runJavac(src, "C4", dest, null, baos));
3.166 + assertTrue(baos.toString(), baos.toString().contains("constructor"));
3.167 +
3.168 + AnnotationProcessorTestUtils.makeSource(src, "p.C5",
3.169 + "@org.openide.util.lookup.ServiceProvider(service=" + xfaceName + ".class)",
3.170 + "public abstract class C5 implements " + xfaceName + " {}");
3.171 + baos = new ByteArrayOutputStream();
3.172 + assertFalse(AnnotationProcessorTestUtils.runJavac(src, "C5", dest, null, baos));
3.173 + assertTrue(baos.toString(), baos.toString().contains("abstract"));
3.174 +
3.175 + AnnotationProcessorTestUtils.makeSource(src, "p.C6",
3.176 + "@org.openide.util.lookup.ServiceProvider(service=" + xfaceName + ".class)",
3.177 + "public class C6 {}");
3.178 + baos = new ByteArrayOutputStream();
3.179 + assertFalse(AnnotationProcessorTestUtils.runJavac(src, "C6", dest, null, baos));
3.180 + assertTrue(baos.toString(), baos.toString().contains("assignable"));
3.181 + }
3.182 +
3.183 +}
4.1 --- a/openide.util.lookup/test/unit/src/org/openide/util/lookup/MetaInfServicesLookupTest.java Sat Oct 31 15:30:02 2009 +0100
4.2 +++ b/openide.util.lookup/test/unit/src/org/openide/util/lookup/MetaInfServicesLookupTest.java Sat Oct 31 16:33:02 2009 +0100
4.3 @@ -75,8 +75,6 @@
4.4 import org.bar.Comparator2;
4.5 import org.netbeans.junit.MockServices;
4.6 import org.netbeans.junit.NbTestCase;
4.7 -import org.openide.util.Enumerations;
4.8 -import org.openide.util.Exceptions;
4.9 import org.openide.util.Lookup;
4.10 import org.openide.util.LookupEvent;
4.11 import org.openide.util.LookupListener;
4.12 @@ -485,7 +483,7 @@
4.13 try {
4.14 wait();
4.15 } catch (InterruptedException ex) {
4.16 - Exceptions.printStackTrace(ex);
4.17 + Logger.getLogger("global").log(Level.WARNING, "", ex);
4.18 }
4.19 }
4.20 }
4.21 @@ -518,7 +516,7 @@
4.22 assertNull(Lookups.metaInfServices(new ClassLoader() {
4.23 protected @Override Enumeration<URL> findResources(String name) throws IOException {
4.24 if (name.equals("META-INF/services/java.lang.Object")) {
4.25 - return Enumerations.singleton(new URL(null, "dummy:stuff", new URLStreamHandler() {
4.26 + return singleton(new URL(null, "dummy:stuff", new URLStreamHandler() {
4.27 protected URLConnection openConnection(URL u) throws IOException {
4.28 return new URLConnection(u) {
4.29 public void connect() throws IOException {}
4.30 @@ -529,9 +527,10 @@
4.31 }
4.32 }));
4.33 } else {
4.34 - return Enumerations.empty();
4.35 + return Collections.enumeration(Collections.<URL>emptyList());
4.36 }
4.37 }
4.38 +
4.39 }).lookup(Object.class));
4.40 }
4.41 public static class Broken1 {
4.42 @@ -547,4 +546,7 @@
4.43 }
4.44 }
4.45
4.46 + static <T> Enumeration<T> singleton(T t) {
4.47 + return Collections.enumeration(Collections.singleton(t));
4.48 + }
4.49 }
5.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
5.2 +++ b/openide.util.lookup/test/unit/src/org/openide/util/test/AnnotationProcessorTestUtils.java Sat Oct 31 16:33:02 2009 +0100
5.3 @@ -0,0 +1,139 @@
5.4 +/*
5.5 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
5.6 + *
5.7 + * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
5.8 + *
5.9 + * The contents of this file are subject to the terms of either the GNU
5.10 + * General Public License Version 2 only ("GPL") or the Common
5.11 + * Development and Distribution License("CDDL") (collectively, the
5.12 + * "License"). You may not use this file except in compliance with the
5.13 + * License. You can obtain a copy of the License at
5.14 + * http://www.netbeans.org/cddl-gplv2.html
5.15 + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
5.16 + * specific language governing permissions and limitations under the
5.17 + * License. When distributing the software, include this License Header
5.18 + * Notice in each file and include the License file at
5.19 + * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
5.20 + * particular file as subject to the "Classpath" exception as provided
5.21 + * by Sun in the GPL Version 2 section of the License file that
5.22 + * accompanied this code. If applicable, add the following below the
5.23 + * License Header, with the fields enclosed by brackets [] replaced by
5.24 + * your own identifying information:
5.25 + * "Portions Copyrighted [year] [name of copyright owner]"
5.26 + *
5.27 + * If you wish your version of this file to be governed by only the CDDL
5.28 + * or only the GPL Version 2, indicate your decision by adding
5.29 + * "[Contributor] elects to include this software in this distribution
5.30 + * under the [CDDL or GPL Version 2] license." If you do not indicate a
5.31 + * single choice of license, a recipient has the option to distribute
5.32 + * your version of this file under either the CDDL, the GPL Version 2 or
5.33 + * to extend the choice of license to its licensees as provided above.
5.34 + * However, if you add GPL Version 2 code and therefore, elected the GPL
5.35 + * Version 2 license, then the option applies only if the new code is
5.36 + * made subject to such option by the copyright holder.
5.37 + *
5.38 + * Contributor(s):
5.39 + *
5.40 + * Portions Copyrighted 2008 Sun Microsystems, Inc.
5.41 + */
5.42 +
5.43 +package org.openide.util.test;
5.44 +
5.45 +import java.io.File;
5.46 +import java.io.FileWriter;
5.47 +import java.io.IOException;
5.48 +import java.io.OutputStream;
5.49 +import java.io.PrintWriter;
5.50 +import java.io.Writer;
5.51 +import java.util.ArrayList;
5.52 +import java.util.List;
5.53 +import java.util.regex.Pattern;
5.54 +import javax.tools.JavaCompiler;
5.55 +import javax.tools.ToolProvider;
5.56 +import junit.framework.Assert;
5.57 +
5.58 +/**
5.59 + * Utilities useful to those testing JSR 269 annotation processors.
5.60 + * <p>If you just want to test that the output of the processor is correct,
5.61 + * you do not need to do anything special:
5.62 + * just use the annotation on some sample classes nested inside your unit test.
5.63 + * They will be processed, and you check that your SPI loads them correctly.
5.64 + * These utilities are useful mainly in case you want to check that the processor
5.65 + * rejects erroneous sources, and that any messages it prints are reasonable;
5.66 + * that it behaves correctly on incremental compilations; etc.
5.67 + */
5.68 +public class AnnotationProcessorTestUtils {
5.69 +
5.70 + private AnnotationProcessorTestUtils() {}
5.71 +
5.72 + /**
5.73 + * Create a source file.
5.74 + * @param dir source root
5.75 + * @param clazz a fully-qualified class name
5.76 + * @param content lines of text (skip package decl)
5.77 + */
5.78 + public static void makeSource(File dir, String clazz, String... content) throws IOException {
5.79 + File f = new File(dir, clazz.replace('.', File.separatorChar) + ".java");
5.80 + f.getParentFile().mkdirs();
5.81 + Writer w = new FileWriter(f);
5.82 + try {
5.83 + PrintWriter pw = new PrintWriter(w);
5.84 + String pkg = clazz.replaceFirst("\\.[^.]+$", "");
5.85 + if (!pkg.equals(clazz)) {
5.86 + pw.println("package " + pkg + ";");
5.87 + }
5.88 + for (String line : content) {
5.89 + pw.println(line);
5.90 + }
5.91 + pw.flush();
5.92 + } finally {
5.93 + w.close();
5.94 + }
5.95 + }
5.96 +
5.97 + /**
5.98 + * Run the Java compiler.
5.99 + * (A JSR 199 implementation must be available.)
5.100 + * @param src a source root (runs javac on all *.java it finds matching {@code srcIncludes})
5.101 + * @param srcIncludes a pattern of source files names without path to compile (useful for testing incremental compiles), or null for all
5.102 + * @param dest a dest dir to compile classes to
5.103 + * @param cp classpath entries; if null, use Java classpath of test
5.104 + * @param stderr output stream to print messages to, or null for test console (i.e. do not capture)
5.105 + * @return true if compilation succeeded, false if it failed
5.106 + */
5.107 + public static boolean runJavac(File src, String srcIncludes, File dest, File[] cp, OutputStream stderr) {
5.108 + List<String> args = new ArrayList<String>();
5.109 + args.add("-classpath");
5.110 + if (cp != null) {
5.111 + StringBuffer b = new StringBuffer();
5.112 + for (File entry : cp) {
5.113 + b.append(File.pathSeparatorChar);
5.114 + b.append(entry.getAbsolutePath());
5.115 + }
5.116 + args.add(b.toString());
5.117 + } else {
5.118 + args.add(System.getProperty("java.class.path"));
5.119 + }
5.120 + args.add("-d");
5.121 + args.add(dest.getAbsolutePath());
5.122 + args.add("-sourcepath");
5.123 + args.add(src.getAbsolutePath());
5.124 + dest.mkdirs();
5.125 + scan(args, src, srcIncludes);
5.126 + JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
5.127 + Assert.assertNotNull("no JSR 199 compiler impl found; try e.g.: " +
5.128 + "test.unit.run.cp.extra=${nb_all}/apisupport.harness/external/openjdk-javac-6-b12.jar", compiler);
5.129 + //System.err.println("running javac with args: " + args);
5.130 + return compiler.run(null, null, stderr, args.toArray(new String[args.size()])) == 0;
5.131 + }
5.132 + private static void scan(List<String> names, File f, String includes) {
5.133 + if (f.isDirectory()) {
5.134 + for (File kid : f.listFiles()) {
5.135 + scan(names, kid, includes);
5.136 + }
5.137 + } else if (f.getName().endsWith(".java") && (includes == null || Pattern.compile(includes).matcher(f.getName()).find())) {
5.138 + names.add(f.getAbsolutePath());
5.139 + }
5.140 + }
5.141 +
5.142 +}
6.1 --- a/openide.util/src/META-INF/services/javax.annotation.processing.Processor Sat Oct 31 15:30:02 2009 +0100
6.2 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
6.3 @@ -1,1 +0,0 @@
6.4 -org.netbeans.modules.openide.util.ServiceProviderProcessor
7.1 --- a/openide.util/src/org/netbeans/modules/openide/util/ServiceProviderProcessor.java Sat Oct 31 15:30:02 2009 +0100
7.2 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
7.3 @@ -1,348 +0,0 @@
7.4 -/*
7.5 - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
7.6 - *
7.7 - * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
7.8 - *
7.9 - * The contents of this file are subject to the terms of either the GNU
7.10 - * General Public License Version 2 only ("GPL") or the Common
7.11 - * Development and Distribution License("CDDL") (collectively, the
7.12 - * "License"). You may not use this file except in compliance with the
7.13 - * License. You can obtain a copy of the License at
7.14 - * http://www.netbeans.org/cddl-gplv2.html
7.15 - * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
7.16 - * specific language governing permissions and limitations under the
7.17 - * License. When distributing the software, include this License Header
7.18 - * Notice in each file and include the License file at
7.19 - * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
7.20 - * particular file as subject to the "Classpath" exception as provided
7.21 - * by Sun in the GPL Version 2 section of the License file that
7.22 - * accompanied this code. If applicable, add the following below the
7.23 - * License Header, with the fields enclosed by brackets [] replaced by
7.24 - * your own identifying information:
7.25 - * "Portions Copyrighted [year] [name of copyright owner]"
7.26 - *
7.27 - * If you wish your version of this file to be governed by only the CDDL
7.28 - * or only the GPL Version 2, indicate your decision by adding
7.29 - * "[Contributor] elects to include this software in this distribution
7.30 - * under the [CDDL or GPL Version 2] license." If you do not indicate a
7.31 - * single choice of license, a recipient has the option to distribute
7.32 - * your version of this file under either the CDDL, the GPL Version 2 or
7.33 - * to extend the choice of license to its licensees as provided above.
7.34 - * However, if you add GPL Version 2 code and therefore, elected the GPL
7.35 - * Version 2 license, then the option applies only if the new code is
7.36 - * made subject to such option by the copyright holder.
7.37 - *
7.38 - * Contributor(s):
7.39 - *
7.40 - * Portions Copyrighted 2008 Sun Microsystems, Inc.
7.41 - */
7.42 -
7.43 -package org.netbeans.modules.openide.util;
7.44 -
7.45 -import java.io.BufferedReader;
7.46 -import java.io.FileNotFoundException;
7.47 -import java.io.IOException;
7.48 -import java.io.InputStream;
7.49 -import java.io.InputStreamReader;
7.50 -import java.io.OutputStream;
7.51 -import java.io.OutputStreamWriter;
7.52 -import java.io.PrintWriter;
7.53 -import java.lang.annotation.Annotation;
7.54 -import java.util.ArrayList;
7.55 -import java.util.Collection;
7.56 -import java.util.Collections;
7.57 -import java.util.HashMap;
7.58 -import java.util.LinkedList;
7.59 -import java.util.List;
7.60 -import java.util.Map;
7.61 -import java.util.Set;
7.62 -import javax.annotation.processing.AbstractProcessor;
7.63 -import javax.annotation.processing.Completion;
7.64 -import javax.annotation.processing.RoundEnvironment;
7.65 -import javax.annotation.processing.SupportedAnnotationTypes;
7.66 -import javax.annotation.processing.SupportedSourceVersion;
7.67 -import javax.lang.model.SourceVersion;
7.68 -import javax.lang.model.element.AnnotationMirror;
7.69 -import javax.lang.model.element.AnnotationValue;
7.70 -import javax.lang.model.element.Element;
7.71 -import javax.lang.model.element.ExecutableElement;
7.72 -import javax.lang.model.element.Modifier;
7.73 -import javax.lang.model.element.TypeElement;
7.74 -import javax.lang.model.type.MirroredTypeException;
7.75 -import javax.lang.model.type.TypeKind;
7.76 -import javax.lang.model.type.TypeMirror;
7.77 -import javax.lang.model.util.ElementFilter;
7.78 -import javax.tools.Diagnostic.Kind;
7.79 -import javax.tools.FileObject;
7.80 -import javax.tools.StandardLocation;
7.81 -import org.openide.util.lookup.ServiceProvider;
7.82 -import org.openide.util.lookup.ServiceProviders;
7.83 -
7.84 -@SupportedSourceVersion(SourceVersion.RELEASE_6)
7.85 -@SupportedAnnotationTypes({"org.openide.util.lookup.ServiceProvider", "org.openide.util.lookup.ServiceProviders"})
7.86 -public class ServiceProviderProcessor extends AbstractProcessor {
7.87 -
7.88 - /** public for ServiceLoader */
7.89 - public ServiceProviderProcessor() {}
7.90 -
7.91 - private final Map<String, List<String>> outputFiles = new HashMap<String,List<String>>();
7.92 - private final Map<String, List<Element>> originatingElements = new HashMap<String,List<Element>>();
7.93 -
7.94 - @Override
7.95 - public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
7.96 - if (roundEnv.errorRaised()) {
7.97 - return false;
7.98 - }
7.99 - if (roundEnv.processingOver()) {
7.100 - writeServices();
7.101 - return false;
7.102 - } else {
7.103 - for (Element el : roundEnv.getElementsAnnotatedWith(ServiceProvider.class)) {
7.104 - TypeElement clazz = (TypeElement) el;
7.105 - if (!verifyServiceProviderSignature(clazz)) {
7.106 - continue;
7.107 - }
7.108 - ServiceProvider sp = clazz.getAnnotation(ServiceProvider.class);
7.109 - register(clazz, sp);
7.110 - }
7.111 - for (Element el : roundEnv.getElementsAnnotatedWith(ServiceProviders.class)) {
7.112 - TypeElement clazz = (TypeElement) el;
7.113 - if (!verifyServiceProviderSignature(clazz)) {
7.114 - continue;
7.115 - }
7.116 - ServiceProviders spp = clazz.getAnnotation(ServiceProviders.class);
7.117 - for (ServiceProvider sp : spp.value()) {
7.118 - register(clazz, sp);
7.119 - }
7.120 - }
7.121 - return true;
7.122 - }
7.123 - }
7.124 -
7.125 - private void register(TypeElement clazz, ServiceProvider svc) {
7.126 - TypeMirror type;
7.127 - try {
7.128 - svc.service();
7.129 - assert false;
7.130 - return;
7.131 - } catch (MirroredTypeException e) {
7.132 - type = e.getTypeMirror();
7.133 - }
7.134 - String impl = processingEnv.getElementUtils().getBinaryName(clazz).toString();
7.135 - String xface = processingEnv.getElementUtils().getBinaryName((TypeElement) processingEnv.getTypeUtils().asElement(type)).toString();
7.136 - if (!processingEnv.getTypeUtils().isAssignable(clazz.asType(), type)) {
7.137 - AnnotationMirror ann = findAnnotationMirror(clazz, ServiceProvider.class);
7.138 - processingEnv.getMessager().printMessage(Kind.ERROR, impl + " is not assignable to " + xface,
7.139 - clazz, ann, findAnnotationValue(ann, "service"));
7.140 - return;
7.141 - }
7.142 - processingEnv.getMessager().printMessage(Kind.NOTE, impl + " to be registered as a " + xface);
7.143 - String rsrc = (svc.path().length() > 0 ? "META-INF/namedservices/" + svc.path() + "/" : "META-INF/services/") + xface;
7.144 - {
7.145 - List<Element> origEls = originatingElements.get(rsrc);
7.146 - if (origEls == null) {
7.147 - origEls = new ArrayList<Element>();
7.148 - originatingElements.put(rsrc, origEls);
7.149 - }
7.150 - origEls.add(clazz);
7.151 - }
7.152 - List<String> lines = outputFiles.get(rsrc);
7.153 - if (lines == null) {
7.154 - lines = new ArrayList<String>();
7.155 - try {
7.156 - try {
7.157 - FileObject in = processingEnv.getFiler().getResource(StandardLocation.SOURCE_PATH, "", rsrc);
7.158 - in.openInputStream().close();
7.159 - processingEnv.getMessager().printMessage(Kind.ERROR,
7.160 - "Cannot generate " + rsrc + " because it already exists in sources: " + in.toUri());
7.161 - return;
7.162 - } catch (FileNotFoundException x) {
7.163 - // Good.
7.164 - }
7.165 - try {
7.166 - FileObject in = processingEnv.getFiler().getResource(StandardLocation.CLASS_OUTPUT, "", rsrc);
7.167 - InputStream is = in.openInputStream();
7.168 - try {
7.169 - BufferedReader r = new BufferedReader(new InputStreamReader(is, "UTF-8"));
7.170 - String line;
7.171 - while ((line = r.readLine()) != null) {
7.172 - lines.add(line);
7.173 - }
7.174 - } finally {
7.175 - is.close();
7.176 - }
7.177 - } catch (FileNotFoundException x) {
7.178 - // OK, created for the first time
7.179 - }
7.180 - } catch (IOException x) {
7.181 - processingEnv.getMessager().printMessage(Kind.ERROR, x.toString());
7.182 - return;
7.183 - }
7.184 - outputFiles.put(rsrc, lines);
7.185 - }
7.186 - int idx = lines.indexOf(impl);
7.187 - if (idx != -1) {
7.188 - lines.remove(idx);
7.189 - while (lines.size() > idx && lines.get(idx).matches("#position=.+|#-.+")) {
7.190 - lines.remove(idx);
7.191 - }
7.192 - }
7.193 - lines.add(impl);
7.194 - if (svc.position() != Integer.MAX_VALUE) {
7.195 - lines.add("#position=" + svc.position());
7.196 - }
7.197 - for (String exclude : svc.supersedes()) {
7.198 - lines.add("#-" + exclude);
7.199 - }
7.200 - }
7.201 -
7.202 - private boolean verifyServiceProviderSignature(TypeElement clazz) {
7.203 - AnnotationMirror ann = findAnnotationMirror(clazz, ServiceProvider.class);
7.204 - if (!clazz.getModifiers().contains(Modifier.PUBLIC)) {
7.205 - processingEnv.getMessager().printMessage(Kind.ERROR, clazz + " must be public", clazz, ann);
7.206 - return false;
7.207 - }
7.208 - if (clazz.getModifiers().contains(Modifier.ABSTRACT)) {
7.209 - processingEnv.getMessager().printMessage(Kind.ERROR, clazz + " must not be abstract", clazz, ann);
7.210 - return false;
7.211 - }
7.212 - {
7.213 - boolean hasDefaultCtor = false;
7.214 - for (ExecutableElement constructor : ElementFilter.constructorsIn(clazz.getEnclosedElements())) {
7.215 - if (constructor.getModifiers().contains(Modifier.PUBLIC) && constructor.getParameters().isEmpty()) {
7.216 - hasDefaultCtor = true;
7.217 - break;
7.218 - }
7.219 - }
7.220 - if (!hasDefaultCtor) {
7.221 - processingEnv.getMessager().printMessage(Kind.ERROR, clazz + " must have a public no-argument constructor", clazz, ann);
7.222 - return false;
7.223 - }
7.224 - }
7.225 - return true;
7.226 - }
7.227 -
7.228 - private void writeServices() {
7.229 - for (Map.Entry<String, List<String>> entry : outputFiles.entrySet()) {
7.230 - try {
7.231 - FileObject out = processingEnv.getFiler().createResource(StandardLocation.CLASS_OUTPUT, "", entry.getKey(),
7.232 - originatingElements.get(entry.getKey()).toArray(new Element[0]));
7.233 - OutputStream os = out.openOutputStream();
7.234 - try {
7.235 - PrintWriter w = new PrintWriter(new OutputStreamWriter(os, "UTF-8"));
7.236 - for (String line : entry.getValue()) {
7.237 - w.println(line);
7.238 - }
7.239 - w.flush();
7.240 - w.close();
7.241 - } finally {
7.242 - os.close();
7.243 - }
7.244 - } catch (IOException x) {
7.245 - processingEnv.getMessager().printMessage(Kind.ERROR, "Failed to write to " + entry.getKey() + ": " + x.toString());
7.246 - }
7.247 - }
7.248 - }
7.249 -
7.250 - /**
7.251 - * @param element a source element
7.252 - * @param annotation a type of annotation
7.253 - * @return the instance of that annotation on the element, or null if not found
7.254 - */
7.255 - private AnnotationMirror findAnnotationMirror(Element element, Class<? extends Annotation> annotation) {
7.256 - for (AnnotationMirror ann : element.getAnnotationMirrors()) {
7.257 - if (processingEnv.getElementUtils().getBinaryName((TypeElement) ann.getAnnotationType().asElement()).
7.258 - contentEquals(annotation.getName())) {
7.259 - return ann;
7.260 - }
7.261 - }
7.262 - return null;
7.263 - }
7.264 -
7.265 - /**
7.266 - * @param annotation an annotation instance (null permitted)
7.267 - * @param name the name of an attribute of that annotation
7.268 - * @return the corresponding value if found
7.269 - */
7.270 - private AnnotationValue findAnnotationValue(AnnotationMirror annotation, String name) {
7.271 - if (annotation != null) {
7.272 - for (Map.Entry<? extends ExecutableElement,? extends AnnotationValue> entry : annotation.getElementValues().entrySet()) {
7.273 - if (entry.getKey().getSimpleName().contentEquals(name)) {
7.274 - return entry.getValue();
7.275 - }
7.276 - }
7.277 - }
7.278 - return null;
7.279 - }
7.280 -
7.281 - @Override
7.282 - public Iterable<? extends Completion> getCompletions(Element annotated, AnnotationMirror annotation, ExecutableElement attr, String userText) {
7.283 - if (processingEnv == null || annotated == null || !annotated.getKind().isClass()) {
7.284 - return Collections.emptyList();
7.285 - }
7.286 -
7.287 - if ( annotation == null
7.288 - || !"org.openide.util.lookup.ServiceProvider".contentEquals(((TypeElement) annotation.getAnnotationType().asElement()).getQualifiedName())) {
7.289 - return Collections.emptyList();
7.290 - }
7.291 -
7.292 - if (!"service".contentEquals(attr.getSimpleName())) {
7.293 - return Collections.emptyList();
7.294 - }
7.295 -
7.296 - TypeElement jlObject = processingEnv.getElementUtils().getTypeElement("java.lang.Object");
7.297 -
7.298 - if (jlObject == null) {
7.299 - return Collections.emptyList();
7.300 - }
7.301 -
7.302 - Collection<Completion> result = new LinkedList<Completion>();
7.303 - List<TypeElement> toProcess = new LinkedList<TypeElement>();
7.304 -
7.305 - toProcess.add((TypeElement) annotated);
7.306 -
7.307 - while (!toProcess.isEmpty()) {
7.308 - TypeElement c = toProcess.remove(0);
7.309 -
7.310 - result.add(new TypeCompletion(c.getQualifiedName().toString() + ".class"));
7.311 -
7.312 - List<TypeMirror> parents = new LinkedList<TypeMirror>();
7.313 -
7.314 - parents.add(c.getSuperclass());
7.315 - parents.addAll(c.getInterfaces());
7.316 -
7.317 - for (TypeMirror tm : parents) {
7.318 - if (tm == null || tm.getKind() != TypeKind.DECLARED) {
7.319 - continue;
7.320 - }
7.321 -
7.322 - TypeElement type = (TypeElement) processingEnv.getTypeUtils().asElement(tm);
7.323 -
7.324 - if (!jlObject.equals(type)) {
7.325 - toProcess.add(type);
7.326 - }
7.327 - }
7.328 - }
7.329 -
7.330 - return result;
7.331 - }
7.332 -
7.333 - private static final class TypeCompletion implements Completion {
7.334 -
7.335 - private final String type;
7.336 -
7.337 - public TypeCompletion(String type) {
7.338 - this.type = type;
7.339 - }
7.340 -
7.341 - public String getValue() {
7.342 - return type;
7.343 - }
7.344 -
7.345 - public String getMessage() {
7.346 - return null;
7.347 - }
7.348 -
7.349 - }
7.350 -
7.351 -}
8.1 --- a/openide.util/test/unit/src/org/netbeans/modules/openide/util/ServiceProviderProcessorTest.java Sat Oct 31 15:30:02 2009 +0100
8.2 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
8.3 @@ -1,180 +0,0 @@
8.4 -/*
8.5 - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
8.6 - *
8.7 - * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
8.8 - *
8.9 - * The contents of this file are subject to the terms of either the GNU
8.10 - * General Public License Version 2 only ("GPL") or the Common
8.11 - * Development and Distribution License("CDDL") (collectively, the
8.12 - * "License"). You may not use this file except in compliance with the
8.13 - * License. You can obtain a copy of the License at
8.14 - * http://www.netbeans.org/cddl-gplv2.html
8.15 - * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
8.16 - * specific language governing permissions and limitations under the
8.17 - * License. When distributing the software, include this License Header
8.18 - * Notice in each file and include the License file at
8.19 - * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
8.20 - * particular file as subject to the "Classpath" exception as provided
8.21 - * by Sun in the GPL Version 2 section of the License file that
8.22 - * accompanied this code. If applicable, add the following below the
8.23 - * License Header, with the fields enclosed by brackets [] replaced by
8.24 - * your own identifying information:
8.25 - * "Portions Copyrighted [year] [name of copyright owner]"
8.26 - *
8.27 - * If you wish your version of this file to be governed by only the CDDL
8.28 - * or only the GPL Version 2, indicate your decision by adding
8.29 - * "[Contributor] elects to include this software in this distribution
8.30 - * under the [CDDL or GPL Version 2] license." If you do not indicate a
8.31 - * single choice of license, a recipient has the option to distribute
8.32 - * your version of this file under either the CDDL, the GPL Version 2 or
8.33 - * to extend the choice of license to its licensees as provided above.
8.34 - * However, if you add GPL Version 2 code and therefore, elected the GPL
8.35 - * Version 2 license, then the option applies only if the new code is
8.36 - * made subject to such option by the copyright holder.
8.37 - *
8.38 - * Contributor(s):
8.39 - *
8.40 - * Portions Copyrighted 2008 Sun Microsystems, Inc.
8.41 - */
8.42 -
8.43 -package org.netbeans.modules.openide.util;
8.44 -
8.45 -import java.io.ByteArrayOutputStream;
8.46 -import java.io.File;
8.47 -import java.util.ArrayList;
8.48 -import java.util.Arrays;
8.49 -import java.util.Collections;
8.50 -import java.util.Comparator;
8.51 -import java.util.List;
8.52 -import org.netbeans.junit.NbTestCase;
8.53 -import org.openide.util.Lookup;
8.54 -import org.openide.util.lookup.Lookups;
8.55 -import org.openide.util.lookup.ServiceProvider;
8.56 -import org.openide.util.lookup.ServiceProviders;
8.57 -import org.openide.util.test.AnnotationProcessorTestUtils;
8.58 -
8.59 -public class ServiceProviderProcessorTest extends NbTestCase {
8.60 -
8.61 - public ServiceProviderProcessorTest(String n) {
8.62 - super(n);
8.63 - }
8.64 -
8.65 - private static List<Class<?>> classesOf(Iterable<?> objects) {
8.66 - List<Class<?>> cs = new ArrayList<Class<?>>();
8.67 - for (Object o : objects) {
8.68 - cs.add(o.getClass());
8.69 - }
8.70 - return cs;
8.71 - }
8.72 -
8.73 - private static List<Class<?>> classesOfLookup(Class<?> xface) {
8.74 - return classesOf(Lookup.getDefault().lookupAll(xface));
8.75 - }
8.76 -
8.77 - private static List<Class<?>> sortClassList(List<Class<?>> classes) {
8.78 - List<Class<?>> sorted = new ArrayList<Class<?>>(classes);
8.79 - Collections.sort(sorted, new Comparator<Class<?>>() {
8.80 - public int compare(Class<?> c1, Class<?> c2) {
8.81 - return c1.getName().compareTo(c2.getName());
8.82 - }
8.83 - });
8.84 - return sorted;
8.85 - }
8.86 -
8.87 - public void testBasicUsage() throws Exception {
8.88 - assertEquals(Collections.singletonList(Implementation.class), classesOfLookup(Interface.class));
8.89 - }
8.90 - public interface Interface {}
8.91 - @ServiceProvider(service=Interface.class)
8.92 - public static class Implementation implements Interface {}
8.93 -
8.94 - public void testPosition() throws Exception {
8.95 - assertEquals(Arrays.<Class<?>>asList(OrderedImpl3.class, OrderedImpl2.class, OrderedImpl1.class), classesOfLookup(OrderedInterface.class));
8.96 - }
8.97 - public interface OrderedInterface {}
8.98 - @ServiceProvider(service=OrderedInterface.class)
8.99 - public static class OrderedImpl1 implements OrderedInterface {}
8.100 - @ServiceProvider(service=OrderedInterface.class, position=200)
8.101 - public static class OrderedImpl2 implements OrderedInterface {}
8.102 - @ServiceProvider(service=OrderedInterface.class, position=100)
8.103 - public static class OrderedImpl3 implements OrderedInterface {}
8.104 -
8.105 - public void testPath() throws Exception {
8.106 - assertEquals(Collections.singletonList(PathImplementation.class), classesOf(Lookups.forPath("some/path").lookupAll(Interface.class)));
8.107 - }
8.108 - @ServiceProvider(service=Interface.class, path="some/path")
8.109 - public static class PathImplementation implements Interface {}
8.110 -
8.111 - public void testSupersedes() throws Exception {
8.112 - assertEquals(Arrays.<Class<?>>asList(Overrider.class, Unrelated.class), sortClassList(classesOfLookup(CancellableInterface.class)));
8.113 - }
8.114 - public interface CancellableInterface {}
8.115 - @ServiceProvider(service=CancellableInterface.class)
8.116 - public static class Overridden implements CancellableInterface {}
8.117 - @ServiceProvider(service=CancellableInterface.class, supersedes="org.netbeans.modules.openide.util.ServiceProviderProcessorTest$Overridden")
8.118 - public static class Overrider implements CancellableInterface {}
8.119 - @ServiceProvider(service=CancellableInterface.class)
8.120 - public static class Unrelated implements CancellableInterface {}
8.121 -
8.122 - public void testMultipleRegistrations() throws Exception {
8.123 - assertEquals(Collections.singletonList(Multitasking.class), classesOfLookup(Interface1.class));
8.124 - assertEquals(Collections.singletonList(Multitasking.class), classesOfLookup(Interface2.class));
8.125 - }
8.126 - public interface Interface1 {}
8.127 - public interface Interface2 {}
8.128 - @ServiceProviders({@ServiceProvider(service=Interface1.class), @ServiceProvider(service=Interface2.class)})
8.129 - public static class Multitasking implements Interface1, Interface2 {}
8.130 -
8.131 - public void testErrorReporting() throws Exception {
8.132 - clearWorkDir();
8.133 - File src = new File(getWorkDir(), "src");
8.134 - File dest = new File(getWorkDir(), "classes");
8.135 - String xfaceName = Interface.class.getCanonicalName();
8.136 -
8.137 - AnnotationProcessorTestUtils.makeSource(src, "p.C1",
8.138 - "@org.openide.util.lookup.ServiceProvider(service=" + xfaceName + ".class)",
8.139 - "public class C1 implements " + xfaceName + " {}");
8.140 - ByteArrayOutputStream baos = new ByteArrayOutputStream();
8.141 - assertTrue(AnnotationProcessorTestUtils.runJavac(src, "C1", dest, null, baos));
8.142 -
8.143 - AnnotationProcessorTestUtils.makeSource(src, "p.C2",
8.144 - "@org.openide.util.lookup.ServiceProvider(service=" + xfaceName + ".class)",
8.145 - "class C2 implements " + xfaceName + " {}");
8.146 - baos = new ByteArrayOutputStream();
8.147 - assertFalse(AnnotationProcessorTestUtils.runJavac(src, "C2", dest, null, baos));
8.148 - assertTrue(baos.toString(), baos.toString().contains("public"));
8.149 -
8.150 - AnnotationProcessorTestUtils.makeSource(src, "p.C3",
8.151 - "@org.openide.util.lookup.ServiceProvider(service=" + xfaceName + ".class)",
8.152 - "public class C3 implements " + xfaceName + " {",
8.153 - "public C3(boolean x) {}",
8.154 - "}");
8.155 - baos = new ByteArrayOutputStream();
8.156 - assertFalse(AnnotationProcessorTestUtils.runJavac(src, "C3", dest, null, baos));
8.157 - assertTrue(baos.toString(), baos.toString().contains("constructor"));
8.158 -
8.159 - AnnotationProcessorTestUtils.makeSource(src, "p.C4",
8.160 - "@org.openide.util.lookup.ServiceProvider(service=" + xfaceName + ".class)",
8.161 - "public class C4 implements " + xfaceName + " {",
8.162 - "C4() {}",
8.163 - "}");
8.164 - baos = new ByteArrayOutputStream();
8.165 - assertFalse(AnnotationProcessorTestUtils.runJavac(src, "C4", dest, null, baos));
8.166 - assertTrue(baos.toString(), baos.toString().contains("constructor"));
8.167 -
8.168 - AnnotationProcessorTestUtils.makeSource(src, "p.C5",
8.169 - "@org.openide.util.lookup.ServiceProvider(service=" + xfaceName + ".class)",
8.170 - "public abstract class C5 implements " + xfaceName + " {}");
8.171 - baos = new ByteArrayOutputStream();
8.172 - assertFalse(AnnotationProcessorTestUtils.runJavac(src, "C5", dest, null, baos));
8.173 - assertTrue(baos.toString(), baos.toString().contains("abstract"));
8.174 -
8.175 - AnnotationProcessorTestUtils.makeSource(src, "p.C6",
8.176 - "@org.openide.util.lookup.ServiceProvider(service=" + xfaceName + ".class)",
8.177 - "public class C6 {}");
8.178 - baos = new ByteArrayOutputStream();
8.179 - assertFalse(AnnotationProcessorTestUtils.runJavac(src, "C6", dest, null, baos));
8.180 - assertTrue(baos.toString(), baos.toString().contains("assignable"));
8.181 - }
8.182 -
8.183 -}
9.1 --- a/openide.util/test/unit/src/org/openide/util/test/AnnotationProcessorTestUtils.java Sat Oct 31 15:30:02 2009 +0100
9.2 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
9.3 @@ -1,139 +0,0 @@
9.4 -/*
9.5 - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
9.6 - *
9.7 - * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
9.8 - *
9.9 - * The contents of this file are subject to the terms of either the GNU
9.10 - * General Public License Version 2 only ("GPL") or the Common
9.11 - * Development and Distribution License("CDDL") (collectively, the
9.12 - * "License"). You may not use this file except in compliance with the
9.13 - * License. You can obtain a copy of the License at
9.14 - * http://www.netbeans.org/cddl-gplv2.html
9.15 - * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
9.16 - * specific language governing permissions and limitations under the
9.17 - * License. When distributing the software, include this License Header
9.18 - * Notice in each file and include the License file at
9.19 - * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
9.20 - * particular file as subject to the "Classpath" exception as provided
9.21 - * by Sun in the GPL Version 2 section of the License file that
9.22 - * accompanied this code. If applicable, add the following below the
9.23 - * License Header, with the fields enclosed by brackets [] replaced by
9.24 - * your own identifying information:
9.25 - * "Portions Copyrighted [year] [name of copyright owner]"
9.26 - *
9.27 - * If you wish your version of this file to be governed by only the CDDL
9.28 - * or only the GPL Version 2, indicate your decision by adding
9.29 - * "[Contributor] elects to include this software in this distribution
9.30 - * under the [CDDL or GPL Version 2] license." If you do not indicate a
9.31 - * single choice of license, a recipient has the option to distribute
9.32 - * your version of this file under either the CDDL, the GPL Version 2 or
9.33 - * to extend the choice of license to its licensees as provided above.
9.34 - * However, if you add GPL Version 2 code and therefore, elected the GPL
9.35 - * Version 2 license, then the option applies only if the new code is
9.36 - * made subject to such option by the copyright holder.
9.37 - *
9.38 - * Contributor(s):
9.39 - *
9.40 - * Portions Copyrighted 2008 Sun Microsystems, Inc.
9.41 - */
9.42 -
9.43 -package org.openide.util.test;
9.44 -
9.45 -import java.io.File;
9.46 -import java.io.FileWriter;
9.47 -import java.io.IOException;
9.48 -import java.io.OutputStream;
9.49 -import java.io.PrintWriter;
9.50 -import java.io.Writer;
9.51 -import java.util.ArrayList;
9.52 -import java.util.List;
9.53 -import java.util.regex.Pattern;
9.54 -import javax.tools.JavaCompiler;
9.55 -import javax.tools.ToolProvider;
9.56 -import junit.framework.Assert;
9.57 -
9.58 -/**
9.59 - * Utilities useful to those testing JSR 269 annotation processors.
9.60 - * <p>If you just want to test that the output of the processor is correct,
9.61 - * you do not need to do anything special:
9.62 - * just use the annotation on some sample classes nested inside your unit test.
9.63 - * They will be processed, and you check that your SPI loads them correctly.
9.64 - * These utilities are useful mainly in case you want to check that the processor
9.65 - * rejects erroneous sources, and that any messages it prints are reasonable;
9.66 - * that it behaves correctly on incremental compilations; etc.
9.67 - */
9.68 -public class AnnotationProcessorTestUtils {
9.69 -
9.70 - private AnnotationProcessorTestUtils() {}
9.71 -
9.72 - /**
9.73 - * Create a source file.
9.74 - * @param dir source root
9.75 - * @param clazz a fully-qualified class name
9.76 - * @param content lines of text (skip package decl)
9.77 - */
9.78 - public static void makeSource(File dir, String clazz, String... content) throws IOException {
9.79 - File f = new File(dir, clazz.replace('.', File.separatorChar) + ".java");
9.80 - f.getParentFile().mkdirs();
9.81 - Writer w = new FileWriter(f);
9.82 - try {
9.83 - PrintWriter pw = new PrintWriter(w);
9.84 - String pkg = clazz.replaceFirst("\\.[^.]+$", "");
9.85 - if (!pkg.equals(clazz)) {
9.86 - pw.println("package " + pkg + ";");
9.87 - }
9.88 - for (String line : content) {
9.89 - pw.println(line);
9.90 - }
9.91 - pw.flush();
9.92 - } finally {
9.93 - w.close();
9.94 - }
9.95 - }
9.96 -
9.97 - /**
9.98 - * Run the Java compiler.
9.99 - * (A JSR 199 implementation must be available.)
9.100 - * @param src a source root (runs javac on all *.java it finds matching {@code srcIncludes})
9.101 - * @param srcIncludes a pattern of source files names without path to compile (useful for testing incremental compiles), or null for all
9.102 - * @param dest a dest dir to compile classes to
9.103 - * @param cp classpath entries; if null, use Java classpath of test
9.104 - * @param stderr output stream to print messages to, or null for test console (i.e. do not capture)
9.105 - * @return true if compilation succeeded, false if it failed
9.106 - */
9.107 - public static boolean runJavac(File src, String srcIncludes, File dest, File[] cp, OutputStream stderr) {
9.108 - List<String> args = new ArrayList<String>();
9.109 - args.add("-classpath");
9.110 - if (cp != null) {
9.111 - StringBuffer b = new StringBuffer();
9.112 - for (File entry : cp) {
9.113 - b.append(File.pathSeparatorChar);
9.114 - b.append(entry.getAbsolutePath());
9.115 - }
9.116 - args.add(b.toString());
9.117 - } else {
9.118 - args.add(System.getProperty("java.class.path"));
9.119 - }
9.120 - args.add("-d");
9.121 - args.add(dest.getAbsolutePath());
9.122 - args.add("-sourcepath");
9.123 - args.add(src.getAbsolutePath());
9.124 - dest.mkdirs();
9.125 - scan(args, src, srcIncludes);
9.126 - JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
9.127 - Assert.assertNotNull("no JSR 199 compiler impl found; try e.g.: " +
9.128 - "test.unit.run.cp.extra=${nb_all}/apisupport.harness/external/openjdk-javac-6-b12.jar", compiler);
9.129 - //System.err.println("running javac with args: " + args);
9.130 - return compiler.run(null, null, stderr, args.toArray(new String[args.size()])) == 0;
9.131 - }
9.132 - private static void scan(List<String> names, File f, String includes) {
9.133 - if (f.isDirectory()) {
9.134 - for (File kid : f.listFiles()) {
9.135 - scan(names, kid, includes);
9.136 - }
9.137 - } else if (f.getName().endsWith(".java") && (includes == null || Pattern.compile(includes).matcher(f.getName()).find())) {
9.138 - names.add(f.getAbsolutePath());
9.139 - }
9.140 - }
9.141 -
9.142 -}