~mwhudson/hudson/2.x

« back to all changes in this revision

Viewing changes to hudson-inject/src/main/java/net/java/sezpoz/SpaceIndex.java

  • Committer: Jason van Zyl
  • Date: 2011-02-17 14:40:01 UTC
  • mfrom: (9408.1.3)
  • Revision ID: git-v1:06a570a521abbbe3a889b27f8fcb6445a39246bf
Merge remote branch 'mcculls-jsr330/JSR330' into JSR330

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
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.
 
5
 *
 
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.
 
8
 *
 
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]"
 
14
 *
 
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.
 
18
 */
 
19
 
 
20
package net.java.sezpoz;
 
21
 
 
22
import java.io.IOException;
 
23
import java.io.ObjectInputStream;
 
24
import java.lang.annotation.Annotation;
 
25
import java.net.URL;
 
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;
 
31
import java.util.Set;
 
32
import java.util.logging.Level;
 
33
import java.util.logging.Logger;
 
34
 
 
35
import net.java.sezpoz.impl.SerAnnotatedElement;
 
36
 
 
37
import org.sonatype.guice.bean.reflect.ClassSpace;
 
38
import org.sonatype.guice.bean.reflect.URLClassSpace;
 
39
 
 
40
/**
 
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
 
46
 */
 
47
public final class SpaceIndex<T extends Annotation, I> implements Iterable<SpaceIndexItem<T,I>> {
 
48
 
 
49
    private static final Logger LOGGER = Logger.getLogger(SpaceIndex.class.getName());
 
50
 
 
51
    /**
 
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}
 
59
     */
 
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()));
 
62
    }
 
63
 
 
64
    /**
 
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}
 
72
     */
 
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);
 
75
    }
 
76
 
 
77
    private final Class<T> annotation;
 
78
    private final Class<I> instanceType;
 
79
    private final ClassSpace space;
 
80
 
 
81
    private SpaceIndex(Class<T> annotation, Class<I> instance, ClassSpace space) {
 
82
        this.annotation = annotation;
 
83
        this.instanceType = instance;
 
84
        this.space = space;
 
85
    }
 
86
 
 
87
    /**
 
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
 
92
     */
 
93
    public Iterator<SpaceIndexItem<T,I>> iterator() {
 
94
        return new LazyIndexIterator();
 
95
    }
 
96
 
 
97
    /**
 
98
     * Lazy iterator. Opens and parses annotation streams only on demand.
 
99
     */
 
100
    private final class LazyIndexIterator implements Iterator<SpaceIndexItem<T,I>> {
 
101
 
 
102
        private Enumeration<URL> resources;
 
103
        private ObjectInputStream ois;
 
104
        private URL resource;
 
105
        private SpaceIndexItem<T,I> next;
 
106
        private boolean end;
 
107
        private final Set<String> loadedMembers = new HashSet<String>();
 
108
 
 
109
        public LazyIndexIterator() {
 
110
            if (LOGGER.isLoggable(Level.FINE)) {
 
111
                String urls;
 
112
                if (space instanceof URLClassSpace) {
 
113
                    urls = " " + Arrays.toString(((URLClassSpace) space).getURLs());
 
114
                } else {
 
115
                    urls = "";
 
116
                }
 
117
                LOGGER.log(Level.FINE, "Searching for indices of {0} in {1}{2}", new Object[] {annotation, space, urls});
 
118
            }
 
119
        }
 
120
 
 
121
        private void peek() throws IndexError {
 
122
            try {
 
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});
 
126
                    }
 
127
                    if (next != null || end) {
 
128
                        return;
 
129
                    }
 
130
                    if (ois == null) {
 
131
                        if (resources == null) {
 
132
                            resources = space.findEntries("META-INF/annotations/", annotation.getName(), false);
 
133
                        }
 
134
                        if (!resources.hasMoreElements()) {
 
135
                            // Exhausted all streams.
 
136
                            end = true;
 
137
                            return;
 
138
                        }
 
139
                        resource = resources.nextElement();
 
140
                        LOGGER.log(Level.FINE, "Loading index from {0}", resource);
 
141
                        ois = new ObjectInputStream(resource.openStream());
 
142
                    }
 
143
                    SerAnnotatedElement el = (SerAnnotatedElement) ois.readObject();
 
144
                    if (el == null) {
 
145
                        // Skip to next stream.
 
146
                        ois.close();
 
147
                        ois = null;
 
148
                        continue;
 
149
                    }
 
150
                    String memberName = el.isMethod ? el.className + '#' + el.memberName + "()" :
 
151
                        el.memberName != null ? el.className + '#' + el.memberName :
 
152
                            el.className;
 
153
                    if (!loadedMembers.add(memberName)) {
 
154
                        // Already encountered this element, so skip it.
 
155
                        LOGGER.log(Level.FINE, "Already loaded index item {0}", el);
 
156
                        continue;
 
157
                    }
 
158
                    // XXX JRE #6865375 would make loader param accurate for duplicated modules
 
159
                    next = new SpaceIndexItem<T,I>(el, annotation, instanceType, space, resource);
 
160
                    break;
 
161
                }
 
162
            } catch (Exception x) {
 
163
                if (ois != null) {
 
164
                    try {
 
165
                        ois.close();
 
166
                    } catch (IOException x2) {
 
167
                        LOGGER.log(Level.WARNING, null, x2);
 
168
                    }
 
169
                }
 
170
                throw new IndexError(x);
 
171
            }
 
172
        }
 
173
 
 
174
        public boolean hasNext() {
 
175
            peek();
 
176
            return !end;
 
177
        }
 
178
 
 
179
        public SpaceIndexItem<T,I> next() {
 
180
            peek();
 
181
            if (!end) {
 
182
                assert next != null;
 
183
                SpaceIndexItem<T,I> _next = next;
 
184
                next = null;
 
185
                return _next;
 
186
            } else {
 
187
                throw new NoSuchElementException();
 
188
            }
 
189
        }
 
190
 
 
191
        public void remove() {
 
192
            throw new UnsupportedOperationException();
 
193
        }
 
194
 
 
195
    }
 
196
 
 
197
}