/*******************************************************************************
* 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.wst.jsdt.internal.core.builder;
import java.io.File;
import java.util.ArrayList;
import org.eclipse.core.resources.IContainer;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IFolder;
import org.eclipse.core.resources.IMarker;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IWorkspaceRoot;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.wst.jsdt.core.IIncludePathAttribute;
import org.eclipse.wst.jsdt.core.IIncludePathEntry;
import org.eclipse.wst.jsdt.core.IJavaScriptUnit;
import org.eclipse.wst.jsdt.core.IJavaScriptProject;
import org.eclipse.wst.jsdt.core.JavaScriptCore;
import org.eclipse.wst.jsdt.core.JavaScriptModelException;
import org.eclipse.wst.jsdt.internal.compiler.env.AccessRuleSet;
import org.eclipse.wst.jsdt.internal.compiler.env.INameEnvironment;
import org.eclipse.wst.jsdt.internal.compiler.env.NameEnvironmentAnswer;
import org.eclipse.wst.jsdt.internal.compiler.impl.ITypeRequestor;
import org.eclipse.wst.jsdt.internal.compiler.util.SimpleLookupTable;
import org.eclipse.wst.jsdt.internal.compiler.util.SimpleSet;
import org.eclipse.wst.jsdt.internal.compiler.util.SuffixConstants;
import org.eclipse.wst.jsdt.internal.core.ClasspathEntry;
import org.eclipse.wst.jsdt.internal.core.CompilationUnit;
import org.eclipse.wst.jsdt.internal.core.JavaModel;
import org.eclipse.wst.jsdt.internal.core.JavaProject;
import org.eclipse.wst.jsdt.internal.core.SearchableEnvironment;
public class NameEnvironment implements INameEnvironment, SuffixConstants {
boolean isIncrementalBuild;
ClasspathMultiDirectory[] sourceLocations;
ClasspathLocation[] binaryLocations;
BuildNotifier notifier;
SimpleSet initialTypeNames; // assumed that each name is of the form "a/b/ClassName"
SimpleLookupTable additionalUnits;
SearchableEnvironment searchableEnvironment;
NameEnvironment(IWorkspaceRoot root, JavaProject javaProject, SimpleLookupTable binaryLocationsPerProject, BuildNotifier notifier) throws CoreException {
this.isIncrementalBuild = false;
this.notifier = notifier;
computeClasspathLocations(root, javaProject, binaryLocationsPerProject);
// setNames(null, null);
this.searchableEnvironment=javaProject.newSearchableNameEnvironment(new IJavaScriptUnit[0]);
}
public NameEnvironment(IJavaScriptProject 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);
try {
this.searchableEnvironment=javaProject.newSearchableNameEnvironment(new IJavaScriptUnit[0]);
} catch (JavaScriptModelException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
/* 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 = JavaScriptCore.ERROR.equals(javaProject.getOption(JavaScriptCore.CORE_CIRCULAR_CLASSPATH, true))
? IMarker.SEVERITY_ERROR
: IMarker.SEVERITY_WARNING;
if (severity != cycleMarker.getAttribute(IMarker.SEVERITY, severity))
cycleMarker.setAttribute(IMarker.SEVERITY, severity);
}
IIncludePathEntry[] classpathEntries = javaProject.getExpandedClasspath();
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;
IIncludePathAttribute[] attribs = entry.getExtraAttributes();
for(int k=0;attribs!=null && k<attribs.length;k++) {
if(attribs[k].getName().equalsIgnoreCase("validate") && attribs[k].getValue().equalsIgnoreCase("false")) continue nextEntry; //$NON-NLS-1$ //$NON-NLS-2$
}
switch(entry.getEntryKind()) {
case IIncludePathEntry.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())
createOutputFolder(outputFolder);
}
sLocations.add(
ClasspathLocation.forSourceFolder((IContainer) target, outputFolder, entry.fullInclusionPatternChars(), entry.fullExclusionPatternChars()));
continue nextEntry;
case IIncludePathEntry.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) JavaScriptCore.create(prereqProject);
IIncludePathEntry[] prereqClasspathEntries = prereqJavaProject.getRawIncludepath();
ArrayList seen = new ArrayList();
nextPrereqEntry: for (int j = 0, m = prereqClasspathEntries.length; j < m; j++) {
IIncludePathEntry prereqEntry = prereqClasspathEntries[j];
if (prereqEntry.getEntryKind() == IIncludePathEntry.CPE_SOURCE) {
Object prereqTarget = JavaModel.getTarget(root, prereqEntry.getPath(), true);
if (!(prereqTarget instanceof IContainer)) continue nextPrereqEntry;
IPath prereqOutputPath = 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 IIncludePathEntry.CPE_LIBRARY :
if(true) continue nextEntry;
if (target instanceof IResource) {
IResource resource = (IResource) target;
ClasspathLocation bLocation = null;
// if (resource instanceof IFile) {
// if (!(org.eclipse.wst.jsdt.internal.compiler.util.Util.isClassFileName(path.lastSegment())))
// continue nextEntry;
// AccessRuleSet accessRuleSet =
// (JavaScriptCore.IGNORE.equals(javaProject.getOption(JavaScriptCore.COMPILER_PB_FORBIDDEN_REFERENCE, true))
// && JavaScriptCore.IGNORE.equals(javaProject.getOption(JavaScriptCore.COMPILER_PB_DISCOURAGED_REFERENCE, true)))
// ? null
// : entry.getAccessRuleSet();
// bLocation = ClasspathLocation.forLibrary((IFile) resource, accessRuleSet);
// } else if (resource instanceof IContainer) {
// AccessRuleSet accessRuleSet =
// (JavaScriptCore.IGNORE.equals(javaProject.getOption(JavaScriptCore.COMPILER_PB_FORBIDDEN_REFERENCE, true))
// && JavaScriptCore.IGNORE.equals(javaProject.getOption(JavaScriptCore.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.wst.jsdt.internal.compiler.util.Util.isClassFileName(path.lastSegment())))
continue nextEntry;
AccessRuleSet accessRuleSet =
(JavaScriptCore.IGNORE.equals(javaProject.getOption(JavaScriptCore.COMPILER_PB_FORBIDDEN_REFERENCE, true))
&& JavaScriptCore.IGNORE.equals(javaProject.getOption(JavaScriptCore.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();
this.searchableEnvironment=null;
}
private void createOutputFolder(IContainer outputFolder) throws CoreException {
createParentFolder(outputFolder.getParent());
((IFolder) outputFolder).create(IResource.FORCE | IResource.DERIVED, true, null);
}
private void createParentFolder(IContainer parent) throws CoreException {
if (!parent.exists()) {
createParentFolder(parent.getParent());
((IFolder) parent).create(true, true, null);
}
}
//private NameEnvironmentAnswer findClass(String qualifiedTypeName, char[] typeName) {
// if (this.notifier != null)
// this.notifier.checkCancelWithinCompiler();
//
// if (this.initialTypeNames != null && this.initialTypeNames.includes(qualifiedTypeName)) {
// 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 (this.additionalUnits != null && this.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.js & 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.js
// SourceFile unit = (SourceFile) this.additionalUnits.get(qualifiedTypeName); // doesn't have file extension
// if (unit != null)
// return new NameEnvironmentAnswer(unit, 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
// NameEnvironmentAnswer suggestedAnswer = null;
// for (int i = 0, l = binaryLocations.length; i < l; i++) {
// NameEnvironmentAnswer answer = binaryLocations[i].findClass(binaryFileName, qPackageName, qBinaryFileName);
// if (answer != null) {
// if (!answer.ignoreIfBetter()) {
// if (answer.isBetter(suggestedAnswer))
// return answer;
// } else if (answer.isBetter(suggestedAnswer))
// // remember suggestion and keep looking
// suggestedAnswer = answer;
// }
// }
// if (suggestedAnswer != null)
// // no better answer was found
// return suggestedAnswer;
// return null;
//}
public NameEnvironmentAnswer findType(char[][] compoundName, ITypeRequestor requestor) {
// if (compoundName != null)
// return findClass(
// new String(CharOperation.concatWith(compoundName, '/')),
// compoundName[compoundName.length - 1]);
// return null;
return this.searchableEnvironment.findType(compoundName, requestor);
}
private SourceFile convertToSourceFile(CompilationUnit compilationUnit)
{
IPath path = compilationUnit.getPath();
for (int i = 0; i < this.sourceLocations.length; i++) {
IContainer srcFolder=sourceLocations[i].sourceFolder;
if (srcFolder.getFullPath().isPrefixOf(path))
{
SourceFile sourceFile=new SourceFile((IFile)compilationUnit.getResource(),sourceLocations[i]);
return sourceFile;
}
}
return null;
}
private NameEnvironmentAnswer convertToSourceFile(NameEnvironmentAnswer answer)
{
if (answer==null )
return answer;
if (answer.getCompilationUnit() instanceof CompilationUnit) {
CompilationUnit compilationUnit = (CompilationUnit) answer.getCompilationUnit();
SourceFile sourceFile=convertToSourceFile(compilationUnit);
if (sourceFile!=null)
return new NameEnvironmentAnswer(sourceFile,answer.getAccessRestriction());
}
else if (answer.getCompilationUnits()!=null)
{
org.eclipse.wst.jsdt.internal.compiler.env.ICompilationUnit[] compilationUnits = answer.getCompilationUnits();
org.eclipse.wst.jsdt.internal.compiler.env.ICompilationUnit[] newcompilationUnits =
new org.eclipse.wst.jsdt.internal.compiler.env.ICompilationUnit[compilationUnits.length];
boolean newAnswer=false;
for (int i = 0; i < compilationUnits.length; i++) {
newcompilationUnits[i]=compilationUnits[i];
if (compilationUnits[i] instanceof CompilationUnit) {
SourceFile sourceFile=convertToSourceFile((CompilationUnit)compilationUnits[i]);
if (sourceFile!=null)
{
newcompilationUnits[i]=sourceFile;
newAnswer=true;
}
}
}
if (newAnswer)
return new NameEnvironmentAnswer(newcompilationUnits,answer.getAccessRestriction());
}
return answer;
}
public NameEnvironmentAnswer findBinding(char[] bindingName, char[][] packageName, int type, ITypeRequestor requestor, boolean returnMultiple, String excludePath) {
if (this.notifier != null)
this.notifier.checkCancelWithinCompiler();
NameEnvironmentAnswer answer= this.searchableEnvironment.findBinding(bindingName, packageName,type, requestor, returnMultiple, excludePath);
answer=convertToSourceFile(answer);
return answer;
// 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
// NameEnvironmentAnswer suggestedAnswer = null;
// for (int i = 0, l = binaryLocations.length; i < l; i++) {
// NameEnvironmentAnswer answer = binaryLocations[i].findClass(binaryFileName, qPackageName, qBinaryFileName);
// if (answer != null) {
// if (!answer.ignoreIfBetter()) {
// if (answer.isBetter(suggestedAnswer))
// return answer;
// } else if (answer.isBetter(suggestedAnswer))
// // remember suggestion and keep looking
// suggestedAnswer = answer;
// }
// }
// if (suggestedAnswer != null)
// // no better answer was found
// return suggestedAnswer;
// return null;
}
public NameEnvironmentAnswer findType(char[] typeName, char[][] packageName, ITypeRequestor requestor) {
return searchableEnvironment.findType( typeName,packageName, requestor);
// if (typeName != null)
// return findClass(
// new String(CharOperation.concatWith(packageName, typeName, '/')),
// typeName);
// return null;
}
public boolean isPackage(char[][] compoundName, char[] packageName) {
return searchableEnvironment.isPackage(compoundName,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[] typeNames, SourceFile[] additionalFiles) {
// convert the initial typeNames to a set
if (typeNames == null) {
this.initialTypeNames = null;
} else {
this.initialTypeNames = new SimpleSet(typeNames.length);
for (int i = 0, l = typeNames.length; i < l; i++)
this.initialTypeNames.add(typeNames[i]);
}
// map the additional source files by qualified type name
if (additionalFiles == null) {
this.additionalUnits = null;
} else {
this.additionalUnits = new SimpleLookupTable(additionalFiles.length);
for (int i = 0, l = additionalFiles.length; i < l; i++) {
SourceFile additionalUnit = additionalFiles[i];
if (additionalUnit != null)
this.additionalUnits.put(additionalUnit.initialTypeName, additionalFiles[i]);
}
}
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();
}
}