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:
042: package org.netbeans.modules.java.api.common.classpath;
043:
044: import java.beans.PropertyChangeEvent;
045: import java.beans.PropertyChangeListener;
046: import java.io.File;
047: import java.util.Map;
048: import java.util.HashMap;
049: import org.netbeans.api.java.classpath.ClassPath;
050: import org.netbeans.modules.java.api.common.SourceRoots;
051: import org.netbeans.spi.java.classpath.ClassPathFactory;
052: import org.netbeans.spi.java.classpath.ClassPathProvider;
053: import org.netbeans.spi.java.project.classpath.support.ProjectClassPathSupport;
054: import org.netbeans.spi.project.support.ant.AntProjectHelper;
055: import org.netbeans.spi.project.support.ant.PropertyEvaluator;
056: import org.openide.filesystems.FileObject;
057: import org.openide.filesystems.FileUtil;
058: import org.openide.util.Parameters;
059: import org.openide.util.WeakListeners;
060:
061: /**
062: * Defines the various class paths for various project types.
063: * @author Tomas Zezula, David Konecny, Tomas Mysik
064: */
065: public final class DefaultClassPathProviderImpl implements
066: ClassPathProvider, PropertyChangeListener {
067:
068: private final AntProjectHelper helper;
069: private final File projectDirectory;
070: private final PropertyEvaluator evaluator;
071: private final SourceRoots sourceRoots;
072: private final SourceRoots testSourceRoots;
073:
074: private final Properties properties;
075:
076: private final Map<ClassPathCache, ClassPath> cache = new HashMap<ClassPathCache, ClassPath>();
077: private final Map<String, FileObject> dirCache = new HashMap<String, FileObject>();
078:
079: private static enum FileType {
080: UNKNOWN, SOURCE, // java source
081: TEST_SOURCE, // junit test source
082: CLASS, // compiled java class
083: TEST_CLASS, // compiled junit test class
084: CLASS_IN_JAR
085: // compiled java class packaged in jar
086: }
087:
088: /**
089: * Constants for different cached classpaths.
090: */
091: private static enum ClassPathCache {
092: SOURCE_COMPILATION, TEST_SOURCE_COMPILATION, SOURCE, TEST_SOURCE, SOURCE_RUNTIME, TEST_SOURCE_RUNTIME, BOOT
093: }
094:
095: public DefaultClassPathProviderImpl(AntProjectHelper helper,
096: PropertyEvaluator evaluator, SourceRoots sourceRoots,
097: SourceRoots testSourceRoots, Properties properties) {
098: Parameters.notNull("helper", helper);
099: Parameters.notNull("evaluator", evaluator);
100: Parameters.notNull("sourceRoots", sourceRoots);
101: Parameters.notNull("testSourceRoots", testSourceRoots);
102: Parameters.notNull("properties", properties);
103:
104: this .helper = helper;
105: this .projectDirectory = FileUtil.toFile(helper
106: .getProjectDirectory());
107: this .evaluator = evaluator;
108: this .sourceRoots = sourceRoots;
109: this .testSourceRoots = testSourceRoots;
110: evaluator.addPropertyChangeListener(WeakListeners
111: .propertyChange(this , evaluator));
112: this .properties = properties;
113: }
114:
115: private synchronized FileObject getDir(String propname) {
116: FileObject fo = dirCache.get(propname);
117: if (fo == null || !fo.isValid()) {
118: String prop = evaluator.getProperty(propname);
119: if (prop != null) {
120: fo = helper.resolveFileObject(prop);
121: dirCache.put(propname, fo);
122: }
123: }
124: return fo;
125: }
126:
127: private FileObject[] getPrimarySrcPath() {
128: return sourceRoots.getRoots();
129: }
130:
131: private FileObject[] getTestSrcDir() {
132: return testSourceRoots.getRoots();
133: }
134:
135: private FileObject getBuildClassesDir() {
136: return getDir(properties.buildClassesDir);
137: }
138:
139: private FileObject getDistJar() {
140: return getDir(properties.distJar);
141: }
142:
143: private FileObject getBuildTestClassesDir() {
144: return getDir(properties.buildTestClassesDir);
145: }
146:
147: /**
148: * Find what a given file represents.
149: * @param file a file in the project
150: * @return one of FileType.* constants
151: */
152: private FileType getType(FileObject file) {
153: for (FileObject root : getPrimarySrcPath()) {
154: if (root.equals(file) || FileUtil.isParentOf(root, file)) {
155: return FileType.SOURCE;
156: }
157: }
158: for (FileObject root : getTestSrcDir()) {
159: if (root.equals(file) || FileUtil.isParentOf(root, file)) {
160: return FileType.TEST_SOURCE;
161: }
162: }
163: FileObject dir = getBuildClassesDir();
164: if (dir != null
165: && (dir.equals(file) || FileUtil.isParentOf(dir, file))) {
166: return FileType.CLASS;
167: }
168: dir = getDistJar(); // not really a dir at all, of course
169: if (dir != null && dir.equals(FileUtil.getArchiveFile(file))) {
170: // XXX check whether this is really the root
171: return FileType.CLASS_IN_JAR;
172: }
173: dir = getBuildTestClassesDir();
174: if (dir != null
175: && (dir.equals(file) || FileUtil.isParentOf(dir, file))) {
176: return FileType.TEST_CLASS;
177: }
178: return FileType.UNKNOWN;
179: }
180:
181: private synchronized ClassPath getCompileTimeClasspath(FileType type) {
182: ClassPath cp = null;
183: switch (type) {
184: case SOURCE:
185: case CLASS:
186: // treat all these types as source
187: cp = cache.get(ClassPathCache.SOURCE_COMPILATION);
188: if (cp == null) {
189: cp = ClassPathFactory
190: .createClassPath(ProjectClassPathSupport
191: .createPropertyBasedClassPathImplementation(
192: projectDirectory,
193: evaluator,
194: properties.sourceCompileTimeClassPath));
195: cache.put(ClassPathCache.SOURCE_COMPILATION, cp);
196: }
197: break;
198:
199: case TEST_SOURCE:
200: cp = cache.get(ClassPathCache.TEST_SOURCE_COMPILATION);
201: if (cp == null) {
202: cp = ClassPathFactory
203: .createClassPath(ProjectClassPathSupport
204: .createPropertyBasedClassPathImplementation(
205: projectDirectory,
206: evaluator,
207: properties.testSourceCompileTimeClassPath));
208: cache.put(ClassPathCache.TEST_SOURCE_COMPILATION, cp);
209: }
210: break;
211:
212: default:
213: // XXX any exception?
214: break;
215: }
216: return cp;
217: }
218:
219: private synchronized ClassPath getRunTimeClasspath(FileType type) {
220: ClassPath cp = null;
221: switch (type) {
222: case SOURCE:
223: case CLASS:
224: // XXX this one as well? see ClassPathProviderImpl in J2SE, line 221
225: case CLASS_IN_JAR:
226: // treat all these types as source
227: cp = cache.get(ClassPathCache.SOURCE_RUNTIME);
228: if (cp == null) {
229: cp = ClassPathFactory
230: .createClassPath(ProjectClassPathSupport
231: .createPropertyBasedClassPathImplementation(
232: projectDirectory,
233: evaluator,
234: properties.sourceRunTimeClassPath));
235: cache.put(ClassPathCache.SOURCE_RUNTIME, cp);
236: }
237: break;
238:
239: case TEST_SOURCE:
240: case TEST_CLASS:
241: cp = cache.get(ClassPathCache.TEST_SOURCE_RUNTIME);
242: if (cp == null) {
243: cp = ClassPathFactory
244: .createClassPath(ProjectClassPathSupport
245: .createPropertyBasedClassPathImplementation(
246: projectDirectory,
247: evaluator,
248: properties.testSourceRunTimeClassPath));
249: cache.put(ClassPathCache.TEST_SOURCE_RUNTIME, cp);
250: }
251: break;
252:
253: default:
254: // XXX any exception?
255: break;
256: }
257: return cp;
258: }
259:
260: private synchronized ClassPath getSourcePath(FileType type) {
261: ClassPath cp = null;
262: switch (type) {
263: case SOURCE:
264: case CLASS:
265: // treat all these types as source
266: cp = cache.get(ClassPathCache.SOURCE);
267: if (cp == null) {
268: cp = ClassPathFactory
269: .createClassPath(new SourcePathImplementation(
270: sourceRoots, helper, evaluator));
271: cache.put(ClassPathCache.SOURCE, cp);
272: }
273: break;
274:
275: case TEST_SOURCE:
276: cp = cache.get(ClassPathCache.TEST_SOURCE);
277: if (cp == null) {
278: cp = ClassPathFactory
279: .createClassPath(new SourcePathImplementation(
280: testSourceRoots, helper, evaluator));
281: cache.put(ClassPathCache.TEST_SOURCE, cp);
282: }
283: break;
284:
285: default:
286: // XXX any exception?
287: break;
288: }
289: return cp;
290: }
291:
292: private synchronized ClassPath getBootClassPath() {
293: ClassPath cp = cache.get(ClassPathCache.BOOT);
294: if (cp == null) {
295: cp = ClassPathFactory
296: .createClassPath(new BootClassPathImplementation(
297: evaluator));
298: cache.put(ClassPathCache.BOOT, cp);
299: }
300: return cp;
301: }
302:
303: /**
304: * @see ClassPathProvider#findClassPath()
305: */
306: public ClassPath findClassPath(FileObject file, String type) {
307: FileType fileType = getType(file);
308: if (type.equals(ClassPath.COMPILE)) {
309: return getCompileTimeClasspath(fileType);
310: } else if (type.equals(ClassPath.EXECUTE)) {
311: return getRunTimeClasspath(fileType);
312: } else if (type.equals(ClassPath.SOURCE)) {
313: return getSourcePath(fileType);
314: } else if (type.equals(ClassPath.BOOT)) {
315: return getBootClassPath();
316: }
317: return null;
318: }
319:
320: /**
321: * Return array of all classpaths of the given type in the project.
322: * The result is used for example for GlobalPathRegistry registrations.
323: * @param type classpath type - boot, compile or source, see {@link ClassPath} for more information.
324: * @return array of all classpaths of the given type in the project.
325: */
326: public ClassPath[] getProjectClassPaths(String type) {
327: if (ClassPath.BOOT.equals(type)) {
328: return new ClassPath[] { getBootClassPath() };
329: } else if (ClassPath.COMPILE.equals(type)) {
330: return new ClassPath[] {
331: getCompileTimeClasspath(FileType.SOURCE),
332: getCompileTimeClasspath(FileType.TEST_SOURCE), };
333: } else if (ClassPath.SOURCE.equals(type)) {
334: return new ClassPath[] { getSourcePath(FileType.SOURCE),
335: getSourcePath(FileType.TEST_SOURCE), };
336: }
337: assert false;
338: return null;
339: }
340:
341: /**
342: * Return the given type of the classpath for the project sources
343: * (i.e., excluding tests roots).
344: * @param type classpath type - boot, compile or source, see {@link ClassPath} for more information.
345: * @return the given type of the classpath for the project sources.
346: */
347: public ClassPath getProjectSourcesClassPath(String type) {
348: if (ClassPath.BOOT.equals(type)) {
349: return getBootClassPath();
350: } else if (ClassPath.COMPILE.equals(type)) {
351: return getCompileTimeClasspath(FileType.SOURCE);
352: } else if (ClassPath.SOURCE.equals(type)) {
353: return getSourcePath(FileType.SOURCE);
354: }
355: assert false;
356: return null;
357: }
358:
359: /**
360: * Clear the related cache if any property changes.
361: */
362: public void propertyChange(PropertyChangeEvent evt) {
363: dirCache.remove(evt.getPropertyName());
364: }
365:
366: /**
367: * Clear directory cache. This method can be suitable while listening to some properties,
368: * e.g. {@link org.netbeans.spi.project.support.ant.AntProjectListener#configurationXmlChanged(AntProjectEvent)}.
369: */
370: public synchronized void clearDirectoryCache() {
371: dirCache.clear();
372: }
373:
374: /**
375: * Class holding different properties like "javac.classpath" etc.
376: */
377: public static final class Properties {
378: final String buildClassesDir;
379: final String buildTestClassesDir;
380: final String distJar;
381:
382: final String[] sourceCompileTimeClassPath;
383: final String[] testSourceCompileTimeClassPath;
384: final String[] sourceRunTimeClassPath;
385: final String[] testSourceRunTimeClassPath;
386:
387: public Properties(String buildClassesDir,
388: String buildTestClassesDir, String distJar,
389: String[] sourceCompileTimeClassPath,
390: String[] testSourceCompileTimeClassPath,
391: String[] sourceRunTimeClassPath,
392: String[] testSourceRunTimeClassPath) {
393: Parameters.notNull("buildClassesDir", buildClassesDir);
394: Parameters.notNull("buildTestClassesDir",
395: buildTestClassesDir);
396: Parameters.notNull("distJar", distJar);
397: Parameters.notNull("sourceCompileTimeClassPath",
398: sourceCompileTimeClassPath);
399: Parameters.notNull("testSourceCompileTimeClassPath",
400: testSourceCompileTimeClassPath);
401: Parameters.notNull("sourceRunTimeClassPath",
402: sourceRunTimeClassPath);
403: Parameters.notNull("testSourceRunTimeClassPath",
404: testSourceRunTimeClassPath);
405:
406: this.buildClassesDir = buildClassesDir;
407: this.buildTestClassesDir = buildTestClassesDir;
408: this.distJar = distJar;
409: this.sourceCompileTimeClassPath = sourceCompileTimeClassPath;
410: this.testSourceCompileTimeClassPath = testSourceCompileTimeClassPath;
411: this.sourceRunTimeClassPath = sourceRunTimeClassPath;
412: this.testSourceRunTimeClassPath = testSourceRunTimeClassPath;
413: }
414: }
415: }
|