001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU
007: * General Public License Version 2 only ("GPL") or the Common
008: * Development and Distribution License("CDDL") (collectively, the
009: * "License"). You may not use this file except in compliance with the
010: * License. You can obtain a copy of the License at
011: * http://www.netbeans.org/cddl-gplv2.html
012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
013: * specific language governing permissions and limitations under the
014: * License. When distributing the software, include this License Header
015: * Notice in each file and include the License file at
016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
017: * particular file as subject to the "Classpath" exception as provided
018: * by Sun in the GPL Version 2 section of the License file that
019: * accompanied this code. If applicable, add the following below the
020: * License Header, with the fields enclosed by brackets [] replaced by
021: * your own identifying information:
022: * "Portions Copyrighted [year] [name of copyright owner]"
023: *
024: * Contributor(s):
025: *
026: * The Original Software is NetBeans. The Initial Developer of the Original
027: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
028: * Microsystems, Inc. All Rights Reserved.
029: *
030: * If you wish your version of this file to be governed by only the CDDL
031: * or only the GPL Version 2, indicate your decision by adding
032: * "[Contributor] elects to include this software in this distribution
033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
034: * single choice of license, a recipient has the option to distribute
035: * your version of this file under either the CDDL, the GPL Version 2 or
036: * to extend the choice of license to its licensees as provided above.
037: * However, if you add GPL Version 2 code and therefore, elected the GPL
038: * Version 2 license, then the option applies only if the new code is
039: * made subject to such option by the copyright holder.
040: */
041:
042: package org.netbeans.modules.gsfret.source.usages;
043:
044: import java.io.IOException;
045: import java.net.URL;
046: import java.util.Collections;
047: import java.util.HashMap;
048: import java.util.HashSet;
049: import java.util.List;
050: import java.util.Map;
051: import java.util.Set; //import java.util.concurrent.CopyOnWriteArrayList;
052: import java.util.concurrent.locks.ReadWriteLock;
053: import java.util.concurrent.locks.ReentrantReadWriteLock;
054: import org.netbeans.modules.gsf.Language;
055: import org.netbeans.modules.gsf.LanguageRegistry;
056: import org.openide.util.Exceptions;
057:
058: /**
059: * This file is originally from Retouche, the Java Support
060: * infrastructure in NetBeans. I have modified the file as little
061: * as possible to make merging Retouche fixes back as simple as
062: * possible.
063: *
064: *
065: * @author Tomas Zezula
066: */
067: public final class ClassIndexManager {
068:
069: private static final byte OP_ADD = 1;
070: private static final byte OP_REMOVE = 2;
071:
072: //private static ClassIndexManager instance;
073: private final Map<URL, ClassIndexImpl> instances = new HashMap<URL, ClassIndexImpl>();
074:
075: // private final ReadWriteLock lock;
076: //// private final List<ClassIndexManagerListener> listeners = new CopyOnWriteArrayList<ClassIndexManagerListener> ();
077: // private boolean invalid;
078: // private Set<URL> added;
079: // private Set<URL> removed;
080: // private int depth = 0;
081: // private Thread owner;
082:
083: // GSF - using a single lock shared among all index managers
084: private static final ReadWriteLock lock = new ReentrantReadWriteLock(
085: false);
086: // private final List<ClassIndexManagerListener> listeners = new CopyOnWriteArrayList<ClassIndexManagerListener> ();
087: private static boolean invalid;
088: private static Set<URL> added;
089: private static Set<URL> removed;
090: private static int depth = 0;
091: private static Thread owner;
092:
093: private ClassIndexManager(Language language) {
094: this .language = language;
095: //this.lock = new ReentrantReadWriteLock (false);
096: }
097:
098: // public void addClassIndexManagerListener (final ClassIndexManagerListener listener) {
099: // assert listener != null;
100: // this.listeners.add(listener);
101: // }
102: //
103: // public void removeClassIndexManagerListener (final ClassIndexManagerListener listener) {
104: // assert listener != null;
105: // this.listeners.remove(listener);
106: // }
107:
108: public static <T> T writeLock(final ExceptionAction<T> r)
109: throws IOException/*, InterruptedException*/{
110: lock.writeLock().lock();
111: try {
112: if (depth == 0) {
113: owner = Thread.currentThread();
114: }
115: try {
116: depth++;
117: try {
118: if (depth == 1) {
119: added = new HashSet<URL>();
120: removed = new HashSet<URL>();
121: }
122: try {
123: return r.run();
124: } finally {
125: if (depth == 1) {
126: if (!removed.isEmpty()) {
127: // fire (removed, OP_REMOVE);
128: removed.clear();
129: }
130: if (!added.isEmpty()) {
131: // fire (added, OP_ADD);
132: added.clear();
133: }
134: }
135: }
136: } finally {
137: depth--;
138: }
139: } finally {
140: if (depth == 0) {
141: owner = null;
142: }
143: }
144: } finally {
145: lock.writeLock().unlock();
146: }
147: }
148:
149: public static <T> T readLock(final ExceptionAction<T> r)
150: throws IOException/*, InterruptedException*/{
151: lock.readLock().lock();
152: try {
153: return r.run();
154: } finally {
155: lock.readLock().unlock();
156: }
157: }
158:
159: public static boolean holdsWriteLock() {
160: return Thread.currentThread().equals(owner);
161: }
162:
163: public synchronized ClassIndexImpl getUsagesQuery(final URL root)
164: throws IOException {
165: assert root != null;
166: if (invalid) {
167: return null;
168: }
169: // BEGIN TOR MODIFICATIONS
170: // XXX Figure out why I often get this on boot class paths
171: //return this.instances.get (root);
172: ClassIndexImpl ci = this .instances.get(root);
173: // if (ci == null) {
174: // ci = createUsagesQuery(root, false);
175: // }
176:
177: return ci;
178: // END TOR MODIFICATIONS
179: }
180:
181: public synchronized ClassIndexImpl createUsagesQuery(
182: final URL root, final boolean source) throws IOException {
183: assert root != null;
184: if (invalid) {
185: return null;
186: }
187: ClassIndexImpl qi = this .instances.get(root);
188: if (qi == null) {
189: qi = PersistentClassIndex.create(language, root, Index
190: .getDataFolder(language, root), source);
191: this .instances.put(root, qi);
192: if (added != null) {
193: added.add(root);
194: }
195: }
196: // else if (source && !qi.isSource()){
197: // //Wrongly set up freeform project, which is common for it, prefer source
198: // qi.close ();
199: // qi = PersistentClassIndex.create (root, Index.getDataFolder(root), source);
200: // this.instances.put(root,qi);
201: // if (added != null) {
202: // added.add (root);
203: // }
204: // }
205: return qi;
206: }
207:
208: synchronized void removeRoot(final URL root) throws IOException {
209: // BEGIN TOR MODIFICATIONS
210: if (bootRoots.contains(root)) {
211: return;
212: }
213: // END TOR MODIFICATIONS
214: ClassIndexImpl ci = this .instances.remove(root);
215: if (ci != null) {
216: ci.close();
217: if (removed != null) {
218: removed.add(root);
219: }
220: }
221: }
222:
223: public synchronized void close() {
224: invalid = true;
225: for (ClassIndexImpl ci : instances.values()) {
226: try {
227: ci.close();
228: } catch (IOException ioe) {
229: Exceptions.printStackTrace(ioe);
230: }
231: }
232: }
233:
234: public static interface ExceptionAction<T> {
235: public T run() throws IOException/*, InterruptedException*/;
236: }
237:
238: // private void fire (final Set<? extends URL> roots, final byte op) {
239: // if (!this.listeners.isEmpty()) {
240: // final ClassIndexManagerEvent event = new ClassIndexManagerEvent (this, roots);
241: // for (ClassIndexManagerListener listener : this.listeners) {
242: // if (op == OP_ADD) {
243: // listener.classIndexAdded(event);
244: // }
245: // else if (op == OP_REMOVE) {
246: // listener.classIndexRemoved(event);
247: // }
248: // else {
249: // assert false : "Unknown op: " + op; //NOI18N
250: // }
251: // }
252: // }
253: // }
254:
255: // public static synchronized ClassIndexManager getDefault () {
256: // if (instance == null) {
257: // instance = new ClassIndexManager ();
258: // }
259: // return instance;
260: // }
261:
262: // BEGIN TOR MODIFICATIONS
263: private Language language;
264: private static Map<Language, ClassIndexManager> instanceMap = new HashMap<Language, ClassIndexManager>();
265:
266: public static synchronized ClassIndexManager get(Language language) {
267: ClassIndexManager manager = instanceMap.get(language);
268: if (manager == null) {
269: manager = new ClassIndexManager(language);
270: instanceMap.put(language, manager);
271: }
272:
273: return manager;
274: }
275:
276: // This is for development use only (the index browser)
277: public synchronized Map<URL, ClassIndexImpl> getAllIndices() {
278: if (invalid) {
279: return Collections.emptyMap();
280: }
281: return Collections.unmodifiableMap(this .instances);
282: }
283:
284: // To avoid excessive compilation at startup etc.
285: private static final Set<URL> allBootRoots = new HashSet<URL>();
286: private final Set<URL> bootRoots = new HashSet<URL>();
287: private Set<ClassIndexImpl> bootIndices;
288:
289: public static boolean isBootRoot(URL root) {
290: return allBootRoots.contains(root);
291: }
292:
293: public void addBootRoot(URL root) {
294: bootRoots.add(root);
295: allBootRoots.add(root);
296: }
297:
298: public void setBootRoots(List<URL> urls) {
299: if (bootRoots.size() > 0) {
300: allBootRoots.removeAll(bootRoots);
301: bootRoots.clear();
302: }
303: for (URL url : LanguageRegistry.getInstance().getLibraryUrls()) {
304: bootRoots.add(url);
305: allBootRoots.add(url);
306: }
307: for (URL url : urls) {
308: bootRoots.add(url);
309: allBootRoots.add(url);
310: }
311:
312: bootIndices = null;
313: }
314:
315: public Set<URL> getBootRoots() {
316: return bootRoots;
317: }
318:
319: public static Set<URL> getAllBootRoots() {
320: return allBootRoots;
321: }
322:
323: /**
324: * I need to store the boot indices such that I don't try to open a
325: * LuceneIndex multiple times (once for each project type that tries
326: * to query the boot index) since as of Lucene 2.1 I get errors in trying
327: * to do this...
328: */
329: public Set<ClassIndexImpl> getBootIndices() {
330: if (bootIndices == null) {
331: bootIndices = new HashSet<ClassIndexImpl>();
332: Set<URL> roots = getBootRoots();
333: for (URL u : roots) {
334: try {
335: ClassIndexImpl ci = getUsagesQuery(u);
336: if (ci != null) {
337: bootIndices.add(ci);
338: } else {
339: ci = createUsagesQuery(u, false);
340: bootIndices.add(ci);
341: }
342: } catch (IOException ioe) {
343: Exceptions.printStackTrace(ioe);
344: }
345: }
346: }
347:
348: return bootIndices;
349: }
350: // END TOR MODIFICATIONS
351: }
|