2
* The contents of this file are subject to the terms of the Common Development
3
* and Distribution License (the License). You may not use this file except in
4
* compliance with the License.
6
* You can obtain a copy of the License at http://www.sun.com/cddl/cddl.html
7
* or http://www.netbeans.org/cddl.txt.
9
* When distributing Covered Code, include this CDDL Header Notice in each file
10
* and include the License file at http://www.netbeans.org/cddl.txt.
11
* If applicable, add the following below the CDDL Header, with the fields
12
* enclosed by brackets [] replaced by your own identifying information:
13
* "Portions Copyrighted [year] [name of copyright owner]"
15
* The Original Software is SezPoz. The Initial Developer of the Original
16
* Software is Sun Microsystems, Inc. Portions Copyright 2006-2007 Sun
17
* Microsystems, Inc. All Rights Reserved.
20
package net.java.sezpoz;
22
import java.io.IOException;
23
import java.io.ObjectInputStream;
24
import java.lang.annotation.Annotation;
26
import java.util.Arrays;
27
import java.util.Enumeration;
28
import java.util.HashSet;
29
import java.util.Iterator;
30
import java.util.NoSuchElementException;
32
import java.util.logging.Level;
33
import java.util.logging.Logger;
35
import net.java.sezpoz.impl.SerAnnotatedElement;
37
import org.sonatype.guice.bean.reflect.ClassSpace;
38
import org.sonatype.guice.bean.reflect.URLClassSpace;
41
* Represents an index of a single annotation.
42
* Indices are <em>not</em> automatically cached
43
* (but reading them should be pretty cheap anyway).
44
* @param T the type of annotation to load
45
* @param I the type of instance which will be created
47
public final class SpaceIndex<T extends Annotation, I> implements Iterable<SpaceIndexItem<T,I>> {
49
private static final Logger LOGGER = Logger.getLogger(SpaceIndex.class.getName());
52
* Load an index for a given annotation type.
53
* Uses the thread's context class loader to find the index and load annotated classes.
54
* @param annotation the type of annotation to find
55
* @param instanceType the type of instance to be created (use {@link Void} if all instances will be null)
56
* @return an index of all elements known to be annotated with it
57
* @throws IllegalArgumentException if the annotation type is not marked with {@link Indexable}
58
* or the instance type is not equal to or a supertype of the annotation's actual {@link Indexable#type}
60
public static <T extends Annotation,I> SpaceIndex<T,I> load(Class<T> annotation, Class<I> instanceType) throws IllegalArgumentException {
61
return load(annotation, instanceType, new URLClassSpace(Thread.currentThread().getContextClassLoader()));
65
* Load an index for a given annotation type.
66
* @param annotation the type of annotation to find
67
* @param instanceType the type of instance to be created (use {@link Void} if all instances will be null)
68
* @param space a class space in which to find the index and any annotated classes
69
* @return an index of all elements known to be annotated with it
70
* @throws IllegalArgumentException if the annotation type is not marked with {@link Indexable}
71
* or the instance type is not equal to or a supertype of the annotation's actual {@link Indexable#type}
73
public static <T extends Annotation,I> SpaceIndex<T,I> load(Class<T> annotation, Class<I> instanceType, ClassSpace space) throws IllegalArgumentException {
74
return new SpaceIndex<T,I>(annotation, instanceType, space);
77
private final Class<T> annotation;
78
private final Class<I> instanceType;
79
private final ClassSpace space;
81
private SpaceIndex(Class<T> annotation, Class<I> instance, ClassSpace space) {
82
this.annotation = annotation;
83
this.instanceType = instance;
88
* Find all items in the index.
89
* Calls to iterator methods may fail with {@link IndexError}
90
* as the index is parsed lazily.
91
* @return an iterator over items in the index
93
public Iterator<SpaceIndexItem<T,I>> iterator() {
94
return new LazyIndexIterator();
98
* Lazy iterator. Opens and parses annotation streams only on demand.
100
private final class LazyIndexIterator implements Iterator<SpaceIndexItem<T,I>> {
102
private Enumeration<URL> resources;
103
private ObjectInputStream ois;
104
private URL resource;
105
private SpaceIndexItem<T,I> next;
107
private final Set<String> loadedMembers = new HashSet<String>();
109
public LazyIndexIterator() {
110
if (LOGGER.isLoggable(Level.FINE)) {
112
if (space instanceof URLClassSpace) {
113
urls = " " + Arrays.toString(((URLClassSpace) space).getURLs());
117
LOGGER.log(Level.FINE, "Searching for indices of {0} in {1}{2}", new Object[] {annotation, space, urls});
121
private void peek() throws IndexError {
123
for (int iteration = 0; true; iteration++) {
124
if (iteration == 9999) {
125
LOGGER.log(Level.WARNING, "possible endless loop getting index for {0} from {1}", new Object[] {annotation, space});
127
if (next != null || end) {
131
if (resources == null) {
132
resources = space.findEntries("META-INF/annotations/", annotation.getName(), false);
134
if (!resources.hasMoreElements()) {
135
// Exhausted all streams.
139
resource = resources.nextElement();
140
LOGGER.log(Level.FINE, "Loading index from {0}", resource);
141
ois = new ObjectInputStream(resource.openStream());
143
SerAnnotatedElement el = (SerAnnotatedElement) ois.readObject();
145
// Skip to next stream.
150
String memberName = el.isMethod ? el.className + '#' + el.memberName + "()" :
151
el.memberName != null ? el.className + '#' + el.memberName :
153
if (!loadedMembers.add(memberName)) {
154
// Already encountered this element, so skip it.
155
LOGGER.log(Level.FINE, "Already loaded index item {0}", el);
158
// XXX JRE #6865375 would make loader param accurate for duplicated modules
159
next = new SpaceIndexItem<T,I>(el, annotation, instanceType, space, resource);
162
} catch (Exception x) {
166
} catch (IOException x2) {
167
LOGGER.log(Level.WARNING, null, x2);
170
throw new IndexError(x);
174
public boolean hasNext() {
179
public SpaceIndexItem<T,I> next() {
183
SpaceIndexItem<T,I> _next = next;
187
throw new NoSuchElementException();
191
public void remove() {
192
throw new UnsupportedOperationException();