/*******************************************************************************
* Copyright (c) 2000, 2010 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.io.BufferedInputStream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStreamWriter;
import java.io.StringReader;
import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import org.eclipse.core.resources.ICommand;
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.IProjectDescription;
import org.eclipse.core.resources.IProjectNature;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IWorkspace;
import org.eclipse.core.resources.IWorkspaceRoot;
import org.eclipse.core.resources.ProjectScope;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.Platform;
import org.eclipse.core.runtime.QualifiedName;
import org.eclipse.core.runtime.content.IContentDescription;
import org.eclipse.core.runtime.preferences.IEclipsePreferences;
import org.eclipse.core.runtime.preferences.IScopeContext;
import org.eclipse.jdt.core.IClasspathAttribute;
import org.eclipse.jdt.core.IClasspathContainer;
import org.eclipse.jdt.core.IClasspathEntry;
import org.eclipse.jdt.core.ICompilationUnit;
import org.eclipse.jdt.core.IJavaElement;
import org.eclipse.jdt.core.IJavaModelMarker;
import org.eclipse.jdt.core.IJavaModelStatus;
import org.eclipse.jdt.core.IJavaModelStatusConstants;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.IPackageFragment;
import org.eclipse.jdt.core.IPackageFragmentRoot;
import org.eclipse.jdt.core.IRegion;
import org.eclipse.jdt.core.IType;
import org.eclipse.jdt.core.ITypeHierarchy;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.core.WorkingCopyOwner;
import org.eclipse.jdt.core.compiler.CategorizedProblem;
import org.eclipse.jdt.core.compiler.CharOperation;
import org.eclipse.jdt.core.eval.IEvaluationContext;
import org.eclipse.jdt.internal.compiler.util.ObjectVector;
import org.eclipse.jdt.internal.compiler.util.SuffixConstants;
import org.eclipse.jdt.internal.core.JavaModelManager.PerProjectInfo;
import org.eclipse.jdt.internal.core.JavaProjectElementInfo.ProjectCache;
import org.eclipse.jdt.internal.core.builder.JavaBuilder;
import org.eclipse.jdt.internal.core.eval.EvaluationContextWrapper;
import org.eclipse.jdt.internal.core.util.JavaElementFinder;
import org.eclipse.jdt.internal.core.util.MementoTokenizer;
import org.eclipse.jdt.internal.core.util.Messages;
import org.eclipse.jdt.internal.core.util.Util;
import org.eclipse.jdt.internal.eval.EvaluationContext;
import org.osgi.service.prefs.BackingStoreException;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
/**
* Handle for a Java Project.
*
* <p>
* A Java Project internally maintains a devpath that corresponds to the project's classpath. The
* classpath may include source folders from the current project; jars in the current project, other
* projects, and the local file system; and binary folders (output location) of other projects. The
* Java Model presents source elements corresponding to output .class files in other projects, and
* thus uses the devpath rather than the classpath (which is really a compilation path). The devpath
* mimics the classpath, except has source folder entries in place of output locations in external
* projects.
*
* <p>
* Each JavaProject has a NameLookup facility that locates elements on by name, based on the
* devpath.
*
* @see IJavaProject
*/
public class JavaProject
extends Openable
implements IJavaProject, IProjectNature, SuffixConstants {
/**
* Name of file containing project classpath
*/
public static final String CLASSPATH_FILENAME= ".classpath"; //$NON-NLS-1$
/**
* Value of the project's raw classpath if the .classpath file contains invalid entries.
*/
public static final IClasspathEntry[] INVALID_CLASSPATH= new IClasspathEntry[0];
/**
* Whether the underlying file system is case sensitive.
*/
protected static final boolean IS_CASE_SENSITIVE= !new File("Temp").equals(new File("temp")); //$NON-NLS-1$ //$NON-NLS-2$
/**
* An empty array of strings indicating that a project doesn't have any prerequesite projects.
*/
protected static final String[] NO_PREREQUISITES= CharOperation.NO_STRINGS;
/**
* Name of file containing custom project preferences
*
* @see <a href="https://bugs.eclipse.org/bugs/show_bug.cgi?id=59258">bug 59258</a>
*/
private static final String PREF_FILENAME= ".jprefs"; //$NON-NLS-1$
/**
* Name of directory containing preferences file
*/
public static final String DEFAULT_PREFERENCES_DIRNAME= ".settings"; //$NON-NLS-1$
/**
* Extension for file containing custom project preferences
*/
public static final String JAVA_CORE_PREFS_FILE= JavaCore.PLUGIN_ID + ".prefs"; //$NON-NLS-1$
/*
* Value of project's resolved classpath while it is being resolved
*/
private static final IClasspathEntry[] RESOLUTION_IN_PROGRESS= new IClasspathEntry[0];
/*
* For testing purpose only
*/
private static ArrayList CP_RESOLUTION_BP_LISTENERS;
public static class ClasspathResolutionBreakpointListener {
public void breakpoint(int bp) {
// override in listener implementation
}
}
/**
* The platform project this <code>IJavaProject</code> is based on
*/
protected IProject project;
/**
* Preferences listeners
*/
private IEclipsePreferences.INodeChangeListener preferencesNodeListener;
private IEclipsePreferences.IPreferenceChangeListener preferencesChangeListener;
/**
* Constructor needed for <code>IProject.getNature()</code> and
* <code>IProject.addNature()</code>.
*
* @see #setProject(IProject)
*/
public JavaProject() {
super(null);
}
public JavaProject(IProject project, JavaElement parent) {
super(parent);
this.project= project;
}
/*
* For testing purpose only
*/
public static synchronized void addCPResolutionBPListener(ClasspathResolutionBreakpointListener listener) {
if (CP_RESOLUTION_BP_LISTENERS == null)
CP_RESOLUTION_BP_LISTENERS= new ArrayList();
CP_RESOLUTION_BP_LISTENERS.add(listener);
}
/*
* For testing purpose only
*/
public static synchronized void removeCPResolutionBPListener(ClasspathResolutionBreakpointListener listener) {
if (CP_RESOLUTION_BP_LISTENERS == null)
return;
CP_RESOLUTION_BP_LISTENERS.remove(listener);
if (CP_RESOLUTION_BP_LISTENERS.size() == 0)
CP_RESOLUTION_BP_LISTENERS= null;
}
private static synchronized ClasspathResolutionBreakpointListener[] getBPListeners() {
if (CP_RESOLUTION_BP_LISTENERS == null)
return null;
return (ClasspathResolutionBreakpointListener[])CP_RESOLUTION_BP_LISTENERS.toArray(new ClasspathResolutionBreakpointListener[CP_RESOLUTION_BP_LISTENERS.size()]);
}
private static void breakpoint(int bp, JavaProject project) {
ClasspathResolutionBreakpointListener[] listeners= getBPListeners();
if (listeners == null)
return;
for (int j= 0, length= listeners.length; j < length; j++) {
listeners[j].breakpoint(bp);
}
}
public static boolean areClasspathsEqual(
IClasspathEntry[] firstClasspath, IClasspathEntry[] secondClasspath,
IPath firstOutputLocation, IPath secondOutputLocation) {
int length= firstClasspath.length;
if (length != secondClasspath.length)
return false;
for (int i= 0; i < length; i++) {
if (!firstClasspath[i].equals(secondClasspath[i]))
return false;
}
if (firstOutputLocation == null)
return secondOutputLocation == null;
return firstOutputLocation.equals(secondOutputLocation);
}
/**
* Compare current classpath with given one to see if any different. Note that the argument
* classpath contains its binary output.
*
* @param newClasspath IClasspathEntry[]
* @param newOutputLocation IPath
* @param otherClasspathWithOutput IClasspathEntry[]
* @return boolean
*/
private static boolean areClasspathsEqual(IClasspathEntry[] newClasspath, IPath newOutputLocation, IClasspathEntry[] otherClasspathWithOutput) {
if (otherClasspathWithOutput == null || otherClasspathWithOutput.length == 0)
return false;
int length= otherClasspathWithOutput.length;
if (length != newClasspath.length + 1)
// output is amongst file entries (last one)
return false;
// compare classpath entries
for (int i= 0; i < length - 1; i++) {
if (!otherClasspathWithOutput[i].equals(newClasspath[i]))
return false;
}
// compare binary outputs
IClasspathEntry output= otherClasspathWithOutput[length - 1];
if (output.getContentKind() != ClasspathEntry.K_OUTPUT
|| !output.getPath().equals(newOutputLocation))
return false;
return true;
}
private static boolean areClasspathsEqual(IClasspathEntry[] first, IClasspathEntry[] second) {
if (first != second) {
if (first == null)
return false;
int length= first.length;
if (second == null || second.length != length)
return false;
for (int i= 0; i < length; i++) {
if (!first[i].equals(second[i]))
return false;
}
}
return true;
}
/**
* Returns a canonicalized path from the given external path. Note that the return path contains
* the same number of segments and it contains a device only if the given path contained one.
*
* @param externalPath IPath
* @see java.io.File for the definition of a canonicalized path
* @return IPath
*/
public static IPath canonicalizedPath(IPath externalPath) {
if (externalPath == null)
return null;
if (IS_CASE_SENSITIVE) {
return externalPath;
}
// if not external path, return original path
IWorkspace workspace= ResourcesPlugin.getWorkspace();
if (workspace == null)
return externalPath; // protection during shutdown (30487)
if (workspace.getRoot().findMember(externalPath) != null) {
return externalPath;
}
IPath canonicalPath= null;
try {
canonicalPath=
new Path(new File(externalPath.toOSString()).getCanonicalPath());
} catch (IOException e) {
// default to original path
return externalPath;
}
IPath result;
int canonicalLength= canonicalPath.segmentCount();
if (canonicalLength == 0) {
// the java.io.File canonicalization failed
return externalPath;
} else if (externalPath.isAbsolute()) {
result= canonicalPath;
} else {
// if path is relative, remove the first segments that were added by the java.io.File canonicalization
// e.g. 'lib/classes.zip' was converted to 'd:/myfolder/lib/classes.zip'
int externalLength= externalPath.segmentCount();
if (canonicalLength >= externalLength) {
result= canonicalPath.removeFirstSegments(canonicalLength - externalLength);
} else {
return externalPath;
}
}
// keep device only if it was specified (this is because File.getCanonicalPath() converts '/lib/classes.zip' to 'd:/lib/classes/zip')
if (externalPath.getDevice() == null) {
result= result.setDevice(null);
}
// keep trailing separator only if it was specified (this is because File.getCanonicalPath() converts 'd:/lib/classes/' to 'd:/lib/classes')
if (externalPath.hasTrailingSeparator()) {
result= result.addTrailingSeparator();
}
return result;
}
/**
* Returns true if the given project is accessible and it has a java nature, otherwise false.
*
* @param project IProject
* @return boolean
*/
public static boolean hasJavaNature(IProject project) {
try {
return project.hasNature(JavaCore.NATURE_ID);
} catch (CoreException e) {
if (ExternalJavaProject.EXTERNAL_PROJECT_NAME.equals(project.getName()))
return true;
// project does not exist or is not open
}
return false;
}
/*
* Detect cycles in the classpath of the workspace's projects
* and create markers if necessary.
* @param preferredClasspaths Map
* @throws JavaModelException
*/
public static void validateCycles(Map preferredClasspaths) throws JavaModelException {
//long start = System.currentTimeMillis();
IWorkspaceRoot workspaceRoot= ResourcesPlugin.getWorkspace().getRoot();
IProject[] rscProjects= workspaceRoot.getProjects();
int length= rscProjects.length;
JavaProject[] projects= new JavaProject[length];
HashSet cycleParticipants= new HashSet();
HashSet traversed= new HashSet();
// compute cycle participants
ArrayList prereqChain= new ArrayList();
for (int i= 0; i < length; i++) {
if (hasJavaNature(rscProjects[i])) {
JavaProject project= (projects[i]= (JavaProject)JavaCore.create(rscProjects[i]));
if (!traversed.contains(project.getPath())) {
prereqChain.clear();
project.updateCycleParticipants(prereqChain, cycleParticipants, workspaceRoot, traversed, preferredClasspaths);
}
}
}
//System.out.println("updateAllCycleMarkers: " + (System.currentTimeMillis() - start) + " ms");
for (int i= 0; i < length; i++) {
JavaProject project= projects[i];
if (project != null) {
if (cycleParticipants.contains(project.getPath())) {
IMarker cycleMarker= project.getCycleMarker();
String circularCPOption= project.getOption(JavaCore.CORE_CIRCULAR_CLASSPATH, true);
int circularCPSeverity= JavaCore.ERROR.equals(circularCPOption) ? IMarker.SEVERITY_ERROR : IMarker.SEVERITY_WARNING;
if (cycleMarker != null) {
// update existing cycle marker if needed
try {
int existingSeverity= ((Integer)cycleMarker.getAttribute(IMarker.SEVERITY)).intValue();
if (existingSeverity != circularCPSeverity) {
cycleMarker.setAttribute(IMarker.SEVERITY, circularCPSeverity);
}
} catch (CoreException e) {
throw new JavaModelException(e);
}
} else {
// create new marker
project.createClasspathProblemMarker(
new JavaModelStatus(IJavaModelStatusConstants.CLASSPATH_CYCLE, project));
}
} else {
project.flushClasspathProblemMarkers(true, false);
}
}
}
}
/**
* Adds a builder to the build spec for the given project.
*/
protected void addToBuildSpec(String builderID) throws CoreException {
IProjectDescription description= this.project.getDescription();
int javaCommandIndex= getJavaCommandIndex(description.getBuildSpec());
if (javaCommandIndex == -1) {
// Add a Java command to the build spec
ICommand command= description.newCommand();
command.setBuilderName(builderID);
setJavaCommand(description, command);
}
}
/**
* @see Openable
*/
protected boolean buildStructure(OpenableElementInfo info, IProgressMonitor pm, Map newElements, IResource underlyingResource) throws JavaModelException {
// cannot refresh cp markers on opening (emulate cp check on startup) since can create deadlocks (see bug 37274)
IClasspathEntry[] resolvedClasspath= getResolvedClasspath();
// compute the pkg fragment roots
info.setChildren(computePackageFragmentRoots(resolvedClasspath, false, null /*no reverse map*/));
return true;
}
/* (non-Javadoc)
* @see org.eclipse.jdt.internal.core.JavaElement#close()
*/
public void close() throws JavaModelException {
if (JavaProject.hasJavaNature(this.project)) {
// Get cached preferences if exist
JavaModelManager.PerProjectInfo perProjectInfo= JavaModelManager.getJavaModelManager().getPerProjectInfo(this.project, false);
if (perProjectInfo != null && perProjectInfo.preferences != null) {
IEclipsePreferences eclipseParentPreferences= (IEclipsePreferences)perProjectInfo.preferences.parent();
if (this.preferencesNodeListener != null) {
eclipseParentPreferences.removeNodeChangeListener(this.preferencesNodeListener);
this.preferencesNodeListener= null;
}
if (this.preferencesChangeListener != null) {
perProjectInfo.preferences.removePreferenceChangeListener(this.preferencesChangeListener);
this.preferencesChangeListener= null;
}
}
}
super.close();
}
/**
* Internal computation of an expanded classpath. It will eliminate duplicates, and produce
* copies of exported or restricted classpath entries to avoid possible side-effects ever after.
*/
private void computeExpandedClasspath(
ClasspathEntry referringEntry,
HashSet rootIDs,
ObjectVector accumulatedEntries) throws JavaModelException {
String projectRootId= rootID();
if (rootIDs.contains(projectRootId)) {
return; // break cycles if any
}
rootIDs.add(projectRootId);
IClasspathEntry[] resolvedClasspath= getResolvedClasspath();
IWorkspaceRoot workspaceRoot= ResourcesPlugin.getWorkspace().getRoot();
boolean isInitialProject= referringEntry == null;
for (int i= 0, length= resolvedClasspath.length; i < length; i++) {
ClasspathEntry entry= (ClasspathEntry)resolvedClasspath[i];
if (isInitialProject || entry.isExported()) {
String rootID= entry.rootID();
if (rootIDs.contains(rootID)) {
continue;
}
// combine restrictions along the project chain
ClasspathEntry combinedEntry= entry.combineWith(referringEntry);
accumulatedEntries.add(combinedEntry);
// recurse in project to get all its indirect exports (only consider exported entries from there on)
if (entry.getEntryKind() == IClasspathEntry.CPE_PROJECT) {
IResource member= workspaceRoot.findMember(entry.getPath());
if (member != null && member.getType() == IResource.PROJECT) { // double check if bound to project (23977)
IProject projRsc= (IProject)member;
if (JavaProject.hasJavaNature(projRsc)) {
JavaProject javaProject= (JavaProject)JavaCore.create(projRsc);
javaProject.computeExpandedClasspath(
combinedEntry,
rootIDs,
accumulatedEntries);
}
}
} else {
rootIDs.add(rootID);
}
}
}
}
/**
* Computes the package fragment roots identified by the given entry. Only works with resolved
* entry
*
* @param resolvedEntry IClasspathEntry
* @return IPackageFragmentRoot[]
*/
public IPackageFragmentRoot[] computePackageFragmentRoots(IClasspathEntry resolvedEntry) {
try {
return computePackageFragmentRoots(
new IClasspathEntry[] { resolvedEntry },
false, // don't retrieve exported roots
null /* no reverse map */
);
} catch (JavaModelException e) {
return new IPackageFragmentRoot[] {};
}
}
/**
* Returns the package fragment roots identified by the given entry. In case it refers to a
* project, it will follow its classpath so as to find exported roots as well. Only works with
* resolved entry
*
* @param resolvedEntry IClasspathEntry
* @param accumulatedRoots ObjectVector
* @param rootIDs HashSet
* @param referringEntry the CP entry (project) referring to this entry, or null if initial
* project
* @param retrieveExportedRoots boolean
* @throws JavaModelException
*/
public void computePackageFragmentRoots(
IClasspathEntry resolvedEntry,
ObjectVector accumulatedRoots,
HashSet rootIDs,
IClasspathEntry referringEntry,
boolean retrieveExportedRoots,
Map rootToResolvedEntries) throws JavaModelException {
String rootID= ((ClasspathEntry)resolvedEntry).rootID();
if (rootIDs.contains(rootID))
return;
IPath projectPath= this.project.getFullPath();
IPath entryPath= resolvedEntry.getPath();
IWorkspaceRoot workspaceRoot= ResourcesPlugin.getWorkspace().getRoot();
IPackageFragmentRoot root= null;
switch (resolvedEntry.getEntryKind()) {
// source folder
case IClasspathEntry.CPE_SOURCE:
if (projectPath.isPrefixOf(entryPath)) {
Object target= JavaModel.getTarget(entryPath, true/*check existency*/);
if (target == null)
return;
if (target instanceof IFolder || target instanceof IProject) {
root= getPackageFragmentRoot((IResource)target);
}
}
break;
// internal/external JAR or folder
case IClasspathEntry.CPE_LIBRARY:
if (referringEntry != null && !resolvedEntry.isExported())
return;
Object target= JavaModel.getTarget(entryPath, true/*check existency*/);
if (target == null)
return;
if (target instanceof IResource) {
// internal target
root= getPackageFragmentRoot((IResource)target, entryPath);
} else if (target instanceof File) {
// external target
if (JavaModel.isFile(target)) {
root= new JarPackageFragmentRoot(entryPath, this);
} else if (((File)target).isDirectory()) {
root= new ExternalPackageFragmentRoot(entryPath, this);
}
}
break;
// recurse into required project
case IClasspathEntry.CPE_PROJECT:
if (!retrieveExportedRoots)
return;
if (referringEntry != null && !resolvedEntry.isExported())
return;
IResource member= workspaceRoot.findMember(entryPath);
if (member != null && member.getType() == IResource.PROJECT) {// double check if bound to project (23977)
IProject requiredProjectRsc= (IProject)member;
if (JavaProject.hasJavaNature(requiredProjectRsc)) { // special builder binary output
rootIDs.add(rootID);
JavaProject requiredProject= (JavaProject)JavaCore.create(requiredProjectRsc);
requiredProject.computePackageFragmentRoots(
requiredProject.getResolvedClasspath(),
accumulatedRoots,
rootIDs,
rootToResolvedEntries == null ? resolvedEntry : ((ClasspathEntry)resolvedEntry).combineWith((ClasspathEntry)referringEntry), // only combine if need to build the reverse map
retrieveExportedRoots,
rootToResolvedEntries);
}
break;
}
}
if (root != null) {
accumulatedRoots.add(root);
rootIDs.add(rootID);
if (rootToResolvedEntries != null)
rootToResolvedEntries.put(root, ((ClasspathEntry)resolvedEntry).combineWith((ClasspathEntry)referringEntry));
}
}
/**
* Returns (local/all) the package fragment roots identified by the given project's classpath.
* Note: this follows project classpath references to find required project contributions,
* eliminating duplicates silently. Only works with resolved entries
*
* @param resolvedClasspath IClasspathEntry[]
* @param retrieveExportedRoots boolean
* @return IPackageFragmentRoot[]
* @throws JavaModelException
*/
public IPackageFragmentRoot[] computePackageFragmentRoots(
IClasspathEntry[] resolvedClasspath,
boolean retrieveExportedRoots,
Map rootToResolvedEntries) throws JavaModelException {
ObjectVector accumulatedRoots= new ObjectVector();
computePackageFragmentRoots(
resolvedClasspath,
accumulatedRoots,
new HashSet(5), // rootIDs
null, // inside original project
retrieveExportedRoots,
rootToResolvedEntries);
IPackageFragmentRoot[] rootArray= new IPackageFragmentRoot[accumulatedRoots.size()];
accumulatedRoots.copyInto(rootArray);
return rootArray;
}
/**
* Returns (local/all) the package fragment roots identified by the given project's classpath.
* Note: this follows project classpath references to find required project contributions,
* eliminating duplicates silently. Only works with resolved entries
*
* @param resolvedClasspath IClasspathEntry[]
* @param accumulatedRoots ObjectVector
* @param rootIDs HashSet
* @param referringEntry project entry referring to this CP or null if initial project
* @param retrieveExportedRoots boolean
* @throws JavaModelException
*/
public void computePackageFragmentRoots(
IClasspathEntry[] resolvedClasspath,
ObjectVector accumulatedRoots,
HashSet rootIDs,
IClasspathEntry referringEntry,
boolean retrieveExportedRoots,
Map rootToResolvedEntries) throws JavaModelException {
if (referringEntry == null) {
rootIDs.add(rootID());
}
for (int i= 0, length= resolvedClasspath.length; i < length; i++) {
computePackageFragmentRoots(
resolvedClasspath[i],
accumulatedRoots,
rootIDs,
referringEntry,
retrieveExportedRoots,
rootToResolvedEntries);
}
}
/**
* Compute the file name to use for a given shared property
*
* @param qName QualifiedName
* @return String
*/
public String computeSharedPropertyFileName(QualifiedName qName) {
return '.' + qName.getLocalName();
}
/**
* Configure the project with Java nature.
*/
public void configure() throws CoreException {
// register Java builder
addToBuildSpec(JavaCore.BUILDER_ID);
}
/*
* Returns whether the given resource is accessible through the children or the non-Java resources of this project.
* Returns true if the resource is not in the project.
* Assumes that the resource is a folder or a file.
*/
public boolean contains(IResource resource) {
IClasspathEntry[] classpath;
IPath output;
try {
classpath= getResolvedClasspath();
output= getOutputLocation();
} catch (JavaModelException e) {
return false;
}
IPath fullPath= resource.getFullPath();
IPath innerMostOutput= output.isPrefixOf(fullPath) ? output : null;
IClasspathEntry innerMostEntry= null;
ExternalFoldersManager foldersManager= JavaModelManager.getExternalManager();
for (int j= 0, cpLength= classpath.length; j < cpLength; j++) {
IClasspathEntry entry= classpath[j];
IPath entryPath= entry.getPath();
if (entry.getEntryKind() == IClasspathEntry.CPE_LIBRARY) {
IResource linkedFolder= foldersManager.getFolder(entryPath);
if (linkedFolder != null)
entryPath= linkedFolder.getFullPath();
}
if ((innerMostEntry == null || innerMostEntry.getPath().isPrefixOf(entryPath))
&& entryPath.isPrefixOf(fullPath)) {
innerMostEntry= entry;
}
IPath entryOutput= classpath[j].getOutputLocation();
if (entryOutput != null && entryOutput.isPrefixOf(fullPath)) {
innerMostOutput= entryOutput;
}
}
if (innerMostEntry != null) {
// special case prj==src and nested output location
if (innerMostOutput != null && innerMostOutput.segmentCount() > 1 // output isn't project
&& innerMostEntry.getPath().segmentCount() == 1) { // 1 segment must be project name
return false;
}
if (resource instanceof IFolder) {
// folders are always included in src/lib entries
return true;
}
switch (innerMostEntry.getEntryKind()) {
case IClasspathEntry.CPE_SOURCE:
// .class files are not visible in source folders
return !org.eclipse.jdt.internal.compiler.util.Util.isClassFileName(fullPath.lastSegment());
case IClasspathEntry.CPE_LIBRARY:
// .java files are not visible in library folders
return !org.eclipse.jdt.internal.core.util.Util.isJavaLikeFileName(fullPath.lastSegment());
}
}
if (innerMostOutput != null) {
return false;
}
return true;
}
/**
* Record a new marker denoting a classpath problem
*/
public void createClasspathProblemMarker(IJavaModelStatus status) {
IMarker marker= null;
int severity;
String[] arguments= CharOperation.NO_STRINGS;
boolean isCycleProblem= false, isClasspathFileFormatProblem= false;
switch (status.getCode()) {
case IJavaModelStatusConstants.CLASSPATH_CYCLE:
isCycleProblem= true;
if (JavaCore.ERROR.equals(getOption(JavaCore.CORE_CIRCULAR_CLASSPATH, true))) {
severity= IMarker.SEVERITY_ERROR;
} else {
severity= IMarker.SEVERITY_WARNING;
}
break;
case IJavaModelStatusConstants.INVALID_CLASSPATH_FILE_FORMAT:
isClasspathFileFormatProblem= true;
severity= IMarker.SEVERITY_ERROR;
break;
case IJavaModelStatusConstants.INCOMPATIBLE_JDK_LEVEL:
String setting= getOption(JavaCore.CORE_INCOMPATIBLE_JDK_LEVEL, true);
if (JavaCore.ERROR.equals(setting)) {
severity= IMarker.SEVERITY_ERROR;
} else if (JavaCore.WARNING.equals(setting)) {
severity= IMarker.SEVERITY_WARNING;
} else {
return; // setting == IGNORE
}
break;
default:
IPath path= status.getPath();
if (path != null)
arguments= new String[] { path.toString() };
if (JavaCore.ERROR.equals(getOption(JavaCore.CORE_INCOMPLETE_CLASSPATH, true)) &&
status.getSeverity() != IStatus.WARNING) {
severity= IMarker.SEVERITY_ERROR;
} else {
severity= IMarker.SEVERITY_WARNING;
}
break;
}
try {
marker= this.project.createMarker(IJavaModelMarker.BUILDPATH_PROBLEM_MARKER);
marker.setAttributes(
new String[] {
IMarker.MESSAGE,
IMarker.SEVERITY,
IMarker.LOCATION,
IJavaModelMarker.CYCLE_DETECTED,
IJavaModelMarker.CLASSPATH_FILE_FORMAT,
IJavaModelMarker.ID,
IJavaModelMarker.ARGUMENTS,
IJavaModelMarker.CATEGORY_ID,
IMarker.SOURCE_ID,
},
new Object[] {
status.getMessage(),
new Integer(severity),
Messages.classpath_buildPath,
isCycleProblem ? "true" : "false",//$NON-NLS-1$ //$NON-NLS-2$
isClasspathFileFormatProblem ? "true" : "false",//$NON-NLS-1$ //$NON-NLS-2$
new Integer(status.getCode()),
Util.getProblemArgumentsForMarker(arguments),
new Integer(CategorizedProblem.CAT_BUILDPATH),
JavaBuilder.SOURCE_ID,
}
);
} catch (CoreException e) {
// could not create marker: cannot do much
if (JavaModelManager.VERBOSE) {
e.printStackTrace();
}
}
}
/**
* Returns a new element info for this element.
*/
protected Object createElementInfo() {
return new JavaProjectElementInfo();
}
/**
* Reads and decode an XML classpath string. Returns a two-dimensional array, where the number
* of elements in the row is fixed to 2. The first element is an array of raw classpath entries
* and the second element is an array of referenced entries that may have been stored by the
* client earlier. See {@link IJavaProject#getReferencedClasspathEntries()} for more details.
*
*/
public IClasspathEntry[][] decodeClasspath(String xmlClasspath, Map unknownElements) throws IOException, ClasspathEntry.AssertionFailedException {
ArrayList paths= new ArrayList();
IClasspathEntry defaultOutput= null;
StringReader reader= new StringReader(xmlClasspath);
Element cpElement;
try {
DocumentBuilder parser= DocumentBuilderFactory.newInstance().newDocumentBuilder();
cpElement= parser.parse(new InputSource(reader)).getDocumentElement();
} catch (SAXException e) {
throw new IOException(Messages.file_badFormat);
} catch (ParserConfigurationException e) {
throw new IOException(Messages.file_badFormat);
} finally {
reader.close();
}
if (!cpElement.getNodeName().equalsIgnoreCase("classpath")) { //$NON-NLS-1$
throw new IOException(Messages.file_badFormat);
}
NodeList list= cpElement.getElementsByTagName(ClasspathEntry.TAG_CLASSPATHENTRY);
int length= list.getLength();
for (int i= 0; i < length; ++i) {
Node node= list.item(i);
if (node.getNodeType() == Node.ELEMENT_NODE) {
IClasspathEntry entry= ClasspathEntry.elementDecode((Element)node, this, unknownElements);
if (entry != null) {
if (entry.getContentKind() == ClasspathEntry.K_OUTPUT) {
defaultOutput= entry; // separate output
} else {
paths.add(entry);
}
}
}
}
int pathSize= paths.size();
IClasspathEntry[][] entries= new IClasspathEntry[2][];
entries[0]= new IClasspathEntry[pathSize + (defaultOutput == null ? 0 : 1)];
paths.toArray(entries[0]);
if (defaultOutput != null)
entries[0][pathSize]= defaultOutput; // ensure output is last item
paths.clear();
list= cpElement.getElementsByTagName(ClasspathEntry.TAG_REFERENCED_ENTRY);
length= list.getLength();
for (int i= 0; i < length; ++i) {
Node node= list.item(i);
if (node.getNodeType() == Node.ELEMENT_NODE) {
IClasspathEntry entry= ClasspathEntry.elementDecode((Element)node, this, unknownElements);
if (entry != null) {
paths.add(entry);
}
}
}
entries[1]= new IClasspathEntry[paths.size()];
paths.toArray(entries[1]);
return entries;
}
public IClasspathEntry decodeClasspathEntry(String encodedEntry) {
try {
if (encodedEntry == null)
return null;
StringReader reader= new StringReader(encodedEntry);
Element node;
try {
DocumentBuilder parser=
DocumentBuilderFactory.newInstance().newDocumentBuilder();
node= parser.parse(new InputSource(reader)).getDocumentElement();
} catch (SAXException e) {
return null;
} catch (ParserConfigurationException e) {
return null;
} finally {
reader.close();
}
if (!node.getNodeName().equalsIgnoreCase(ClasspathEntry.TAG_CLASSPATHENTRY)
|| node.getNodeType() != Node.ELEMENT_NODE) {
return null;
}
return ClasspathEntry.elementDecode(node, this, null/*not interested in unknown elements*/);
} catch (IOException e) {
// bad format
return null;
}
}
/**
* /** Removes the Java nature from the project.
*/
public void deconfigure() throws CoreException {
// deregister Java builder
removeFromBuildSpec(JavaCore.BUILDER_ID);
// remove .classpath file
// getProject().getFile(ClasspathHelper.CLASSPATH_FILENAME).delete(false, null);
}
/**
* Returns a default class path. This is the root of the project
*/
protected IClasspathEntry[] defaultClasspath() {
return new IClasspathEntry[] { JavaCore.newSourceEntry(this.project.getFullPath()) };
}
/**
* Returns a default output location. This is the project bin folder
*/
protected IPath defaultOutputLocation() {
return this.project.getFullPath().append("bin"); //$NON-NLS-1$
}
/**
* Returns the XML String encoding of the class path.
*/
protected String encodeClasspath(IClasspathEntry[] classpath, IClasspathEntry[] referencedEntries, IPath outputLocation, boolean indent, Map unknownElements) throws JavaModelException {
try {
ByteArrayOutputStream s= new ByteArrayOutputStream();
OutputStreamWriter writer= new OutputStreamWriter(s, "UTF8"); //$NON-NLS-1$
XMLWriter xmlWriter= new XMLWriter(writer, this, true/*print XML version*/);
xmlWriter.startTag(ClasspathEntry.TAG_CLASSPATH, indent);
for (int i= 0; i < classpath.length; ++i) {
((ClasspathEntry)classpath[i]).elementEncode(xmlWriter, this.project.getFullPath(), indent, true, unknownElements, false);
}
if (outputLocation != null) {
outputLocation= outputLocation.removeFirstSegments(1);
outputLocation= outputLocation.makeRelative();
HashMap parameters= new HashMap();
parameters.put(ClasspathEntry.TAG_KIND, ClasspathEntry.kindToString(ClasspathEntry.K_OUTPUT));
parameters.put(ClasspathEntry.TAG_PATH, String.valueOf(outputLocation));
xmlWriter.printTag(ClasspathEntry.TAG_CLASSPATHENTRY, parameters, indent, true, true);
}
if (referencedEntries != null) {
for (int i= 0; i < referencedEntries.length; ++i) {
((ClasspathEntry)referencedEntries[i]).elementEncode(xmlWriter, this.project.getFullPath(), indent, true, unknownElements, true);
}
}
xmlWriter.endTag(ClasspathEntry.TAG_CLASSPATH, indent, true/*insert new line*/);
writer.flush();
writer.close();
return s.toString("UTF8");//$NON-NLS-1$
} catch (IOException e) {
throw new JavaModelException(e, IJavaModelStatusConstants.IO_EXCEPTION);
}
}
public String encodeClasspathEntry(IClasspathEntry classpathEntry) {
try {
ByteArrayOutputStream s= new ByteArrayOutputStream();
OutputStreamWriter writer= new OutputStreamWriter(s, "UTF8"); //$NON-NLS-1$
XMLWriter xmlWriter= new XMLWriter(writer, this, false/*don't print XML version*/);
((ClasspathEntry)classpathEntry).elementEncode(xmlWriter, this.project.getFullPath(), true/*indent*/, true/*insert new line*/, null/*not interested in unknown elements*/,
(classpathEntry.getReferencingEntry() != null));
writer.flush();
writer.close();
return s.toString("UTF8");//$NON-NLS-1$
} catch (IOException e) {
return null; // never happens since all is done in memory
}
}
/**
* Returns true if this handle represents the same Java project as the given handle. Two handles
* represent the same project if they are identical or if they represent a project with the same
* underlying resource and occurrence counts.
*
* @see JavaElement#equals(Object)
*/
public boolean equals(Object o) {
if (this == o)
return true;
if (!(o instanceof JavaProject))
return false;
JavaProject other= (JavaProject)o;
return this.project.equals(other.getProject());
}
/**
* @see IJavaProject#findElement(IPath)
*/
public IJavaElement findElement(IPath path) throws JavaModelException {
return findElement(path, DefaultWorkingCopyOwner.PRIMARY);
}
/**
* @see IJavaProject#findElement(IPath, WorkingCopyOwner)
*/
public IJavaElement findElement(IPath path, WorkingCopyOwner owner) throws JavaModelException {
if (path == null || path.isAbsolute()) {
throw new JavaModelException(
new JavaModelStatus(IJavaModelStatusConstants.INVALID_PATH, path));
}
try {
String extension= path.getFileExtension();
if (extension == null) {
String packageName= path.toString().replace(IPath.SEPARATOR, '.');
return findPackageFragment(packageName);
} else if (Util.isJavaLikeFileName(path.lastSegment())
|| extension.equalsIgnoreCase(EXTENSION_class)) {
IPath packagePath= path.removeLastSegments(1);
String packageName= packagePath.toString().replace(IPath.SEPARATOR, '.');
String typeName= path.lastSegment();
typeName= typeName.substring(0, typeName.length() - extension.length() - 1);
String qualifiedName= null;
if (packageName.length() > 0) {
qualifiedName= packageName + "." + typeName; //$NON-NLS-1$
} else {
qualifiedName= typeName;
}
// lookup type
NameLookup lookup= newNameLookup(owner);
NameLookup.Answer answer= lookup.findType(
qualifiedName,
false,
NameLookup.ACCEPT_ALL,
true/* consider secondary types */,
false/* do NOT wait for indexes */,
false/*don't check restrictions*/,
null);
if (answer != null) {
return answer.type.getParent();
} else {
return null;
}
} else {
// unsupported extension
return null;
}
} catch (JavaModelException e) {
if (e.getStatus().getCode() == IJavaModelStatusConstants.ELEMENT_DOES_NOT_EXIST) {
return null;
} else {
throw e;
}
}
}
public IJavaElement findPackageFragment(String packageName)
throws JavaModelException {
NameLookup lookup= newNameLookup((WorkingCopyOwner)null/*no need to look at working copies for pkgs*/);
IPackageFragment[] pkgFragments= lookup.findPackageFragments(packageName, false);
if (pkgFragments == null) {
return null;
} else {
// try to return one that is a child of this project
for (int i= 0, length= pkgFragments.length; i < length; i++) {
IPackageFragment pkgFragment= pkgFragments[i];
if (equals(pkgFragment.getParent().getParent())) {
return pkgFragment;
}
}
// default to the first one
return pkgFragments[0];
}
}
public IJavaElement findElement(String bindingKey, WorkingCopyOwner owner) throws JavaModelException {
JavaElementFinder elementFinder= new JavaElementFinder(bindingKey, this, owner);
elementFinder.parse();
if (elementFinder.exception != null)
throw elementFinder.exception;
return elementFinder.element;
}
/**
* @see IJavaProject
*/
public IPackageFragment findPackageFragment(IPath path)
throws JavaModelException {
return findPackageFragment0(JavaProject.canonicalizedPath(path));
}
/*
* non path canonicalizing version
*/
private IPackageFragment findPackageFragment0(IPath path)
throws JavaModelException {
NameLookup lookup= newNameLookup((WorkingCopyOwner)null/*no need to look at working copies for pkgs*/);
return lookup.findPackageFragment(path);
}
/**
* @see IJavaProject
*/
public IPackageFragmentRoot findPackageFragmentRoot(IPath path)
throws JavaModelException {
return findPackageFragmentRoot0(JavaProject.canonicalizedPath(path));
}
/*
* no path canonicalization
*/
public IPackageFragmentRoot findPackageFragmentRoot0(IPath path)
throws JavaModelException {
IPackageFragmentRoot[] allRoots= this.getAllPackageFragmentRoots();
if (!path.isAbsolute()) {
throw new IllegalArgumentException(Messages.path_mustBeAbsolute);
}
for (int i= 0; i < allRoots.length; i++) {
IPackageFragmentRoot classpathRoot= allRoots[i];
if (classpathRoot.getPath().equals(path)) {
return classpathRoot;
}
}
return null;
}
/**
* @see IJavaProject
*/
public IPackageFragmentRoot[] findPackageFragmentRoots(IClasspathEntry entry) {
try {
IClasspathEntry[] classpath= getRawClasspath();
for (int i= 0, length= classpath.length; i < length; i++) {
if (classpath[i].equals(entry)) { // entry may need to be resolved
return computePackageFragmentRoots(
resolveClasspath(new IClasspathEntry[] { entry }),
false, // don't retrieve exported roots
null); /*no reverse map*/
}
}
} catch (JavaModelException e) {
// project doesn't exist: return an empty array
}
return new IPackageFragmentRoot[] {};
}
/**
* @see IJavaProject#findType(String)
*/
public IType findType(String fullyQualifiedName) throws JavaModelException {
return findType(fullyQualifiedName, DefaultWorkingCopyOwner.PRIMARY);
}
/**
* @see IJavaProject#findType(String, IProgressMonitor)
*/
public IType findType(String fullyQualifiedName, IProgressMonitor progressMonitor) throws JavaModelException {
return findType(fullyQualifiedName, DefaultWorkingCopyOwner.PRIMARY, progressMonitor);
}
/*
* Internal findType with instanciated name lookup
*/
IType findType(String fullyQualifiedName, NameLookup lookup, boolean considerSecondaryTypes, IProgressMonitor progressMonitor) throws JavaModelException {
NameLookup.Answer answer= lookup.findType(
fullyQualifiedName,
false,
NameLookup.ACCEPT_ALL,
considerSecondaryTypes,
true, /* wait for indexes (only if consider secondary types)*/
false/*don't check restrictions*/,
progressMonitor);
if (answer == null) {
// try to find enclosing type
int lastDot= fullyQualifiedName.lastIndexOf('.');
if (lastDot == -1)
return null;
IType type= findType(fullyQualifiedName.substring(0, lastDot), lookup, considerSecondaryTypes, progressMonitor);
if (type != null) {
type= type.getType(fullyQualifiedName.substring(lastDot + 1));
if (!type.exists()) {
return null;
}
}
return type;
}
return answer.type;
}
/**
* @see IJavaProject#findType(String, String)
*/
public IType findType(String packageName, String typeQualifiedName) throws JavaModelException {
return findType(packageName, typeQualifiedName, DefaultWorkingCopyOwner.PRIMARY);
}
/**
* @see IJavaProject#findType(String, String, IProgressMonitor)
*/
public IType findType(String packageName, String typeQualifiedName, IProgressMonitor progressMonitor) throws JavaModelException {
return findType(packageName, typeQualifiedName, DefaultWorkingCopyOwner.PRIMARY, progressMonitor);
}
/*
* Internal findType with instanciated name lookup
*/
IType findType(String packageName, String typeQualifiedName, NameLookup lookup, boolean considerSecondaryTypes, IProgressMonitor progressMonitor) throws JavaModelException {
NameLookup.Answer answer= lookup.findType(
typeQualifiedName,
packageName,
false,
NameLookup.ACCEPT_ALL,
considerSecondaryTypes,
true, // wait for indexes (in case we need to consider secondary types)
false/*don't check restrictions*/,
progressMonitor);
return answer == null ? null : answer.type;
}
/**
* @see IJavaProject#findType(String, String, WorkingCopyOwner)
*/
public IType findType(String packageName, String typeQualifiedName, WorkingCopyOwner owner) throws JavaModelException {
NameLookup lookup= newNameLookup(owner);
return findType(
packageName,
typeQualifiedName,
lookup,
false, // do not consider secondary types
null);
}
/**
* @see IJavaProject#findType(String, String, WorkingCopyOwner, IProgressMonitor)
*/
public IType findType(String packageName, String typeQualifiedName, WorkingCopyOwner owner, IProgressMonitor progressMonitor) throws JavaModelException {
NameLookup lookup= newNameLookup(owner);
return findType(
packageName,
typeQualifiedName,
lookup,
true, // consider secondary types
progressMonitor);
}
/**
* @see IJavaProject#findType(String, WorkingCopyOwner)
*/
public IType findType(String fullyQualifiedName, WorkingCopyOwner owner) throws JavaModelException {
NameLookup lookup= newNameLookup(owner);
return findType(fullyQualifiedName, lookup, false, null);
}
/**
* @see IJavaProject#findType(String, WorkingCopyOwner, IProgressMonitor)
*/
public IType findType(String fullyQualifiedName, WorkingCopyOwner owner, IProgressMonitor progressMonitor) throws JavaModelException {
NameLookup lookup= newNameLookup(owner);
return findType(fullyQualifiedName, lookup, true, progressMonitor);
}
/**
* Remove all markers denoting classpath problems
*/
//TODO (philippe) should improve to use a bitmask instead of booleans (CYCLE, FORMAT, VALID)
protected void flushClasspathProblemMarkers(boolean flushCycleMarkers, boolean flushClasspathFormatMarkers) {
try {
if (this.project.isAccessible()) {
IMarker[] markers= this.project.findMarkers(IJavaModelMarker.BUILDPATH_PROBLEM_MARKER, false, IResource.DEPTH_ZERO);
for (int i= 0, length= markers.length; i < length; i++) {
IMarker marker= markers[i];
if (flushCycleMarkers && flushClasspathFormatMarkers) {
marker.delete();
} else {
String cycleAttr= (String)marker.getAttribute(IJavaModelMarker.CYCLE_DETECTED);
String classpathFileFormatAttr= (String)marker.getAttribute(IJavaModelMarker.CLASSPATH_FILE_FORMAT);
if ((flushCycleMarkers == (cycleAttr != null && cycleAttr.equals("true"))) //$NON-NLS-1$
&& (flushClasspathFormatMarkers == (classpathFileFormatAttr != null && classpathFileFormatAttr.equals("true")))) { //$NON-NLS-1$
marker.delete();
}
}
}
}
} catch (CoreException e) {
// could not flush markers: not much we can do
if (JavaModelManager.VERBOSE) {
e.printStackTrace();
}
}
}
/**
* Returns the set of patterns corresponding to this project visibility given rules
*
* @return an array of IPath or null if none
*/
public IPath[] getAccessRestrictions(String optionName) {
String sequence= getOption(optionName, true); // inherit from workspace
if (sequence == null || sequence.length() == 0)
return null;
IPath[] rules= null;
char[][] patterns= CharOperation.splitOn('|', sequence.toCharArray());
int patternCount;
if ((patternCount= patterns.length) > 0) {
rules= new IPath[patternCount];
for (int j= 0; j < patterns.length; j++) {
rules[j]= new Path(new String(patterns[j]));
}
}
return rules;
}
/**
* @see IJavaProject
*/
public IPackageFragmentRoot[] getAllPackageFragmentRoots()
throws JavaModelException {
return getAllPackageFragmentRoots(null /*no reverse map*/);
}
public IPackageFragmentRoot[] getAllPackageFragmentRoots(Map rootToResolvedEntries) throws JavaModelException {
return computePackageFragmentRoots(getResolvedClasspath(), true/*retrieveExportedRoots*/, rootToResolvedEntries);
}
/**
* Returns the classpath entry that refers to the given path or <code>null</code> if there is no
* reference to the path.
*
* @param path IPath
* @return IClasspathEntry
* @throws JavaModelException
*/
public IClasspathEntry getClasspathEntryFor(IPath path) throws JavaModelException {
getResolvedClasspath(); // force resolution
PerProjectInfo perProjectInfo= getPerProjectInfo();
if (perProjectInfo == null)
return null;
Map rootPathToResolvedEntries= perProjectInfo.rootPathToResolvedEntries;
if (rootPathToResolvedEntries == null)
return null;
IClasspathEntry classpathEntry= (IClasspathEntry)rootPathToResolvedEntries.get(path);
if (classpathEntry == null) {
path= getProject().getWorkspace().getRoot().getLocation().append(path);
classpathEntry= (IClasspathEntry)rootPathToResolvedEntries.get(path);
}
return classpathEntry;
}
/*
* Returns the cycle marker associated with this project or null if none.
*/
public IMarker getCycleMarker() {
try {
if (this.project.isAccessible()) {
IMarker[] markers= this.project.findMarkers(IJavaModelMarker.BUILDPATH_PROBLEM_MARKER, false, IResource.DEPTH_ZERO);
for (int i= 0, length= markers.length; i < length; i++) {
IMarker marker= markers[i];
String cycleAttr= (String)marker.getAttribute(IJavaModelMarker.CYCLE_DETECTED);
if (cycleAttr != null && cycleAttr.equals("true")) { //$NON-NLS-1$
return marker;
}
}
}
} catch (CoreException e) {
// could not get markers: return null
}
return null;
}
/**
* Returns the project custom preference pool. Project preferences may include custom encoding.
*
* @return IEclipsePreferences or <code>null</code> if the project does not have a java nature.
*/
public IEclipsePreferences getEclipsePreferences() {
if (!JavaProject.hasJavaNature(this.project))
return null;
// Get cached preferences if exist
JavaModelManager.PerProjectInfo perProjectInfo= JavaModelManager.getJavaModelManager().getPerProjectInfo(this.project, true);
if (perProjectInfo.preferences != null)
return perProjectInfo.preferences;
// Init project preferences
IScopeContext context= new ProjectScope(getProject());
final IEclipsePreferences eclipsePreferences= context.getNode(JavaCore.PLUGIN_ID);
updatePreferences(eclipsePreferences);
perProjectInfo.preferences= eclipsePreferences;
// Listen to new preferences node
final IEclipsePreferences eclipseParentPreferences= (IEclipsePreferences)eclipsePreferences.parent();
if (eclipseParentPreferences != null) {
if (this.preferencesNodeListener != null) {
eclipseParentPreferences.removeNodeChangeListener(this.preferencesNodeListener);
}
this.preferencesNodeListener= new IEclipsePreferences.INodeChangeListener() {
public void added(IEclipsePreferences.NodeChangeEvent event) {
// do nothing
}
public void removed(IEclipsePreferences.NodeChangeEvent event) {
if (event.getChild() == eclipsePreferences) {
JavaModelManager.getJavaModelManager().resetProjectPreferences(JavaProject.this);
}
}
};
eclipseParentPreferences.addNodeChangeListener(this.preferencesNodeListener);
}
// Listen to preferences changes
if (this.preferencesChangeListener != null) {
eclipsePreferences.removePreferenceChangeListener(this.preferencesChangeListener);
}
this.preferencesChangeListener= new IEclipsePreferences.IPreferenceChangeListener() {
public void preferenceChange(IEclipsePreferences.PreferenceChangeEvent event) {
String propertyName= event.getKey();
JavaModelManager manager= JavaModelManager.getJavaModelManager();
if (propertyName.startsWith(JavaCore.PLUGIN_ID)) {
if (propertyName.equals(JavaCore.CORE_JAVA_BUILD_CLEAN_OUTPUT_FOLDER) ||
propertyName.equals(JavaCore.CORE_JAVA_BUILD_RESOURCE_COPY_FILTER) ||
propertyName.equals(JavaCore.CORE_JAVA_BUILD_DUPLICATE_RESOURCE) ||
propertyName.equals(JavaCore.CORE_JAVA_BUILD_RECREATE_MODIFIED_CLASS_FILES_IN_OUTPUT_FOLDER) ||
propertyName.equals(JavaCore.CORE_JAVA_BUILD_INVALID_CLASSPATH) ||
propertyName.equals(JavaCore.CORE_ENABLE_CLASSPATH_EXCLUSION_PATTERNS) ||
propertyName.equals(JavaCore.CORE_ENABLE_CLASSPATH_MULTIPLE_OUTPUT_LOCATIONS) ||
propertyName.equals(JavaCore.CORE_INCOMPLETE_CLASSPATH) ||
propertyName.equals(JavaCore.CORE_CIRCULAR_CLASSPATH) ||
propertyName.equals(JavaCore.CORE_INCOMPATIBLE_JDK_LEVEL))
{
manager.deltaState.addClasspathValidation(JavaProject.this);
}
manager.resetProjectOptions(JavaProject.this);
JavaProject.this.resetCaches(); // see https://bugs.eclipse.org/bugs/show_bug.cgi?id=233568
}
}
};
eclipsePreferences.addPreferenceChangeListener(this.preferencesChangeListener);
return eclipsePreferences;
}
public String getElementName() {
return this.project.getName();
}
/**
* @see IJavaElement
*/
public int getElementType() {
return JAVA_PROJECT;
}
/**
* This is a helper method returning the expanded classpath for the project, as a list of
* classpath entries, where all classpath variable entries have been resolved and substituted
* with their final target entries. All project exports have been appended to project entries.
*
* @return IClasspathEntry[]
* @throws JavaModelException
*/
public IClasspathEntry[] getExpandedClasspath() throws JavaModelException {
ObjectVector accumulatedEntries= new ObjectVector();
computeExpandedClasspath(null, new HashSet(5), accumulatedEntries);
IClasspathEntry[] expandedPath= new IClasspathEntry[accumulatedEntries.size()];
accumulatedEntries.copyInto(expandedPath);
return expandedPath;
}
/**
* The path is known to match a source/library folder entry.
*
* @param path IPath
* @return IPackageFragmentRoot
*/
public IPackageFragmentRoot getFolderPackageFragmentRoot(IPath path) {
if (path.segmentCount() == 1) { // default project root
return getPackageFragmentRoot(this.project);
}
return getPackageFragmentRoot(this.project.getWorkspace().getRoot().getFolder(path));
}
/*
* @see JavaElement
*/
public IJavaElement getHandleFromMemento(String token, MementoTokenizer memento, WorkingCopyOwner owner) {
switch (token.charAt(0)) {
case JEM_PACKAGEFRAGMENTROOT:
String rootPath= IPackageFragmentRoot.DEFAULT_PACKAGEROOT_PATH;
token= null;
while (memento.hasMoreTokens()) {
token= memento.nextToken();
char firstChar= token.charAt(0);
if (firstChar != JEM_PACKAGEFRAGMENT && firstChar != JEM_COUNT) {
rootPath+= token;
} else {
break;
}
}
JavaElement root= (JavaElement)getPackageFragmentRoot(new Path(rootPath));
if (token != null && token.charAt(0) == JEM_PACKAGEFRAGMENT) {
return root.getHandleFromMemento(token, memento, owner);
} else {
return root.getHandleFromMemento(memento, owner);
}
}
return null;
}
/**
* Returns the <code>char</code> that marks the start of this handles contribution to a memento.
*/
protected char getHandleMementoDelimiter() {
return JEM_JAVAPROJECT;
}
/**
* Find the specific Java command amongst the given build spec and return its index or -1 if not
* found.
*/
private int getJavaCommandIndex(ICommand[] buildSpec) {
for (int i= 0; i < buildSpec.length; ++i) {
if (buildSpec[i].getBuilderName().equals(JavaCore.BUILDER_ID)) {
return i;
}
}
return -1;
}
/**
* Convenience method that returns the specific type of info for a Java project.
*/
protected JavaProjectElementInfo getJavaProjectElementInfo()
throws JavaModelException {
return (JavaProjectElementInfo)getElementInfo();
}
/**
* Returns an array of non-java resources contained in the receiver.
*/
public Object[] getNonJavaResources() throws JavaModelException {
return ((JavaProjectElementInfo)getElementInfo()).getNonJavaResources(this);
}
/**
* @see org.eclipse.jdt.core.IJavaProject#getOption(String, boolean)
*/
public String getOption(String optionName, boolean inheritJavaCoreOptions) {
if (JavaModelManager.getJavaModelManager().optionNames.contains(optionName)) {
IEclipsePreferences projectPreferences= getEclipsePreferences();
String javaCoreDefault= inheritJavaCoreOptions ? JavaCore.getOption(optionName) : null;
if (projectPreferences == null)
return javaCoreDefault;
String value= projectPreferences.get(optionName, javaCoreDefault);
return value == null ? null : value.trim();
}
return null;
}
/**
* @see org.eclipse.jdt.core.IJavaProject#getOptions(boolean)
*/
public Map getOptions(boolean inheritJavaCoreOptions) {
// initialize to the defaults from JavaCore options pool
Map options= inheritJavaCoreOptions ? JavaCore.getOptions() : new Hashtable(5);
// Get project specific options
JavaModelManager.PerProjectInfo perProjectInfo= null;
Hashtable projectOptions= null;
HashSet optionNames= JavaModelManager.getJavaModelManager().optionNames;
try {
perProjectInfo= getPerProjectInfo();
projectOptions= perProjectInfo.options;
if (projectOptions == null) {
// get eclipse preferences
IEclipsePreferences projectPreferences= getEclipsePreferences();
if (projectPreferences == null)
return options; // cannot do better (non-Java project)
// create project options
String[] propertyNames= projectPreferences.keys();
projectOptions= new Hashtable(propertyNames.length);
for (int i= 0; i < propertyNames.length; i++) {
String propertyName= propertyNames[i];
String value= projectPreferences.get(propertyName, null);
if (value != null && optionNames.contains(propertyName)) {
projectOptions.put(propertyName, value.trim());
}
}
// cache project options
perProjectInfo.options= projectOptions;
}
} catch (JavaModelException jme) {
projectOptions= new Hashtable();
} catch (BackingStoreException e) {
projectOptions= new Hashtable();
}
// Inherit from JavaCore options if specified
if (inheritJavaCoreOptions) {
Iterator propertyNames= projectOptions.entrySet().iterator();
while (propertyNames.hasNext()) {
Map.Entry entry= (Map.Entry)propertyNames.next();
String propertyName= (String)entry.getKey();
String propertyValue= (String)entry.getValue();
if (propertyValue != null && optionNames.contains(propertyName)) {
options.put(propertyName, propertyValue.trim());
}
}
Util.fixTaskTags(options);
return options;
}
Util.fixTaskTags(projectOptions);
return projectOptions;
}
/**
* @see IJavaProject
*/
public IPath getOutputLocation() throws JavaModelException {
// Do not create marker while getting output location
JavaModelManager.PerProjectInfo perProjectInfo= getPerProjectInfo();
IPath outputLocation= perProjectInfo.outputLocation;
if (outputLocation != null)
return outputLocation;
// force to read classpath - will position output location as well
getRawClasspath();
outputLocation= perProjectInfo.outputLocation;
if (outputLocation == null) {
return defaultOutputLocation();
}
return outputLocation;
}
/**
* @param path IPath
* @return A handle to the package fragment root identified by the given path. This method is
* handle-only and the element may or may not exist. Returns <code>null</code> if unable
* to generate a handle from the path (for example, an absolute path that has less than
* 1 segment. The path may be relative or absolute.
*/
public IPackageFragmentRoot getPackageFragmentRoot(IPath path) {
if (!path.isAbsolute()) {
path= getPath().append(path);
}
int segmentCount= path.segmentCount();
if (segmentCount == 0) {
return null;
}
if (path.getDevice() != null || JavaModel.getExternalTarget(path, true/*check existence*/) != null) {
// external path
return getPackageFragmentRoot0(path);
}
IWorkspaceRoot workspaceRoot= this.project.getWorkspace().getRoot();
IResource resource= workspaceRoot.findMember(path);
if (resource == null) {
// resource doesn't exist in workspace
if (path.getFileExtension() != null) {
if (!workspaceRoot.getProject(path.lastSegment()).exists()) {
// assume it is an external ZIP archive
return getPackageFragmentRoot0(path);
} else {
// assume it is an internal ZIP archive
resource= workspaceRoot.getFile(path);
}
} else if (segmentCount == 1) {
// assume it is a project
String projectName= path.segment(0);
if (getElementName().equals(projectName)) { // see https://bugs.eclipse.org/bugs/show_bug.cgi?id=75814
// default root
resource= this.project;
} else {
// lib being another project
resource= workspaceRoot.getProject(projectName);
}
} else {
// assume it is an internal folder
resource= workspaceRoot.getFolder(path);
}
}
return getPackageFragmentRoot(resource);
}
/**
* @see IJavaProject
*/
public IPackageFragmentRoot getPackageFragmentRoot(IResource resource) {
return getPackageFragmentRoot(resource, null/*no entry path*/);
}
private IPackageFragmentRoot getPackageFragmentRoot(IResource resource, IPath entryPath) {
switch (resource.getType()) {
case IResource.FILE:
return new JarPackageFragmentRoot(resource, this);
case IResource.FOLDER:
if (ExternalFoldersManager.isInternalPathForExternalFolder(resource.getFullPath()))
return new ExternalPackageFragmentRoot(resource, entryPath, this);
return new PackageFragmentRoot(resource, this);
case IResource.PROJECT:
return new PackageFragmentRoot(resource, this);
default:
return null;
}
}
/**
* @see IJavaProject
*/
public IPackageFragmentRoot getPackageFragmentRoot(String externalLibraryPath) {
return getPackageFragmentRoot0(JavaProject.canonicalizedPath(new Path(externalLibraryPath)));
}
/*
* no path canonicalization
*/
public IPackageFragmentRoot getPackageFragmentRoot0(IPath externalLibraryPath) {
IFolder linkedFolder= JavaModelManager.getExternalManager().getFolder(externalLibraryPath);
if (linkedFolder != null)
return new ExternalPackageFragmentRoot(linkedFolder, externalLibraryPath, this);
return new JarPackageFragmentRoot(externalLibraryPath, this);
}
/**
* @see IJavaProject
*/
public IPackageFragmentRoot[] getPackageFragmentRoots()
throws JavaModelException {
Object[] children;
int length;
IPackageFragmentRoot[] roots;
System.arraycopy(
children= getChildren(),
0,
roots= new IPackageFragmentRoot[length= children.length],
0,
length);
return roots;
}
/**
* @see IJavaProject
* @deprecated
*/
public IPackageFragmentRoot[] getPackageFragmentRoots(IClasspathEntry entry) {
return findPackageFragmentRoots(entry);
}
/**
* @see IJavaProject
*/
public IPackageFragment[] getPackageFragments() throws JavaModelException {
IPackageFragmentRoot[] roots= getPackageFragmentRoots();
return getPackageFragmentsInRoots(roots);
}
/**
* Returns all the package fragments found in the specified package fragment roots.
*
* @param roots IPackageFragmentRoot[]
* @return IPackageFragment[]
*/
public IPackageFragment[] getPackageFragmentsInRoots(IPackageFragmentRoot[] roots) {
ArrayList frags= new ArrayList();
for (int i= 0; i < roots.length; i++) {
IPackageFragmentRoot root= roots[i];
try {
IJavaElement[] rootFragments= root.getChildren();
for (int j= 0; j < rootFragments.length; j++) {
frags.add(rootFragments[j]);
}
} catch (JavaModelException e) {
// do nothing
}
}
IPackageFragment[] fragments= new IPackageFragment[frags.size()];
frags.toArray(fragments);
return fragments;
}
/**
* @see IJavaElement
*/
public IPath getPath() {
return this.project.getFullPath();
}
public JavaModelManager.PerProjectInfo getPerProjectInfo() throws JavaModelException {
return JavaModelManager.getJavaModelManager().getPerProjectInfoCheckExistence(this.project);
}
private IPath getPluginWorkingLocation() {
return this.project.getWorkingLocation(JavaCore.PLUGIN_ID);
}
/**
* @see IJavaProject#getProject()
*/
public IProject getProject() {
return this.project;
}
public ProjectCache getProjectCache() throws JavaModelException {
return ((JavaProjectElementInfo)getElementInfo()).getProjectCache(this);
}
/**
* @see IJavaProject
*/
public IClasspathEntry[] getRawClasspath() throws JavaModelException {
JavaModelManager.PerProjectInfo perProjectInfo= getPerProjectInfo();
IClasspathEntry[] classpath= perProjectInfo.rawClasspath;
if (classpath != null)
return classpath;
classpath= perProjectInfo.readAndCacheClasspath(this)[0];
if (classpath == JavaProject.INVALID_CLASSPATH)
return defaultClasspath();
return classpath;
}
/**
* @see IJavaProject
*/
public IClasspathEntry[] getReferencedClasspathEntries() throws JavaModelException {
return getPerProjectInfo().referencedEntries;
}
/**
* @see IJavaProject#getRequiredProjectNames()
*/
public String[] getRequiredProjectNames() throws JavaModelException {
return projectPrerequisites(getResolvedClasspath());
}
public IClasspathEntry[] getResolvedClasspath() throws JavaModelException {
PerProjectInfo perProjectInfo= getPerProjectInfo();
IClasspathEntry[] resolvedClasspath= perProjectInfo.getResolvedClasspath();
if (resolvedClasspath == null) {
resolveClasspath(perProjectInfo, false/*don't use previous session values*/, true/*add classpath change*/);
resolvedClasspath= perProjectInfo.getResolvedClasspath();
if (resolvedClasspath == null) {
// another thread reset the resolved classpath, use a temporary PerProjectInfo
PerProjectInfo temporaryInfo= newTemporaryInfo();
resolveClasspath(temporaryInfo, false/*don't use previous session values*/, true/*add classpath change*/);
resolvedClasspath= temporaryInfo.getResolvedClasspath();
}
}
return resolvedClasspath;
}
/**
* @see IJavaProject
*/
public IClasspathEntry[] getResolvedClasspath(boolean ignoreUnresolvedEntry) throws JavaModelException {
if (JavaModelManager.getJavaModelManager().isClasspathBeingResolved(this)) {
if (JavaModelManager.CP_RESOLVE_VERBOSE_ADVANCED)
verbose_reentering_classpath_resolution();
return RESOLUTION_IN_PROGRESS;
}
PerProjectInfo perProjectInfo= getPerProjectInfo();
// use synchronized block to ensure consistency
IClasspathEntry[] resolvedClasspath;
IJavaModelStatus unresolvedEntryStatus;
synchronized (perProjectInfo) {
resolvedClasspath= perProjectInfo.getResolvedClasspath();
unresolvedEntryStatus= perProjectInfo.unresolvedEntryStatus;
}
if (resolvedClasspath == null
|| (unresolvedEntryStatus != null && !unresolvedEntryStatus.isOK())) { // force resolution to ensure initializers are run again
resolveClasspath(perProjectInfo, false/*don't use previous session values*/, true/*add classpath change*/);
synchronized (perProjectInfo) {
resolvedClasspath= perProjectInfo.getResolvedClasspath();
unresolvedEntryStatus= perProjectInfo.unresolvedEntryStatus;
}
if (resolvedClasspath == null) {
// another thread reset the resolved classpath, use a temporary PerProjectInfo
PerProjectInfo temporaryInfo= newTemporaryInfo();
resolveClasspath(temporaryInfo, false/*don't use previous session values*/, true/*add classpath change*/);
resolvedClasspath= temporaryInfo.getResolvedClasspath();
unresolvedEntryStatus= temporaryInfo.unresolvedEntryStatus;
}
}
if (!ignoreUnresolvedEntry && unresolvedEntryStatus != null && !unresolvedEntryStatus.isOK())
throw new JavaModelException(unresolvedEntryStatus);
return resolvedClasspath;
}
private void verbose_reentering_classpath_resolution() {
Util.verbose(
"CPResolution: reentering raw classpath resolution, will use empty classpath instead" + //$NON-NLS-1$
" project: " + getElementName() + '\n' + //$NON-NLS-1$
" invocation stack trace:"); //$NON-NLS-1$
new Exception("<Fake exception>").printStackTrace(System.out); //$NON-NLS-1$
}
/**
* @see IJavaElement
*/
public IResource resource(PackageFragmentRoot root) {
return this.project;
}
/**
* Retrieve a shared property on a project. If the property is not defined, answers null. Note
* that it is orthogonal to IResource persistent properties, and client code has to decide which
* form of storage to use appropriately. Shared properties produce real resource files which can
* be shared through a VCM onto a server. Persistent properties are not shareable.
*
* @param key String
* @see JavaProject#setSharedProperty(String, String)
* @return String
* @throws CoreException
*/
public String getSharedProperty(String key) throws CoreException {
String property= null;
IFile rscFile= this.project.getFile(key);
if (rscFile.exists()) {
byte[] bytes= Util.getResourceContentsAsByteArray(rscFile);
try {
property= new String(bytes, org.eclipse.jdt.internal.compiler.util.Util.UTF_8); // .classpath always encoded with UTF-8
} catch (UnsupportedEncodingException e) {
Util.log(e, "Could not read .classpath with UTF-8 encoding"); //$NON-NLS-1$
// fallback to default
property= new String(bytes);
}
} else {
// when a project is imported, we get a first delta for the addition of the .project, but the .classpath is not accessible
// so default to using java.io.File
// see https://bugs.eclipse.org/bugs/show_bug.cgi?id=96258
URI location= rscFile.getLocationURI();
if (location != null) {
File file= Util.toLocalFile(location, null/*no progress monitor available*/);
if (file != null && file.exists()) {
byte[] bytes;
try {
bytes= org.eclipse.jdt.internal.compiler.util.Util.getFileByteContent(file);
} catch (IOException e) {
return null;
}
try {
property= new String(bytes, org.eclipse.jdt.internal.compiler.util.Util.UTF_8); // .classpath always encoded with UTF-8
} catch (UnsupportedEncodingException e) {
Util.log(e, "Could not read .classpath with UTF-8 encoding"); //$NON-NLS-1$
// fallback to default
property= new String(bytes);
}
}
}
}
return property;
}
/**
* @see JavaElement
*/
public SourceMapper getSourceMapper() {
return null;
}
/**
* @see IJavaElement
*/
public IResource getUnderlyingResource() throws JavaModelException {
if (!exists())
throw newNotPresentException();
return this.project;
}
/**
* @see IJavaProject
*/
public boolean hasBuildState() {
return JavaModelManager.getJavaModelManager().getLastBuiltState(this.project, null) != null;
}
/**
* @see IJavaProject
*/
public boolean hasClasspathCycle(IClasspathEntry[] preferredClasspath) {
HashSet cycleParticipants= new HashSet();
HashMap preferredClasspaths= new HashMap(1);
preferredClasspaths.put(this, preferredClasspath);
updateCycleParticipants(new ArrayList(2), cycleParticipants, ResourcesPlugin.getWorkspace().getRoot(), new HashSet(2), preferredClasspaths);
return !cycleParticipants.isEmpty();
}
public boolean hasCycleMarker() {
return getCycleMarker() != null;
}
public int hashCode() {
return this.project.hashCode();
}
private boolean hasUTF8BOM(byte[] bytes) {
if (bytes.length > IContentDescription.BOM_UTF_8.length) {
for (int i= 0, length= IContentDescription.BOM_UTF_8.length; i < length; i++) {
if (IContentDescription.BOM_UTF_8[i] != bytes[i])
return false;
}
return true;
}
return false;
}
/**
* Answers true if the project potentially contains any source. A project which has no source is
* immutable.
*
* @return boolean
*/
public boolean hasSource() {
// look if any source folder on the classpath
// no need for resolved path given source folder cannot be abstracted
IClasspathEntry[] entries;
try {
entries= getRawClasspath();
} catch (JavaModelException e) {
return true; // unsure
}
for (int i= 0, max= entries.length; i < max; i++) {
if (entries[i].getEntryKind() == IClasspathEntry.CPE_SOURCE) {
return true;
}
}
return false;
}
/*
* @see IJavaProject
*/
public boolean isOnClasspath(IJavaElement element) {
IClasspathEntry[] rawClasspath;
try {
rawClasspath= getRawClasspath();
} catch (JavaModelException e) {
return false; // not a Java project
}
int elementType= element.getElementType();
boolean isPackageFragmentRoot= false;
boolean isFolderPath= false;
boolean isSource= false;
switch (elementType) {
case IJavaElement.JAVA_MODEL:
return false;
case IJavaElement.JAVA_PROJECT:
break;
case IJavaElement.PACKAGE_FRAGMENT_ROOT:
isPackageFragmentRoot= true;
break;
case IJavaElement.PACKAGE_FRAGMENT:
isFolderPath= !((IPackageFragmentRoot)element.getParent()).isArchive();
break;
case IJavaElement.COMPILATION_UNIT:
isSource= true;
break;
default:
isSource= element.getAncestor(IJavaElement.COMPILATION_UNIT) != null;
break;
}
IPath elementPath= element.getPath();
// first look at unresolved entries
int length= rawClasspath.length;
for (int i= 0; i < length; i++) {
IClasspathEntry entry= rawClasspath[i];
switch (entry.getEntryKind()) {
case IClasspathEntry.CPE_LIBRARY:
case IClasspathEntry.CPE_PROJECT:
case IClasspathEntry.CPE_SOURCE:
if (isOnClasspathEntry(elementPath, isFolderPath, isPackageFragmentRoot, entry))
return true;
break;
}
}
// no need to go further for compilation units and elements inside a compilation unit
// it can only be in a source folder, thus on the raw classpath
if (isSource)
return false;
// https://bugs.eclipse.org/bugs/show_bug.cgi?id=304081
// All the resolved classpath entries need to be considered, including the referenced classpath entries
IClasspathEntry[] resolvedClasspath= null;
try {
resolvedClasspath= getResolvedClasspath();
} catch (JavaModelException e) {
return false; // Perhaps, not a Java project
}
for (int index= 0; index < resolvedClasspath.length; index++) {
if (isOnClasspathEntry(elementPath, isFolderPath, isPackageFragmentRoot, resolvedClasspath[index]))
return true;
}
return false;
}
/*
* @see IJavaProject
*/
public boolean isOnClasspath(IResource resource) {
IPath exactPath= resource.getFullPath();
IPath path= exactPath;
// ensure that folders are only excluded if all of their children are excluded
int resourceType= resource.getType();
boolean isFolderPath= resourceType == IResource.FOLDER || resourceType == IResource.PROJECT;
IClasspathEntry[] classpath;
try {
classpath= this.getResolvedClasspath();
} catch (JavaModelException e) {
return false; // not a Java project
}
for (int i= 0; i < classpath.length; i++) {
IClasspathEntry entry= classpath[i];
IPath entryPath= entry.getPath();
if (entryPath.equals(exactPath)) { // package fragment roots must match exactly entry pathes (no exclusion there)
return true;
}
// https://bugs.eclipse.org/bugs/show_bug.cgi?id=276373
// When a classpath entry is absolute, convert the resource's relative path to a file system path and compare
// e.g - /P/lib/variableLib.jar and /home/P/lib/variableLib.jar when compared should return true
if (entryPath.isAbsolute()
&& entryPath.equals(ResourcesPlugin.getWorkspace().getRoot().getLocation().append(exactPath))) {
return true;
}
if (entryPath.isPrefixOf(path)
&& !Util.isExcluded(path, ((ClasspathEntry)entry).fullInclusionPatternChars(), ((ClasspathEntry)entry).fullExclusionPatternChars(), isFolderPath)) {
return true;
}
}
return false;
}
private boolean isOnClasspathEntry(IPath elementPath, boolean isFolderPath, boolean isPackageFragmentRoot, IClasspathEntry entry) {
IPath entryPath= entry.getPath();
if (isPackageFragmentRoot) {
// package fragment roots must match exactly entry pathes (no exclusion there)
if (entryPath.equals(elementPath))
return true;
} else {
if (entryPath.isPrefixOf(elementPath)
&& !Util.isExcluded(elementPath, ((ClasspathEntry)entry).fullInclusionPatternChars(), ((ClasspathEntry)entry).fullExclusionPatternChars(), isFolderPath))
return true;
}
// https://bugs.eclipse.org/bugs/show_bug.cgi?id=276373
if (entryPath.isAbsolute()
&& entryPath.equals(ResourcesPlugin.getWorkspace().getRoot().getLocation().append(elementPath))) {
return true;
}
return false;
}
/**
* load preferences from a shareable format (VCM-wise)
*/
private IEclipsePreferences loadPreferences() {
IEclipsePreferences preferences= null;
IPath projectMetaLocation= getPluginWorkingLocation();
if (projectMetaLocation != null) {
File prefFile= projectMetaLocation.append(PREF_FILENAME).toFile();
if (prefFile.exists()) { // load preferences from file
InputStream in= null;
try {
in= new BufferedInputStream(new FileInputStream(prefFile));
preferences= Platform.getPreferencesService().readPreferences(in);
} catch (CoreException e) { // problems loading preference store - quietly ignore
} catch (IOException e) { // problems loading preference store - quietly ignore
} finally {
if (in != null) {
try {
in.close();
} catch (IOException e) { // ignore problems with close
}
}
}
// one shot read, delete old preferences
prefFile.delete();
return preferences;
}
}
return null;
}
/**
* @see IJavaProject#newEvaluationContext()
*/
public IEvaluationContext newEvaluationContext() {
EvaluationContext context= new EvaluationContext();
context.setLineSeparator(Util.getLineSeparator(null/*no existing source*/, this));
return new EvaluationContextWrapper(context, this);
}
/*
* Returns a new name lookup. This name lookup first looks in the given working copies.
*/
public NameLookup newNameLookup(ICompilationUnit[] workingCopies) throws JavaModelException {
return getJavaProjectElementInfo().newNameLookup(this, workingCopies);
}
/*
* Returns a new name lookup. This name lookup first looks in the working copies of the given owner.
*/
public NameLookup newNameLookup(WorkingCopyOwner owner) throws JavaModelException {
JavaModelManager manager= JavaModelManager.getJavaModelManager();
ICompilationUnit[] workingCopies= owner == null ? null : manager.getWorkingCopies(owner, true/*add primary WCs*/);
return newNameLookup(workingCopies);
}
/*
* Returns a new search name environment for this project. This name environment first looks in the given working copies.
*/
public SearchableEnvironment newSearchableNameEnvironment(ICompilationUnit[] workingCopies) throws JavaModelException {
return new SearchableEnvironment(this, workingCopies);
}
/*
* Returns a new search name environment for this project. This name environment first looks in the working copies
* of the given owner.
*/
public SearchableEnvironment newSearchableNameEnvironment(WorkingCopyOwner owner) throws JavaModelException {
return new SearchableEnvironment(this, owner);
}
/*
* Returns a PerProjectInfo that doesn't register classpath change
* and that should be used as a temporary info.
*/
public PerProjectInfo newTemporaryInfo() {
return new PerProjectInfo(this.project.getProject()) {
protected ClasspathChange addClasspathChange() {
return null;
}
};
}
/**
* @see IJavaProject
*/
public ITypeHierarchy newTypeHierarchy(
IRegion region,
IProgressMonitor monitor)
throws JavaModelException {
return newTypeHierarchy(region, DefaultWorkingCopyOwner.PRIMARY, monitor);
}
/**
* @see IJavaProject
*/
public ITypeHierarchy newTypeHierarchy(
IRegion region,
WorkingCopyOwner owner,
IProgressMonitor monitor)
throws JavaModelException {
if (region == null) {
throw new IllegalArgumentException(Messages.hierarchy_nullRegion);
}
ICompilationUnit[] workingCopies= JavaModelManager.getJavaModelManager().getWorkingCopies(owner, true/*add primary working copies*/);
CreateTypeHierarchyOperation op=
new CreateTypeHierarchyOperation(region, workingCopies, null, true);
op.runOperation(monitor);
return op.getResult();
}
/**
* @see IJavaProject
*/
public ITypeHierarchy newTypeHierarchy(
IType type,
IRegion region,
IProgressMonitor monitor)
throws JavaModelException {
return newTypeHierarchy(type, region, DefaultWorkingCopyOwner.PRIMARY, monitor);
}
/**
* @see IJavaProject
*/
public ITypeHierarchy newTypeHierarchy(
IType type,
IRegion region,
WorkingCopyOwner owner,
IProgressMonitor monitor)
throws JavaModelException {
if (type == null) {
throw new IllegalArgumentException(Messages.hierarchy_nullFocusType);
}
if (region == null) {
throw new IllegalArgumentException(Messages.hierarchy_nullRegion);
}
ICompilationUnit[] workingCopies= JavaModelManager.getJavaModelManager().getWorkingCopies(owner, true/*add primary working copies*/);
CreateTypeHierarchyOperation op=
new CreateTypeHierarchyOperation(region, workingCopies, type, true/*compute subtypes*/);
op.runOperation(monitor);
return op.getResult();
}
public String[] projectPrerequisites(IClasspathEntry[] resolvedClasspath)
throws JavaModelException {
ArrayList prerequisites= new ArrayList();
for (int i= 0, length= resolvedClasspath.length; i < length; i++) {
IClasspathEntry entry= resolvedClasspath[i];
if (entry.getEntryKind() == IClasspathEntry.CPE_PROJECT) {
prerequisites.add(entry.getPath().lastSegment());
}
}
int size= prerequisites.size();
if (size == 0) {
return NO_PREREQUISITES;
} else {
String[] result= new String[size];
prerequisites.toArray(result);
return result;
}
}
/**
* Reads the classpath file entries of this project's .classpath file. Returns a two-dimensional
* array, where the number of elements in the row is fixed to 2. The first element is an array
* of raw classpath entries, which includes the output entry, and the second element is an array
* of referenced entries that may have been stored by the client earlier. See
* {@link IJavaProject#getReferencedClasspathEntries()} for more details. As a side effect,
* unknown elements are stored in the given map (if not null) Throws exceptions if the file
* cannot be accessed or is malformed.
*/
public IClasspathEntry[][] readFileEntriesWithException(Map unknownElements) throws CoreException, IOException, ClasspathEntry.AssertionFailedException {
IFile rscFile= this.project.getFile(JavaProject.CLASSPATH_FILENAME);
byte[] bytes;
if (rscFile.exists()) {
bytes= Util.getResourceContentsAsByteArray(rscFile);
} else {
// when a project is imported, we get a first delta for the addition of the .project, but the .classpath is not accessible
// so default to using java.io.File
// see https://bugs.eclipse.org/bugs/show_bug.cgi?id=96258
URI location= rscFile.getLocationURI();
if (location == null)
throw new IOException("Cannot obtain a location URI for " + rscFile); //$NON-NLS-1$
File file= Util.toLocalFile(location, null/*no progress monitor available*/);
if (file == null)
throw new IOException("Unable to fetch file from " + location); //$NON-NLS-1$
try {
bytes= org.eclipse.jdt.internal.compiler.util.Util.getFileByteContent(file);
} catch (IOException e) {
if (!file.exists())
return new IClasspathEntry[][] { defaultClasspath(), ClasspathEntry.NO_ENTRIES };
throw e;
}
}
if (hasUTF8BOM(bytes)) { // see https://bugs.eclipse.org/bugs/show_bug.cgi?id=240034
int length= bytes.length - IContentDescription.BOM_UTF_8.length;
System.arraycopy(bytes, IContentDescription.BOM_UTF_8.length, bytes= new byte[length], 0, length);
}
String xmlClasspath;
try {
xmlClasspath= new String(bytes, org.eclipse.jdt.internal.compiler.util.Util.UTF_8); // .classpath always encoded with UTF-8
} catch (UnsupportedEncodingException e) {
Util.log(e, "Could not read .classpath with UTF-8 encoding"); //$NON-NLS-1$
// fallback to default
xmlClasspath= new String(bytes);
}
return decodeClasspath(xmlClasspath, unknownElements);
}
/*
* Reads the classpath file entries of this project's .classpath file.
* This includes the output entry.
* As a side effect, unknown elements are stored in the given map (if not null)
*/
private IClasspathEntry[][] readFileEntries(Map unkwownElements) {
try {
return readFileEntriesWithException(unkwownElements);
} catch (CoreException e) {
Util.log(e, "Exception while reading " + getPath().append(JavaProject.CLASSPATH_FILENAME)); //$NON-NLS-1$
return new IClasspathEntry[][] { JavaProject.INVALID_CLASSPATH, ClasspathEntry.NO_ENTRIES };
} catch (IOException e) {
Util.log(e, "Exception while reading " + getPath().append(JavaProject.CLASSPATH_FILENAME)); //$NON-NLS-1$
return new IClasspathEntry[][] { JavaProject.INVALID_CLASSPATH, ClasspathEntry.NO_ENTRIES };
} catch (ClasspathEntry.AssertionFailedException e) {
Util.log(e, "Exception while reading " + getPath().append(JavaProject.CLASSPATH_FILENAME)); //$NON-NLS-1$
return new IClasspathEntry[][] { JavaProject.INVALID_CLASSPATH, ClasspathEntry.NO_ENTRIES };
}
}
/**
* @see IJavaProject
*/
public IPath readOutputLocation() {
// Read classpath file without creating markers nor logging problems
IClasspathEntry[][] classpath= readFileEntries(null/*not interested in unknown elements*/);
if (classpath[0] == JavaProject.INVALID_CLASSPATH)
return defaultOutputLocation();
// extract the output location
IPath outputLocation= null;
if (classpath[0].length > 0) {
IClasspathEntry entry= classpath[0][classpath[0].length - 1];
if (entry.getContentKind() == ClasspathEntry.K_OUTPUT) {
outputLocation= entry.getPath();
}
}
return outputLocation;
}
/**
* @see IJavaProject
*/
public IClasspathEntry[] readRawClasspath() {
// Read classpath file without creating markers nor logging problems
IClasspathEntry[][] classpath= readFileEntries(null/*not interested in unknown elements*/);
if (classpath[0] == JavaProject.INVALID_CLASSPATH)
return defaultClasspath();
// discard the output location
if (classpath[0].length > 0) {
IClasspathEntry entry= classpath[0][classpath[0].length - 1];
if (entry.getContentKind() == ClasspathEntry.K_OUTPUT) {
IClasspathEntry[] copy= new IClasspathEntry[classpath[0].length - 1];
System.arraycopy(classpath[0], 0, copy, 0, copy.length);
classpath[0]= copy;
}
}
return classpath[0];
}
/**
* Removes the given builder from the build spec for the given project.
*/
protected void removeFromBuildSpec(String builderID) throws CoreException {
IProjectDescription description= this.project.getDescription();
ICommand[] commands= description.getBuildSpec();
for (int i= 0; i < commands.length; ++i) {
if (commands[i].getBuilderName().equals(builderID)) {
ICommand[] newCommands= new ICommand[commands.length - 1];
System.arraycopy(commands, 0, newCommands, 0, i);
System.arraycopy(commands, i + 1, newCommands, i, commands.length - i - 1);
description.setBuildSpec(newCommands);
this.project.setDescription(description, null);
return;
}
}
}
/*
* Resets this project's caches
*/
public void resetCaches() {
JavaProjectElementInfo info= (JavaProjectElementInfo)JavaModelManager.getJavaModelManager().peekAtInfo(this);
if (info != null) {
info.resetCaches();
}
}
public ClasspathChange resetResolvedClasspath() {
try {
return getPerProjectInfo().resetResolvedClasspath();
} catch (JavaModelException e) {
// project doesn't exist
return null;
}
}
/*
* Resolve the given raw classpath.
*/
public IClasspathEntry[] resolveClasspath(IClasspathEntry[] rawClasspath) throws JavaModelException {
return resolveClasspath(rawClasspath, false/*don't use previous session*/, true/*resolve chained libraries*/).resolvedClasspath;
}
static class ResolvedClasspath {
IClasspathEntry[] resolvedClasspath;
IJavaModelStatus unresolvedEntryStatus= JavaModelStatus.VERIFIED_OK;
HashMap rawReverseMap= new HashMap();
Map rootPathToResolvedEntries= new HashMap();
IClasspathEntry[] referencedEntries= null;
}
public ResolvedClasspath resolveClasspath(IClasspathEntry[] rawClasspath, boolean usePreviousSession, boolean resolveChainedLibraries) throws JavaModelException {
return resolveClasspath(rawClasspath, null, usePreviousSession, resolveChainedLibraries);
}
public ResolvedClasspath resolveClasspath(IClasspathEntry[] rawClasspath, IClasspathEntry[] referencedEntries, boolean usePreviousSession, boolean resolveChainedLibraries)
throws JavaModelException {
JavaModelManager manager= JavaModelManager.getJavaModelManager();
ExternalFoldersManager externalFoldersManager= JavaModelManager.getExternalManager();
ResolvedClasspath result= new ResolvedClasspath();
Map referencedEntriesMap= new HashMap();
List rawLibrariesPath= new ArrayList();
LinkedHashSet resolvedEntries= new LinkedHashSet();
if (resolveChainedLibraries) {
for (int index= 0; index < rawClasspath.length; index++) {
IClasspathEntry currentEntry= rawClasspath[index];
if (currentEntry.getEntryKind() == IClasspathEntry.CPE_LIBRARY) {
rawLibrariesPath.add(ClasspathEntry.resolveDotDot(currentEntry.getPath()));
}
}
if (referencedEntries != null) {
// The Set is required to keep the order intact while the referencedEntriesMap (Map)
// is used to map the referenced entries with path
LinkedHashSet referencedEntriesSet= new LinkedHashSet();
for (int index= 0; index < referencedEntries.length; index++) {
IPath path= referencedEntries[index].getPath();
if (!rawLibrariesPath.contains(path) && referencedEntriesMap.get(path) == null) {
referencedEntriesMap.put(path, referencedEntries[index]);
referencedEntriesSet.add(referencedEntries[index]);
}
}
if (referencedEntriesSet.size() > 0) {
result.referencedEntries= new IClasspathEntry[referencedEntriesSet.size()];
referencedEntriesSet.toArray(result.referencedEntries);
}
}
}
int length= rawClasspath.length;
for (int i= 0; i < length; i++) {
IClasspathEntry rawEntry= rawClasspath[i];
IClasspathEntry resolvedEntry= rawEntry;
switch (rawEntry.getEntryKind()) {
case IClasspathEntry.CPE_VARIABLE:
try {
resolvedEntry= manager.resolveVariableEntry(rawEntry, usePreviousSession);
} catch (ClasspathEntry.AssertionFailedException e) {
// Catch the assertion failure and set status instead
// see bug https://bugs.eclipse.org/bugs/show_bug.cgi?id=55992
result.unresolvedEntryStatus= new JavaModelStatus(IJavaModelStatusConstants.INVALID_PATH, e.getMessage());
break;
}
if (resolvedEntry == null) {
result.unresolvedEntryStatus= new JavaModelStatus(IJavaModelStatusConstants.CP_VARIABLE_PATH_UNBOUND, this, rawEntry.getPath());
} else {
// If the entry is already present in the rawReversetMap, it means the entry and the chained libraries
// have already been processed. So, skip it.
if (resolveChainedLibraries && resolvedEntry.getEntryKind() == IClasspathEntry.CPE_LIBRARY
&& result.rawReverseMap.get(resolvedEntry.getPath()) == null) {
// resolve Class-Path: in manifest
ClasspathEntry[] extraEntries= ((ClasspathEntry)resolvedEntry).resolvedChainedLibraries();
for (int j= 0, length2= extraEntries.length; j < length2; j++) {
if (!rawLibrariesPath.contains(extraEntries[j].getPath())) {
// https://bugs.eclipse.org/bugs/show_bug.cgi?id=305037
// referenced entries for variable entries could also be persisted with extra attributes, so addAsChainedEntry = true
addToResult(rawEntry, extraEntries[j], result, resolvedEntries, externalFoldersManager, referencedEntriesMap, true);
}
}
}
addToResult(rawEntry, resolvedEntry, result, resolvedEntries, externalFoldersManager, referencedEntriesMap, false);
}
break;
case IClasspathEntry.CPE_CONTAINER:
IClasspathContainer container= usePreviousSession ? manager.getPreviousSessionContainer(rawEntry.getPath(), this) : JavaCore.getClasspathContainer(rawEntry.getPath(), this);
if (container == null) {
result.unresolvedEntryStatus= new JavaModelStatus(IJavaModelStatusConstants.CP_CONTAINER_PATH_UNBOUND, this, rawEntry.getPath());
break;
}
IClasspathEntry[] containerEntries= container.getClasspathEntries();
if (containerEntries == null) {
if (JavaModelManager.CP_RESOLVE_VERBOSE || JavaModelManager.CP_RESOLVE_VERBOSE_FAILURE) {
JavaModelManager.getJavaModelManager().verbose_missbehaving_container_null_entries(this, rawEntry.getPath());
}
break;
}
// container was bound
for (int j= 0, containerLength= containerEntries.length; j < containerLength; j++) {
ClasspathEntry cEntry= (ClasspathEntry)containerEntries[j];
if (cEntry == null) {
if (JavaModelManager.CP_RESOLVE_VERBOSE || JavaModelManager.CP_RESOLVE_VERBOSE_FAILURE) {
JavaModelManager.getJavaModelManager().verbose_missbehaving_container(this, rawEntry.getPath(), containerEntries);
}
break;
}
// if container is exported or restricted, then its nested entries must in turn be exported (21749) and/or propagate restrictions
cEntry= cEntry.combineWith((ClasspathEntry)rawEntry);
if (cEntry.getEntryKind() == IClasspathEntry.CPE_LIBRARY) {
// resolve ".." in library path
cEntry= cEntry.resolvedDotDot();
// https://bugs.eclipse.org/bugs/show_bug.cgi?id=305037
// responsibility of resolving chained (referenced) libraries lies with the container
}
addToResult(rawEntry, cEntry, result, resolvedEntries, externalFoldersManager, referencedEntriesMap, false);
}
break;
case IClasspathEntry.CPE_LIBRARY:
// resolve ".." in library path
resolvedEntry= ((ClasspathEntry)rawEntry).resolvedDotDot();
if (resolveChainedLibraries && result.rawReverseMap.get(resolvedEntry.getPath()) == null) {
// resolve Class-Path: in manifest
ClasspathEntry[] extraEntries= ((ClasspathEntry)resolvedEntry).resolvedChainedLibraries();
for (int k= 0, length2= extraEntries.length; k < length2; k++) {
if (!rawLibrariesPath.contains(extraEntries[k].getPath())) {
addToResult(rawEntry, extraEntries[k], result, resolvedEntries, externalFoldersManager, referencedEntriesMap, true);
}
}
}
addToResult(rawEntry, resolvedEntry, result, resolvedEntries, externalFoldersManager, referencedEntriesMap, false);
break;
default:
addToResult(rawEntry, resolvedEntry, result, resolvedEntries, externalFoldersManager, referencedEntriesMap, false);
break;
}
}
result.resolvedClasspath= new IClasspathEntry[resolvedEntries.size()];
resolvedEntries.toArray(result.resolvedClasspath);
return result;
}
private void addToResult(IClasspathEntry rawEntry, IClasspathEntry resolvedEntry, ResolvedClasspath result,
LinkedHashSet resolvedEntries, ExternalFoldersManager externalFoldersManager,
Map oldChainedEntriesMap, boolean addAsChainedEntry) {
IPath resolvedPath;
// If it's already been resolved, do not add to resolvedEntries
if (result.rawReverseMap.get(resolvedPath= resolvedEntry.getPath()) == null) {
result.rawReverseMap.put(resolvedPath, rawEntry);
result.rootPathToResolvedEntries.put(resolvedPath, resolvedEntry);
resolvedEntries.add(resolvedEntry);
if (addAsChainedEntry) {
IClasspathEntry chainedEntry= null;
chainedEntry= (ClasspathEntry)oldChainedEntriesMap.get(resolvedPath);
if (chainedEntry != null) {
// This is required to keep the attributes if any added by the user in
// the previous session such as source attachment path etc.
copyFromOldChainedEntry((ClasspathEntry)resolvedEntry, (ClasspathEntry)chainedEntry);
}
}
}
if (resolvedEntry.getEntryKind() == IClasspathEntry.CPE_LIBRARY && ExternalFoldersManager.isExternalFolderPath(resolvedPath)) {
externalFoldersManager.addFolder(resolvedPath); // no-op if not an external folder or if already registered
}
}
private void copyFromOldChainedEntry(ClasspathEntry resolvedEntry, ClasspathEntry chainedEntry) {
IPath path= chainedEntry.getSourceAttachmentPath();
if (path != null) {
resolvedEntry.sourceAttachmentPath= path;
}
path= chainedEntry.getSourceAttachmentRootPath();
if (path != null) {
resolvedEntry.sourceAttachmentRootPath= path;
}
IClasspathAttribute[] attributes= chainedEntry.getExtraAttributes();
if (attributes != null) {
resolvedEntry.extraAttributes= attributes;
}
}
/*
* Resolve the given perProjectInfo's raw classpath and store the resolved classpath in the perProjectInfo.
*/
public void resolveClasspath(PerProjectInfo perProjectInfo, boolean usePreviousSession, boolean addClasspathChange) throws JavaModelException {
if (CP_RESOLUTION_BP_LISTENERS != null)
breakpoint(1, this);
JavaModelManager manager= JavaModelManager.getJavaModelManager();
boolean isClasspathBeingResolved= manager.isClasspathBeingResolved(this);
try {
if (!isClasspathBeingResolved) {
manager.setClasspathBeingResolved(this, true);
}
// get raw info inside a synchronized block to ensure that it is consistent
IClasspathEntry[][] classpath= new IClasspathEntry[2][];
int timeStamp;
synchronized (perProjectInfo) {
classpath[0]= perProjectInfo.rawClasspath;
classpath[1]= perProjectInfo.referencedEntries;
// Checking null only for rawClasspath enough
if (classpath[0] == null)
classpath= perProjectInfo.readAndCacheClasspath(this);
timeStamp= perProjectInfo.rawTimeStamp;
}
ResolvedClasspath result= resolveClasspath(classpath[0], classpath[1], usePreviousSession, true/*resolve chained libraries*/);
if (CP_RESOLUTION_BP_LISTENERS != null)
breakpoint(2, this);
// store resolved info along with the raw info to ensure consistency
perProjectInfo.setResolvedClasspath(result.resolvedClasspath, result.referencedEntries, result.rawReverseMap, result.rootPathToResolvedEntries, usePreviousSession
? PerProjectInfo.NEED_RESOLUTION
: result.unresolvedEntryStatus, timeStamp, addClasspathChange);
} finally {
if (!isClasspathBeingResolved) {
manager.setClasspathBeingResolved(this, false);
}
if (CP_RESOLUTION_BP_LISTENERS != null)
breakpoint(3, this);
}
}
/**
* Answers an ID which is used to distinguish project/entries during package fragment root
* computations
*
* @return String
*/
public String rootID() {
return "[PRJ]" + this.project.getFullPath(); //$NON-NLS-1$
}
/**
* Writes the classpath in a sharable format (VCM-wise) only when necessary, that is, if it is
* semantically different from the existing one in file. Will never write an identical one.
*
* @param newClasspath IClasspathEntry[]
* @param newOutputLocation IPath
* @return boolean Return whether the .classpath file was modified.
* @throws JavaModelException
*/
public boolean writeFileEntries(IClasspathEntry[] newClasspath, IClasspathEntry[] referencedEntries, IPath newOutputLocation) throws JavaModelException {
if (!this.project.isAccessible())
return false;
Map unknownElements= new HashMap();
IClasspathEntry[][] fileEntries= readFileEntries(unknownElements);
if (fileEntries[0] != JavaProject.INVALID_CLASSPATH &&
areClasspathsEqual(newClasspath, newOutputLocation, fileEntries[0])
&& (referencedEntries == null || areClasspathsEqual(referencedEntries, fileEntries[1]))) {
// no need to save it, it is the same
return false;
}
// actual file saving
try {
setSharedProperty(JavaProject.CLASSPATH_FILENAME, encodeClasspath(newClasspath, referencedEntries, newOutputLocation, true, unknownElements));
return true;
} catch (CoreException e) {
throw new JavaModelException(e);
}
}
public boolean writeFileEntries(IClasspathEntry[] newClasspath, IPath newOutputLocation) throws JavaModelException {
return writeFileEntries(newClasspath, ClasspathEntry.NO_ENTRIES, newOutputLocation);
}
/**
* Update the Java command in the build spec (replace existing one if present, add one first if
* none).
*/
private void setJavaCommand(
IProjectDescription description,
ICommand newCommand)
throws CoreException {
ICommand[] oldBuildSpec= description.getBuildSpec();
int oldJavaCommandIndex= getJavaCommandIndex(oldBuildSpec);
ICommand[] newCommands;
if (oldJavaCommandIndex == -1) {
// Add a Java build spec before other builders (1FWJK7I)
newCommands= new ICommand[oldBuildSpec.length + 1];
System.arraycopy(oldBuildSpec, 0, newCommands, 1, oldBuildSpec.length);
newCommands[0]= newCommand;
} else {
oldBuildSpec[oldJavaCommandIndex]= newCommand;
newCommands= oldBuildSpec;
}
// Commit the spec change into the project
description.setBuildSpec(newCommands);
this.project.setDescription(description, null);
}
/**
* @see org.eclipse.jdt.core.IJavaProject#setOption(java.lang.String, java.lang.String)
*/
public void setOption(String optionName, String optionValue) {
if (!JavaModelManager.getJavaModelManager().optionNames.contains(optionName))
return; // unrecognized option
IEclipsePreferences projectPreferences= getEclipsePreferences();
if (optionValue == null) {
// remove preference
projectPreferences.remove(optionName);
} else {
projectPreferences.put(optionName, optionValue);
}
// Dump changes
try {
projectPreferences.flush();
} catch (BackingStoreException e) {
// problem with pref store - quietly ignore
}
}
/**
* @see org.eclipse.jdt.core.IJavaProject#setOptions(Map)
*/
public void setOptions(Map newOptions) {
IEclipsePreferences projectPreferences= getEclipsePreferences();
if (projectPreferences == null)
return;
try {
if (newOptions == null) {
projectPreferences.clear();
} else {
Iterator entries= newOptions.entrySet().iterator();
while (entries.hasNext()) {
Map.Entry entry= (Map.Entry)entries.next();
String key= (String)entry.getKey();
if (!JavaModelManager.getJavaModelManager().optionNames.contains(key))
continue; // unrecognized option
// no filtering for encoding (custom encoding for project is allowed)
projectPreferences.put(key, (String)entry.getValue());
}
// reset to default all options not in new map
// @see https://bugs.eclipse.org/bugs/show_bug.cgi?id=26255
// @see https://bugs.eclipse.org/bugs/show_bug.cgi?id=49691
String[] pNames= projectPreferences.keys();
int ln= pNames.length;
for (int i= 0; i < ln; i++) {
String key= pNames[i];
if (!newOptions.containsKey(key)) {
projectPreferences.remove(key); // old preferences => remove from preferences table
}
}
}
// persist options
projectPreferences.flush();
// flush cache immediately
try {
getPerProjectInfo().options= null;
} catch (JavaModelException e) {
// do nothing
}
} catch (BackingStoreException e) {
// problem with pref store - quietly ignore
}
}
/**
* @see IJavaProject
*/
public void setOutputLocation(IPath path, IProgressMonitor monitor) throws JavaModelException {
if (path == null) {
throw new IllegalArgumentException(Messages.path_nullPath);
}
if (path.equals(getOutputLocation())) {
return;
}
setRawClasspath(getRawClasspath(), path, monitor);
}
/**
* Sets the underlying kernel project of this Java project, and fills in its parent and name.
* Called by IProject.getNature().
*
* @see IProjectNature#setProject(IProject)
*/
public void setProject(IProject project) {
this.project= project;
this.parent= JavaModelManager.getJavaModelManager().getJavaModel();
}
/**
* @see IJavaProject#setRawClasspath(IClasspathEntry[],boolean,IProgressMonitor)
*/
public void setRawClasspath(
IClasspathEntry[] entries,
boolean canModifyResources,
IProgressMonitor monitor)
throws JavaModelException {
setRawClasspath(
entries,
getOutputLocation()/*don't change output*/,
canModifyResources,
monitor);
}
/**
* @see IJavaProject#setRawClasspath(IClasspathEntry[],IPath,boolean,IProgressMonitor)
*/
public void setRawClasspath(
IClasspathEntry[] newRawClasspath,
IPath newOutputLocation,
boolean canModifyResources,
IProgressMonitor monitor)
throws JavaModelException {
setRawClasspath(newRawClasspath, null, newOutputLocation, canModifyResources, monitor);
}
/**
* @see IJavaProject#setRawClasspath(IClasspathEntry[],IPath,IProgressMonitor)
*/
public void setRawClasspath(
IClasspathEntry[] entries,
IPath outputLocation,
IProgressMonitor monitor)
throws JavaModelException {
setRawClasspath(
entries,
outputLocation,
true/*can change resource (as per API contract)*/,
monitor);
}
public void setRawClasspath(IClasspathEntry[] entries, IClasspathEntry[] referencedEntries, IPath outputLocation,
IProgressMonitor monitor) throws JavaModelException {
setRawClasspath(entries, referencedEntries, outputLocation, true, monitor);
}
protected void setRawClasspath(IClasspathEntry[] newRawClasspath, IClasspathEntry[] referencedEntries, IPath newOutputLocation,
boolean canModifyResources, IProgressMonitor monitor) throws JavaModelException {
try {
if (newRawClasspath == null) //are we already with the default classpath
newRawClasspath= defaultClasspath();
SetClasspathOperation op=
new SetClasspathOperation(
this,
newRawClasspath,
referencedEntries,
newOutputLocation,
canModifyResources);
op.runOperation(monitor);
} catch (JavaModelException e) {
JavaModelManager.getJavaModelManager().getDeltaProcessor().flush();
throw e;
}
}
/**
* @see IJavaProject
*/
public void setRawClasspath(
IClasspathEntry[] entries,
IProgressMonitor monitor)
throws JavaModelException {
setRawClasspath(
entries,
getOutputLocation()/*don't change output*/,
true/*can change resource (as per API contract)*/,
monitor);
}
/**
* Record a shared persistent property onto a project. Note that it is orthogonal to IResource
* persistent properties, and client code has to decide which form of storage to use
* appropriately. Shared properties produce real resource files which can be shared through a
* VCM onto a server. Persistent properties are not shareable.
*
* shared properties end up in resource files, and thus cannot be modified during delta
* notifications (a CoreException would then be thrown).
*
* @param key String
* @param value String
* @see JavaProject#getSharedProperty(String key)
* @throws CoreException
*/
public void setSharedProperty(String key, String value) throws CoreException {
IFile rscFile= this.project.getFile(key);
byte[] bytes= null;
try {
bytes= value.getBytes(org.eclipse.jdt.internal.compiler.util.Util.UTF_8); // .classpath always encoded with UTF-8
} catch (UnsupportedEncodingException e) {
Util.log(e, "Could not write .classpath with UTF-8 encoding "); //$NON-NLS-1$
// fallback to default
bytes= value.getBytes();
}
InputStream inputStream= new ByteArrayInputStream(bytes);
// update the resource content
if (rscFile.exists()) {
if (rscFile.isReadOnly()) {
// provide opportunity to checkout read-only .classpath file (23984)
ResourcesPlugin.getWorkspace().validateEdit(new IFile[] { rscFile }, null);
}
rscFile.setContents(inputStream, IResource.FORCE, null);
} else {
rscFile.create(inputStream, IResource.FORCE, null);
}
}
/**
* If a cycle is detected, then cycleParticipants contains all the paths of projects involved in
* this cycle (directly and indirectly), no cycle if the set is empty (and started empty)
*
* @param prereqChain ArrayList
* @param cycleParticipants HashSet
* @param workspaceRoot IWorkspaceRoot
* @param traversed HashSet
* @param preferredClasspaths Map
*/
public void updateCycleParticipants(
ArrayList prereqChain,
HashSet cycleParticipants,
IWorkspaceRoot workspaceRoot,
HashSet traversed,
Map preferredClasspaths) {
IPath path= getPath();
prereqChain.add(path);
traversed.add(path);
try {
IClasspathEntry[] classpath= null;
if (preferredClasspaths != null)
classpath= (IClasspathEntry[])preferredClasspaths.get(this);
if (classpath == null)
classpath= getResolvedClasspath();
for (int i= 0, length= classpath.length; i < length; i++) {
IClasspathEntry entry= classpath[i];
if (entry.getEntryKind() == IClasspathEntry.CPE_PROJECT) {
IPath prereqProjectPath= entry.getPath();
int index= cycleParticipants.contains(prereqProjectPath) ? 0 : prereqChain.indexOf(prereqProjectPath);
if (index >= 0) { // refer to cycle, or in cycle itself
for (int size= prereqChain.size(); index < size; index++) {
cycleParticipants.add(prereqChain.get(index));
}
} else {
if (!traversed.contains(prereqProjectPath)) {
IResource member= workspaceRoot.findMember(prereqProjectPath);
if (member != null && member.getType() == IResource.PROJECT) {
JavaProject javaProject= (JavaProject)JavaCore.create((IProject)member);
javaProject.updateCycleParticipants(prereqChain, cycleParticipants, workspaceRoot, traversed, preferredClasspaths);
}
}
}
}
}
} catch (JavaModelException e) {
// project doesn't exist: ignore
}
prereqChain.remove(path);
}
/*
* Update eclipse preferences from old preferences.
*/
private void updatePreferences(IEclipsePreferences preferences) {
IEclipsePreferences oldPreferences= loadPreferences();
if (oldPreferences != null) {
try {
String[] propertyNames= oldPreferences.childrenNames();
for (int i= 0; i < propertyNames.length; i++) {
String propertyName= propertyNames[i];
String propertyValue= oldPreferences.get(propertyName, ""); //$NON-NLS-1$
if (!"".equals(propertyValue)) { //$NON-NLS-1$
preferences.put(propertyName, propertyValue);
}
}
// save immediately new preferences
preferences.flush();
} catch (BackingStoreException e) {
// fails silently
}
}
}
protected IStatus validateExistence(IResource underlyingResource) {
// check whether the java project can be opened
try {
if (!((IProject)underlyingResource).hasNature(JavaCore.NATURE_ID))
return newDoesNotExistStatus();
} catch (CoreException e) {
return newDoesNotExistStatus();
}
return JavaModelStatus.VERIFIED_OK;
}
}