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.apisupport.project.queries;
043:
044: import java.beans.PropertyChangeEvent;
045: import java.beans.PropertyChangeListener;
046: import java.beans.PropertyChangeSupport;
047: import java.io.File;
048: import java.net.URL;
049: import java.util.ArrayList;
050: import java.util.HashMap;
051: import java.util.HashSet;
052: import java.util.List;
053: import java.util.Map;
054: import java.util.Set;
055: import java.util.regex.Matcher;
056: import java.util.regex.Pattern;
057: import org.netbeans.api.java.classpath.ClassPath;
058: import org.netbeans.spi.java.classpath.ClassPathFactory;
059: import org.netbeans.spi.java.classpath.ClassPathImplementation;
060: import org.netbeans.spi.java.classpath.ClassPathProvider;
061: import org.netbeans.spi.java.classpath.support.ClassPathSupport;
062: import org.netbeans.modules.apisupport.project.NbModuleProject;
063: import org.netbeans.modules.apisupport.project.NbModuleProjectType;
064: import org.netbeans.modules.apisupport.project.Util;
065: import org.netbeans.spi.java.classpath.PathResourceImplementation;
066: import org.netbeans.spi.java.project.classpath.support.ProjectClassPathSupport;
067: import org.netbeans.spi.project.support.ant.AntProjectEvent;
068: import org.netbeans.spi.project.support.ant.AntProjectListener;
069: import org.netbeans.spi.project.support.ant.PropertyUtils;
070: import org.openide.ErrorManager;
071: import org.openide.filesystems.FileObject;
072: import org.openide.filesystems.FileStateInvalidException;
073: import org.openide.filesystems.FileUtil;
074: import org.openide.util.WeakListeners;
075: import org.w3c.dom.Element;
076:
077: public final class ClassPathProviderImpl implements ClassPathProvider {
078:
079: private final NbModuleProject project;
080:
081: public ClassPathProviderImpl(NbModuleProject project) {
082: this .project = project;
083: }
084:
085: private ClassPath boot;
086: private ClassPath source;
087: private ClassPath compile;
088: private ClassPath execute;
089: private ClassPath testSource;
090: private ClassPath testCompile;
091: private ClassPath testExecute;
092: private ClassPath funcTestSource;
093: private ClassPath funcTestCompile;
094: private ClassPath funcTestExecute;
095: private Map<FileObject, ClassPath> extraCompilationUnitsCompile = null;
096: private Map<FileObject, ClassPath> extraCompilationUnitsExecute = null;
097:
098: public ClassPath findClassPath(FileObject file, String type) {
099: if (type.equals(ClassPath.BOOT)) {
100: if (boot == null) {
101: boot = ClassPathFactory
102: .createClassPath(createPathFromProperty("nbjdk.bootclasspath")); // NOI18N
103: }
104: return boot;
105: }
106: FileObject srcDir = project.getSourceDirectory();
107: FileObject testSrcDir = project.getTestSourceDirectory();
108: FileObject funcTestSrcDir = project
109: .getFunctionalTestSourceDirectory();
110: File dir = project.getClassesDirectory();
111: FileObject classesDir = dir == null ? null : FileUtil
112: .toFileObject(FileUtil.normalizeFile(dir));
113: dir = project.getTestClassesDirectory();
114: FileObject testClassesDir = dir == null ? null : FileUtil
115: .toFileObject(FileUtil.normalizeFile(dir));
116: File moduleJar = project.getModuleJarLocation();
117: if (srcDir != null
118: && (FileUtil.isParentOf(srcDir, file) || file == srcDir)) {
119: // Regular sources.
120: if (type.equals(ClassPath.COMPILE)) {
121: if (compile == null) {
122: compile = ClassPathFactory
123: .createClassPath(createCompileClasspath());
124: Util.err.log("compile/execute-time classpath for "
125: + project + ": " + compile);
126: }
127: return compile;
128: } else if (type.equals(ClassPath.EXECUTE)) {
129: if (execute == null) {
130: execute = ClassPathFactory
131: .createClassPath(createExecuteClasspath());
132: }
133: return execute;
134: } else if (type.equals(ClassPath.SOURCE)) {
135: if (source == null) {
136: source = ClassPathSupport
137: .createClassPath(new FileObject[] { srcDir });
138: }
139: return source;
140: }
141: } else if (testSrcDir != null
142: && (FileUtil.isParentOf(testSrcDir, file) || file == testSrcDir)) {
143: // Unit tests.
144: if (type.equals(ClassPath.COMPILE)) {
145: if (testCompile == null) {
146: testCompile = ClassPathFactory
147: .createClassPath(createTestCompileClasspath());
148: Util.err.log("compile-time classpath for tests in "
149: + project + ": " + testCompile);
150: }
151: return testCompile;
152: } else if (type.equals(ClassPath.EXECUTE)) {
153: if (testExecute == null) {
154: testExecute = ClassPathFactory
155: .createClassPath(createTestExecuteClasspath());
156: Util.err.log("runtime classpath for tests in "
157: + project + ": " + testExecute);
158: }
159: return testExecute;
160: } else if (type.equals(ClassPath.SOURCE)) {
161: if (testSource == null) {
162: testSource = ClassPathSupport
163: .createClassPath(new FileObject[] { testSrcDir });
164: }
165: return testSource;
166: }
167: } else if (funcTestSrcDir != null
168: && (FileUtil.isParentOf(funcTestSrcDir, file) || file == funcTestSrcDir)) {
169: // Functional tests.
170: if (type.equals(ClassPath.SOURCE)) {
171: if (funcTestSource == null) {
172: funcTestSource = ClassPathSupport
173: .createClassPath(new FileObject[] { funcTestSrcDir });
174: }
175: return funcTestSource;
176: } else if (type.equals(ClassPath.COMPILE)) {
177: // See #42331.
178: if (funcTestCompile == null) {
179: funcTestCompile = ClassPathFactory
180: .createClassPath(createFuncTestCompileClasspath());
181: Util.err
182: .log("compile-time classpath for func tests in "
183: + project + ": " + funcTestCompile);
184: }
185: return funcTestCompile;
186: } else if (type.equals(ClassPath.EXECUTE)) {
187: if (funcTestExecute == null) {
188: funcTestExecute = ClassPathFactory
189: .createClassPath(createFuncTestExecuteClasspath());
190: }
191: return funcTestExecute;
192: }
193: } else if (classesDir != null
194: && (classesDir.equals(file) || FileUtil.isParentOf(
195: classesDir, file))) {
196: if (ClassPath.EXECUTE.equals(type)) {
197: try {
198: List<PathResourceImplementation> roots = new ArrayList<PathResourceImplementation>();
199: roots.add(ClassPathSupport
200: .createResource(classesDir.getURL()));
201: roots.addAll(createCompileClasspath()
202: .getResources());
203: return ClassPathSupport.createClassPath(roots);
204: } catch (FileStateInvalidException e) {
205: ErrorManager.getDefault().notify(e);
206: return null;
207: }
208: }
209: } else if (testClassesDir != null
210: && (testClassesDir.equals(file) || FileUtil.isParentOf(
211: testClassesDir, file))) {
212: if (ClassPath.EXECUTE.equals(type)) {
213: if (testExecute == null) {
214: testExecute = ClassPathFactory
215: .createClassPath(createTestExecuteClasspath());
216: Util.err.log("runtime classpath for tests in "
217: + project + ": " + testExecute);
218: }
219: return testExecute;
220: }
221: } else if (FileUtil.getArchiveFile(file) != null
222: && FileUtil.toFile(FileUtil.getArchiveFile(file))
223: .equals(moduleJar)
224: && file.equals(FileUtil.getArchiveRoot(FileUtil
225: .getArchiveFile(file)))) {
226: if (ClassPath.EXECUTE.equals(type)) {
227: List<PathResourceImplementation> roots = new ArrayList<PathResourceImplementation>();
228: roots.add(ClassPathSupport.createResource(Util
229: .urlForJar(moduleJar)));
230: roots.addAll(createCompileClasspath().getResources());
231: return ClassPathSupport.createClassPath(roots);
232: }
233: } else {
234: calculateExtraCompilationUnits();
235: for (Map.Entry<FileObject, ClassPath> entry : extraCompilationUnitsCompile
236: .entrySet()) {
237: FileObject pkgroot = entry.getKey();
238: if (FileUtil.isParentOf(pkgroot, file)
239: || file == pkgroot) {
240: if (type.equals(ClassPath.COMPILE)) {
241: return entry.getValue();
242: } else if (type.equals(ClassPath.EXECUTE)) {
243: return extraCompilationUnitsExecute
244: .get(pkgroot);
245: } else if (type.equals(ClassPath.SOURCE)) {
246: // XXX should these be cached?
247: return ClassPathSupport
248: .createClassPath(new FileObject[] { pkgroot });
249: } else {
250: break;
251: }
252: }
253: }
254: }
255: // Something not supported.
256: return null;
257: }
258:
259: private ClassPathImplementation createPathFromProperty(String prop) {
260: return ProjectClassPathSupport
261: .createPropertyBasedClassPathImplementation(project
262: .getProjectDirectoryFile(),
263: project.evaluator(), new String[] { prop });
264: }
265:
266: /** <compile-dependency> is what we care about. */
267: private ClassPathImplementation createCompileClasspath() {
268: return createPathFromProperty("cp"); // NOI18N
269: }
270:
271: private void addPathFromProjectEvaluated(
272: List<PathResourceImplementation> entries, String path) {
273: if (path != null) {
274: String[] pieces = PropertyUtils.tokenizePath(path);
275: for (int i = 0; i < pieces.length; i++) {
276: entries.add(ClassPathSupport.createResource(Util
277: .urlForDirOrJar(project.getHelper()
278: .resolveFile(pieces[i]))));
279: }
280: }
281: }
282:
283: private ClassPathImplementation createTestCompileClasspath() {
284: return createPathFromProperty("test.unit.cp"); // NOI18N
285: }
286:
287: private ClassPathImplementation createTestExecuteClasspath() {
288: return createPathFromProperty("test.unit.run.cp"); // NOI18N
289: }
290:
291: private ClassPathImplementation createFuncTestCompileClasspath() {
292: return createPathFromProperty("test.qa-functional.cp"); // NOI18N
293: }
294:
295: private ClassPathImplementation createFuncTestExecuteClasspath() {
296: return createPathFromProperty("test.qa-functional.run.cp"); // NOI18N
297: }
298:
299: private ClassPathImplementation createExecuteClasspath() {
300: return createPathFromProperty("run.cp"); // NOI18N
301: }
302:
303: private void calculateExtraCompilationUnits() {
304: if (extraCompilationUnitsCompile != null) {
305: return;
306: }
307: extraCompilationUnitsCompile = new HashMap<FileObject, ClassPath>();
308: extraCompilationUnitsExecute = new HashMap<FileObject, ClassPath>();
309: for (Map.Entry<FileObject, Element> entry : project
310: .getExtraCompilationUnits().entrySet()) {
311: final FileObject pkgroot = entry.getKey();
312: Element pkgrootEl = entry.getValue();
313: Element classpathEl = Util.findElement(pkgrootEl,
314: "classpath", NbModuleProjectType.NAMESPACE_SHARED); // NOI18N
315: assert classpathEl != null : "no <classpath> in "
316: + pkgrootEl;
317: final String classpathS = Util.findText(classpathEl);
318: if (classpathS == null) {
319: extraCompilationUnitsCompile.put(pkgroot,
320: ClassPathSupport.createClassPath(new URL[0]));
321: extraCompilationUnitsExecute.put(pkgroot,
322: ClassPathSupport.createClassPath(new URL[0]));
323: } else {
324: class CPI implements ClassPathImplementation,
325: PropertyChangeListener, AntProjectListener {
326: final Set<String> relevantProperties = new HashSet<String>();
327: final PropertyChangeSupport pcs = new PropertyChangeSupport(
328: this );
329: String cpS = classpathS;
330:
331: CPI() {
332: project.evaluator().addPropertyChangeListener(
333: WeakListeners.propertyChange(this ,
334: project.evaluator()));
335: project.getHelper().addAntProjectListener(
336: WeakListeners.create(
337: AntProjectListener.class, this ,
338: project.getHelper()));
339: Matcher m = Pattern
340: .compile("\\$\\{([^{}]+)\\}").matcher(
341: cpS);
342: while (m.find()) {
343: relevantProperties.add(m.group(1));
344: }
345: }
346:
347: public List<? extends PathResourceImplementation> getResources() {
348: List<PathResourceImplementation> resources = new ArrayList<PathResourceImplementation>();
349: addPathFromProjectEvaluated(resources, project
350: .evaluator().evaluate(cpS));
351: return resources;
352: }
353:
354: public void addPropertyChangeListener(
355: PropertyChangeListener listener) {
356: pcs.addPropertyChangeListener(listener);
357: }
358:
359: public void removePropertyChangeListener(
360: PropertyChangeListener listener) {
361: pcs.removePropertyChangeListener(listener);
362: }
363:
364: public void propertyChange(PropertyChangeEvent evt) {
365: if (relevantProperties.contains(evt
366: .getPropertyName())) {
367: pcs.firePropertyChange(PROP_RESOURCES,
368: null, null);
369: }
370: }
371:
372: public void configurationXmlChanged(
373: AntProjectEvent ev) {
374: Element pkgrootEl = project
375: .getExtraCompilationUnits()
376: .get(pkgroot);
377: Element classpathEl = Util.findElement(
378: pkgrootEl, "classpath",
379: NbModuleProjectType.NAMESPACE_SHARED); // NOI18N
380: assert classpathEl != null : "no <classpath> in "
381: + pkgrootEl;
382: cpS = Util.findText(classpathEl);
383: pcs.firePropertyChange(PROP_RESOURCES, null,
384: null);
385: }
386:
387: public void propertiesChanged(AntProjectEvent ev) {
388: }
389: }
390: ClassPathImplementation ecuCompile = new CPI();
391: extraCompilationUnitsCompile.put(pkgroot,
392: ClassPathFactory.createClassPath(ecuCompile));
393: // Add <built-to> dirs and JARs for ClassPath.EXECUTE.
394: List<PathResourceImplementation> extraEntries = new ArrayList<PathResourceImplementation>();
395: for (Element kid : Util.findSubElements(pkgrootEl)) {
396: if (!kid.getLocalName().equals("built-to")) { // NOI18N
397: continue;
398: }
399: String rawtext = Util.findText(kid);
400: assert rawtext != null : "Null content for <built-to> in "
401: + project;
402: String text = project.evaluator().evaluate(rawtext);
403: if (text == null) {
404: continue;
405: }
406: addPathFromProjectEvaluated(extraEntries, text);
407: }
408: extraCompilationUnitsExecute
409: .put(
410: pkgroot,
411: ClassPathFactory
412: .createClassPath(ClassPathSupport
413: .createProxyClassPathImplementation(
414: ecuCompile,
415: ClassPathSupport
416: .createClassPathImplementation(extraEntries))));
417: }
418: }
419: }
420:
421: /**
422: * Returns array of all classpaths of the given type in the project.
423: * The result is used for example for GlobalPathRegistry registrations.
424: */
425: public ClassPath[] getProjectClassPaths(String type) {
426: if (ClassPath.BOOT.equals(type)) {
427: FileObject srcDir = project.getSourceDirectory();
428: if (srcDir != null) {
429: return new ClassPath[] { findClassPath(srcDir,
430: ClassPath.BOOT) };
431: }
432: }
433: List<ClassPath> paths = new ArrayList<ClassPath>(3);
434: if (ClassPath.COMPILE.equals(type)) {
435: FileObject srcDir = project.getSourceDirectory();
436: if (srcDir != null) {
437: paths.add(findClassPath(srcDir, ClassPath.COMPILE));
438: }
439: FileObject testSrcDir = project.getTestSourceDirectory();
440: if (testSrcDir != null) {
441: paths.add(findClassPath(testSrcDir, ClassPath.COMPILE));
442: }
443: FileObject funcTestSrcDir = project
444: .getFunctionalTestSourceDirectory();
445: if (funcTestSrcDir != null) {
446: paths.add(findClassPath(funcTestSrcDir,
447: ClassPath.COMPILE));
448: }
449: calculateExtraCompilationUnits();
450: paths.addAll(extraCompilationUnitsCompile.values());
451: } else if (ClassPath.EXECUTE.equals(type)) {
452: FileObject srcDir = project.getSourceDirectory();
453: if (srcDir != null) {
454: paths.add(findClassPath(srcDir, ClassPath.EXECUTE));
455: }
456: FileObject testSrcDir = project.getTestSourceDirectory();
457: if (testSrcDir != null) {
458: paths.add(findClassPath(testSrcDir, ClassPath.EXECUTE));
459: }
460: FileObject funcTestSrcDir = project
461: .getFunctionalTestSourceDirectory();
462: if (funcTestSrcDir != null) {
463: paths.add(findClassPath(funcTestSrcDir,
464: ClassPath.EXECUTE));
465: }
466: calculateExtraCompilationUnits();
467: paths.addAll(extraCompilationUnitsExecute.values());
468: } else if (ClassPath.SOURCE.equals(type)) {
469: FileObject srcDir = project.getSourceDirectory();
470: if (srcDir != null) {
471: paths.add(findClassPath(srcDir, ClassPath.SOURCE));
472: }
473: FileObject testSrcDir = project.getTestSourceDirectory();
474: if (testSrcDir != null) {
475: paths.add(findClassPath(testSrcDir, ClassPath.SOURCE));
476: }
477: FileObject funcTestSrcDir = project
478: .getFunctionalTestSourceDirectory();
479: if (funcTestSrcDir != null) {
480: paths.add(findClassPath(funcTestSrcDir,
481: ClassPath.SOURCE));
482: }
483: calculateExtraCompilationUnits();
484: for (FileObject root : extraCompilationUnitsCompile
485: .keySet()) {
486: paths.add(ClassPathSupport
487: .createClassPath(new FileObject[] { root }));
488: }
489: }
490: return paths.toArray(new ClassPath[paths.size()]);
491: }
492:
493: }
|