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-2007 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: package org.netbeans.api.java.source;
042:
043: import java.beans.PropertyChangeEvent;
044: import java.beans.PropertyChangeListener;
045: import java.io.File;
046: import java.net.URL;
047: import javax.swing.event.ChangeEvent;
048: import javax.swing.event.ChangeListener;
049: import javax.swing.event.EventListenerList;
050: import javax.tools.JavaFileManager;
051: import org.netbeans.api.java.classpath.ClassPath;
052: import org.netbeans.api.java.platform.JavaPlatformManager;
053: import org.netbeans.modules.java.source.classpath.CacheClassPath;
054: import org.netbeans.modules.java.source.parsing.CachingArchiveProvider;
055: import org.netbeans.modules.java.source.parsing.CachingFileManager;
056: import org.netbeans.modules.java.source.parsing.OutputFileManager;
057: import org.netbeans.modules.java.source.parsing.ProxyFileManager;
058: import org.netbeans.modules.java.source.parsing.SourceFileManager;
059: import org.netbeans.modules.java.preprocessorbridge.spi.JavaFileFilterImplementation;
060: import org.netbeans.modules.java.source.classpath.GlobalSourcePath;
061: import org.netbeans.modules.java.source.usages.ClasspathInfoAccessor;
062: import org.netbeans.spi.java.classpath.support.ClassPathSupport;
063: import org.openide.ErrorManager;
064: import org.openide.filesystems.FileObject;
065: import org.openide.filesystems.FileUtil;
066: import org.openide.util.WeakListeners;
067:
068: /** Class which contains info about classpath
069: *
070: * @author Tomas Zezula, Petr Hrebejk
071: */
072: public final class ClasspathInfo {
073:
074: private static final ClassPath EMPTY_PATH = ClassPathSupport
075: .createClassPath(new URL[0]);
076:
077: static {
078: ClasspathInfoAccessor
079: .setINSTANCE(new ClasspathInfoAccessorImpl());
080: try {
081: Class.forName(ClassIndex.class.getName(), true,
082: CompilationInfo.class.getClassLoader());
083: } catch (ClassNotFoundException ex) {
084: ErrorManager.getDefault().notify(ex);
085: }
086: }
087:
088: private final CachingArchiveProvider archiveProvider;
089:
090: private final ClassPath srcClassPath;
091: private final ClassPath bootClassPath;
092: private final ClassPath compileClassPath;
093: private final ClassPath cachedBootClassPath;
094: private final ClassPath cachedCompileClassPath;
095: private ClassPath outputClassPath;
096:
097: private final ClassPathListener cpListener;
098: private final boolean backgroundCompilation;
099: private final boolean ignoreExcludes;
100: private final JavaFileFilterImplementation filter;
101: private JavaFileManager fileManager;
102: private EventListenerList listenerList = null;
103: private ClassIndex usagesQuery;
104:
105: /** Creates a new instance of ClasspathInfo (private use the fatctory methods) */
106: private ClasspathInfo(CachingArchiveProvider archiveProvider,
107: ClassPath bootCp, ClassPath compileCp, ClassPath srcCp,
108: JavaFileFilterImplementation filter,
109: boolean backgroundCompilation, boolean ignoreExcludes) {
110: assert archiveProvider != null && bootCp != null
111: && compileCp != null;
112: this .cpListener = new ClassPathListener();
113: this .archiveProvider = archiveProvider;
114: this .bootClassPath = bootCp;
115: this .compileClassPath = compileCp;
116: this .cachedBootClassPath = CacheClassPath
117: .forBootPath(this .bootClassPath);
118: this .cachedCompileClassPath = CacheClassPath
119: .forClassPath(this .compileClassPath);
120: this .cachedBootClassPath
121: .addPropertyChangeListener(WeakListeners
122: .propertyChange(this .cpListener,
123: this .cachedBootClassPath));
124: this .cachedCompileClassPath
125: .addPropertyChangeListener(WeakListeners
126: .propertyChange(this .cpListener,
127: this .cachedCompileClassPath));
128: if (srcCp != null
129: && !GlobalSourcePath.getDefault().isLibrary(srcCp)) {
130: this .srcClassPath = srcCp;
131: this .outputClassPath = CacheClassPath
132: .forSourcePath(this .srcClassPath);
133: this .srcClassPath
134: .addPropertyChangeListener(WeakListeners
135: .propertyChange(this .cpListener,
136: this .srcClassPath));
137: } else {
138: this .srcClassPath = ClassPathSupport
139: .createClassPath(new URL[0]);
140: this .outputClassPath = ClassPathSupport
141: .createClassPath(new URL[0]);
142: }
143: this .backgroundCompilation = backgroundCompilation;
144: this .ignoreExcludes = ignoreExcludes;
145: this .filter = filter;
146: }
147:
148: public String toString() {
149: return "ClasspathInfo boot:[" + cachedBootClassPath
150: + "],compile:[" + cachedCompileClassPath + "],src:["
151: + srcClassPath + "]," + "out:[" + outputClassPath + "]"; //NOI18N
152: }
153:
154: // Factory methods ---------------------------------------------------------
155:
156: /** Creates new interface to the compiler
157: * @param file for which the CompilerInterface should be created
158: * @return ClasspathInfo or null if the file does not exist on the
159: * local file system or it has no classpath associated
160: */
161: public static ClasspathInfo create(final File file) {
162: if (file == null) {
163: throw new IllegalArgumentException(
164: "Cannot pass null as parameter of ClasspathInfo.create(java.io.File)"); //NOI18N
165: }
166: final FileObject fo = FileUtil.toFileObject(file);
167: if (fo == null) {
168: return null;
169: } else {
170: return create(fo);
171: }
172: }
173:
174: private static ClasspathInfo create(FileObject fo,
175: JavaFileFilterImplementation filter,
176: boolean backgroundCompilation, boolean ignoreExcludes) {
177: ClassPath bootPath = ClassPath.getClassPath(fo, ClassPath.BOOT);
178: if (bootPath == null) {
179: //javac requires at least java.lang
180: bootPath = JavaPlatformManager.getDefault()
181: .getDefaultPlatform().getBootstrapLibraries();
182: }
183: ClassPath compilePath = ClassPath.getClassPath(fo,
184: ClassPath.COMPILE);
185: if (compilePath == null) {
186: compilePath = EMPTY_PATH;
187: }
188: ClassPath srcPath = ClassPath
189: .getClassPath(fo, ClassPath.SOURCE);
190: if (srcPath == null) {
191: srcPath = EMPTY_PATH;
192: }
193: return create(bootPath, compilePath, srcPath, filter,
194: backgroundCompilation, ignoreExcludes);
195: }
196:
197: /** Creates new interface to the compiler
198: * @param fo for which the CompilerInterface should be created
199: */
200: public static ClasspathInfo create(FileObject fo) {
201: return create(fo, null, false, false);
202: }
203:
204: private static ClasspathInfo create(ClassPath bootPath,
205: ClassPath classPath, ClassPath sourcePath,
206: JavaFileFilterImplementation filter,
207: boolean backgroundCompilation, boolean ignoreExcludes) {
208: return new ClasspathInfo(CachingArchiveProvider.getDefault(),
209: bootPath, classPath, sourcePath, filter,
210: backgroundCompilation, ignoreExcludes);
211: }
212:
213: public static ClasspathInfo create(ClassPath bootPath,
214: ClassPath classPath, ClassPath sourcePath) {
215: return new ClasspathInfo(CachingArchiveProvider.getDefault(),
216: bootPath, classPath, sourcePath, null, false, false);
217: }
218:
219: // Public methods ----------------------------------------------------------
220:
221: /** Registers ChangeListener which will be notified about the changes in the classpath.
222: * @param listener The listener to register.
223: */
224: public synchronized void addChangeListener(ChangeListener listener) {
225: if (listenerList == null) {
226: listenerList = new EventListenerList();
227: }
228: listenerList.add(ChangeListener.class, listener);
229: }
230:
231: /**Removes ChangeListener from the list of listeners.
232: * @param listener The listener to remove.
233: */
234: public synchronized void removeChangeListener(
235: ChangeListener listener) {
236: listenerList.remove(ChangeListener.class, listener);
237: }
238:
239: public ClassPath getClassPath(PathKind pathKind) {
240: switch (pathKind) {
241: case BOOT:
242: return this .bootClassPath;
243: case COMPILE:
244: return this .compileClassPath;
245: case SOURCE:
246: return this .srcClassPath;
247: default:
248: assert false : "Unknown path type"; //NOI18N
249: return null;
250: }
251: }
252:
253: ClassPath getCachedClassPath(PathKind pathKind) {
254: switch (pathKind) {
255: case BOOT:
256: return this .cachedBootClassPath;
257: case COMPILE:
258: return this .cachedCompileClassPath;
259: case SOURCE:
260: return this .srcClassPath;
261: case OUTPUT:
262: return this .outputClassPath;
263: default:
264: assert false : "Unknown path type"; //NOI18N
265: return null;
266: }
267: }
268:
269: public synchronized ClassIndex getClassIndex() {
270: if (usagesQuery == null) {
271: usagesQuery = new ClassIndex(this .bootClassPath,
272: this .compileClassPath, this .srcClassPath);
273: }
274: return usagesQuery;
275: }
276:
277: // Package private methods -------------------------------------------------
278:
279: synchronized JavaFileManager getFileManager() {
280: if (this .fileManager == null) {
281: boolean hasSources = this .srcClassPath != null;
282: this .fileManager = new ProxyFileManager(
283: new CachingFileManager(this .archiveProvider,
284: this .cachedBootClassPath, true, true),
285: new CachingFileManager(this .archiveProvider,
286: this .cachedCompileClassPath, false, true),
287: hasSources ? (backgroundCompilation ? new CachingFileManager(
288: this .archiveProvider, this .srcClassPath,
289: filter, false, ignoreExcludes)
290: : new SourceFileManager(this .srcClassPath,
291: ignoreExcludes))
292: : null, hasSources ? new OutputFileManager(
293: this .archiveProvider, this .outputClassPath,
294: this .srcClassPath) : null);
295: }
296: return this .fileManager;
297: }
298:
299: // Private methods ---------------------------------------------------------
300:
301: private void fireChangeListenerStateChanged() {
302: ChangeEvent e = null;
303: if (listenerList == null)
304: return;
305: Object[] listeners = listenerList.getListenerList();
306: for (int i = listeners.length - 2; i >= 0; i -= 2) {
307: if (listeners[i] == ChangeListener.class) {
308: if (e == null)
309: e = new ChangeEvent(this );
310: ((ChangeListener) listeners[i + 1]).stateChanged(e);
311: }
312: }
313: }
314:
315: // Innerclasses ------------------------------------------------------------
316:
317: public static enum PathKind {
318: BOOT, COMPILE, SOURCE, OUTPUT,
319:
320: }
321:
322: private class ClassPathListener implements PropertyChangeListener {
323:
324: public void propertyChange(PropertyChangeEvent event) {
325: if (ClassPath.PROP_ROOTS.equals(event.getPropertyName())) {
326: synchronized (this ) {
327: // Kill FileManager
328: fileManager = null;
329: }
330: fireChangeListenerStateChanged();
331: }
332: }
333: }
334:
335: private static class ClasspathInfoAccessorImpl extends
336: ClasspathInfoAccessor {
337:
338: @Override
339: public JavaFileManager getFileManager(ClasspathInfo cpInfo) {
340: return cpInfo.getFileManager();
341: }
342:
343: public ClassPath getCachedClassPath(final ClasspathInfo cpInfo,
344: final PathKind kind) {
345: return cpInfo.getCachedClassPath(kind);
346: }
347:
348: @Override
349: public ClasspathInfo create(ClassPath bootPath,
350: ClassPath classPath, ClassPath sourcePath,
351: JavaFileFilterImplementation filter,
352: boolean backgroundCompilation, boolean ignoreExcludes) {
353: return ClasspathInfo.create(bootPath, classPath,
354: sourcePath, filter, backgroundCompilation,
355: ignoreExcludes);
356: }
357:
358: @Override
359: public ClasspathInfo create(FileObject fo,
360: JavaFileFilterImplementation filter,
361: boolean backgroundCompilation, boolean ignoreExcludes) {
362: return ClasspathInfo.create(fo, filter,
363: backgroundCompilation, ignoreExcludes);
364: }
365: }
366: }
|