blob: 3fe31fcb7e4d440e4733d819b4b681b2c1f2e863 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 2004 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.builder;
import org.eclipse.core.resources.*;
import org.eclipse.core.runtime.*;
import org.eclipse.jdt.core.*;
import org.eclipse.jdt.core.compiler.CharOperation;
import org.eclipse.jdt.internal.compiler.env.*;
import org.eclipse.jdt.internal.compiler.problem.AbortCompilation;
import org.eclipse.jdt.internal.compiler.util.SimpleLookupTable;
import org.eclipse.jdt.internal.compiler.util.SuffixConstants;
import org.eclipse.jdt.internal.core.*;
import org.eclipse.jdt.internal.core.util.Util;
import java.io.*;
import java.util.*;
public class NameEnvironment implements INameEnvironment, SuffixConstants {
boolean isIncrementalBuild;
ClasspathMultiDirectory[] sourceLocations;
ClasspathLocation[] binaryLocations;
String[] initialTypeNames; // assumed that each name is of the form "a/b/ClassName"
SourceFile[] additionalUnits;
NameEnvironment(IWorkspaceRoot root, JavaProject javaProject, SimpleLookupTable binaryLocationsPerProject) throws CoreException {
this.isIncrementalBuild = false;
computeClasspathLocations(root, javaProject, binaryLocationsPerProject);
setNames(null, null);
}
public NameEnvironment(IJavaProject javaProject) {
this.isIncrementalBuild = false;
try {
computeClasspathLocations(javaProject.getProject().getWorkspace().getRoot(), (JavaProject) javaProject, null);
} catch(CoreException e) {
this.sourceLocations = new ClasspathMultiDirectory[0];
this.binaryLocations = new ClasspathLocation[0];
}
setNames(null, null);
}
/* Some examples of resolved class path entries.
* Remember to search class path in the order that it was defined.
*
* 1a. typical project with no source folders:
* /Test[CPE_SOURCE][K_SOURCE] -> D:/eclipse.test/Test
* 1b. project with source folders:
* /Test/src1[CPE_SOURCE][K_SOURCE] -> D:/eclipse.test/Test/src1
* /Test/src2[CPE_SOURCE][K_SOURCE] -> D:/eclipse.test/Test/src2
* NOTE: These can be in any order & separated by prereq projects or libraries
* 1c. project external to workspace (only detectable using getLocation()):
* /Test/src[CPE_SOURCE][K_SOURCE] -> d:/eclipse.zzz/src
* Need to search source folder & output folder
*
* 2. zip files:
* D:/j9/lib/jclMax/classes.zip[CPE_LIBRARY][K_BINARY][sourcePath:d:/j9/lib/jclMax/source/source.zip]
* -> D:/j9/lib/jclMax/classes.zip
* ALWAYS want to take the library path as is
*
* 3a. prereq project (regardless of whether it has a source or output folder):
* /Test[CPE_PROJECT][K_SOURCE] -> D:/eclipse.test/Test
* ALWAYS want to append the output folder & ONLY search for .class files
*/
private void computeClasspathLocations(
IWorkspaceRoot root,
JavaProject javaProject,
SimpleLookupTable binaryLocationsPerProject) throws CoreException {
/* Update cycle marker */
IMarker cycleMarker = javaProject.getCycleMarker();
if (cycleMarker != null) {
int severity = JavaCore.ERROR.equals(javaProject.getOption(JavaCore.CORE_CIRCULAR_CLASSPATH, true))
? IMarker.SEVERITY_ERROR
: IMarker.SEVERITY_WARNING;
if (severity != ((Integer) cycleMarker.getAttribute(IMarker.SEVERITY)).intValue())
cycleMarker.setAttribute(IMarker.SEVERITY, severity);
}
IClasspathEntry[] classpathEntries = javaProject.getExpandedClasspath(true/*ignore unresolved variable*/, false/*don't create markers*/, null/*preferred cp*/, null/*preferred output*/);
ArrayList sLocations = new ArrayList(classpathEntries.length);
ArrayList bLocations = new ArrayList(classpathEntries.length);
nextEntry : for (int i = 0, l = classpathEntries.length; i < l; i++) {
ClasspathEntry entry = (ClasspathEntry) classpathEntries[i];
IPath path = entry.getPath();
Object target = JavaModel.getTarget(root, path, true);
if (target == null) continue nextEntry;
switch(entry.getEntryKind()) {
case IClasspathEntry.CPE_SOURCE :
if (!(target instanceof IContainer)) continue nextEntry;
IPath outputPath = entry.getOutputLocation() != null
? entry.getOutputLocation()
: javaProject.getOutputLocation();
IContainer outputFolder;
if (outputPath.segmentCount() == 1) {
outputFolder = javaProject.getProject();
} else {
outputFolder = root.getFolder(outputPath);
if (!outputFolder.exists())
createFolder(outputFolder);
}
sLocations.add(
ClasspathLocation.forSourceFolder((IContainer) target, outputFolder, entry.fullInclusionPatternChars(), entry.fullExclusionPatternChars()));
continue nextEntry;
case IClasspathEntry.CPE_PROJECT :
if (!(target instanceof IProject)) continue nextEntry;
IProject prereqProject = (IProject) target;
if (!JavaProject.hasJavaNature(prereqProject)) continue nextEntry; // if project doesn't have java nature or is not accessible
JavaProject prereqJavaProject = (JavaProject) JavaCore.create(prereqProject);
IClasspathEntry[] prereqClasspathEntries = prereqJavaProject.getRawClasspath();
ArrayList seen = new ArrayList();
nextPrereqEntry: for (int j = 0, m = prereqClasspathEntries.length; j < m; j++) {
IClasspathEntry prereqEntry = prereqClasspathEntries[j];
if (prereqEntry.getEntryKind() == IClasspathEntry.CPE_SOURCE) {
Object prereqTarget = JavaModel.getTarget(root, prereqEntry.getPath(), true);
if (!(prereqTarget instanceof IContainer)) continue nextPrereqEntry;
IPath prereqOutputPath = prereqEntry.getOutputLocation() != null
? prereqEntry.getOutputLocation()
: prereqJavaProject.getOutputLocation();
IContainer binaryFolder = prereqOutputPath.segmentCount() == 1
? (IContainer) prereqProject
: (IContainer) root.getFolder(prereqOutputPath);
if (binaryFolder.exists() && !seen.contains(binaryFolder)) {
seen.add(binaryFolder);
ClasspathLocation bLocation =
ClasspathLocation.forBinaryFolder(
binaryFolder,
true,
entry.getAccessRuleSet());
bLocations.add(bLocation);
if (binaryLocationsPerProject != null) { // normal builder mode
ClasspathLocation[] existingLocations = (ClasspathLocation[]) binaryLocationsPerProject.get(prereqProject);
if (existingLocations == null) {
existingLocations = new ClasspathLocation[] {bLocation};
} else {
int size = existingLocations.length;
System.arraycopy(existingLocations, 0, existingLocations = new ClasspathLocation[size + 1], 0, size);
existingLocations[size] = bLocation;
}
binaryLocationsPerProject.put(prereqProject, existingLocations);
}
}
}
}
continue nextEntry;
case IClasspathEntry.CPE_LIBRARY :
if (target instanceof IResource) {
IResource resource = (IResource) target;
ClasspathLocation bLocation = null;
if (resource instanceof IFile) {
if (!(org.eclipse.jdt.internal.compiler.util.Util.isArchiveFileName(path.lastSegment())))
continue nextEntry;
AccessRuleSet accessRuleSet =
(JavaCore.IGNORE.equals(javaProject.getOption(JavaCore.COMPILER_PB_FORBIDDEN_REFERENCE, true))
&& JavaCore.IGNORE.equals(javaProject.getOption(JavaCore.COMPILER_PB_DISCOURAGED_REFERENCE, true)))
? null
: entry.getAccessRuleSet();
bLocation = ClasspathLocation.forLibrary((IFile) resource, accessRuleSet);
} else if (resource instanceof IContainer) {
AccessRuleSet accessRuleSet =
(JavaCore.IGNORE.equals(javaProject.getOption(JavaCore.COMPILER_PB_FORBIDDEN_REFERENCE, true))
&& JavaCore.IGNORE.equals(javaProject.getOption(JavaCore.COMPILER_PB_DISCOURAGED_REFERENCE, true)))
? null
: entry.getAccessRuleSet();
bLocation = ClasspathLocation.forBinaryFolder((IContainer) target, false, accessRuleSet); // is library folder not output folder
}
bLocations.add(bLocation);
if (binaryLocationsPerProject != null) { // normal builder mode
IProject p = resource.getProject(); // can be the project being built
ClasspathLocation[] existingLocations = (ClasspathLocation[]) binaryLocationsPerProject.get(p);
if (existingLocations == null) {
existingLocations = new ClasspathLocation[] {bLocation};
} else {
int size = existingLocations.length;
System.arraycopy(existingLocations, 0, existingLocations = new ClasspathLocation[size + 1], 0, size);
existingLocations[size] = bLocation;
}
binaryLocationsPerProject.put(p, existingLocations);
}
} else if (target instanceof File) {
if (!(org.eclipse.jdt.internal.compiler.util.Util.isArchiveFileName(path.lastSegment())))
continue nextEntry;
AccessRuleSet accessRuleSet =
(JavaCore.IGNORE.equals(javaProject.getOption(JavaCore.COMPILER_PB_FORBIDDEN_REFERENCE, true))
&& JavaCore.IGNORE.equals(javaProject.getOption(JavaCore.COMPILER_PB_DISCOURAGED_REFERENCE, true)))
? null
: entry.getAccessRuleSet();
bLocations.add(ClasspathLocation.forLibrary(path.toString(), accessRuleSet));
}
continue nextEntry;
}
}
// now split the classpath locations... place the output folders ahead of the other .class file folders & jars
ArrayList outputFolders = new ArrayList(1);
this.sourceLocations = new ClasspathMultiDirectory[sLocations.size()];
if (!sLocations.isEmpty()) {
sLocations.toArray(this.sourceLocations);
// collect the output folders, skipping duplicates
next : for (int i = 0, l = sourceLocations.length; i < l; i++) {
ClasspathMultiDirectory md = sourceLocations[i];
IPath outputPath = md.binaryFolder.getFullPath();
for (int j = 0; j < i; j++) { // compare against previously walked source folders
if (outputPath.equals(sourceLocations[j].binaryFolder.getFullPath())) {
md.hasIndependentOutputFolder = sourceLocations[j].hasIndependentOutputFolder;
continue next;
}
}
outputFolders.add(md);
// also tag each source folder whose output folder is an independent folder & is not also a source folder
for (int j = 0, m = sourceLocations.length; j < m; j++)
if (outputPath.equals(sourceLocations[j].sourceFolder.getFullPath()))
continue next;
md.hasIndependentOutputFolder = true;
}
}
// combine the output folders with the binary folders & jars... place the output folders before other .class file folders & jars
this.binaryLocations = new ClasspathLocation[outputFolders.size() + bLocations.size()];
int index = 0;
for (int i = 0, l = outputFolders.size(); i < l; i++)
this.binaryLocations[index++] = (ClasspathLocation) outputFolders.get(i);
for (int i = 0, l = bLocations.size(); i < l; i++)
this.binaryLocations[index++] = (ClasspathLocation) bLocations.get(i);
}
public void cleanup() {
this.initialTypeNames = null;
this.additionalUnits = null;
for (int i = 0, l = sourceLocations.length; i < l; i++)
sourceLocations[i].cleanup();
for (int i = 0, l = binaryLocations.length; i < l; i++)
binaryLocations[i].cleanup();
}
private void createFolder(IContainer folder) throws CoreException {
if (!folder.exists()) {
createFolder(folder.getParent());
((IFolder) folder).create(true, true, null);
}
}
private NameEnvironmentAnswer findClass(String qualifiedTypeName, char[] typeName) {
if (initialTypeNames != null) {
// TODO (kent) should use a hash set to avoid linear search once massive source set is being processed
for (int i = 0, l = initialTypeNames.length; i < l; i++) {
if (qualifiedTypeName.equals(initialTypeNames[i])) {
if (isIncrementalBuild)
// catch the case that a type inside a source file has been renamed but other class files are looking for it
throw new AbortCompilation(true, new AbortIncrementalBuildException(qualifiedTypeName));
return null; // looking for a file which we know was provided at the beginning of the compilation
}
}
}
if (additionalUnits != null && sourceLocations.length > 0) {
// if an additional source file is waiting to be compiled, answer it BUT not if this is a secondary type search
// if we answer X.java & it no longer defines Y then the binary type looking for Y will think the class path is wrong
// let the recompile loop fix up dependents when the secondary type Y has been deleted from X.java
IPath qSourceFilePath = new Path(qualifiedTypeName); // doesn't have file extension
int qSegmentCount = qSourceFilePath.segmentCount();
next : for (int i = 0, l = additionalUnits.length; i < l; i++) {
SourceFile additionalUnit = additionalUnits[i];
IPath fullPath = additionalUnit.resource.getFullPath();
int prefixCount = additionalUnit.sourceLocation.sourceFolder.getFullPath().segmentCount();
if (qSegmentCount == fullPath.segmentCount() - prefixCount) {
for (int j = 0; j < qSegmentCount - 1; j++)
if (!qSourceFilePath.segment(j).equals(fullPath.segment(j + prefixCount)))
continue next;
if (!Util.equalsIgnoreJavaLikeExtension(fullPath.segment(qSegmentCount-1 + prefixCount), qSourceFilePath.segment(qSegmentCount-1)))
continue next;
return new NameEnvironmentAnswer(additionalUnit, null /*no access restriction*/);
}
}
}
String qBinaryFileName = qualifiedTypeName + SUFFIX_STRING_class;
String binaryFileName = qBinaryFileName;
String qPackageName = ""; //$NON-NLS-1$
if (qualifiedTypeName.length() > typeName.length) {
int typeNameStart = qBinaryFileName.length() - typeName.length - 6; // size of ".class"
qPackageName = qBinaryFileName.substring(0, typeNameStart - 1);
binaryFileName = qBinaryFileName.substring(typeNameStart);
}
// NOTE: the output folders are added at the beginning of the binaryLocations
for (int i = 0, l = binaryLocations.length; i < l; i++) {
NameEnvironmentAnswer answer = binaryLocations[i].findClass(binaryFileName, qPackageName, qBinaryFileName);
if (answer != null) return answer;
}
return null;
}
public NameEnvironmentAnswer findType(char[][] compoundName) {
if (compoundName != null)
return findClass(
new String(CharOperation.concatWith(compoundName, '/')),
compoundName[compoundName.length - 1]);
return null;
}
public NameEnvironmentAnswer findType(char[] typeName, char[][] packageName) {
if (typeName != null)
return findClass(
new String(CharOperation.concatWith(packageName, typeName, '/')),
typeName);
return null;
}
public boolean isPackage(char[][] compoundName, char[] packageName) {
return isPackage(new String(CharOperation.concatWith(compoundName, packageName, '/')));
}
public boolean isPackage(String qualifiedPackageName) {
// NOTE: the output folders are added at the beginning of the binaryLocations
for (int i = 0, l = binaryLocations.length; i < l; i++)
if (binaryLocations[i].isPackage(qualifiedPackageName))
return true;
return false;
}
void setNames(String[] initialTypeNames, SourceFile[] additionalUnits) {
this.initialTypeNames = initialTypeNames;
this.additionalUnits = additionalUnits;
for (int i = 0, l = sourceLocations.length; i < l; i++)
sourceLocations[i].reset();
for (int i = 0, l = binaryLocations.length; i < l; i++)
binaryLocations[i].reset();
}
}