/******************************************************************************* * 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.*; import org.eclipse.jdt.internal.core.util.HashSetOfArray; import org.eclipse.jdt.internal.core.util.Util; import org.eclipse.jdt.internal.core.util.HashtableOfArrayToObject; /** * 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; } }