/*******************************************************************************
* Copyright (c) 2000, 2009 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.jdt.internal.core;
import java.util.HashMap;
import java.util.Map;
import org.eclipse.core.resources.IContainer;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.jdt.core.IClasspathEntry;
import org.eclipse.jdt.core.ICompilationUnit;
import org.eclipse.jdt.core.IJavaElement;
import org.eclipse.jdt.core.IPackageFragmentRoot;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.internal.core.util.HashSetOfArray;
import org.eclipse.jdt.internal.core.util.HashtableOfArrayToObject;
import org.eclipse.jdt.internal.core.util.Util;
/**
* Info for IJavaProject.
* <p>
* Note: <code>getChildren()</code> returns all of the <code>IPackageFragmentRoots</code> specified
* on the classpath for the project. This can include roots external to the project. See
* <code>JavaProject#getAllPackageFragmentRoots()</code> and
* <code>JavaProject#getPackageFragmentRoots()</code>. To get only the
* <code>IPackageFragmentRoots</code> that are internal to the project, use
* <code>JavaProject#getChildren()</code>.
*/
/* package */
class JavaProjectElementInfo extends OpenableElementInfo {
static final IPackageFragmentRoot[] NO_ROOTS= new IPackageFragmentRoot[0];
static class ProjectCache {
ProjectCache(IPackageFragmentRoot[] allPkgFragmentRootsCache, Map rootToResolvedEntries, Map pkgFragmentsCaches) {
this.allPkgFragmentRootsCache= allPkgFragmentRootsCache;
this.rootToResolvedEntries= rootToResolvedEntries;
this.pkgFragmentsCaches= pkgFragmentsCaches;
}
/*
* A cache of all package fragment roots of this project.
*/
public IPackageFragmentRoot[] allPkgFragmentRootsCache;
/*
* A cache of all package fragments in this project.
* (a map from String[] (the package name) to IPackageFragmentRoot[] (the package fragment roots that contain a package fragment with this name))
*/
public HashtableOfArrayToObject allPkgFragmentsCache;
/*
* A cache of package fragments for each package fragment root of this project
* (a map from IPackageFragmentRoot to a set of String[] (the package name))
*/
public Map pkgFragmentsCaches;
public Map rootToResolvedEntries;
}
/**
* A array with all the non-java resources contained by this PackageFragment
*/
private Object[] nonJavaResources;
ProjectCache projectCache;
/*
* Adds the given name and its super names to the given set
* (e.g. for {"a", "b", "c"}, adds {"a", "b", "c"}, {"a", "b"}, and {"a"})
*/
static void addSuperPackageNames(String[] pkgName, HashtableOfArrayToObject packageFragments) {
for (int i= pkgName.length - 1; i > 0; i--) {
if (packageFragments.getKey(pkgName, i) == null) {
System.arraycopy(pkgName, 0, pkgName= new String[i], 0, i);
packageFragments.put(pkgName, NO_ROOTS);
}
}
}
/**
* Create and initialize a new instance of the receiver
*/
public JavaProjectElementInfo() {
this.nonJavaResources= null;
}
/**
* Compute the non-java resources contained in this java project.
*/
private Object[] computeNonJavaResources(JavaProject project) {
// determine if src == project and/or if bin == project
IPath projectPath= project.getProject().getFullPath();
boolean srcIsProject= false;
boolean binIsProject= false;
char[][] inclusionPatterns= null;
char[][] exclusionPatterns= null;
IPath projectOutput= null;
boolean isClasspathResolved= true;
try {
IClasspathEntry entry= project.getClasspathEntryFor(projectPath);
if (entry != null) {
srcIsProject= true;
inclusionPatterns= ((ClasspathEntry)entry).fullInclusionPatternChars();
exclusionPatterns= ((ClasspathEntry)entry).fullExclusionPatternChars();
}
projectOutput= project.getOutputLocation();
binIsProject= projectPath.equals(projectOutput);
} catch (JavaModelException e) {
isClasspathResolved= false;
}
Object[] resources= new IResource[5];
int resourcesCounter= 0;
try {
IResource[] members= ((IContainer)project.getResource()).members();
int length= members.length;
if (length > 0) {
String sourceLevel= project.getOption(JavaCore.COMPILER_SOURCE, true);
String complianceLevel= project.getOption(JavaCore.COMPILER_COMPLIANCE, true);
IClasspathEntry[] classpath= project.getResolvedClasspath();
for (int i= 0; i < length; i++) {
IResource res= members[i];
switch (res.getType()) {
case IResource.FILE:
IPath resFullPath= res.getFullPath();
String resName= res.getName();
// ignore a jar file on the classpath
if (isClasspathResolved &&
isClasspathEntryOrOutputLocation(resFullPath, res.getLocation()/* see https://bugs.eclipse.org/bugs/show_bug.cgi?id=244406 */, classpath, projectOutput)) {
break;
}
// ignore .java file if src == project
if (srcIsProject
&& Util.isValidCompilationUnitName(resName, sourceLevel, complianceLevel)
&& !Util.isExcluded(res, inclusionPatterns, exclusionPatterns)) {
break;
}
// ignore .class file if bin == project
if (binIsProject && Util.isValidClassFileName(resName, sourceLevel, complianceLevel)) {
break;
}
// else add non java resource
if (resources.length == resourcesCounter) {
// resize
System.arraycopy(
resources,
0,
(resources= new IResource[resourcesCounter * 2]),
0,
resourcesCounter);
}
resources[resourcesCounter++]= res;
break;
case IResource.FOLDER:
resFullPath= res.getFullPath();
// ignore non-excluded folders on the classpath or that correspond to an output location
if ((srcIsProject && !Util.isExcluded(res, inclusionPatterns, exclusionPatterns) && Util.isValidFolderNameForPackage(res.getName(), sourceLevel, complianceLevel))
|| (isClasspathResolved && isClasspathEntryOrOutputLocation(resFullPath, res.getLocation(), classpath, projectOutput))) {
break;
}
// else add non java resource
if (resources.length == resourcesCounter) {
// resize
System.arraycopy(
resources,
0,
(resources= new IResource[resourcesCounter * 2]),
0,
resourcesCounter);
}
resources[resourcesCounter++]= res;
}
}
}
if (resources.length != resourcesCounter) {
System.arraycopy(
resources,
0,
(resources= new IResource[resourcesCounter]),
0,
resourcesCounter);
}
} catch (CoreException e) {
resources= NO_NON_JAVA_RESOURCES;
resourcesCounter= 0;
}
return resources;
}
ProjectCache getProjectCache(JavaProject project) {
ProjectCache cache= this.projectCache;
if (cache == null) {
IPackageFragmentRoot[] roots;
Map reverseMap= new HashMap(3);
try {
roots= project.getAllPackageFragmentRoots(reverseMap);
} catch (JavaModelException e) {
// project does not exist: cannot happen since this is the info of the project
roots= new IPackageFragmentRoot[0];
reverseMap.clear();
}
HashMap rootInfos= JavaModelManager.getJavaModelManager().deltaState.roots;
HashMap pkgFragmentsCaches= new HashMap();
int length= roots.length;
JavaModelManager manager= JavaModelManager.getJavaModelManager();
for (int i= 0; i < length; i++) {
IPackageFragmentRoot root= roots[i];
DeltaProcessor.RootInfo rootInfo= (DeltaProcessor.RootInfo)rootInfos.get(root.getPath());
if (rootInfo == null || rootInfo.project.equals(project)) {
// ensure that an identical root is used (see https://bugs.eclipse.org/bugs/show_bug.cgi?id=217059 )
roots[i]= root= (IPackageFragmentRoot)manager.getExistingElement(root);
// compute fragment cache
HashSetOfArray fragmentsCache= new HashSetOfArray();
initializePackageNames(root, fragmentsCache);
pkgFragmentsCaches.put(root, fragmentsCache);
}
}
cache= new ProjectCache(roots, reverseMap, pkgFragmentsCaches);
this.projectCache= cache;
}
return cache;
}
/**
* Returns an array of non-java resources contained in the receiver.
*/
Object[] getNonJavaResources(JavaProject project) {
if (this.nonJavaResources == null) {
this.nonJavaResources= computeNonJavaResources(project);
}
return this.nonJavaResources;
}
private void initializePackageNames(IPackageFragmentRoot root, HashSetOfArray fragmentsCache) {
IJavaElement[] frags= null;
try {
if (!root.isOpen()) {
PackageFragmentRootInfo info= root.isArchive() ? new JarPackageFragmentRootInfo() : new PackageFragmentRootInfo();
((PackageFragmentRoot)root).computeChildren(info, ((JavaElement)root).resource());
frags= info.children;
} else
frags= root.getChildren();
} catch (JavaModelException e) {
// root doesn't exist: ignore
return;
}
for (int j= 0, length= frags.length; j < length; j++) {
fragmentsCache.add(((PackageFragment)frags[j]).names);
}
}
/*
* Returns whether the given path is a classpath entry or an output location.
*/
private boolean isClasspathEntryOrOutputLocation(IPath path, IPath location, IClasspathEntry[] resolvedClasspath, IPath projectOutput) {
if (projectOutput.equals(path))
return true;
for (int i= 0, length= resolvedClasspath.length; i < length; i++) {
IClasspathEntry entry= resolvedClasspath[i];
IPath entryPath;
if ((entryPath= entry.getPath()).equals(path) || entryPath.equals(location)) {
return true;
}
IPath output;
if ((output= entry.getOutputLocation()) != null && output.equals(path)) {
return true;
}
}
return false;
}
/*
* Creates a new name lookup for this project info.
* The given project is assumed to be the handle of this info.
* This name lookup first looks in the given working copies.
*/
NameLookup newNameLookup(JavaProject project, ICompilationUnit[] workingCopies) {
ProjectCache cache= getProjectCache(project);
HashtableOfArrayToObject allPkgFragmentsCache= cache.allPkgFragmentsCache;
if (allPkgFragmentsCache == null) {
HashMap rootInfos= JavaModelManager.getJavaModelManager().deltaState.roots;
IPackageFragmentRoot[] allRoots= cache.allPkgFragmentRootsCache;
int length= allRoots.length;
allPkgFragmentsCache= new HashtableOfArrayToObject();
for (int i= 0; i < length; i++) {
IPackageFragmentRoot root= allRoots[i];
DeltaProcessor.RootInfo rootInfo= (DeltaProcessor.RootInfo)rootInfos.get(root.getPath());
JavaProject rootProject= rootInfo == null ? project : rootInfo.project;
HashSetOfArray fragmentsCache;
if (rootProject.equals(project)) {
// retrieve package fragments cache from this project
fragmentsCache= (HashSetOfArray)cache.pkgFragmentsCaches.get(root);
} else {
// retrieve package fragments cache from the root's project
ProjectCache rootProjectCache;
try {
rootProjectCache= rootProject.getProjectCache();
} catch (JavaModelException e) {
// project doesn't exit
continue;
}
fragmentsCache= (HashSetOfArray)rootProjectCache.pkgFragmentsCaches.get(root);
}
if (fragmentsCache == null) { // see https://bugs.eclipse.org/bugs/show_bug.cgi?id=183833
fragmentsCache= new HashSetOfArray();
initializePackageNames(root, fragmentsCache);
}
Object[][] set= fragmentsCache.set;
for (int j= 0, length2= set.length; j < length2; j++) {
String[] pkgName= (String[])set[j];
if (pkgName == null)
continue;
Object existing= allPkgFragmentsCache.get(pkgName);
if (existing == null || existing == NO_ROOTS) {
allPkgFragmentsCache.put(pkgName, root);
// ensure super packages (see https://bugs.eclipse.org/bugs/show_bug.cgi?id=119161)
// are also in the map
addSuperPackageNames(pkgName, allPkgFragmentsCache);
} else {
if (existing instanceof PackageFragmentRoot) {
allPkgFragmentsCache.put(pkgName, new IPackageFragmentRoot[] { (PackageFragmentRoot)existing, root });
} else {
IPackageFragmentRoot[] roots= (IPackageFragmentRoot[])existing;
int rootLength= roots.length;
System.arraycopy(roots, 0, roots= new IPackageFragmentRoot[rootLength + 1], 0, rootLength);
roots[rootLength]= root;
allPkgFragmentsCache.put(pkgName, roots);
}
}
}
}
cache.allPkgFragmentsCache= allPkgFragmentsCache;
}
return new NameLookup(cache.allPkgFragmentRootsCache, cache.allPkgFragmentsCache, workingCopies, cache.rootToResolvedEntries);
}
/*
* Reset the package fragment roots and package fragment caches
*/
void resetCaches() {
this.projectCache= null;
}
/**
* Set the fNonJavaResources to res value
*/
void setNonJavaResources(Object[] resources) {
this.nonJavaResources= resources;
}
}