|
What this is
This file is included in the DevDaily.com
"Java Source Code
Warehouse" project. The intent of this project is to help you "Learn
Java by Example" TM.
Other links
The source code
/*
* Sun Public License Notice
*
* The contents of this file are subject to the Sun Public License
* Version 1.0 (the "License"). You may not use this file except in
* compliance with the License. A copy of the License is available at
* http://www.sun.com/
*
* The Original Code is NetBeans. The Initial Developer of the Original
* Code is Sun Microsystems, Inc. Portions Copyright 1997-2004 Sun
* Microsystems, Inc. All Rights Reserved.
*/
package org.netbeans.modules.ant.freeform;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import java.io.File;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.WeakHashMap;
import org.netbeans.api.java.classpath.ClassPath;
import org.netbeans.api.java.classpath.GlobalPathRegistry;
import org.netbeans.api.java.platform.JavaPlatform;
import org.netbeans.api.java.platform.JavaPlatformManager;
import org.netbeans.api.java.platform.Specification;
import org.netbeans.spi.java.classpath.ClassPathFactory;
import org.netbeans.spi.java.classpath.ClassPathImplementation;
import org.netbeans.spi.java.classpath.ClassPathProvider;
import org.netbeans.spi.java.classpath.support.ClassPathSupport;
import org.netbeans.spi.project.AuxiliaryConfiguration;
import org.netbeans.spi.project.support.ant.AntProjectEvent;
import org.netbeans.spi.project.support.ant.AntProjectListener;
import org.netbeans.spi.project.support.ant.PropertyUtils;
import org.openide.ErrorManager;
import org.openide.filesystems.FileObject;
import org.openide.filesystems.FileUtil;
import org.openide.modules.SpecificationVersion;
import org.w3c.dom.Element;
/**
* Handle classpaths for the freeform project.
* Keeps three caches:
*
* - Classpaths registered when the project is opened. The same are unregistered
* when it is closed again, regardless of what might have changed since.
*
- A map from abstract compilation units (keyed by the literal text of the
*
<package-root> elements) to implementations which do the
* actual listening and (re-)computation of roots.
* - A map from actual package roots to the matching classpath.
*
* The complexity here is needed because
*
* - It is necessary to always unregister the exact same set of ClassPath objects
* you initially registered (even if some have since become invalid, etc.).
* Ideally, adding or removing whole paths would dynamically register or unregister
* them (if the project is currently open); the current code does not do this.
*
- A given ClassPath object must fire changes if its list of roots changes.
*
- It is necessary to return the same ClassPath object for the same FileObject.
*
* @author Jesse Glick
*/
final class Classpaths implements ClassPathProvider, AntProjectListener, PropertyChangeListener {
private final FreeformProject project;
/**
* Map from classpath types to maps from package roots to classpaths.
*/
private final Map/*>*/ classpaths = new HashMap();
/**
* Map from classpath types to maps from lists of package root names to classpath impls.
*/
private final Map/*,MutableClassPathImplementation>>*/ mutablePathImpls = new HashMap();
/**
* Map from classpath types to sets of classpaths we last registered to GlobalPathRegistry.
*/
private Map/*>*/ registeredClasspaths = null;
public Classpaths(FreeformProject project) {
this.project = project;
project.helper().addAntProjectListener(this);
project.evaluator().addPropertyChangeListener(this);
}
public synchronized ClassPath findClassPath(FileObject file, String type) {
Map/**/ classpathsByType = (Map)classpaths.get(type);
if (classpathsByType == null) {
classpathsByType = new WeakHashMap();
classpaths.put(type, classpathsByType);
}
// Check for cached value.
Iterator it = classpathsByType.entrySet().iterator();
while (it.hasNext()) {
Map.Entry entry = (Map.Entry)it.next();
FileObject root = (FileObject)entry.getKey();
if (root == file || FileUtil.isParentOf(root, file)) {
// Already have it.
return (ClassPath)entry.getValue();
}
}
// Need to create it.
AuxiliaryConfiguration aux = (AuxiliaryConfiguration)project.getLookup().lookup(AuxiliaryConfiguration.class);
assert aux != null;
Element java = aux.getConfigurationFragment("java-data", FreeformProjectType.NS_JAVA, true); // NOI18N
if (java == null) {
return null;
}
List/**/ compilationUnits = Util.findSubElements(java);
it = compilationUnits.iterator();
while (it.hasNext()) {
Element compilationUnitEl = (Element)it.next();
assert compilationUnitEl.getLocalName().equals("compilation-unit") : compilationUnitEl;
List/**/ packageRoots = findPackageRoots(project, compilationUnitEl);
Iterator it2 = packageRoots.iterator();
while (it2.hasNext()) {
FileObject root = (FileObject)it2.next();
if (root == file || FileUtil.isParentOf(root, file)) {
// Got it. Compute classpath and cache it (for each root).
ClassPath cp = createPath(compilationUnitEl, packageRoots, type);
it2 = packageRoots.iterator();
while (it2.hasNext()) {
FileObject root2 = (FileObject)it2.next();
classpathsByType.put(root2, cp);
}
return cp;
}
}
}
//WebModule classpath
WebModules wms = (WebModules) project.getLookup ().lookup (WebModules.class);
if (wms != null) {
return wms.findClassPath (file, type);
}
// Didn't find anything.
return null;
}
/** All classpath types we handle. */
private static final String[] TYPES = {
ClassPath.SOURCE,
ClassPath.BOOT,
ClassPath.EXECUTE,
ClassPath.COMPILE,
};
/**
* Called when project is opened.
* Tries to find all compilation units and calculate all the paths needed
* for each of them and register them all.
*/
public synchronized void opened() {
if (registeredClasspaths != null) {
return;
}
Map/*>*/ _registeredClasspaths = new HashMap();
for (int i = 0; i < TYPES.length; i++) {
String type = TYPES[i];
_registeredClasspaths.put(type, new HashSet());
}
AuxiliaryConfiguration aux = (AuxiliaryConfiguration)project.getLookup().lookup(AuxiliaryConfiguration.class);
assert aux != null;
Element java = aux.getConfigurationFragment("java-data", FreeformProjectType.NS_JAVA, true); // NOI18N
if (java == null) {
return;
}
List/**/ compilationUnits = Util.findSubElements(java);
Iterator it = compilationUnits.iterator();
while (it.hasNext()) {
Element compilationUnitEl = (Element)it.next();
assert compilationUnitEl.getLocalName().equals("compilation-unit") : compilationUnitEl;
// For each compilation unit, find the package roots first.
List/**/ packageRoots = findPackageRoots(project, compilationUnitEl);
for (int i = 0; i < TYPES.length; i++) {
String type = TYPES[i];
// Then for each type, collect the classpath (creating it as needed).
Map/**/ classpathsByType = (Map)classpaths.get(type);
if (classpathsByType == null) {
classpathsByType = new WeakHashMap();
classpaths.put(type, classpathsByType);
}
Set/**/ registeredClasspathsOfType = (Set) _registeredClasspaths.get(type);
assert registeredClasspathsOfType != null;
// Check if there is already a ClassPath registered to one of these roots.
ClassPath cp = null;
Iterator it2 = packageRoots.iterator();
while (cp == null && it2.hasNext()) {
FileObject root = (FileObject)it2.next();
cp = (ClassPath)classpathsByType.get(root);
}
if (cp == null) {
// Nope. Calculate and register it now.
cp = createPath(compilationUnitEl, packageRoots, type);
it2 = packageRoots.iterator();
while (it2.hasNext()) {
FileObject root = (FileObject)it2.next();
classpathsByType.put(root, cp);
}
}
assert cp != null;
registeredClasspathsOfType.add(cp);
}
}
if (Util.err.isLoggable(ErrorManager.INFORMATIONAL)) {
Util.err.log("classpaths for " + project.getProjectDirectory() + ": " + classpaths);
}
// Don't do this before it is calculated, or a runtime error above might corrupt state:
this.registeredClasspaths = _registeredClasspaths;
// Register all of the classpaths we found.
GlobalPathRegistry gpr = GlobalPathRegistry.getDefault();
for (int i = 0; i < TYPES.length; i++) {
String type = TYPES[i];
Set/**/ registeredClasspathsOfType = (Set)registeredClasspaths.get(type);
gpr.register(type, (ClassPath[])registeredClasspathsOfType.toArray(new ClassPath[registeredClasspathsOfType.size()]));
}
}
/**
* Called when project is closed.
* Unregisters any previously registered classpaths.
*/
public synchronized void closed() {
if (registeredClasspaths == null) {
return;
}
GlobalPathRegistry gpr = GlobalPathRegistry.getDefault();
for (int i = 0; i < TYPES.length; i++) {
String type = TYPES[i];
Set/**/ registeredClasspathsOfType = (Set)registeredClasspaths.get(type);
gpr.unregister(type, (ClassPath[])registeredClasspathsOfType.toArray(new ClassPath[registeredClasspathsOfType.size()]));
}
registeredClasspaths = null;
}
private static List/**/ findPackageRootNames(Element compilationUnitEl) {
List/**/ names = new ArrayList();
Iterator it = Util.findSubElements(compilationUnitEl).iterator();
while (it.hasNext()) {
Element e = (Element) it.next();
if (!e.getLocalName().equals("package-root")) { // NOI18N
continue;
}
String location = Util.findText(e);
names.add(location);
}
return names;
}
private static List/**/ findPackageRoots(FreeformProject project, List/**/ packageRootNames) {
List/**/ roots = new ArrayList(packageRootNames.size());
Iterator it = packageRootNames.iterator();
while (it.hasNext()) {
String location = (String) it.next();
String locationEval = project.evaluator().evaluate(location);
if (locationEval != null) {
File locationFile = project.helper().resolveFile(locationEval);
FileObject locationFileObject = FileUtil.toFileObject(locationFile);
if (locationFileObject != null) {
if (FileUtil.isArchiveFile(locationFileObject)) {
locationFileObject = FileUtil.getArchiveRoot(locationFileObject);
}
roots.add(locationFileObject);
}
}
}
return roots;
}
public static List/**/ findPackageRoots(FreeformProject project, Element compilationUnitEl) {
return findPackageRoots(project, findPackageRootNames(compilationUnitEl));
}
private ClassPath createPath(Element compilationUnitEl, List/**/ packageRoots, String type) {
if (type.equals(ClassPath.SOURCE) || type.equals(ClassPath.COMPILE) ||
type.equals(ClassPath.EXECUTE) || type.equals(ClassPath.BOOT)) {
List/**/ packageRootNames = findPackageRootNames(compilationUnitEl);
Map/*,MutableClassPathImplementation>*/ mutablePathImplsByType = (Map) mutablePathImpls.get(type);
if (mutablePathImplsByType == null) {
mutablePathImplsByType = new HashMap();
mutablePathImpls.put(type, mutablePathImplsByType);
}
MutableClassPathImplementation impl = (MutableClassPathImplementation) mutablePathImplsByType.get(packageRootNames);
if (impl == null) {
// XXX will it ever not be null?
impl = new MutableClassPathImplementation(packageRootNames, type, compilationUnitEl);
mutablePathImplsByType.put(packageRootNames, impl);
}
return ClassPathFactory.createClassPath(impl);
} else {
// Unknown.
return null;
}
}
private List/**/ createSourcePath(List/**/ packageRootNames) {
List/**/ roots = new ArrayList(packageRootNames.size());
Iterator it = packageRootNames.iterator();
while (it.hasNext()) {
String location = (String) it.next();
String locationEval = project.evaluator().evaluate(location);
if (locationEval != null) {
File locationFile = project.helper().resolveFile(locationEval);
URL root;
try {
root = locationFile.toURI().toURL();
} catch (MalformedURLException e) {
Util.err.notify(e);
continue;
}
if (FileUtil.isArchiveFile(root)) {
root = FileUtil.getArchiveRoot(root);
}
roots.add(root);
}
}
return roots;
}
private List/**/ createCompileClasspath(Element compilationUnitEl) {
Iterator/**/ it = Util.findSubElements(compilationUnitEl).iterator();
while (it.hasNext()) {
Element e = (Element)it.next();
if (e.getLocalName().equals("classpath") && e.getAttribute("mode").equals("compile")) { // NOI18N
return createClasspath(e);
}
}
// None specified; assume it is empty.
return Collections.EMPTY_LIST;
}
/**
* Create a classpath from a <classpath> element.
*/
private List/**/ createClasspath(Element classpathEl) {
String cp = Util.findText(classpathEl);
if (cp == null) {
cp = "";
}
String cpEval = project.evaluator().evaluate(cp);
if (cpEval == null) {
return null;
}
String[] path = PropertyUtils.tokenizePath(cpEval);
URL[] pathURL = new URL[path.length];
for (int i = 0; i < path.length; i++) {
File entryFile = project.helper().resolveFile(path[i]);
URL entry;
try {
entry = entryFile.toURI().toURL();
} catch (MalformedURLException x) {
throw new AssertionError(x);
}
if (FileUtil.isArchiveFile(entry)) {
entry = FileUtil.getArchiveRoot(entry);
} else {
String entryS = entry.toExternalForm();
if (!entryS.endsWith("/")) { // NOI18N
// A nonexistent dir. Have to add trailing slash ourselves.
try {
entry = new URL(entryS + '/');
} catch (MalformedURLException x) {
throw new AssertionError(x);
}
}
}
pathURL[i] = entry;
}
return Arrays.asList(pathURL);
}
private List/**/ createExecuteClasspath(Element compilationUnitEl) {
Iterator/**/ it = Util.findSubElements(compilationUnitEl).iterator();
while (it.hasNext()) {
Element e = (Element)it.next();
if (e.getLocalName().equals("classpath") && e.getAttribute("mode").equals("execute")) { // NOI18N
return createClasspath(e);
}
}
// None specified; assume it is same as compile classpath.
// XXX but add built-to dirs
return createCompileClasspath(compilationUnitEl);
}
private List/**/ createBootClasspath(Element compilationUnitEl) {
Iterator/**/ it = Util.findSubElements(compilationUnitEl).iterator();
while (it.hasNext()) {
Element e = (Element)it.next();
if (e.getLocalName().equals("classpath") && e.getAttribute("mode").equals("boot")) { // NOI18N
return createClasspath(e);
}
}
// None specified; try to find a matching Java platform.
JavaPlatformManager jpm = JavaPlatformManager.getDefault();
JavaPlatform platform = jpm.getDefaultPlatform(); // fallback
it = Util.findSubElements(compilationUnitEl).iterator();
while (it.hasNext()) {
Element e = (Element)it.next();
if (e.getLocalName().equals("source-level")) { // NOI18N
String level = Util.findText(e);
Specification spec = new Specification("j2se", new SpecificationVersion(level)); // NOI18N
JavaPlatform[] matchingPlatforms = jpm.getPlatforms(null, spec);
if (matchingPlatforms.length > 0) {
// Pick one.
platform = matchingPlatforms[0];
}
break;
}
}
if (platform != null) {
// XXX this is not ideal; should try to reuse the ClassPath as is?
// The current impl will not listen to changes in the platform classpath correctly.
List/**/ entries = platform.getBootstrapLibraries().entries();
List/**/ urls = new ArrayList(entries.size());
Iterator it2 = entries.iterator();
while (it2.hasNext()) {
ClassPath.Entry entry = (ClassPath.Entry) it2.next();
urls.add(entry.getURL());
}
return urls;
} else {
assert false : "JavaPlatformManager has no default platform";
return Collections.EMPTY_LIST;
}
}
public void configurationXmlChanged(AntProjectEvent ev) {
pathsChanged();
}
public void propertiesChanged(AntProjectEvent ev) {
// ignore
}
public void propertyChange(PropertyChangeEvent evt) {
pathsChanged();
}
private void pathsChanged() {
// Not safe, since need to return same ClassPath's where possible.
// But might need to check for now-ignored roots?
//classpaths.clear();
//System.err.println("pathsChanged: " + mutablePathImpls);
Iterator/*
|