Issue #20838: declarative registration of URLStreamHandler instances by protocol.
1.1 --- a/openide.util/apichanges.xml Fri Oct 30 10:41:18 2009 -0400
1.2 +++ b/openide.util/apichanges.xml Fri Oct 30 13:29:24 2009 -0400
1.3 @@ -49,6 +49,27 @@
1.4 <apidef name="actions">Actions API</apidef>
1.5 </apidefs>
1.6 <changes>
1.7 + <change id="URLStreamHandlerRegistration">
1.8 + <api name="util"/>
1.9 + <summary>Added <code>@URLStreamHandlerRegistration</code></summary>
1.10 + <version major="7" minor="31"/>
1.11 + <date day="30" month="10" year="2009"/>
1.12 + <author login="jglick"/>
1.13 + <compatibility addition="yes">
1.14 + <p>
1.15 + Modules registering <code>URLStreamHandlerFactory</code>s into
1.16 + global lookup will still work but are advised to switch to this
1.17 + annotation, which is both easier to use and more efficient.
1.18 + </p>
1.19 + </compatibility>
1.20 + <description>
1.21 + <p>
1.22 + Introduced an annotation to register URL protocols.
1.23 + </p>
1.24 + </description>
1.25 + <class package="org.openide.util" name="URLStreamHandlerRegistration"/>
1.26 + <issue number="20838"/>
1.27 + </change>
1.28 <change id="ImageUtilities.createDisabled">
1.29 <api name="util"/>
1.30 <summary><code>ImageUtilities.createDisabledIcon</code> and <code>ImageUtilities.createDisabledImage</code> added.</summary>
2.1 --- a/openide.util/nbproject/project.properties Fri Oct 30 10:41:18 2009 -0400
2.2 +++ b/openide.util/nbproject/project.properties Fri Oct 30 13:29:24 2009 -0400
2.3 @@ -42,7 +42,7 @@
2.4 module.jar.dir=lib
2.5 cp.extra=${nb_all}/apisupport.harness/external/openjdk-javac-6-b12.jar
2.6
2.7 -spec.version.base=7.30.0
2.8 +spec.version.base=7.31.0
2.9
2.10 # For XMLSerializer, needed for XMLUtil.write to work w/ namespaces under JDK 1.4:
2.11
3.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
3.2 +++ b/openide.util/src/META-INF/services/java.net.URLStreamHandlerFactory Fri Oct 30 13:29:24 2009 -0400
3.3 @@ -0,0 +1,1 @@
3.4 +org.netbeans.modules.openide.util.ProxyURLStreamHandlerFactory
4.1 --- a/openide.util/src/META-INF/services/javax.annotation.processing.Processor Fri Oct 30 10:41:18 2009 -0400
4.2 +++ b/openide.util/src/META-INF/services/javax.annotation.processing.Processor Fri Oct 30 13:29:24 2009 -0400
4.3 @@ -1,1 +1,2 @@
4.4 org.netbeans.modules.openide.util.ServiceProviderProcessor
4.5 +org.netbeans.modules.openide.util.URLStreamHandlerRegistrationProcessor
5.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
5.2 +++ b/openide.util/src/org/netbeans/modules/openide/util/AbstractServiceProviderProcessor.java Fri Oct 30 13:29:24 2009 -0400
5.3 @@ -0,0 +1,289 @@
5.4 +/*
5.5 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
5.6 + *
5.7 + * Copyright 2009 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 2009 Sun Microsystems, Inc.
5.41 + */
5.42 +
5.43 +package org.netbeans.modules.openide.util;
5.44 +
5.45 +import java.io.BufferedReader;
5.46 +import java.io.FileNotFoundException;
5.47 +import java.io.IOException;
5.48 +import java.io.InputStream;
5.49 +import java.io.InputStreamReader;
5.50 +import java.io.OutputStream;
5.51 +import java.io.OutputStreamWriter;
5.52 +import java.io.PrintWriter;
5.53 +import java.lang.annotation.Annotation;
5.54 +import java.util.ArrayList;
5.55 +import java.util.HashMap;
5.56 +import java.util.List;
5.57 +import java.util.Map;
5.58 +import java.util.Set;
5.59 +import java.util.WeakHashMap;
5.60 +import javax.annotation.processing.AbstractProcessor;
5.61 +import javax.annotation.processing.ProcessingEnvironment;
5.62 +import javax.annotation.processing.RoundEnvironment;
5.63 +import javax.lang.model.element.AnnotationMirror;
5.64 +import javax.lang.model.element.AnnotationValue;
5.65 +import javax.lang.model.element.Element;
5.66 +import javax.lang.model.element.ExecutableElement;
5.67 +import javax.lang.model.element.Modifier;
5.68 +import javax.lang.model.element.TypeElement;
5.69 +import javax.lang.model.type.TypeMirror;
5.70 +import javax.lang.model.util.ElementFilter;
5.71 +import javax.tools.Diagnostic.Kind;
5.72 +import javax.tools.FileObject;
5.73 +import javax.tools.StandardLocation;
5.74 +
5.75 +/**
5.76 + * Infrastructure for generating {@code META-INF/services/*} and
5.77 + * {@code META-INF/namedservices/*} registrations from annotations.
5.78 + */
5.79 +public abstract class AbstractServiceProviderProcessor extends AbstractProcessor {
5.80 +
5.81 + private final Map<ProcessingEnvironment,Map<String,List<String>>> outputFilesByProcessor = new WeakHashMap<ProcessingEnvironment,Map<String,List<String>>>();
5.82 + private final Map<ProcessingEnvironment,Map<String,List<Element>>> originatingElementsByProcessor = new WeakHashMap<ProcessingEnvironment,Map<String,List<Element>>>();
5.83 + private final Map<TypeElement,Boolean> verifiedClasses = new WeakHashMap<TypeElement,Boolean>();
5.84 +
5.85 + /** For access by subclasses. */
5.86 + protected AbstractServiceProviderProcessor() {}
5.87 +
5.88 + public @Override final boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
5.89 + if (roundEnv.errorRaised()) {
5.90 + return false;
5.91 + }
5.92 + if (roundEnv.processingOver()) {
5.93 + writeServices();
5.94 + outputFilesByProcessor.clear();
5.95 + originatingElementsByProcessor.clear();
5.96 + return true;
5.97 + } else {
5.98 + return handleProcess(annotations, roundEnv);
5.99 + }
5.100 + }
5.101 +
5.102 + /**
5.103 + * The regular body of {@link #process}.
5.104 + * Called during regular rounds if there are no outstanding errors.
5.105 + * In the last round, one of the processors will write out generated registrations.
5.106 + * @param annotations as in {@link #process}
5.107 + * @param roundEnv as in {@link #process}
5.108 + * @return as in {@link #process}
5.109 + */
5.110 + protected abstract boolean handleProcess(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv);
5.111 +
5.112 + /**
5.113 + * Register a service.
5.114 + * If the class does not have an appropriate signature, an error will be printed and the registration skipped.
5.115 + * @param clazz the service implementation type
5.116 + * @param annotation the (top-level) annotation registering the service, for diagnostic purposes
5.117 + * @param type the type to which the implementation must be assignable
5.118 + * @param path a path under which to register, or "" if inapplicable
5.119 + * @param position a position at which to register, or {@link Integer#MAX_VALUE} to skip
5.120 + * @param supersedes possibly empty list of implementation to supersede
5.121 + */
5.122 + protected final void register(TypeElement clazz, Class<? extends Annotation> annotation,
5.123 + TypeMirror type, String path, int position, String[] supersedes) {
5.124 + Boolean verify = verifiedClasses.get(clazz);
5.125 + if (verify == null) {
5.126 + verify = verifyServiceProviderSignature(clazz, annotation);
5.127 + verifiedClasses.put(clazz, verify);
5.128 + }
5.129 + if (!verify) {
5.130 + return;
5.131 + }
5.132 + String impl = processingEnv.getElementUtils().getBinaryName(clazz).toString();
5.133 + String xface = processingEnv.getElementUtils().getBinaryName((TypeElement) processingEnv.getTypeUtils().asElement(type)).toString();
5.134 + if (!processingEnv.getTypeUtils().isAssignable(clazz.asType(), type)) {
5.135 + AnnotationMirror ann = findAnnotationMirror(clazz, annotation);
5.136 + processingEnv.getMessager().printMessage(Kind.ERROR, impl + " is not assignable to " + xface,
5.137 + clazz, ann, findAnnotationValue(ann, "service"));
5.138 + return;
5.139 + }
5.140 + processingEnv.getMessager().printMessage(Kind.NOTE,
5.141 + impl + " to be registered as a " + xface + (path.length() > 0 ? " under " + path : ""));
5.142 + String rsrc = (path.length() > 0 ? "META-INF/namedservices/" + path + "/" : "META-INF/services/") + xface;
5.143 + {
5.144 + Map<String,List<Element>> originatingElements = originatingElementsByProcessor.get(processingEnv);
5.145 + if (originatingElements == null) {
5.146 + originatingElements = new HashMap<String,List<Element>>();
5.147 + originatingElementsByProcessor.put(processingEnv, originatingElements);
5.148 + }
5.149 + List<Element> origEls = originatingElements.get(rsrc);
5.150 + if (origEls == null) {
5.151 + origEls = new ArrayList<Element>();
5.152 + originatingElements.put(rsrc, origEls);
5.153 + }
5.154 + origEls.add(clazz);
5.155 + }
5.156 + Map<String,List<String>> outputFiles = outputFilesByProcessor.get(processingEnv);
5.157 + if (outputFiles == null) {
5.158 + outputFiles = new HashMap<String,List<String>>();
5.159 + outputFilesByProcessor.put(processingEnv, outputFiles);
5.160 + }
5.161 + List<String> lines = outputFiles.get(rsrc);
5.162 + if (lines == null) {
5.163 + lines = new ArrayList<String>();
5.164 + try {
5.165 + try {
5.166 + FileObject in = processingEnv.getFiler().getResource(StandardLocation.SOURCE_PATH, "", rsrc);
5.167 + in.openInputStream().close();
5.168 + processingEnv.getMessager().printMessage(Kind.ERROR,
5.169 + "Cannot generate " + rsrc + " because it already exists in sources: " + in.toUri());
5.170 + return;
5.171 + } catch (FileNotFoundException x) {
5.172 + // Good.
5.173 + }
5.174 + try {
5.175 + FileObject in = processingEnv.getFiler().getResource(StandardLocation.CLASS_OUTPUT, "", rsrc);
5.176 + InputStream is = in.openInputStream();
5.177 + try {
5.178 + BufferedReader r = new BufferedReader(new InputStreamReader(is, "UTF-8"));
5.179 + String line;
5.180 + while ((line = r.readLine()) != null) {
5.181 + lines.add(line);
5.182 + }
5.183 + } finally {
5.184 + is.close();
5.185 + }
5.186 + } catch (FileNotFoundException x) {
5.187 + // OK, created for the first time
5.188 + }
5.189 + } catch (IOException x) {
5.190 + processingEnv.getMessager().printMessage(Kind.ERROR, x.toString());
5.191 + return;
5.192 + }
5.193 + outputFiles.put(rsrc, lines);
5.194 + }
5.195 + int idx = lines.indexOf(impl);
5.196 + if (idx != -1) {
5.197 + lines.remove(idx);
5.198 + while (lines.size() > idx && lines.get(idx).matches("#position=.+|#-.+")) {
5.199 + lines.remove(idx);
5.200 + }
5.201 + }
5.202 + lines.add(impl);
5.203 + if (position != Integer.MAX_VALUE) {
5.204 + lines.add("#position=" + position);
5.205 + }
5.206 + for (String exclude : supersedes) {
5.207 + lines.add("#-" + exclude);
5.208 + }
5.209 + }
5.210 +
5.211 + /**
5.212 + * @param element a source element
5.213 + * @param annotation a type of annotation
5.214 + * @return the instance of that annotation on the element, or null if not found
5.215 + */
5.216 + private AnnotationMirror findAnnotationMirror(Element element, Class<? extends Annotation> annotation) {
5.217 + for (AnnotationMirror ann : element.getAnnotationMirrors()) {
5.218 + if (processingEnv.getElementUtils().getBinaryName((TypeElement) ann.getAnnotationType().asElement()).
5.219 + contentEquals(annotation.getName())) {
5.220 + return ann;
5.221 + }
5.222 + }
5.223 + return null;
5.224 + }
5.225 +
5.226 + /**
5.227 + * @param annotation an annotation instance (null permitted)
5.228 + * @param name the name of an attribute of that annotation
5.229 + * @return the corresponding value if found
5.230 + */
5.231 + private AnnotationValue findAnnotationValue(AnnotationMirror annotation, String name) {
5.232 + if (annotation != null) {
5.233 + for (Map.Entry<? extends ExecutableElement,? extends AnnotationValue> entry : annotation.getElementValues().entrySet()) {
5.234 + if (entry.getKey().getSimpleName().contentEquals(name)) {
5.235 + return entry.getValue();
5.236 + }
5.237 + }
5.238 + }
5.239 + return null;
5.240 + }
5.241 +
5.242 + private final boolean verifyServiceProviderSignature(TypeElement clazz, Class<? extends Annotation> annotation) {
5.243 + AnnotationMirror ann = findAnnotationMirror(clazz, annotation);
5.244 + if (!clazz.getModifiers().contains(Modifier.PUBLIC)) {
5.245 + processingEnv.getMessager().printMessage(Kind.ERROR, clazz + " must be public", clazz, ann);
5.246 + return false;
5.247 + }
5.248 + if (clazz.getModifiers().contains(Modifier.ABSTRACT)) {
5.249 + processingEnv.getMessager().printMessage(Kind.ERROR, clazz + " must not be abstract", clazz, ann);
5.250 + return false;
5.251 + }
5.252 + {
5.253 + boolean hasDefaultCtor = false;
5.254 + for (ExecutableElement constructor : ElementFilter.constructorsIn(clazz.getEnclosedElements())) {
5.255 + if (constructor.getModifiers().contains(Modifier.PUBLIC) && constructor.getParameters().isEmpty()) {
5.256 + hasDefaultCtor = true;
5.257 + break;
5.258 + }
5.259 + }
5.260 + if (!hasDefaultCtor) {
5.261 + processingEnv.getMessager().printMessage(Kind.ERROR, clazz + " must have a public no-argument constructor", clazz, ann);
5.262 + return false;
5.263 + }
5.264 + }
5.265 + return true;
5.266 + }
5.267 +
5.268 + private void writeServices() {
5.269 + for (Map.Entry<ProcessingEnvironment,Map<String,List<String>>> outputFiles : outputFilesByProcessor.entrySet()) {
5.270 + for (Map.Entry<String, List<String>> entry : outputFiles.getValue().entrySet()) {
5.271 + try {
5.272 + FileObject out = processingEnv.getFiler().createResource(StandardLocation.CLASS_OUTPUT, "", entry.getKey(),
5.273 + originatingElementsByProcessor.get(outputFiles.getKey()).get(entry.getKey()).toArray(new Element[0]));
5.274 + OutputStream os = out.openOutputStream();
5.275 + try {
5.276 + PrintWriter w = new PrintWriter(new OutputStreamWriter(os, "UTF-8"));
5.277 + for (String line : entry.getValue()) {
5.278 + w.println(line);
5.279 + }
5.280 + w.flush();
5.281 + w.close();
5.282 + } finally {
5.283 + os.close();
5.284 + }
5.285 + } catch (IOException x) {
5.286 + processingEnv.getMessager().printMessage(Kind.ERROR, "Failed to write to " + entry.getKey() + ": " + x.toString());
5.287 + }
5.288 + }
5.289 + }
5.290 + }
5.291 +
5.292 +}
6.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
6.2 +++ b/openide.util/src/org/netbeans/modules/openide/util/ProxyURLStreamHandlerFactory.java Fri Oct 30 13:29:24 2009 -0400
6.3 @@ -0,0 +1,83 @@
6.4 +/*
6.5 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
6.6 + *
6.7 + * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
6.8 + *
6.9 + * The contents of this file are subject to the terms of either the GNU
6.10 + * General Public License Version 2 only ("GPL") or the Common
6.11 + * Development and Distribution License("CDDL") (collectively, the
6.12 + * "License"). You may not use this file except in compliance with the
6.13 + * License. You can obtain a copy of the License at
6.14 + * http://www.netbeans.org/cddl-gplv2.html
6.15 + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
6.16 + * specific language governing permissions and limitations under the
6.17 + * License. When distributing the software, include this License Header
6.18 + * Notice in each file and include the License file at
6.19 + * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
6.20 + * particular file as subject to the "Classpath" exception as provided
6.21 + * by Sun in the GPL Version 2 section of the License file that
6.22 + * accompanied this code. If applicable, add the following below the
6.23 + * License Header, with the fields enclosed by brackets [] replaced by
6.24 + * your own identifying information:
6.25 + * "Portions Copyrighted [year] [name of copyright owner]"
6.26 + *
6.27 + * If you wish your version of this file to be governed by only the CDDL
6.28 + * or only the GPL Version 2, indicate your decision by adding
6.29 + * "[Contributor] elects to include this software in this distribution
6.30 + * under the [CDDL or GPL Version 2] license." If you do not indicate a
6.31 + * single choice of license, a recipient has the option to distribute
6.32 + * your version of this file under either the CDDL, the GPL Version 2 or
6.33 + * to extend the choice of license to its licensees as provided above.
6.34 + * However, if you add GPL Version 2 code and therefore, elected the GPL
6.35 + * Version 2 license, then the option applies only if the new code is
6.36 + * made subject to such option by the copyright holder.
6.37 + *
6.38 + * Contributor(s):
6.39 + *
6.40 + * Portions Copyrighted 2009 Sun Microsystems, Inc.
6.41 + */
6.42 +
6.43 +package org.netbeans.modules.openide.util;
6.44 +
6.45 +import java.net.URLStreamHandler;
6.46 +import java.net.URLStreamHandlerFactory;
6.47 +import java.util.Collection;
6.48 +import java.util.HashMap;
6.49 +import java.util.Map;
6.50 +import org.openide.util.Lookup;
6.51 +import org.openide.util.LookupEvent;
6.52 +import org.openide.util.LookupListener;
6.53 +import org.openide.util.URLStreamHandlerRegistration;
6.54 +import org.openide.util.lookup.Lookups;
6.55 +
6.56 +/**
6.57 + * @see URLStreamHandlerRegistration
6.58 + */
6.59 +public final class ProxyURLStreamHandlerFactory implements URLStreamHandlerFactory {
6.60 +
6.61 + /** prevents GC only */
6.62 + private final Map<String, Lookup.Result<URLStreamHandler>> results = new HashMap<String, Lookup.Result<URLStreamHandler>>();
6.63 + private final Map<String, URLStreamHandler> handlers = new HashMap<String, URLStreamHandler>();
6.64 +
6.65 + /** for lookup */
6.66 + public ProxyURLStreamHandlerFactory() {}
6.67 +
6.68 + public synchronized URLStreamHandler createURLStreamHandler(final String protocol) {
6.69 + if (!results.containsKey(protocol)) {
6.70 + final Lookup.Result<URLStreamHandler> result = Lookups.forPath(URLStreamHandlerRegistrationProcessor.REGISTRATION_PREFIX + protocol).lookupResult(URLStreamHandler.class);
6.71 + LookupListener listener = new LookupListener() {
6.72 +
6.73 + public void resultChanged(LookupEvent ev) {
6.74 + synchronized (ProxyURLStreamHandlerFactory.this) {
6.75 + Collection<? extends URLStreamHandler> instances = result.allInstances();
6.76 + handlers.put(protocol, instances.isEmpty() ? null : instances.iterator().next());
6.77 + }
6.78 + }
6.79 + };
6.80 + result.addLookupListener(listener);
6.81 + listener.resultChanged(null);
6.82 + results.put(protocol, result);
6.83 + }
6.84 + return handlers.get(protocol);
6.85 + }
6.86 +}
7.1 --- a/openide.util/src/org/netbeans/modules/openide/util/ServiceProviderProcessor.java Fri Oct 30 10:41:18 2009 -0400
7.2 +++ b/openide.util/src/org/netbeans/modules/openide/util/ServiceProviderProcessor.java Fri Oct 30 13:29:24 2009 -0400
7.3 @@ -39,240 +39,58 @@
7.4
7.5 package org.netbeans.modules.openide.util;
7.6
7.7 -import java.io.BufferedReader;
7.8 -import java.io.FileNotFoundException;
7.9 -import java.io.IOException;
7.10 -import java.io.InputStream;
7.11 -import java.io.InputStreamReader;
7.12 -import java.io.OutputStream;
7.13 -import java.io.OutputStreamWriter;
7.14 -import java.io.PrintWriter;
7.15 import java.lang.annotation.Annotation;
7.16 -import java.util.ArrayList;
7.17 import java.util.Collection;
7.18 import java.util.Collections;
7.19 -import java.util.HashMap;
7.20 import java.util.LinkedList;
7.21 import java.util.List;
7.22 -import java.util.Map;
7.23 import java.util.Set;
7.24 -import javax.annotation.processing.AbstractProcessor;
7.25 import javax.annotation.processing.Completion;
7.26 import javax.annotation.processing.RoundEnvironment;
7.27 import javax.annotation.processing.SupportedAnnotationTypes;
7.28 import javax.annotation.processing.SupportedSourceVersion;
7.29 import javax.lang.model.SourceVersion;
7.30 import javax.lang.model.element.AnnotationMirror;
7.31 -import javax.lang.model.element.AnnotationValue;
7.32 import javax.lang.model.element.Element;
7.33 import javax.lang.model.element.ExecutableElement;
7.34 -import javax.lang.model.element.Modifier;
7.35 import javax.lang.model.element.TypeElement;
7.36 import javax.lang.model.type.MirroredTypeException;
7.37 import javax.lang.model.type.TypeKind;
7.38 import javax.lang.model.type.TypeMirror;
7.39 -import javax.lang.model.util.ElementFilter;
7.40 -import javax.tools.Diagnostic.Kind;
7.41 -import javax.tools.FileObject;
7.42 -import javax.tools.StandardLocation;
7.43 import org.openide.util.lookup.ServiceProvider;
7.44 import org.openide.util.lookup.ServiceProviders;
7.45
7.46 @SupportedSourceVersion(SourceVersion.RELEASE_6)
7.47 @SupportedAnnotationTypes({"org.openide.util.lookup.ServiceProvider", "org.openide.util.lookup.ServiceProviders"})
7.48 -public class ServiceProviderProcessor extends AbstractProcessor {
7.49 +public class ServiceProviderProcessor extends AbstractServiceProviderProcessor {
7.50
7.51 /** public for ServiceLoader */
7.52 public ServiceProviderProcessor() {}
7.53
7.54 - private final Map<String, List<String>> outputFiles = new HashMap<String,List<String>>();
7.55 - private final Map<String, List<Element>> originatingElements = new HashMap<String,List<Element>>();
7.56 -
7.57 - @Override
7.58 - public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
7.59 - if (roundEnv.errorRaised()) {
7.60 - return false;
7.61 + protected boolean handleProcess(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
7.62 + for (Element el : roundEnv.getElementsAnnotatedWith(ServiceProvider.class)) {
7.63 + TypeElement clazz = (TypeElement) el;
7.64 + ServiceProvider sp = clazz.getAnnotation(ServiceProvider.class);
7.65 + register(clazz, ServiceProvider.class, sp);
7.66 }
7.67 - if (roundEnv.processingOver()) {
7.68 - writeServices();
7.69 - return false;
7.70 - } else {
7.71 - for (Element el : roundEnv.getElementsAnnotatedWith(ServiceProvider.class)) {
7.72 - TypeElement clazz = (TypeElement) el;
7.73 - if (!verifyServiceProviderSignature(clazz)) {
7.74 - continue;
7.75 - }
7.76 - ServiceProvider sp = clazz.getAnnotation(ServiceProvider.class);
7.77 - register(clazz, sp);
7.78 + for (Element el : roundEnv.getElementsAnnotatedWith(ServiceProviders.class)) {
7.79 + TypeElement clazz = (TypeElement) el;
7.80 + ServiceProviders spp = clazz.getAnnotation(ServiceProviders.class);
7.81 + for (ServiceProvider sp : spp.value()) {
7.82 + register(clazz, ServiceProviders.class, sp);
7.83 }
7.84 - for (Element el : roundEnv.getElementsAnnotatedWith(ServiceProviders.class)) {
7.85 - TypeElement clazz = (TypeElement) el;
7.86 - if (!verifyServiceProviderSignature(clazz)) {
7.87 - continue;
7.88 - }
7.89 - ServiceProviders spp = clazz.getAnnotation(ServiceProviders.class);
7.90 - for (ServiceProvider sp : spp.value()) {
7.91 - register(clazz, sp);
7.92 - }
7.93 - }
7.94 - return true;
7.95 }
7.96 + return true;
7.97 }
7.98
7.99 - private void register(TypeElement clazz, ServiceProvider svc) {
7.100 - TypeMirror type;
7.101 + private void register(TypeElement clazz, Class<? extends Annotation> annotation, ServiceProvider svc) {
7.102 try {
7.103 svc.service();
7.104 assert false;
7.105 return;
7.106 } catch (MirroredTypeException e) {
7.107 - type = e.getTypeMirror();
7.108 + register(clazz, annotation, e.getTypeMirror(), svc.path(), svc.position(), svc.supersedes());
7.109 }
7.110 - String impl = processingEnv.getElementUtils().getBinaryName(clazz).toString();
7.111 - String xface = processingEnv.getElementUtils().getBinaryName((TypeElement) processingEnv.getTypeUtils().asElement(type)).toString();
7.112 - if (!processingEnv.getTypeUtils().isAssignable(clazz.asType(), type)) {
7.113 - AnnotationMirror ann = findAnnotationMirror(clazz, ServiceProvider.class);
7.114 - processingEnv.getMessager().printMessage(Kind.ERROR, impl + " is not assignable to " + xface,
7.115 - clazz, ann, findAnnotationValue(ann, "service"));
7.116 - return;
7.117 - }
7.118 - processingEnv.getMessager().printMessage(Kind.NOTE, impl + " to be registered as a " + xface);
7.119 - String rsrc = (svc.path().length() > 0 ? "META-INF/namedservices/" + svc.path() + "/" : "META-INF/services/") + xface;
7.120 - {
7.121 - List<Element> origEls = originatingElements.get(rsrc);
7.122 - if (origEls == null) {
7.123 - origEls = new ArrayList<Element>();
7.124 - originatingElements.put(rsrc, origEls);
7.125 - }
7.126 - origEls.add(clazz);
7.127 - }
7.128 - List<String> lines = outputFiles.get(rsrc);
7.129 - if (lines == null) {
7.130 - lines = new ArrayList<String>();
7.131 - try {
7.132 - try {
7.133 - FileObject in = processingEnv.getFiler().getResource(StandardLocation.SOURCE_PATH, "", rsrc);
7.134 - in.openInputStream().close();
7.135 - processingEnv.getMessager().printMessage(Kind.ERROR,
7.136 - "Cannot generate " + rsrc + " because it already exists in sources: " + in.toUri());
7.137 - return;
7.138 - } catch (FileNotFoundException x) {
7.139 - // Good.
7.140 - }
7.141 - try {
7.142 - FileObject in = processingEnv.getFiler().getResource(StandardLocation.CLASS_OUTPUT, "", rsrc);
7.143 - InputStream is = in.openInputStream();
7.144 - try {
7.145 - BufferedReader r = new BufferedReader(new InputStreamReader(is, "UTF-8"));
7.146 - String line;
7.147 - while ((line = r.readLine()) != null) {
7.148 - lines.add(line);
7.149 - }
7.150 - } finally {
7.151 - is.close();
7.152 - }
7.153 - } catch (FileNotFoundException x) {
7.154 - // OK, created for the first time
7.155 - }
7.156 - } catch (IOException x) {
7.157 - processingEnv.getMessager().printMessage(Kind.ERROR, x.toString());
7.158 - return;
7.159 - }
7.160 - outputFiles.put(rsrc, lines);
7.161 - }
7.162 - int idx = lines.indexOf(impl);
7.163 - if (idx != -1) {
7.164 - lines.remove(idx);
7.165 - while (lines.size() > idx && lines.get(idx).matches("#position=.+|#-.+")) {
7.166 - lines.remove(idx);
7.167 - }
7.168 - }
7.169 - lines.add(impl);
7.170 - if (svc.position() != Integer.MAX_VALUE) {
7.171 - lines.add("#position=" + svc.position());
7.172 - }
7.173 - for (String exclude : svc.supersedes()) {
7.174 - lines.add("#-" + exclude);
7.175 - }
7.176 - }
7.177 -
7.178 - private boolean verifyServiceProviderSignature(TypeElement clazz) {
7.179 - AnnotationMirror ann = findAnnotationMirror(clazz, ServiceProvider.class);
7.180 - if (!clazz.getModifiers().contains(Modifier.PUBLIC)) {
7.181 - processingEnv.getMessager().printMessage(Kind.ERROR, clazz + " must be public", clazz, ann);
7.182 - return false;
7.183 - }
7.184 - if (clazz.getModifiers().contains(Modifier.ABSTRACT)) {
7.185 - processingEnv.getMessager().printMessage(Kind.ERROR, clazz + " must not be abstract", clazz, ann);
7.186 - return false;
7.187 - }
7.188 - {
7.189 - boolean hasDefaultCtor = false;
7.190 - for (ExecutableElement constructor : ElementFilter.constructorsIn(clazz.getEnclosedElements())) {
7.191 - if (constructor.getModifiers().contains(Modifier.PUBLIC) && constructor.getParameters().isEmpty()) {
7.192 - hasDefaultCtor = true;
7.193 - break;
7.194 - }
7.195 - }
7.196 - if (!hasDefaultCtor) {
7.197 - processingEnv.getMessager().printMessage(Kind.ERROR, clazz + " must have a public no-argument constructor", clazz, ann);
7.198 - return false;
7.199 - }
7.200 - }
7.201 - return true;
7.202 - }
7.203 -
7.204 - private void writeServices() {
7.205 - for (Map.Entry<String, List<String>> entry : outputFiles.entrySet()) {
7.206 - try {
7.207 - FileObject out = processingEnv.getFiler().createResource(StandardLocation.CLASS_OUTPUT, "", entry.getKey(),
7.208 - originatingElements.get(entry.getKey()).toArray(new Element[0]));
7.209 - OutputStream os = out.openOutputStream();
7.210 - try {
7.211 - PrintWriter w = new PrintWriter(new OutputStreamWriter(os, "UTF-8"));
7.212 - for (String line : entry.getValue()) {
7.213 - w.println(line);
7.214 - }
7.215 - w.flush();
7.216 - w.close();
7.217 - } finally {
7.218 - os.close();
7.219 - }
7.220 - } catch (IOException x) {
7.221 - processingEnv.getMessager().printMessage(Kind.ERROR, "Failed to write to " + entry.getKey() + ": " + x.toString());
7.222 - }
7.223 - }
7.224 - }
7.225 -
7.226 - /**
7.227 - * @param element a source element
7.228 - * @param annotation a type of annotation
7.229 - * @return the instance of that annotation on the element, or null if not found
7.230 - */
7.231 - private AnnotationMirror findAnnotationMirror(Element element, Class<? extends Annotation> annotation) {
7.232 - for (AnnotationMirror ann : element.getAnnotationMirrors()) {
7.233 - if (processingEnv.getElementUtils().getBinaryName((TypeElement) ann.getAnnotationType().asElement()).
7.234 - contentEquals(annotation.getName())) {
7.235 - return ann;
7.236 - }
7.237 - }
7.238 - return null;
7.239 - }
7.240 -
7.241 - /**
7.242 - * @param annotation an annotation instance (null permitted)
7.243 - * @param name the name of an attribute of that annotation
7.244 - * @return the corresponding value if found
7.245 - */
7.246 - private AnnotationValue findAnnotationValue(AnnotationMirror annotation, String name) {
7.247 - if (annotation != null) {
7.248 - for (Map.Entry<? extends ExecutableElement,? extends AnnotationValue> entry : annotation.getElementValues().entrySet()) {
7.249 - if (entry.getKey().getSimpleName().contentEquals(name)) {
7.250 - return entry.getValue();
7.251 - }
7.252 - }
7.253 - }
7.254 - return null;
7.255 }
7.256
7.257 @Override
8.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
8.2 +++ b/openide.util/src/org/netbeans/modules/openide/util/URLStreamHandlerRegistrationProcessor.java Fri Oct 30 13:29:24 2009 -0400
8.3 @@ -0,0 +1,78 @@
8.4 +/*
8.5 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
8.6 + *
8.7 + * Copyright 2009 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 2009 Sun Microsystems, Inc.
8.41 + */
8.42 +
8.43 +package org.netbeans.modules.openide.util;
8.44 +
8.45 +import java.net.URLStreamHandler;
8.46 +import java.util.Set;
8.47 +import javax.annotation.processing.RoundEnvironment;
8.48 +import javax.annotation.processing.SupportedAnnotationTypes;
8.49 +import javax.annotation.processing.SupportedSourceVersion;
8.50 +import javax.lang.model.SourceVersion;
8.51 +import javax.lang.model.element.Element;
8.52 +import javax.lang.model.element.TypeElement;
8.53 +import javax.lang.model.type.TypeMirror;
8.54 +import org.openide.util.URLStreamHandlerRegistration;
8.55 +
8.56 +@SupportedSourceVersion(SourceVersion.RELEASE_6)
8.57 +@SupportedAnnotationTypes({
8.58 + "org.openide.util.URLStreamHandlerRegistration"
8.59 +})
8.60 +public class URLStreamHandlerRegistrationProcessor extends AbstractServiceProviderProcessor {
8.61 +
8.62 + public static final String REGISTRATION_PREFIX = "URLStreamHandler/"; // NOI18N
8.63 +
8.64 + /** public for ServiceLoader */
8.65 + public URLStreamHandlerRegistrationProcessor() {}
8.66 +
8.67 + protected boolean handleProcess(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
8.68 + for (Element el : roundEnv.getElementsAnnotatedWith(URLStreamHandlerRegistration.class)) {
8.69 + TypeElement clazz = (TypeElement) el;
8.70 + URLStreamHandlerRegistration r = clazz.getAnnotation(URLStreamHandlerRegistration.class);
8.71 + TypeMirror type = processingEnv.getTypeUtils().getDeclaredType(
8.72 + processingEnv.getElementUtils().getTypeElement(URLStreamHandler.class.getName()));
8.73 + for (String protocol : r.protocol()) {
8.74 + register(clazz, URLStreamHandlerRegistration.class, type,
8.75 + REGISTRATION_PREFIX + protocol, r.position(), new String[0]);
8.76 + }
8.77 + }
8.78 + return true;
8.79 + }
8.80 +
8.81 +}
9.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
9.2 +++ b/openide.util/src/org/openide/util/URLStreamHandlerRegistration.java Fri Oct 30 13:29:24 2009 -0400
9.3 @@ -0,0 +1,78 @@
9.4 +/*
9.5 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
9.6 + *
9.7 + * Copyright 2009 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 2009 Sun Microsystems, Inc.
9.41 + */
9.42 +
9.43 +package org.openide.util;
9.44 +
9.45 +import java.lang.annotation.ElementType;
9.46 +import java.lang.annotation.Retention;
9.47 +import java.lang.annotation.RetentionPolicy;
9.48 +import java.lang.annotation.Target;
9.49 +import java.net.URL;
9.50 +import java.net.URLStreamHandler;
9.51 +import java.net.URLStreamHandlerFactory;
9.52 +
9.53 +/**
9.54 + * Replacement for {@link URLStreamHandlerFactory} within the NetBeans platform.
9.55 + * (The JVM only permits one global factory to be set at a time,
9.56 + * whereas various independent modules may wish to register handlers.)
9.57 + * May be placed on a {@link URLStreamHandler} implementation to register it.
9.58 + * Your handler will be loaded and used if and when a URL of a matching protocol is created.
9.59 + * <p>A {@link URLStreamHandlerFactory} which uses these registrations may be found in {@link Lookup#getDefault}.
9.60 + * This factory is active whenever the module system is loaded.
9.61 + * You may also wish to call {@link URL#setURLStreamHandlerFactory}
9.62 + * from a unit test or otherwise without the module system active.
9.63 + * @since org.openide.util 7.31
9.64 + */
9.65 +@Retention(RetentionPolicy.SOURCE)
9.66 +@Target(ElementType.TYPE)
9.67 +public @interface URLStreamHandlerRegistration {
9.68 +
9.69 + /**
9.70 + * URL protocol(s) which are handled.
9.71 + * {@link URLStreamHandler#openConnection} will be called with a matching {@link URL#getProtocol}.
9.72 + */
9.73 + String[] protocol();
9.74 +
9.75 + /**
9.76 + * An optional position in which to register this handler relative to others.
9.77 + * The lowest-numbered handler is used in favor of any others, including unnumbered handlers.
9.78 + */
9.79 + int position() default Integer.MAX_VALUE;
9.80 +
9.81 +}
10.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
10.2 +++ b/openide.util/test/unit/src/org/netbeans/modules/openide/util/ProxyURLStreamHandlerFactoryTest.java Fri Oct 30 13:29:24 2009 -0400
10.3 @@ -0,0 +1,70 @@
10.4 +/*
10.5 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
10.6 + *
10.7 + * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
10.8 + *
10.9 + * The contents of this file are subject to the terms of either the GNU
10.10 + * General Public License Version 2 only ("GPL") or the Common
10.11 + * Development and Distribution License("CDDL") (collectively, the
10.12 + * "License"). You may not use this file except in compliance with the
10.13 + * License. You can obtain a copy of the License at
10.14 + * http://www.netbeans.org/cddl-gplv2.html
10.15 + * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
10.16 + * specific language governing permissions and limitations under the
10.17 + * License. When distributing the software, include this License Header
10.18 + * Notice in each file and include the License file at
10.19 + * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
10.20 + * particular file as subject to the "Classpath" exception as provided
10.21 + * by Sun in the GPL Version 2 section of the License file that
10.22 + * accompanied this code. If applicable, add the following below the
10.23 + * License Header, with the fields enclosed by brackets [] replaced by
10.24 + * your own identifying information:
10.25 + * "Portions Copyrighted [year] [name of copyright owner]"
10.26 + *
10.27 + * If you wish your version of this file to be governed by only the CDDL
10.28 + * or only the GPL Version 2, indicate your decision by adding
10.29 + * "[Contributor] elects to include this software in this distribution
10.30 + * under the [CDDL or GPL Version 2] license." If you do not indicate a
10.31 + * single choice of license, a recipient has the option to distribute
10.32 + * your version of this file under either the CDDL, the GPL Version 2 or
10.33 + * to extend the choice of license to its licensees as provided above.
10.34 + * However, if you add GPL Version 2 code and therefore, elected the GPL
10.35 + * Version 2 license, then the option applies only if the new code is
10.36 + * made subject to such option by the copyright holder.
10.37 + *
10.38 + * Contributor(s):
10.39 + *
10.40 + * Portions Copyrighted 2009 Sun Microsystems, Inc.
10.41 + */
10.42 +
10.43 +package org.netbeans.modules.openide.util;
10.44 +
10.45 +import java.io.IOException;
10.46 +import java.net.URL;
10.47 +import java.net.URLConnection;
10.48 +import java.net.URLStreamHandler;
10.49 +import java.net.URLStreamHandlerFactory;
10.50 +import org.netbeans.junit.NbTestCase;
10.51 +import org.openide.util.URLStreamHandlerRegistration;
10.52 +
10.53 +public class ProxyURLStreamHandlerFactoryTest extends NbTestCase {
10.54 +
10.55 + public ProxyURLStreamHandlerFactoryTest(String n) {
10.56 + super(n);
10.57 + }
10.58 +
10.59 + public void testURLStreamHandlerRegistration() throws Exception {
10.60 + URLStreamHandlerFactory factory = new ProxyURLStreamHandlerFactory();
10.61 + assertEquals(MyHandler.class, factory.createURLStreamHandler("stuff").getClass());
10.62 + assertEquals(MyHandler.class, factory.createURLStreamHandler("stuff").getClass());
10.63 + assertNull(factory.createURLStreamHandler("whatever"));
10.64 + }
10.65 +
10.66 + @URLStreamHandlerRegistration(protocol="stuff")
10.67 + public static class MyHandler extends URLStreamHandler {
10.68 + protected URLConnection openConnection(URL u) throws IOException {
10.69 + throw new IOException("unsupported");
10.70 + }
10.71 + }
10.72 +
10.73 +}