X-Git-Url: http://git.phpeclipse.com diff --git a/net.sourceforge.phpeclipse/src/net/sourceforge/phpdt/internal/core/JavaProject.java b/net.sourceforge.phpeclipse/src/net/sourceforge/phpdt/internal/core/JavaProject.java index f803855..e3fdb4d 100644 --- a/net.sourceforge.phpeclipse/src/net/sourceforge/phpdt/internal/core/JavaProject.java +++ b/net.sourceforge.phpeclipse/src/net/sourceforge/phpdt/internal/core/JavaProject.java @@ -49,6 +49,7 @@ import net.sourceforge.phpdt.core.JavaModelException; import net.sourceforge.phpdt.core.WorkingCopyOwner; import net.sourceforge.phpdt.internal.codeassist.ISearchableNameEnvironment; import net.sourceforge.phpdt.internal.compiler.util.ObjectVector; +import net.sourceforge.phpdt.internal.core.util.MementoTokenizer; import net.sourceforge.phpdt.internal.core.util.Util; import net.sourceforge.phpdt.internal.corext.Assert; import net.sourceforge.phpeclipse.LoadPathEntry; @@ -136,7 +137,11 @@ public class JavaProject public static final IClasspathEntry[] INVALID_CLASSPATH = new IClasspathEntry[0]; private static final String CUSTOM_DEFAULT_OPTION_VALUE = "#\r\n\r#custom-non-empty-default-value#\r\n\r#"; //$NON-NLS-1$ - + /* + * Value of project's resolved classpath while it is being resolved + */ + private static final IClasspathEntry[] RESOLUTION_IN_PROGRESS = new IClasspathEntry[0]; + /** * Returns a canonicalized path from the given external path. * Note that the return path contains the same number of segments @@ -426,57 +431,117 @@ public class JavaProject // } - /** * Internal computation of an expanded classpath. It will eliminate duplicates, and produce copies * of exported classpath entries to avoid possible side-effects ever after. */ private void computeExpandedClasspath( - JavaProject initialProject, + JavaProject initialProject, boolean ignoreUnresolvedVariable, boolean generateMarkerOnError, - HashSet visitedProjects, - ObjectVector accumulatedEntries) throws JavaModelException { + HashSet rootIDs, + ObjectVector accumulatedEntries, + Map preferredClasspaths, + Map preferredOutputs) throws JavaModelException { - if (visitedProjects.contains(this)){ + String projectRootId = this.rootID(); + if (rootIDs.contains(projectRootId)){ return; // break cycles if any } - visitedProjects.add(this); + rootIDs.add(projectRootId); - if (generateMarkerOnError && !this.equals(initialProject)){ - generateMarkerOnError = false; - } + IClasspathEntry[] preferredClasspath = preferredClasspaths != null ? (IClasspathEntry[])preferredClasspaths.get(this) : null; + IPath preferredOutput = preferredOutputs != null ? (IPath)preferredOutputs.get(this) : null; IClasspathEntry[] immediateClasspath = - getResolvedClasspath(ignoreUnresolvedVariable, generateMarkerOnError); + preferredClasspath != null + ? getResolvedClasspath(preferredClasspath, preferredOutput, ignoreUnresolvedVariable, generateMarkerOnError, null) + : getResolvedClasspath(ignoreUnresolvedVariable, generateMarkerOnError, false/*don't returnResolutionInProgress*/); IWorkspaceRoot workspaceRoot = ResourcesPlugin.getWorkspace().getRoot(); + boolean isInitialProject = this.equals(initialProject); for (int i = 0, length = immediateClasspath.length; i < length; i++){ - IClasspathEntry entry = immediateClasspath[i]; - - boolean isInitialProject = this.equals(initialProject); + ClasspathEntry entry = (ClasspathEntry) immediateClasspath[i]; if (isInitialProject || entry.isExported()){ + String rootID = entry.rootID(); + if (rootIDs.contains(rootID)) { + continue; + } accumulatedEntries.add(entry); // recurse in project to get all its indirect exports (only consider exported entries from there on) - if (entry.getEntryKind() == ClasspathEntry.CPE_PROJECT) { + 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 project = (JavaProject) JavaCore.create(projRsc); - project.computeExpandedClasspath( + JavaProject javaProject = (JavaProject) JavaCore.create(projRsc); + javaProject.computeExpandedClasspath( initialProject, ignoreUnresolvedVariable, - generateMarkerOnError, - visitedProjects, - accumulatedEntries); + false /* no marker when recursing in prereq*/, + rootIDs, + accumulatedEntries, + preferredClasspaths, + preferredOutputs); } } + } else { + rootIDs.add(rootID); } } } } + /** + * Internal computation of an expanded classpath. It will eliminate duplicates, and produce copies + * of exported classpath entries to avoid possible side-effects ever after. + */ +// private void computeExpandedClasspath( +// JavaProject initialProject, +// boolean ignoreUnresolvedVariable, +// boolean generateMarkerOnError, +// HashSet visitedProjects, +// ObjectVector accumulatedEntries) throws JavaModelException { +// +// if (visitedProjects.contains(this)){ +// return; // break cycles if any +// } +// visitedProjects.add(this); +// +// if (generateMarkerOnError && !this.equals(initialProject)){ +// generateMarkerOnError = false; +// } +// IClasspathEntry[] immediateClasspath = +// getResolvedClasspath(ignoreUnresolvedVariable, generateMarkerOnError); +// +// IWorkspaceRoot workspaceRoot = ResourcesPlugin.getWorkspace().getRoot(); +// for (int i = 0, length = immediateClasspath.length; i < length; i++){ +// IClasspathEntry entry = immediateClasspath[i]; +// +// boolean isInitialProject = this.equals(initialProject); +// if (isInitialProject || entry.isExported()){ +// +// accumulatedEntries.add(entry); +// +// // recurse in project to get all its indirect exports (only consider exported entries from there on) +// if (entry.getEntryKind() == ClasspathEntry.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 project = (JavaProject) JavaCore.create(projRsc); +// project.computeExpandedClasspath( +// initialProject, +// ignoreUnresolvedVariable, +// generateMarkerOnError, +// visitedProjects, +// accumulatedEntries); +// } +// } +// } +// } +// } +// } /** * Returns (local/all) the package fragment roots identified by the given project's classpath. @@ -1301,38 +1366,95 @@ public class JavaProject * 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. + * @param ignoreUnresolvedVariable boolean + * @return IClasspathEntry[] + * @throws JavaModelException */ public IClasspathEntry[] getExpandedClasspath(boolean ignoreUnresolvedVariable) throws JavaModelException { - return getExpandedClasspath(ignoreUnresolvedVariable, false); + return getExpandedClasspath(ignoreUnresolvedVariable, false/*don't create markers*/, null, null); } + /* + * @see JavaElement + */ + public IJavaElement getHandleFromMemento(String token, MementoTokenizer memento, WorkingCopyOwner owner) { + switch (token.charAt(0)) { + case JEM_COUNT: + return getHandleUpdatingCountFromMemento(memento, owner); + 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 char that marks the start of this handles + * contribution to a memento. + */ + protected char getHandleMementoDelimiter() { + + return JEM_JAVAPROJECT; + } + /** * Internal variant which can create marker on project for invalid entries, * it will also perform classpath expansion in presence of project prerequisites * exporting their entries. + * @param ignoreUnresolvedVariable boolean + * @param generateMarkerOnError boolean + * @param preferredClasspaths Map + * @param preferredOutputs Map + * @return IClasspathEntry[] + * @throws JavaModelException */ public IClasspathEntry[] getExpandedClasspath( boolean ignoreUnresolvedVariable, - boolean generateMarkerOnError) throws JavaModelException { + boolean generateMarkerOnError, + Map preferredClasspaths, + Map preferredOutputs) throws JavaModelException { ObjectVector accumulatedEntries = new ObjectVector(); - computeExpandedClasspath(this, ignoreUnresolvedVariable, generateMarkerOnError, new HashSet(5), accumulatedEntries); + computeExpandedClasspath(this, ignoreUnresolvedVariable, generateMarkerOnError, new HashSet(5), accumulatedEntries, preferredClasspaths, preferredOutputs); IClasspathEntry[] expandedPath = new IClasspathEntry[accumulatedEntries.size()]; accumulatedEntries.copyInto(expandedPath); return expandedPath; } - - /** - * Returns the char that marks the start of this handles - * contribution to a memento. - */ - protected char getHandleMementoDelimiter() { - - return JEM_JAVAPROJECT; - } +// /** +// * Internal variant which can create marker on project for invalid entries, +// * it will also perform classpath expansion in presence of project prerequisites +// * exporting their entries. +// */ +// public IClasspathEntry[] getExpandedClasspath( +// boolean ignoreUnresolvedVariable, +// boolean generateMarkerOnError) throws JavaModelException { +// +// ObjectVector accumulatedEntries = new ObjectVector(); +// computeExpandedClasspath(this, ignoreUnresolvedVariable, generateMarkerOnError, new HashSet(5), accumulatedEntries); +// +// IClasspathEntry[] expandedPath = new IClasspathEntry[accumulatedEntries.size()]; +// accumulatedEntries.copyInto(expandedPath); +// +// return expandedPath; +// } /** * Find the specific Java command amongst the build spec of a given description @@ -1405,7 +1527,7 @@ public class JavaProject // } /** - * @see org.eclipse.jdt.core.IJavaProject#getOption(String, boolean) + * @see net.sourceforge.phpdt.core.IJavaProject#getOption(String, boolean) */ public String getOption(String optionName, boolean inheritJavaCoreOptions) { @@ -1421,7 +1543,7 @@ public class JavaProject } /** - * @see org.eclipse.jdt.core.IJavaProject#getOptions(boolean) + * @see net.sourceforge.phpdt.core.IJavaProject#getOptions(boolean) */ public Map getOptions(boolean inheritJavaCoreOptions) { @@ -1456,21 +1578,48 @@ public class JavaProject /** * @see IJavaProject */ +// public IPath getOutputLocation() throws JavaModelException { +// +// JavaModelManager.PerProjectInfo perProjectInfo = JavaModelManager.getJavaModelManager().getPerProjectInfoCheckExistence(project); +// IPath outputLocation = perProjectInfo.outputLocation; +// if (outputLocation != null) return outputLocation; +// +// // force to read classpath - will position output location as well +// this.getRawClasspath(); +// outputLocation = perProjectInfo.outputLocation; +// if (outputLocation == null) { +// return defaultOutputLocation(); +// } +// return outputLocation; +// } + /** + * @see IJavaProject + */ public IPath getOutputLocation() throws JavaModelException { + // Do not create marker but log problems while getting output location + return this.getOutputLocation(false, true); + } + + /** + * @param createMarkers boolean + * @param logProblems boolean + * @return IPath + * @throws JavaModelException + */ + public IPath getOutputLocation(boolean createMarkers, boolean logProblems) throws JavaModelException { - JavaModelManager.PerProjectInfo perProjectInfo = JavaModelManager.getJavaModelManager().getPerProjectInfoCheckExistence(project); + JavaModelManager.PerProjectInfo perProjectInfo = getPerProjectInfo(); IPath outputLocation = perProjectInfo.outputLocation; if (outputLocation != null) return outputLocation; // force to read classpath - will position output location as well - this.getRawClasspath(); + this.getRawClasspath(createMarkers, logProblems); outputLocation = perProjectInfo.outputLocation; if (outputLocation == null) { return defaultOutputLocation(); } return outputLocation; } - /** * @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 @@ -1638,6 +1787,10 @@ public class JavaProject return this.getProject().getFullPath(); } + public JavaModelManager.PerProjectInfo getPerProjectInfo() throws JavaModelException { + return JavaModelManager.getJavaModelManager().getPerProjectInfoCheckExistence(this.project); + } + /** * @see IJavaProject */ @@ -1694,13 +1847,61 @@ public class JavaProject /** * @see IJavaProject */ +// public IClasspathEntry[] getRawClasspath() throws JavaModelException { +// +// JavaModelManager.PerProjectInfo perProjectInfo = JavaModelManager.getJavaModelManager().getPerProjectInfoCheckExistence(project); +// IClasspathEntry[] classpath = perProjectInfo.classpath; +// if (classpath != null) return classpath; +// classpath = this.readClasspathFile(false/*don't create markers*/, true/*log problems*/); +// +// // extract out the output location +// IPath outputLocation = null; +// if (classpath != null && classpath.length > 0) { +// IClasspathEntry entry = classpath[classpath.length - 1]; +// if (entry.getContentKind() == ClasspathEntry.K_OUTPUT) { +// outputLocation = entry.getPath(); +// IClasspathEntry[] copy = new IClasspathEntry[classpath.length - 1]; +// System.arraycopy(classpath, 0, copy, 0, copy.length); +// classpath = copy; +// } +// } +// if (classpath == null) { +// return defaultClasspath(); +// } +// /* Disable validate: classpath can contain CP variables and container that need to be resolved +// if (classpath != INVALID_CLASSPATH +// && !JavaConventions.validateClasspath(this, classpath, outputLocation).isOK()) { +// classpath = INVALID_CLASSPATH; +// } +// */ +// perProjectInfo.classpath = classpath; +// perProjectInfo.outputLocation = outputLocation; +// return classpath; +// } + /** + * @see IJavaProject + */ public IClasspathEntry[] getRawClasspath() throws JavaModelException { + // Do not create marker but log problems while getting raw classpath + return getRawClasspath(false, true); + } - JavaModelManager.PerProjectInfo perProjectInfo = JavaModelManager.getJavaModelManager().getPerProjectInfoCheckExistence(project); - IClasspathEntry[] classpath = perProjectInfo.classpath; - if (classpath != null) return classpath; - classpath = this.readClasspathFile(false/*don't create markers*/, true/*log problems*/); - + /* + * Internal variant allowing to parameterize problem creation/logging + */ + public IClasspathEntry[] getRawClasspath(boolean createMarkers, boolean logProblems) throws JavaModelException { + + JavaModelManager.PerProjectInfo perProjectInfo = null; + IClasspathEntry[] classpath; + if (createMarkers) { + this.flushClasspathProblemMarkers(false/*cycle*/, true/*format*/); + classpath = this.readClasspathFile(createMarkers, logProblems); + } else { + perProjectInfo = getPerProjectInfo(); + classpath = perProjectInfo.rawClasspath; + if (classpath != null) return classpath; + classpath = this.readClasspathFile(createMarkers, logProblems); + } // extract out the output location IPath outputLocation = null; if (classpath != null && classpath.length > 0) { @@ -1721,11 +1922,13 @@ public class JavaProject classpath = INVALID_CLASSPATH; } */ - perProjectInfo.classpath = classpath; - perProjectInfo.outputLocation = outputLocation; + if (!createMarkers) { + perProjectInfo.rawClasspath = classpath; + perProjectInfo.outputLocation = outputLocation; + } return classpath; } - + /** * @see IJavaProject#getRequiredProjectNames */ @@ -1754,39 +1957,108 @@ public class JavaProject boolean ignoreUnresolvedEntry, boolean generateMarkerOnError) throws JavaModelException { + return + getResolvedClasspath( + ignoreUnresolvedEntry, + generateMarkerOnError, + true // returnResolutionInProgress + ); +// JavaModelManager manager = JavaModelManager.getJavaModelManager(); +// JavaModelManager.PerProjectInfo perProjectInfo = manager.getPerProjectInfoCheckExistence(project); +// +// // reuse cache if not needing to refresh markers or checking bound variables +// if (ignoreUnresolvedEntry && !generateMarkerOnError && perProjectInfo != null){ +// // resolved path is cached on its info +// IClasspathEntry[] infoPath = perProjectInfo.lastResolvedClasspath; +// if (infoPath != null) return infoPath; +// } +// Map reverseMap = perProjectInfo == null ? null : new HashMap(5); +// IClasspathEntry[] resolvedPath = getResolvedClasspath( +// getRawClasspath(), +// generateMarkerOnError ? getOutputLocation() : null, +// ignoreUnresolvedEntry, +// generateMarkerOnError, +// reverseMap); +// +// if (perProjectInfo != null){ +// if (perProjectInfo.classpath == null // .classpath file could not be read +// && generateMarkerOnError +// && JavaProject.hasJavaNature(project)) { +// this.createClasspathProblemMarker(new JavaModelStatus( +// IJavaModelStatusConstants.INVALID_CLASSPATH_FILE_FORMAT, +// Util.bind("classpath.cannotReadClasspathFile", this.getElementName()))); //$NON-NLS-1$ +// } +// +// perProjectInfo.lastResolvedClasspath = resolvedPath; +// perProjectInfo.resolvedPathToRawEntries = reverseMap; +// } +// return resolvedPath; + } + /* + * Internal variant which can create marker on project for invalid entries + * and caches the resolved classpath on perProjectInfo. + * If requested, return a special classpath (RESOLUTION_IN_PROGRESS) if the classpath is being resolved. + */ + public IClasspathEntry[] getResolvedClasspath( + boolean ignoreUnresolvedEntry, + boolean generateMarkerOnError, + boolean returnResolutionInProgress) + throws JavaModelException { - JavaModelManager manager = JavaModelManager.getJavaModelManager(); - JavaModelManager.PerProjectInfo perProjectInfo = manager.getPerProjectInfoCheckExistence(project); - - // reuse cache if not needing to refresh markers or checking bound variables - if (ignoreUnresolvedEntry && !generateMarkerOnError && perProjectInfo != null){ - // resolved path is cached on its info - IClasspathEntry[] infoPath = perProjectInfo.lastResolvedClasspath; - if (infoPath != null) return infoPath; + JavaModelManager manager = JavaModelManager.getJavaModelManager(); + JavaModelManager.PerProjectInfo perProjectInfo = null; + if (ignoreUnresolvedEntry && !generateMarkerOnError) { + perProjectInfo = getPerProjectInfo(); + if (perProjectInfo != null) { + // resolved path is cached on its info + IClasspathEntry[] infoPath = perProjectInfo.resolvedClasspath; + if (infoPath != null) { + return infoPath; + } else if (returnResolutionInProgress && manager.isClasspathBeingResolved(this)) { + if (JavaModelManager.CP_RESOLVE_VERBOSE) { + 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("").printStackTrace(System.out); //$NON-NLS-1$ + } + return RESOLUTION_IN_PROGRESS; + } + } } Map reverseMap = perProjectInfo == null ? null : new HashMap(5); - IClasspathEntry[] resolvedPath = getResolvedClasspath( - getRawClasspath(), - generateMarkerOnError ? getOutputLocation() : null, - ignoreUnresolvedEntry, - generateMarkerOnError, - reverseMap); + IClasspathEntry[] resolvedPath = null; + boolean nullOldResolvedCP = perProjectInfo != null && perProjectInfo.resolvedClasspath == null; + try { + // protect against misbehaving clients (see https://bugs.eclipse.org/bugs/show_bug.cgi?id=61040) + if (nullOldResolvedCP) manager.setClasspathBeingResolved(this, true); + resolvedPath = getResolvedClasspath( + getRawClasspath(generateMarkerOnError, !generateMarkerOnError), + generateMarkerOnError ? getOutputLocation() : null, + ignoreUnresolvedEntry, + generateMarkerOnError, + reverseMap); + } finally { + if (nullOldResolvedCP) perProjectInfo.resolvedClasspath = null; + } if (perProjectInfo != null){ - if (perProjectInfo.classpath == null // .classpath file could not be read + if (perProjectInfo.rawClasspath == null // .classpath file could not be read && generateMarkerOnError - && JavaProject.hasJavaNature(project)) { + && JavaProject.hasJavaNature(this.project)) { + // flush .classpath format markers (bug 39877), but only when file cannot be read (bug 42366) + this.flushClasspathProblemMarkers(false, true); this.createClasspathProblemMarker(new JavaModelStatus( IJavaModelStatusConstants.INVALID_CLASSPATH_FILE_FORMAT, Util.bind("classpath.cannotReadClasspathFile", this.getElementName()))); //$NON-NLS-1$ - } + } - perProjectInfo.lastResolvedClasspath = resolvedPath; + perProjectInfo.resolvedClasspath = resolvedPath; perProjectInfo.resolvedPathToRawEntries = reverseMap; + manager.setClasspathBeingResolved(this, false); } return resolvedPath; } - /** * Internal variant which can process any arbitrary classpath */ @@ -1894,11 +2166,12 @@ public class JavaProject public ISearchableNameEnvironment getSearchableNameEnvironment() throws JavaModelException { - JavaProjectElementInfo info = getJavaProjectElementInfo(); - if (info.getSearchableEnvironment() == null) { - info.setSearchableEnvironment(new SearchableEnvironment(this)); - } - return info.getSearchableEnvironment(); +// JavaProjectElementInfo info = getJavaProjectElementInfo(); +// if (info.getSearchableEnvironment() == null) { +// info.setSearchableEnvironment(new SearchableEnvironment(this)); +// } +// return info.getSearchableEnvironment(); + return null; } /** @@ -2055,7 +2328,7 @@ public class JavaProject for (int i = 0; i < classpath.length; i++) { IClasspathEntry entry = classpath[i]; if (entry.getPath().isPrefixOf(path) - && !Util.isExcluded(path, ((ClasspathEntry)entry).fullExclusionPatternChars())) { + && !Util.isExcluded(path, null,((ClasspathEntry)entry).fullExclusionPatternChars(),true)) { return true; } } @@ -2356,7 +2629,7 @@ public class JavaProject } /** - * @see org.eclipse.jdt.core.IJavaProject#setOptions(Map) + * @see net.sourceforge.phpdt.core.IJavaProject#setOptions(Map) */ public void setOptions(Map newOptions) { @@ -2480,21 +2753,21 @@ public class JavaProject * * @exception NotPresentException if this project does not exist. */ - protected void setRawClasspath0(IClasspathEntry[] rawEntries) - throws JavaModelException { - - JavaModelManager.PerProjectInfo info = JavaModelManager.getJavaModelManager().getPerProjectInfoCheckExistence(project); - - synchronized (info) { - if (rawEntries != null) { - info.classpath = rawEntries; - } - - // clear cache of resolved classpath - info.lastResolvedClasspath = null; - info.resolvedPathToRawEntries = null; - } - } +// protected void setRawClasspath0(IClasspathEntry[] rawEntries) +// throws JavaModelException { +// +// JavaModelManager.PerProjectInfo info = JavaModelManager.getJavaModelManager().getPerProjectInfoCheckExistence(project); +// +// synchronized (info) { +// if (rawEntries != null) { +// info.classpath = rawEntries; +// } +// +// // clear cache of resolved classpath +// info.lastResolvedClasspath = null; +// info.resolvedPathToRawEntries = null; +// } +// } /** * Record a shared persistent property onto a project.