diff options
Diffstat (limited to 'org.eclipse.jdt.core/model/org/eclipse/jdt')
21 files changed, 716 insertions, 222 deletions
diff --git a/org.eclipse.jdt.core/model/org/eclipse/jdt/core/IOpenable.java b/org.eclipse.jdt.core/model/org/eclipse/jdt/core/IOpenable.java index 070206105..515a8ffda 100644 --- a/org.eclipse.jdt.core/model/org/eclipse/jdt/core/IOpenable.java +++ b/org.eclipse.jdt.core/model/org/eclipse/jdt/core/IOpenable.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2000, 2008 IBM Corporation and others. + * Copyright (c) 2000, 2016 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 @@ -10,6 +10,7 @@ *******************************************************************************/ package org.eclipse.jdt.core; +import org.eclipse.core.resources.IProject; import org.eclipse.core.runtime.IProgressMonitor; /** @@ -46,7 +47,7 @@ public interface IOpenable { * Closes this element and its buffer (if any). * Closing an element which is not open has no effect. * - * <p>Note: although {@link #close} is exposed in the API, clients are + * <p>Note: Although {@link #close} is exposed in the API, clients are * not expected to open and close elements - the Java model does this automatically * as elements are accessed. * @@ -114,6 +115,13 @@ boolean hasUnsavedChanges() throws JavaModelException; boolean isConsistent() throws JavaModelException; /** * Returns whether this openable is open. This is a handle-only method. + * + * <p>Note: This method doesn't tell whether an {@link IJavaProject}'s {@link IJavaProject#getProject() getProject()} is open. + * It is <b>not</b> equivalent to {@link IProject#isOpen()}!</p> + * + * <p>Note: Although {@link #isOpen} is exposed in the API, clients + * rarely have a need to rely on this internal state of the Java model.</p> + * @return true if this openable is open, false otherwise */ boolean isOpen(); @@ -142,7 +150,7 @@ void makeConsistent(IProgressMonitor progress) throws JavaModelException; * Opens this element and all parent elements that are not already open. * For compilation units, a buffer is opened on the contents of the underlying resource. * - * <p>Note: although {@link #open} is exposed in the API, clients are + * <p>Note: Although {@link #open} is exposed in the API, clients are * not expected to open and close elements - the Java model does this automatically * as elements are accessed. * diff --git a/org.eclipse.jdt.core/model/org/eclipse/jdt/core/IType.java b/org.eclipse.jdt.core/model/org/eclipse/jdt/core/IType.java index 5ee63a6ad..f2156427f 100644 --- a/org.eclipse.jdt.core/model/org/eclipse/jdt/core/IType.java +++ b/org.eclipse.jdt.core/model/org/eclipse/jdt/core/IType.java @@ -36,6 +36,11 @@ import org.eclipse.core.runtime.IProgressMonitor; * <code>IMethod</code>, <code>IInitializer</code> and <code>IType</code>. * The children are listed in the order in which they appear in the source or class file. * </p> + * <p> + * Caveat: The {@link #getChildren() children} of a {@link #isBinary() binary} type include + * nested types. However, the {@link #getParent() parent} of such a nested binary type is + * <em>not</em> the enclosing type, but that nested type's {@link IClassFile}! + * </p> * * @noimplement This interface is not intended to be implemented by clients. */ diff --git a/org.eclipse.jdt.core/model/org/eclipse/jdt/core/JavaCore.java b/org.eclipse.jdt.core/model/org/eclipse/jdt/core/JavaCore.java index 84bd065e9..5eef53310 100644 --- a/org.eclipse.jdt.core/model/org/eclipse/jdt/core/JavaCore.java +++ b/org.eclipse.jdt.core/model/org/eclipse/jdt/core/JavaCore.java @@ -157,6 +157,7 @@ import org.eclipse.jdt.internal.compiler.problem.ProblemReporter; import org.eclipse.jdt.internal.core.*; import org.eclipse.jdt.internal.core.builder.JavaBuilder; import org.eclipse.jdt.internal.core.builder.State; +import org.eclipse.jdt.internal.core.nd.indexer.Indexer; import org.eclipse.jdt.internal.core.util.MementoTokenizer; import org.eclipse.jdt.internal.core.util.Messages; import org.eclipse.jdt.internal.core.util.Util; @@ -5877,5 +5878,6 @@ public final class JavaCore extends Plugin { super.start(context); JavaModelManager.registerDebugOptionsListener(context); JavaModelManager.getJavaModelManager().startup(); + Indexer.getInstance().rescanAll(); } } diff --git a/org.eclipse.jdt.core/model/org/eclipse/jdt/core/compiler/CompilationParticipant.java b/org.eclipse.jdt.core/model/org/eclipse/jdt/core/compiler/CompilationParticipant.java index c40c1392e..34ae1a2cc 100644 --- a/org.eclipse.jdt.core/model/org/eclipse/jdt/core/compiler/CompilationParticipant.java +++ b/org.eclipse.jdt.core/model/org/eclipse/jdt/core/compiler/CompilationParticipant.java @@ -101,8 +101,11 @@ public void cleanStarting(IJavaProject project) { * given project should return <code>false</code> for that project. * </p><p> * Note: In {@link org.eclipse.jdt.core.WorkingCopyOwner#newWorkingCopy(String, org.eclipse.jdt.core.IClasspathEntry[], org.eclipse.core.runtime.IProgressMonitor) - * special cases}, the project may be closed and not exist. Participants typically return false for projects that are - * !{@link IJavaProject#isOpen()}. + * special cases}, the project may be closed and not exist. Participants typically return false when the + * underlying project is closed. I.e. when the following check returns false: + * <pre> + * javaProject.getProject().isOpen(); + * </pre> * </p> * @param project the project to participate in * @return whether this participant is active for a given project diff --git a/org.eclipse.jdt.core/model/org/eclipse/jdt/core/util/ExternalAnnotationUtil.java b/org.eclipse.jdt.core/model/org/eclipse/jdt/core/util/ExternalAnnotationUtil.java index 6d15fb255..dad83972d 100644 --- a/org.eclipse.jdt.core/model/org/eclipse/jdt/core/util/ExternalAnnotationUtil.java +++ b/org.eclipse.jdt.core/model/org/eclipse/jdt/core/util/ExternalAnnotationUtil.java @@ -294,7 +294,7 @@ public final class ExternalAnnotationUtil { newContent.append('\n'); continue; } - if (!Character.isJavaIdentifierStart(line.charAt(0))) { + if (!Character.isJavaIdentifierStart(line.charAt(0)) && line.charAt(0) != '<') { newContent.append(line).append('\n'); continue; } diff --git a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/BinaryType.java b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/BinaryType.java index f895be97f..978704c96 100644 --- a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/BinaryType.java +++ b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/BinaryType.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2000, 2014 IBM Corporation and others. + * Copyright (c) 2000, 2016 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 diff --git a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/ClassFile.java b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/ClassFile.java index 000c46382..9871f47cd 100644 --- a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/ClassFile.java +++ b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/ClassFile.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2000, 2015 IBM Corporation and others. + * Copyright (c) 2000, 2016 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 @@ -37,11 +37,15 @@ import org.eclipse.core.runtime.Path; import org.eclipse.core.runtime.Status; import org.eclipse.jdt.core.*; import org.eclipse.jdt.core.compiler.IProblem; -import org.eclipse.jdt.internal.compiler.classfmt.ClassFileReader; import org.eclipse.jdt.internal.compiler.classfmt.ClassFormatException; +import org.eclipse.jdt.internal.compiler.classfmt.ExternalAnnotationDecorator; +import org.eclipse.jdt.internal.compiler.classfmt.ExternalAnnotationProvider; import org.eclipse.jdt.internal.compiler.env.IBinaryType; -import org.eclipse.jdt.internal.compiler.env.IDependent; import org.eclipse.jdt.internal.compiler.util.SuffixConstants; +import org.eclipse.jdt.internal.core.nd.java.JavaNames; +import org.eclipse.jdt.internal.core.nd.java.model.BinaryTypeDescriptor; +import org.eclipse.jdt.internal.core.nd.java.model.BinaryTypeFactory; +import org.eclipse.jdt.internal.core.nd.util.CharArrayUtils; import org.eclipse.jdt.internal.core.util.MementoTokenizer; import org.eclipse.jdt.internal.core.util.Util; import org.eclipse.objectteams.otdt.core.OTModelManager; @@ -93,8 +97,9 @@ public ICompilationUnit becomeWorkingCopy(IProblemRequestor problemRequestor, Wo * @see Openable * @see Signature */ +@Override protected boolean buildStructure(OpenableElementInfo info, IProgressMonitor pm, Map newElements, IResource underlyingResource) throws JavaModelException { - IBinaryType typeInfo = getBinaryTypeInfo((IFile) underlyingResource); + IBinaryType typeInfo = getBinaryTypeInfo(); if (typeInfo == null) { // The structure of a class file is unknown if a class file format errors occurred //during the creation of the diet class file representative of this ClassFile. @@ -124,6 +129,7 @@ protected boolean buildStructure(OpenableElementInfo info, IProgressMonitor pm, * @see ICodeAssist#codeComplete(int, ICompletionRequestor) * @deprecated */ +@Deprecated public void codeComplete(int offset, ICompletionRequestor requestor) throws JavaModelException { codeComplete(offset, requestor, DefaultWorkingCopyOwner.PRIMARY); } @@ -131,6 +137,7 @@ public void codeComplete(int offset, ICompletionRequestor requestor) throws Java * @see ICodeAssist#codeComplete(int, ICompletionRequestor, WorkingCopyOwner) * @deprecated */ +@Deprecated public void codeComplete(int offset, ICompletionRequestor requestor, WorkingCopyOwner owner) throws JavaModelException { if (requestor == null) { throw new IllegalArgumentException("Completion requestor cannot be null"); //$NON-NLS-1$ @@ -197,9 +204,11 @@ public IJavaElement[] codeSelect(int offset, int length, WorkingCopyOwner owner) /** * Returns a new element info for this element. */ +@Override protected Object createElementInfo() { return new ClassFileInfo(); } +@Override public boolean equals(Object o) { if (!(o instanceof ClassFile)) return false; ClassFile other = (ClassFile) o; @@ -227,7 +236,7 @@ public boolean existsUsingJarTypeCache() { return false; } try { - info = getJarBinaryTypeInfo((PackageFragment) getParent(), true/*fully initialize so as to not keep a reference to the byte array*/); + info = getJarBinaryTypeInfo(); } catch (CoreException e) { // leave info null } catch (IOException e) { @@ -277,6 +286,7 @@ public IType findPrimaryType() { } return null; } +@Override public String getAttachedJavadoc(IProgressMonitor monitor) throws JavaModelException { return getType().getAttachedJavadoc(monitor); } @@ -292,40 +302,26 @@ public String getAttachedJavadoc(IProgressMonitor monitor) throws JavaModelExcep * @exception JavaModelException when the IFile resource or JAR is not available * or when this class file is not present in the JAR */ -public IBinaryType getBinaryTypeInfo(IFile file) throws JavaModelException { - return getBinaryTypeInfo(file, true/*fully initialize so as to not keep a reference to the byte array*/); -} -public IBinaryType getBinaryTypeInfo(IFile file, boolean fullyInitialize) throws JavaModelException { - JavaElement pkg = (JavaElement) getParent(); - if (pkg instanceof JarPackageFragment) { - try { - IBinaryType info = getJarBinaryTypeInfo((PackageFragment) pkg, fullyInitialize); - if (info == null) { - throw newNotPresentException(); - } - return info; - } catch (ClassFormatException cfe) { - //the structure remains unknown - if (JavaCore.getPlugin().isDebugging()) { - cfe.printStackTrace(System.err); - } - return null; - } catch (IOException ioe) { - throw new JavaModelException(ioe, IJavaModelStatusConstants.IO_EXCEPTION); - } catch (CoreException e) { - if (e instanceof JavaModelException) { - throw (JavaModelException)e; - } else { - throw new JavaModelException(e); - } +public IBinaryType getBinaryTypeInfo() throws JavaModelException { + try { + IBinaryType info = getJarBinaryTypeInfo(); + if (info == null) { + throw newNotPresentException(); } - } else { - byte[] contents = Util.getResourceContentsAsByteArray(file); - try { - return new ClassFileReader(contents, file.getFullPath().toString().toCharArray(), fullyInitialize); - } catch (ClassFormatException cfe) { - //the structure remains unknown - return null; + return info; + } catch (ClassFormatException cfe) { + //the structure remains unknown + if (JavaCore.getPlugin().isDebugging()) { + cfe.printStackTrace(System.err); + } + return null; + } catch (IOException ioe) { + throw new JavaModelException(ioe, IJavaModelStatusConstants.IO_EXCEPTION); + } catch (CoreException e) { + if (e instanceof JavaModelException) { + throw (JavaModelException)e; + } else { + throw new JavaModelException(e); } } } @@ -359,44 +355,58 @@ public byte[] getBytes() throws JavaModelException { return Util.getResourceContentsAsByteArray(file); } } -private IBinaryType getJarBinaryTypeInfo(PackageFragment pkg, boolean fullyInitialize) throws CoreException, IOException, ClassFormatException { - JarPackageFragmentRoot root = (JarPackageFragmentRoot) pkg.getParent(); - ZipFile zip = null; - ZipFile annotationZip = null; - try { - zip = root.getJar(); - String entryName = Util.concatWith(pkg.names, getElementName(), '/'); - ZipEntry ze = zip.getEntry(entryName); - if (ze != null) { - byte contents[] = org.eclipse.jdt.internal.compiler.util.Util.getZipEntryByteContent(ze, zip); - String fileName = root.getHandleIdentifier() + IDependent.JAR_FILE_ENTRY_SEPARATOR + entryName; - ClassFileReader reader = new ClassFileReader(contents, fileName.toCharArray(), fullyInitialize); - if (root.getKind() == IPackageFragmentRoot.K_BINARY) { - JavaProject javaProject = (JavaProject) getAncestor(IJavaElement.JAVA_PROJECT); - IClasspathEntry entry = javaProject.getClasspathEntryFor(getPath()); - if (entry != null) { - IProject project = javaProject.getProject(); - IPath externalAnnotationPath = ClasspathEntry.getExternalAnnotationPath(entry, project, false); // unresolved for use in ExternalAnnotationTracker - if (externalAnnotationPath != null) { - setupExternalAnnotationProvider(project, externalAnnotationPath, annotationZip, reader, - entryName.substring(0, entryName.length() - SuffixConstants.SUFFIX_CLASS.length)); - } else if (entry.getEntryKind() == IClasspathEntry.CPE_SOURCE) { - reader.markAsFromSource(); - } + +public String getName() { + return this.name; +} + +private IBinaryType getJarBinaryTypeInfo() throws CoreException, IOException, ClassFormatException { + BinaryTypeDescriptor descriptor = BinaryTypeFactory.createDescriptor(this); + + if (descriptor == null) { + return null; + } + + IBinaryType result = BinaryTypeFactory.readType(descriptor, null); + + if (result == null) { + return null; + } + + // TODO(sxenos): setup the external annotation provider if the IBinaryType came from the index + // TODO(sxenos): the old code always passed null as the third argument to setupExternalAnnotationProvider, + // but this looks like a bug. I've preserved it for now but we need to figure out what was supposed to go + // there. + PackageFragment pkg = (PackageFragment) getParent(); + IJavaElement grandparent = pkg.getParent(); + if (grandparent instanceof JarPackageFragmentRoot) { + JarPackageFragmentRoot root = (JarPackageFragmentRoot) grandparent; + + if (root.getKind() == IPackageFragmentRoot.K_BINARY) { + JavaProject javaProject = (JavaProject) getAncestor(IJavaElement.JAVA_PROJECT); + IClasspathEntry entry = javaProject.getClasspathEntryFor(getPath()); + if (entry != null) { + String entryName = new String(CharArrayUtils.concat( + JavaNames.fieldDescriptorToBinaryName(descriptor.fieldDescriptor), SuffixConstants.SUFFIX_CLASS)); + IProject project = javaProject.getProject(); + IPath externalAnnotationPath = ClasspathEntry.getExternalAnnotationPath(entry, project, false); // unresolved for use in ExternalAnnotationTracker + if (externalAnnotationPath != null) { + result = setupExternalAnnotationProvider(project, externalAnnotationPath, null, result, + entryName.substring(0, entryName.length() - SuffixConstants.SUFFIX_CLASS.length)); + } else if (entry.getEntryKind() == IClasspathEntry.CPE_SOURCE) { + result = new ExternalAnnotationDecorator(result, true); } } - return reader; } - } finally { - JavaModelManager.getJavaModelManager().closeZipFile(zip); - JavaModelManager.getJavaModelManager().closeZipFile(annotationZip); } - return null; + + return result; } -private void setupExternalAnnotationProvider(IProject project, final IPath externalAnnotationPath, - ZipFile annotationZip, ClassFileReader reader, final String typeName) +private IBinaryType setupExternalAnnotationProvider(IProject project, final IPath externalAnnotationPath, + ZipFile annotationZip, IBinaryType reader, final String typeName) { + IBinaryType result = reader; // try resolve path within the workspace: IWorkspaceRoot root = project.getWorkspace().getRoot(); IResource resource; @@ -410,26 +420,32 @@ private void setupExternalAnnotationProvider(IProject project, final IPath exter String resolvedPath; if (resource.exists()) { if (resource.isVirtual()) { - Util.log(new Status(IStatus.ERROR, JavaCore.PLUGIN_ID, + Util.log(new Status(IStatus.ERROR, JavaCore.PLUGIN_ID, "Virtual resource "+externalAnnotationPath+" cannot be used as annotationpath for project "+project.getName())); //$NON-NLS-1$ //$NON-NLS-2$ - return; + return reader; } resolvedPath = resource.getLocation().toString(); // workspace lookup succeeded -> resolve it } else { resolvedPath = externalAnnotationPath.toString(); // not in workspace, use as is } try { - annotationZip = reader.setExternalAnnotationProvider(resolvedPath, typeName, annotationZip, new ClassFileReader.ZipFileProducer() { - @Override public ZipFile produce() throws IOException { - try { - return JavaModelManager.getJavaModelManager().getZipFile(externalAnnotationPath); // use (absolute, but) unresolved path here - } catch (CoreException e) { - throw new IOException("Failed to read annotation file for "+typeName+" from "+externalAnnotationPath.toString(), e); //$NON-NLS-1$ //$NON-NLS-2$ - } - }}); + if (annotationZip == null) { + annotationZip = ExternalAnnotationDecorator.getAnnotationZipFile(resolvedPath, new ExternalAnnotationDecorator.ZipFileProducer() { + @Override public ZipFile produce() throws IOException { + try { + return JavaModelManager.getJavaModelManager().getZipFile(externalAnnotationPath); // use (absolute, but) unresolved path here + } catch (CoreException e) { + throw new IOException("Failed to read annotation file for "+typeName+" from "+externalAnnotationPath.toString(), e); //$NON-NLS-1$ //$NON-NLS-2$ + } + }}); + } + + ExternalAnnotationProvider annotationProvider = ExternalAnnotationDecorator + .externalAnnotationProvider(resolvedPath, typeName, annotationZip); + result = new ExternalAnnotationDecorator(reader, annotationProvider); } catch (IOException e) { Util.log(e); - return; + return result; } if (annotationZip == null) { // Additional change listening for individual types only when annotations are in individual files. @@ -437,6 +453,7 @@ private void setupExternalAnnotationProvider(IProject project, final IPath exter this.externalAnnotationBase = externalAnnotationPath; // remember so we can unregister later ExternalAnnotationTracker.registerClassFile(externalAnnotationPath, new Path(typeName), this); } + return result; } void closeAndRemoveFromJarTypeCache() throws JavaModelException { super.close(); @@ -451,6 +468,7 @@ public void close() throws JavaModelException { } super.close(); } +@Override public IBuffer getBuffer() throws JavaModelException { IStatus status = validateClassFile(); if (status.isOK()) { @@ -468,6 +486,7 @@ public IBuffer getBuffer() throws JavaModelException { /** * @see IMember */ +@Override public IClassFile getClassFile() { return this; } @@ -483,6 +502,7 @@ public ITypeRoot getTypeRoot() { * * @see IJavaElement */ +@Override public IResource getCorrespondingResource() throws JavaModelException { IPackageFragmentRoot root= (IPackageFragmentRoot)getParent().getParent(); if (root.isArchive()) { @@ -554,6 +574,7 @@ public IJavaElement getElementAtConsideringSibling(int position) throws JavaMode return null; } } +@Override public String getElementName() { return this.name + SuffixConstants.SUFFIX_STRING_class; } @@ -566,6 +587,7 @@ public int getElementType() { /* * @see JavaElement */ +@Override public IJavaElement getHandleFromMemento(String token, MementoTokenizer memento, WorkingCopyOwner owner) { switch (token.charAt(0)) { case JEM_TYPE: @@ -579,6 +601,7 @@ public IJavaElement getHandleFromMemento(String token, MementoTokenizer memento, /** * @see JavaElement#getHandleMemento() */ +@Override protected char getHandleMementoDelimiter() { return JavaElement.JEM_CLASSFILE; } @@ -596,6 +619,7 @@ public IPath getPath() { /* * @see IJavaElement */ +@Override public IResource resource(PackageFragmentRoot root) { return ((IContainer) ((Openable) this.parent).resource(root)).getFile(new Path(getElementName())); } @@ -707,15 +731,18 @@ public ICompilationUnit getWorkingCopy(WorkingCopyOwner owner, IProgressMonitor * @see IClassFile * @deprecated */ +@Deprecated public IJavaElement getWorkingCopy(IProgressMonitor monitor, org.eclipse.jdt.core.IBufferFactory factory) throws JavaModelException { return getWorkingCopy(BufferFactoryWrapper.create(factory), monitor); } /** * @see Openable */ +@Override protected boolean hasBuffer() { return true; } +@Override public int hashCode() { return Util.combineHashCodes(this.name.hashCode(), this.parent.hashCode()); } @@ -734,6 +761,7 @@ public boolean isInterface() throws JavaModelException { /** * Returns true - class files are always read only. */ +@Override public boolean isReadOnly() { return true; } @@ -756,6 +784,7 @@ private IStatus validateClassFile() { * * @see Openable */ +@Override protected IBuffer openBuffer(IProgressMonitor pm, Object info) throws JavaModelException { // Check the cache for the top-level type first IType outerMostEnclosingType = getOuterMostEnclosingType(); @@ -896,6 +925,7 @@ public static char[] translatedName(char[] name) { * @see ICodeAssist#codeComplete(int, ICodeCompletionRequestor) * @deprecated - should use codeComplete(int, ICompletionRequestor) instead */ +@Deprecated public void codeComplete(int offset, final org.eclipse.jdt.core.ICodeCompletionRequestor requestor) throws JavaModelException { if (requestor == null){ @@ -951,6 +981,7 @@ public void codeComplete(int offset, final org.eclipse.jdt.core.ICodeCompletionR }); } +@Override protected IStatus validateExistence(IResource underlyingResource) { // check whether the class file can be opened IStatus status = validateClassFile(); diff --git a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/DeltaProcessingState.java b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/DeltaProcessingState.java index 1854aeea6..a8e3b27d0 100644 --- a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/DeltaProcessingState.java +++ b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/DeltaProcessingState.java @@ -24,13 +24,16 @@ import org.eclipse.core.resources.*; import org.eclipse.core.runtime.*; import org.eclipse.jdt.core.*; import org.eclipse.jdt.internal.core.JavaModelManager.PerProjectInfo; +import org.eclipse.jdt.internal.core.nd.indexer.Indexer; +import org.eclipse.jdt.internal.core.nd.indexer.IndexerEvent; +import org.eclipse.jdt.internal.core.nd.java.JavaIndex; import org.eclipse.jdt.internal.core.util.Util; /** * Keep the global states used during Java element delta processing. */ @SuppressWarnings({ "rawtypes", "unchecked" }) -public class DeltaProcessingState implements IResourceChangeListener { +public class DeltaProcessingState implements IResourceChangeListener, Indexer.Listener { /* * Collection of listeners for Java element deltas @@ -643,4 +646,15 @@ public class DeltaProcessingState implements IResourceChangeListener { } } + @Override + public void consume(IndexerEvent event) { + if (JavaIndex.isEnabled()) { + DeltaProcessor processor = getDeltaProcessor(); + JavaElementDelta delta = (JavaElementDelta) event.getDelta(); + delta.ignoreFromTests = true; + processor.notifyAndFire(delta); + this.deltaProcessors.set(null); + } + } + } diff --git a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/DeltaProcessor.java b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/DeltaProcessor.java index 18f5b752a..0e2c59896 100644 --- a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/DeltaProcessor.java +++ b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/DeltaProcessor.java @@ -502,8 +502,24 @@ public class DeltaProcessor { break; case IResource.FOLDER: - if (delta.getKind() == IResourceDelta.CHANGED) { // look for .jar file change to update classpath - children = delta.getAffectedChildren(); + switch (delta.getKind()) { + case IResourceDelta.ADDED: + case IResourceDelta.REMOVED: + // Close the containing package fragment root to reset its cached children. + // See http://bugs.eclipse.org/500714 + IPackageFragmentRoot root = findContainingPackageFragmentRoot(resource); + if (root != null && root.isOpen()) { + try { + root.close(); + } catch (JavaModelException e) { + Util.log(e); + } + } + break; + + case IResourceDelta.CHANGED: // look for .jar file change to update classpath + children = delta.getAffectedChildren(); + break; } break; case IResource.FILE : @@ -548,6 +564,27 @@ public class DeltaProcessor { } } + private IPackageFragmentRoot findContainingPackageFragmentRoot(IResource resource) { + IProject project = resource.getProject(); + if (JavaProject.hasJavaNature(project)) { + IJavaProject javaProject = JavaCore.create(project); + try { + IPath path = resource.getProjectRelativePath(); + IPackageFragmentRoot[] roots = javaProject.getPackageFragmentRoots(); + for (IPackageFragmentRoot root : roots) { + IResource rootResource = root.getUnderlyingResource(); + if (rootResource != null && !resource.equals(rootResource) && + rootResource.getProjectRelativePath().isPrefixOf(path)) { + return root; + } + } + } catch (JavaModelException e) { + Util.log(e); + } + } + return null; + } + private void checkExternalFolderChange(IProject project, JavaProject javaProject) { ClasspathChange change = this.state.getClasspathChange(project); this.state.addExternalFolderChange(javaProject, change == null ? null : change.oldResolvedClasspath); @@ -1020,6 +1057,9 @@ public class DeltaProcessor { if (VERBOSE){ System.out.println("- External JAR CHANGED, affecting root: "+root.getElementName()); //$NON-NLS-1$ } + // TODO(sxenos): this is causing each change event for an external jar file to be fired twice. + // We need to preserve the clearing of cached information in the jar but defer the actual firing of + // the event until after the indexer has processed the jar. contentChanged(root); deltaContainsModifiedJar = true; hasDelta = true; @@ -1908,7 +1948,7 @@ public class DeltaProcessor { * caches and their dependents */ public void resetProjectCaches() { - if (this.projectCachesToReset.size() == 0) + if (this.projectCachesToReset.isEmpty()) return; JavaModelManager.getJavaModelManager().resetJarTypeCache(); @@ -2064,14 +2104,7 @@ public class DeltaProcessor { this.sourceElementParserCache = null; // don't hold onto parser longer than necessary startDeltas(); } - IElementChangedListener[] listeners; - int listenerCount; - synchronized (this.state) { - listeners = this.state.elementChangedListeners; - listenerCount = this.state.elementChangedListenerCount; - } - notifyTypeHierarchies(listeners, listenerCount); - fire(null, ElementChangedEvent.POST_CHANGE); + notifyAndFire(null); } finally { // workaround for bug 15168 circular errors not reported this.state.resetOldJavaProjectNames(); @@ -2180,6 +2213,17 @@ public class DeltaProcessor { } } + public void notifyAndFire(IJavaElementDelta delta) { + IElementChangedListener[] listeners; + int listenerCount; + synchronized (this.state) { + listeners = this.state.elementChangedListeners; + listenerCount = this.state.elementChangedListenerCount; + } + notifyTypeHierarchies(listeners, listenerCount); + fire(delta, ElementChangedEvent.POST_CHANGE); + } + /* * Returns the root info for the given path. Look in the old roots table if kind is REMOVED. */ diff --git a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/JavaElementDelta.java b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/JavaElementDelta.java index a7beb47bc..86abfafba 100644 --- a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/JavaElementDelta.java +++ b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/JavaElementDelta.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2000, 2014 IBM Corporation and others. + * Copyright (c) 2000, 2016 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 @@ -82,6 +82,8 @@ public class JavaElementDelta extends SimpleDelta implements IJavaElementDelta { */ Map<Key, Integer> childIndex; + public boolean ignoreFromTests = false; + /** * The delta key */ diff --git a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/JavaModelCache.java b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/JavaModelCache.java index c2ad7c360..5c6ca12da 100644 --- a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/JavaModelCache.java +++ b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/JavaModelCache.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2000, 2015 IBM Corporation and others. + * Copyright (c) 2000, 2016 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 @@ -28,6 +28,7 @@ import org.eclipse.objectteams.otdt.internal.core.OTType; @SuppressWarnings({"rawtypes", "unchecked"}) public class JavaModelCache { public static boolean VERBOSE = false; + public static boolean DEBUG_CACHE_INSERTIONS = false; public static final int DEFAULT_PROJECT_SIZE = 5; // average 25552 bytes per project. public static final int DEFAULT_ROOT_SIZE = 50; // average 2590 bytes per root -> maximum size : 25900*BASE_VALUE bytes @@ -224,6 +225,9 @@ protected Object peekAtInfo(IJavaElement element) { * Remember the info for the element. */ protected void putInfo(IJavaElement element, Object info) { + if (DEBUG_CACHE_INSERTIONS) { + System.out.println(Thread.currentThread() + " cache putInfo (" + getElementType(element) + " " + element.toString() + ", " + info + ")"); //$NON-NLS-1$//$NON-NLS-2$//$NON-NLS-3$//$NON-NLS-4$ + } switch (element.getElementType()) { case IJavaElement.JAVA_MODEL: this.modelInfo = info; @@ -248,10 +252,39 @@ protected void putInfo(IJavaElement element, Object info) { this.childrenCache.put(element, info); } } + +public static String getElementType(IJavaElement element) { + String elementType; + switch (element.getElementType()) { + case IJavaElement.JAVA_PROJECT: + elementType = "project"; //$NON-NLS-1$ + break; + case IJavaElement.PACKAGE_FRAGMENT_ROOT: + elementType = "root"; //$NON-NLS-1$ + break; + case IJavaElement.PACKAGE_FRAGMENT: + elementType = "package"; //$NON-NLS-1$ + break; + case IJavaElement.CLASS_FILE: + elementType = "class file"; //$NON-NLS-1$ + break; + case IJavaElement.COMPILATION_UNIT: + elementType = "compilation unit"; //$NON-NLS-1$ + break; + default: + elementType = "element"; //$NON-NLS-1$ + } + return elementType; +} + /** * Removes the info of the element from the cache. */ protected void removeInfo(JavaElement element) { + if (DEBUG_CACHE_INSERTIONS) { + String elementToString = element.toString(); + System.out.println(Thread.currentThread() + " cache removeInfo " + getElementType(element) + " " + elementToString); //$NON-NLS-1$//$NON-NLS-2$ + } switch (element.getElementType()) { case IJavaElement.JAVA_MODEL: this.modelInfo = null; diff --git a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/JavaModelManager.java b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/JavaModelManager.java index cdb4157ef..8b4feaa1a 100644 --- a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/JavaModelManager.java +++ b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/JavaModelManager.java @@ -130,6 +130,8 @@ import org.eclipse.jdt.internal.core.builder.JavaBuilder; import org.eclipse.jdt.internal.core.dom.SourceRangeVerifier; import org.eclipse.jdt.internal.core.dom.rewrite.RewriteEventStore; import org.eclipse.jdt.internal.core.hierarchy.TypeHierarchy; +import org.eclipse.jdt.internal.core.nd.Nd; +import org.eclipse.jdt.internal.core.nd.indexer.Indexer; import org.eclipse.jdt.internal.core.search.AbstractSearchScope; import org.eclipse.jdt.internal.core.search.BasicSearchEngine; import org.eclipse.jdt.internal.core.search.IRestrictedAccessTypeRequestor; @@ -176,6 +178,14 @@ public class JavaModelManager implements ISaveParticipant, IContentTypeChangeLis private static final String EXTERNAL_FILES_CACHE = "externalFilesCache"; //$NON-NLS-1$ private static final String ASSUMED_EXTERNAL_FILES_CACHE = "assumedExternalFilesCache"; //$NON-NLS-1$ + public static enum ArchiveValidity { + BAD_FORMAT, UNABLE_TO_READ, VALID; + + public boolean isValid() { + return this == VALID; + } + } + /** * Define a zip cache object. */ @@ -333,8 +343,11 @@ public class JavaModelManager implements ISaveParticipant, IContentTypeChangeLis private static final String INDEX_MANAGER_DEBUG = JavaCore.PLUGIN_ID + "/debug/indexmanager" ; //$NON-NLS-1$ private static final String INDEX_MANAGER_ADVANCED_DEBUG = JavaCore.PLUGIN_ID + "/debug/indexmanager/advanced" ; //$NON-NLS-1$ private static final String COMPILER_DEBUG = JavaCore.PLUGIN_ID + "/debug/compiler" ; //$NON-NLS-1$ + private static final String JAVAMODEL_CLASSPATH = JavaCore.PLUGIN_ID + "/debug/javamodel/classpath" ; //$NON-NLS-1$ private static final String JAVAMODEL_DEBUG = JavaCore.PLUGIN_ID + "/debug/javamodel" ; //$NON-NLS-1$ + private static final String JAVAMODEL_INVALID_ARCHIVES = JavaCore.PLUGIN_ID + "/debug/javamodel/invalid_archives" ; //$NON-NLS-1$ private static final String JAVAMODELCACHE_DEBUG = JavaCore.PLUGIN_ID + "/debug/javamodel/cache" ; //$NON-NLS-1$ + private static final String JAVAMODELCACHE_INSERTIONS_DEBUG = JavaCore.PLUGIN_ID + "/debug/javamodel/insertions" ; //$NON-NLS-1$ private static final String CP_RESOLVE_DEBUG = JavaCore.PLUGIN_ID + "/debug/cpresolution" ; //$NON-NLS-1$ private static final String CP_RESOLVE_ADVANCED_DEBUG = JavaCore.PLUGIN_ID + "/debug/cpresolution/advanced" ; //$NON-NLS-1$ private static final String CP_RESOLVE_FAILURE_DEBUG = JavaCore.PLUGIN_ID + "/debug/cpresolution/failure" ; //$NON-NLS-1$ @@ -354,6 +367,12 @@ public class JavaModelManager implements ISaveParticipant, IContentTypeChangeLis private static final String SEARCH_DEBUG = JavaCore.PLUGIN_ID + "/debug/search" ; //$NON-NLS-1$ private static final String SOURCE_MAPPER_DEBUG_VERBOSE = JavaCore.PLUGIN_ID + "/debug/sourcemapper" ; //$NON-NLS-1$ private static final String FORMATTER_DEBUG = JavaCore.PLUGIN_ID + "/debug/formatter" ; //$NON-NLS-1$ + private static final String INDEX_INDEXER_DEBUG = JavaCore.PLUGIN_ID + "/debug/index/indexer" ; //$NON-NLS-1$ + private static final String INDEX_INDEXER_INSERTIONS = JavaCore.PLUGIN_ID + "/debug/index/insertions" ; //$NON-NLS-1$ + private static final String INDEX_INDEXER_SELFTEST = JavaCore.PLUGIN_ID + "/debug/index/selftest" ; //$NON-NLS-1$ + private static final String INDEX_LOCKS_DEBUG = JavaCore.PLUGIN_ID + "/debug/index/locks" ; //$NON-NLS-1$ + private static final String INDEX_INDEXER_SPACE = JavaCore.PLUGIN_ID + "/debug/index/space" ; //$NON-NLS-1$ + private static final String INDEX_INDEXER_TIMING = JavaCore.PLUGIN_ID + "/debug/index/timing" ; //$NON-NLS-1$ public static final String COMPLETION_PERF = JavaCore.PLUGIN_ID + "/perf/completion" ; //$NON-NLS-1$ public static final String SELECTION_PERF = JavaCore.PLUGIN_ID + "/perf/selection" ; //$NON-NLS-1$ @@ -1290,6 +1309,16 @@ public class JavaModelManager implements ISaveParticipant, IContentTypeChangeLis } private ClasspathChange setClasspath(IClasspathEntry[] newRawClasspath, IClasspathEntry[] referencedEntries, IPath newOutputLocation, IJavaModelStatus newRawClasspathStatus, IClasspathEntry[] newResolvedClasspath, Map newRootPathToRawEntries, Map newRootPathToResolvedEntries, IJavaModelStatus newUnresolvedEntryStatus, boolean addClasspathChange) { + if (DEBUG_CLASSPATH) { + System.out.println("Setting resolved classpath for " + this.project.getFullPath()); //$NON-NLS-1$ + if (newResolvedClasspath == null) { + System.out.println("New classpath = null"); //$NON-NLS-1$ + } else { + for (IClasspathEntry next : newResolvedClasspath) { + System.out.println(" " + next); //$NON-NLS-1$ + } + } + } ClasspathChange classpathChange = addClasspathChange ? addClasspathChange() : null; if (referencedEntries != null) this.referencedEntries = referencedEntries; @@ -1510,6 +1539,8 @@ public class JavaModelManager implements ISaveParticipant, IContentTypeChangeLis } public static boolean VERBOSE = false; + public static boolean DEBUG_CLASSPATH = false; + public static boolean DEBUG_INVALID_ARCHIVES = false; public static boolean CP_RESOLVE_VERBOSE = false; public static boolean CP_RESOLVE_VERBOSE_ADVANCED = false; public static boolean CP_RESOLVE_VERBOSE_FAILURE = false; @@ -1531,10 +1562,29 @@ public class JavaModelManager implements ISaveParticipant, IContentTypeChangeLis // The amount of time from when an invalid archive is first sensed until that state is considered stale. private static long INVALID_ARCHIVE_TTL_MILLISECONDS = 2 * 60 * 1000; + private static class InvalidArchiveInfo { + /** + * Time at which this entry will be removed from the invalid archive list. + */ + final long evictionTimestamp; + + /** + * Reason the entry was added to the invalid archive list. + */ + final ArchiveValidity reason; + + InvalidArchiveInfo(long evictionTimestamp, ArchiveValidity reason) { + this.evictionTimestamp = evictionTimestamp; + this.reason = reason; + } + } + /* * A map of IPaths for jars that are known to be invalid (such as not being in a valid/known format), to an eviction timestamp. + * Synchronize on invalidArchivesMutex before accessing. */ - private Map<IPath, Long> invalidArchives; + private final Map<IPath, InvalidArchiveInfo> invalidArchives = new HashMap<IPath, InvalidArchiveInfo>(); + private final Object invalidArchivesMutex = new Object(); /* * A set of IPaths for files that are known to be external to the workspace. @@ -1698,12 +1748,13 @@ public class JavaModelManager implements ISaveParticipant, IContentTypeChangeLis this.nonChainingJars.add(path); } - public void addInvalidArchive(IPath path) { - // unlikely to be null - if (this.invalidArchives == null) { - this.invalidArchives = Collections.synchronizedMap(new HashMap()); + public void addInvalidArchive(IPath path, ArchiveValidity reason) { + if (DEBUG_INVALID_ARCHIVES) { + System.out.println("Invalid JAR cache: adding " + path + ", reason: " + reason); //$NON-NLS-1$//$NON-NLS-2$ + } + synchronized (this.invalidArchivesMutex) { + this.invalidArchives.put(path, new InvalidArchiveInfo(System.currentTimeMillis() + INVALID_ARCHIVE_TTL_MILLISECONDS, reason)); } - this.invalidArchives.put(path, System.currentTimeMillis() + INVALID_ARCHIVE_TTL_MILLISECONDS); } /** @@ -1773,8 +1824,11 @@ public class JavaModelManager implements ISaveParticipant, IContentTypeChangeLis TypeHierarchy.DEBUG = debug && options.getBooleanOption(HIERARCHY_DEBUG, false); JobManager.VERBOSE = debug && options.getBooleanOption(INDEX_MANAGER_DEBUG, false); IndexManager.DEBUG = debug && options.getBooleanOption(INDEX_MANAGER_ADVANCED_DEBUG, false); + JavaModelManager.DEBUG_CLASSPATH = debug && options.getBooleanOption(JAVAMODEL_CLASSPATH, false); + JavaModelManager.DEBUG_INVALID_ARCHIVES = debug && options.getBooleanOption(JAVAMODEL_INVALID_ARCHIVES, false); JavaModelManager.VERBOSE = debug && options.getBooleanOption(JAVAMODEL_DEBUG, false); JavaModelCache.VERBOSE = debug && options.getBooleanOption(JAVAMODELCACHE_DEBUG, false); + JavaModelCache.DEBUG_CACHE_INSERTIONS = debug && options.getBooleanOption(JAVAMODELCACHE_INSERTIONS_DEBUG, false); JavaModelOperation.POST_ACTION_VERBOSE = debug && options.getBooleanOption(POST_ACTION_DEBUG, false); NameLookup.VERBOSE = debug && options.getBooleanOption(RESOLUTION_DEBUG, false); BasicSearchEngine.VERBOSE = debug && options.getBooleanOption(SEARCH_DEBUG, false); @@ -1782,6 +1836,12 @@ public class JavaModelManager implements ISaveParticipant, IContentTypeChangeLis JavaModelManager.ZIP_ACCESS_VERBOSE = debug && options.getBooleanOption(ZIP_ACCESS_DEBUG, false); SourceMapper.VERBOSE = debug && options.getBooleanOption(SOURCE_MAPPER_DEBUG_VERBOSE, false); DefaultCodeFormatter.DEBUG = debug && options.getBooleanOption(FORMATTER_DEBUG, false); + Indexer.DEBUG = debug && options.getBooleanOption(INDEX_INDEXER_DEBUG, false); + Indexer.DEBUG_INSERTIONS = debug && options.getBooleanOption(INDEX_INDEXER_INSERTIONS, false); + Indexer.DEBUG_ALLOCATIONS = debug && options.getBooleanOption(INDEX_INDEXER_SPACE, false); + Indexer.DEBUG_TIMING = debug && options.getBooleanOption(INDEX_INDEXER_TIMING, false); + Indexer.DEBUG_SELFTEST = debug && options.getBooleanOption(INDEX_INDEXER_SELFTEST, false); + Nd.sDEBUG_LOCKS = debug && options.getBooleanOption(INDEX_LOCKS_DEBUG, false); // configure performance options if(PerformanceStats.ENABLED) { @@ -2702,9 +2762,7 @@ public class JavaModelManager implements ISaveParticipant, IContentTypeChangeLis } public void verifyArchiveContent(IPath path) throws CoreException { - if (isInvalidArchive(path)) { - throw new CoreException(new Status(IStatus.ERROR, JavaCore.PLUGIN_ID, -1, Messages.status_IOException, new ZipException())); - } + throwExceptionIfArchiveInvalid(path); ZipFile file = getZipFile(path); closeZipFile(file); } @@ -2724,16 +2782,47 @@ public class JavaModelManager implements ISaveParticipant, IContentTypeChangeLis return getZipFile(path, true); } + /** + * For use in the JDT unit tests only. Used for testing error handling. Causes an + * {@link IOException} to be thrown in {@link #getZipFile} whenever it attempts to + * read a zip file. + * + * @noreference This field is not intended to be referenced by clients. + */ + public static boolean throwIoExceptionsInGetZipFile = false; + private ZipFile getZipFile(IPath path, boolean checkInvalidArchiveCache) throws CoreException { - if (checkInvalidArchiveCache && isInvalidArchive(path)) - throw new CoreException(new Status(IStatus.ERROR, JavaCore.PLUGIN_ID, -1, Messages.status_IOException, new ZipException())); - + if (checkInvalidArchiveCache) { + throwExceptionIfArchiveInvalid(path); + } ZipCache zipCache; ZipFile zipFile; if ((zipCache = (ZipCache)this.zipFiles.get()) != null && (zipFile = zipCache.getCache(path)) != null) { return zipFile; } + File localFile = getLocalFile(path); + + try { + if (ZIP_ACCESS_VERBOSE) { + System.out.println("(" + Thread.currentThread() + ") [JavaModelManager.getZipFile(IPath)] Creating ZipFile on " + localFile ); //$NON-NLS-1$ //$NON-NLS-2$ + } + if (throwIoExceptionsInGetZipFile) { + throw new IOException(); + } + zipFile = new ZipFile(localFile); + if (zipCache != null) { + zipCache.setCache(path, zipFile); + } + return zipFile; + } catch (IOException e) { + ArchiveValidity reason = (e instanceof ZipException) ? ArchiveValidity.BAD_FORMAT : ArchiveValidity.UNABLE_TO_READ; + addInvalidArchive(path, reason); + throw new CoreException(new Status(IStatus.ERROR, JavaCore.PLUGIN_ID, -1, Messages.status_IOException, e)); + } + } + + public static File getLocalFile(IPath path) throws CoreException { File localFile = null; IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot(); IResource file = root.findMember(path); @@ -2750,19 +2839,19 @@ public class JavaModelManager implements ISaveParticipant, IContentTypeChangeLis // external resource -> it is ok to use toFile() localFile= path.toFile(); } + return localFile; + } - try { - if (ZIP_ACCESS_VERBOSE) { - System.out.println("(" + Thread.currentThread() + ") [JavaModelManager.getZipFile(IPath)] Creating ZipFile on " + localFile ); //$NON-NLS-1$ //$NON-NLS-2$ - } - zipFile = new ZipFile(localFile); - if (zipCache != null) { - zipCache.setCache(path, zipFile); + private void throwExceptionIfArchiveInvalid(IPath path) throws CoreException { + ArchiveValidity validity = getArchiveValidity(path); + if (!validity.isValid()) { + IOException reason; + if (validity == ArchiveValidity.BAD_FORMAT) { + reason = new ZipException(); + } else { + reason = new IOException(); } - return zipFile; - } catch (IOException e) { - addInvalidArchive(path); - throw new CoreException(new Status(IStatus.ERROR, JavaCore.PLUGIN_ID, -1, Messages.status_IOException, e)); + throw new CoreException(new Status(IStatus.ERROR, JavaCore.PLUGIN_ID, -1, Messages.status_IOException, reason)); } } @@ -3186,31 +3275,36 @@ public class JavaModelManager implements ISaveParticipant, IContentTypeChangeLis return this.nonChainingJars != null && this.nonChainingJars.contains(path); } - public boolean isInvalidArchive(IPath path) { - if (this.invalidArchives == null) - return false; - Long evictionTime = this.invalidArchives.get(path); - if (evictionTime == null) - return false; + public ArchiveValidity getArchiveValidity(IPath path) { + InvalidArchiveInfo invalidArchiveInfo; + synchronized (this.invalidArchivesMutex) { + invalidArchiveInfo = this.invalidArchives.get(path); + } + if (invalidArchiveInfo == null) + return ArchiveValidity.VALID; long now = System.currentTimeMillis(); // If the TTL for this cache entry has expired, directly check whether the archive is still invalid. // If it transitioned to being valid, remove it from the cache and force an update to project caches. - if (now > evictionTime) { + if (now > invalidArchiveInfo.evictionTimestamp) { try { getZipFile(path, false); removeFromInvalidArchiveCache(path); - return false; } catch (CoreException e) { // Archive is still invalid, fall through to reporting it is invalid. } + // Retry the test from the start, now that we have an up-to-date result + return getArchiveValidity(path); } - return true; + return invalidArchiveInfo.reason; } public void removeFromInvalidArchiveCache(IPath path) { - if (this.invalidArchives != null) { + synchronized(this.invalidArchivesMutex) { if (this.invalidArchives.remove(path) != null) { + if (DEBUG_INVALID_ARCHIVES) { + System.out.println("Invalid JAR cache: removed " + path); //$NON-NLS-1$ + } try { // Bug 455042: Force an update of the JavaProjectElementInfo project caches. for (IJavaProject project : getJavaModel().getJavaProjects()) { @@ -3985,26 +4079,7 @@ public class JavaModelManager implements ISaveParticipant, IContentTypeChangeLis boolean wasVerbose = false; try { if (JavaModelCache.VERBOSE) { - String elementType; - switch (element.getElementType()) { - case IJavaElement.JAVA_PROJECT: - elementType = "project"; //$NON-NLS-1$ - break; - case IJavaElement.PACKAGE_FRAGMENT_ROOT: - elementType = "root"; //$NON-NLS-1$ - break; - case IJavaElement.PACKAGE_FRAGMENT: - elementType = "package"; //$NON-NLS-1$ - break; - case IJavaElement.CLASS_FILE: - elementType = "class file"; //$NON-NLS-1$ - break; - case IJavaElement.COMPILATION_UNIT: - elementType = "compilation unit"; //$NON-NLS-1$ - break; - default: - elementType = "element"; //$NON-NLS-1$ - } + String elementType = JavaModelCache.getElementType(element); System.out.println(Thread.currentThread() + " CLOSING "+ elementType + " " + element.toStringWithAncestors()); //$NON-NLS-1$//$NON-NLS-2$ wasVerbose = true; JavaModelCache.VERBOSE = false; @@ -4085,8 +4160,16 @@ public class JavaModelManager implements ISaveParticipant, IContentTypeChangeLis public void resetClasspathListCache() { if (this.nonChainingJars != null) this.nonChainingJars.clear(); - if (this.invalidArchives != null) + if (DEBUG_INVALID_ARCHIVES) { + synchronized(this.invalidArchivesMutex) { + if (!this.invalidArchives.isEmpty()) { + System.out.println("Invalid JAR cache: clearing cache"); //$NON-NLS-1$ + } + } + } + synchronized(this.invalidArchivesMutex) { this.invalidArchives.clear(); + } if (this.externalFiles != null) this.externalFiles.clear(); if (this.assumedExternalFiles != null) @@ -5163,6 +5246,8 @@ public class JavaModelManager implements ISaveParticipant, IContentTypeChangeLis | IResourceChangeEvent.PRE_CLOSE | IResourceChangeEvent.PRE_REFRESH); + Indexer.getInstance().addListener(this.deltaState); + // listen to resource changes affecting external annotations ExternalAnnotationTracker.start(workspace); diff --git a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/JavadocConstants.java b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/JavadocConstants.java index ed53500c3..e52d2e190 100644 --- a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/JavadocConstants.java +++ b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/JavadocConstants.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2005, 2014 IBM Corporation and others. + * Copyright (c) 2005, 2016 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 @@ -14,7 +14,9 @@ public interface JavadocConstants { String ANCHOR_PREFIX_END = "\""; //$NON-NLS-1$ char[] ANCHOR_PREFIX_START = "<A NAME=\"".toCharArray(); //$NON-NLS-1$ - int ANCHOR_PREFIX_START_LENGHT = ANCHOR_PREFIX_START.length; + char[] ANCHOR_PREFIX_START_2 = "<A ID=\"".toCharArray(); //$NON-NLS-1$ + int ANCHOR_PREFIX_START_LENGTH = ANCHOR_PREFIX_START.length; + int ANCHOR_PREFIX_START2_LENGTH = ANCHOR_PREFIX_START_2.length; char[] ANCHOR_SUFFIX = "</A>".toCharArray(); //$NON-NLS-1$ int ANCHOR_SUFFIX_LENGTH = JavadocConstants.ANCHOR_SUFFIX.length; char[] CONSTRUCTOR_DETAIL = "<!-- ========= CONSTRUCTOR DETAIL ======== -->".toCharArray(); //$NON-NLS-1$ diff --git a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/JavadocContents.java b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/JavadocContents.java index 02341d5b1..e29b96de1 100644 --- a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/JavadocContents.java +++ b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/JavadocContents.java @@ -185,14 +185,15 @@ public class JavadocContents { } int fromIndex = this.tempLastAnchorFoundIndex; - int index; + int[] index; // check each next unknown anchor locations - while ((index = CharOperation.indexOf(JavadocConstants.ANCHOR_PREFIX_START, this.content, false, fromIndex)) != -1 && (index < indexOfSectionBottom || indexOfSectionBottom == -1)) { - fromIndex = index + 1; - - int anchorEndStart = index + JavadocConstants.ANCHOR_PREFIX_START_LENGHT; - + index = getAnchorIndex(fromIndex); + while (index[0] != -1 && (index[0] < indexOfSectionBottom || indexOfSectionBottom == -1)) { + fromIndex = index[0] + 1; + + int anchorEndStart = index[0] + index[1]; + this.tempLastAnchorFoundIndex = anchorEndStart; if (CharOperation.prefixEquals(anchor, this.content, false, anchorEndStart)) { @@ -204,11 +205,25 @@ public class JavadocContents { this.tempAnchorIndexes[this.tempAnchorIndexesCount++] = anchorEndStart; } + index = getAnchorIndex(fromIndex); } return null; } - + private int[] getAnchorIndex(int fromIndex) { + int index = CharOperation.indexOf(JavadocConstants.ANCHOR_PREFIX_START, this.content, false, fromIndex); + if (index != -1) { + return new int[]{index, JavadocConstants.ANCHOR_PREFIX_START_LENGTH}; + } + if (index == -1) { + index = CharOperation.indexOf(JavadocConstants.ANCHOR_PREFIX_START_2, this.content, false, fromIndex); + } + if (index == -1) { + return new int[]{-1, -1}; + } else { + return new int[]{index, JavadocConstants.ANCHOR_PREFIX_START2_LENGTH}; + } + } private int[] computeChildRange(int anchorEndStart, char[] anchor, int indexOfBottom) { int[] range = null; @@ -218,7 +233,7 @@ public class JavadocContents { int indexOfEndLink = CharOperation.indexOf(JavadocConstants.ANCHOR_SUFFIX, this.content, false, anchorEndStart + anchor.length); if (indexOfEndLink != -1) { // try to find the next anchor - int indexOfNextElement = CharOperation.indexOf(JavadocConstants.ANCHOR_PREFIX_START, this.content, false, indexOfEndLink); + int indexOfNextElement = getAnchorIndex(indexOfEndLink)[0]; int javadocStart = indexOfEndLink + JavadocConstants.ANCHOR_SUFFIX_LENGTH; int javadocEnd = indexOfNextElement == -1 ? indexOfBottom : Math.min(indexOfNextElement, indexOfBottom); diff --git a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/builder/ClasspathDirectory.java b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/builder/ClasspathDirectory.java index 72917d571..fa1e11d10 100644 --- a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/builder/ClasspathDirectory.java +++ b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/builder/ClasspathDirectory.java @@ -15,10 +15,10 @@ import java.util.zip.ZipFile; import org.eclipse.core.resources.*; import org.eclipse.core.runtime.*; - -import org.eclipse.jdt.internal.compiler.classfmt.ClassFileReader; import org.eclipse.jdt.internal.compiler.classfmt.ClassFormatException; +import org.eclipse.jdt.internal.compiler.classfmt.ExternalAnnotationDecorator; import org.eclipse.jdt.internal.compiler.env.AccessRuleSet; +import org.eclipse.jdt.internal.compiler.env.IBinaryType; import org.eclipse.jdt.internal.compiler.env.NameEnvironmentAnswer; import org.eclipse.jdt.internal.compiler.util.SimpleLookupTable; import org.eclipse.jdt.internal.compiler.util.SuffixConstants; @@ -107,7 +107,7 @@ public boolean equals(Object o) { public NameEnvironmentAnswer findClass(String binaryFileName, String qualifiedPackageName, String qualifiedBinaryFileName) { if (!doesFileExist(binaryFileName, qualifiedPackageName, qualifiedBinaryFileName)) return null; // most common case - ClassFileReader reader = null; + IBinaryType reader = null; try { reader = Util.newClassFileReader(this.binaryFolder.getFile(new Path(qualifiedBinaryFileName))); } catch (CoreException e) { @@ -121,7 +121,12 @@ public NameEnvironmentAnswer findClass(String binaryFileName, String qualifiedPa String fileNameWithoutExtension = qualifiedBinaryFileName.substring(0, qualifiedBinaryFileName.length() - SuffixConstants.SUFFIX_CLASS.length); if (this.externalAnnotationPath != null) { try { - this.annotationZipFile = reader.setExternalAnnotationProvider(this.externalAnnotationPath, fileNameWithoutExtension, this.annotationZipFile, null); + if (this.annotationZipFile == null) { + this.annotationZipFile = ExternalAnnotationDecorator + .getAnnotationZipFile(this.externalAnnotationPath, null); + } + reader = ExternalAnnotationDecorator.create(reader, this.externalAnnotationPath, + fileNameWithoutExtension, this.annotationZipFile); } catch (IOException e) { // don't let error on annotations fail class reading } diff --git a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/builder/ClasspathJar.java b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/builder/ClasspathJar.java index 1bda5fb27..657567583 100644 --- a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/builder/ClasspathJar.java +++ b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/builder/ClasspathJar.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2000, 2015 IBM Corporation and others. + * Copyright (c) 2000, 2016 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 @@ -13,22 +13,27 @@ *******************************************************************************/ package org.eclipse.jdt.internal.core.builder; -import org.eclipse.core.resources.IFile; -import org.eclipse.core.runtime.*; +import java.io.File; +import java.io.IOException; +import java.util.Date; +import java.util.Enumeration; +import java.util.zip.ZipEntry; +import java.util.zip.ZipFile; +import org.eclipse.core.resources.IFile; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IPath; import org.eclipse.jdt.internal.compiler.classfmt.ClassFileReader; import org.eclipse.jdt.internal.compiler.classfmt.ClassFormatException; +import org.eclipse.jdt.internal.compiler.classfmt.ExternalAnnotationDecorator; import org.eclipse.jdt.internal.compiler.env.AccessRuleSet; +import org.eclipse.jdt.internal.compiler.env.IBinaryType; import org.eclipse.jdt.internal.compiler.env.NameEnvironmentAnswer; import org.eclipse.jdt.internal.compiler.util.SimpleLookupTable; import org.eclipse.jdt.internal.compiler.util.SimpleSet; import org.eclipse.jdt.internal.compiler.util.SuffixConstants; import org.eclipse.jdt.internal.core.util.Util; -import java.io.*; -import java.util.*; -import java.util.zip.*; - @SuppressWarnings("rawtypes") public class ClasspathJar extends ClasspathLocation { @@ -165,12 +170,18 @@ public NameEnvironmentAnswer findClass(String binaryFileName, String qualifiedPa if (!isPackage(qualifiedPackageName)) return null; // most common case try { - ClassFileReader reader = ClassFileReader.read(this.zipFile, qualifiedBinaryFileName); + IBinaryType reader = ClassFileReader.read(this.zipFile, qualifiedBinaryFileName); if (reader != null) { String fileNameWithoutExtension = qualifiedBinaryFileName.substring(0, qualifiedBinaryFileName.length() - SuffixConstants.SUFFIX_CLASS.length); if (this.externalAnnotationPath != null) { try { - this.annotationZipFile = reader.setExternalAnnotationProvider(this.externalAnnotationPath, fileNameWithoutExtension, this.annotationZipFile, null); + if (this.annotationZipFile == null) { + this.annotationZipFile = ExternalAnnotationDecorator + .getAnnotationZipFile(this.externalAnnotationPath, null); + } + + reader = ExternalAnnotationDecorator.create(reader, this.externalAnnotationPath, + fileNameWithoutExtension, this.annotationZipFile); } catch (IOException e) { // don't let error on annotations fail class reading } diff --git a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/hierarchy/BindingMap.java b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/hierarchy/BindingMap.java new file mode 100644 index 000000000..3070193d8 --- /dev/null +++ b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/hierarchy/BindingMap.java @@ -0,0 +1,100 @@ +/******************************************************************************* + * Copyright (c) 2016 Google, Inc 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: + * Stefan Xenos (Google) - Initial implementation + *******************************************************************************/ +package org.eclipse.jdt.internal.core.hierarchy; + +import java.util.HashSet; +import java.util.IdentityHashMap; +import java.util.Iterator; +import java.util.Map; +import java.util.Set; + +import org.eclipse.jdt.internal.compiler.lookup.TypeBinding; +import org.eclipse.jdt.internal.compiler.lookup.TypeIds; + +/** + * Maps a {@link TypeBinding} onto values. Two {@link TypeBinding}s are considered equivalent + * if their IDs are the same or if they have TypeIds.NoId and they are identical objects. + * <p> + * Takes into account the fact that a ReferenceBinding may have its ID change from NoId + * to a real ID at any time without notice. (This is a behavior that was observed in + * TypeHierarchyTests.testAnonymousType01 -- if type IDs could be made invariant then it + * would be possible to implement a more efficient map that never needs to perform an + * exhaustive search.) + */ +public class BindingMap<V> { + private Map<TypeBinding, V> identityMap = new IdentityHashMap<>(); + private Object[] mapIdToValue = new Object[0]; + private Set<TypeBinding> bindingsWithoutAnId = new HashSet<>(); + + public void put(TypeBinding key, V value) { + this.identityMap.put(key, value); + if (key.id != TypeIds.NoId) { + int targetId = key.id; + insertIntoIdMap(targetId, value); + } else { + this.bindingsWithoutAnId.add(key); + } + } + + @SuppressWarnings("unchecked") + public V get(TypeBinding key) { + // Check if we can find this binding by identity + V value = this.identityMap.get(key); + if (value != null) { + return value; + } + int targetId = key.id; + if (targetId != TypeIds.NoId) { + // Check if we can find this binding by value + if (targetId < this.mapIdToValue.length) { + value = (V)this.mapIdToValue[targetId]; + } + if (value != null) { + return value; + } + + // Check if there are any bindings that previously had no ID that have + // subsequently been assigned one. + for (Iterator<TypeBinding> bindingIter = this.bindingsWithoutAnId.iterator(); bindingIter.hasNext();) { + TypeBinding nextBinding = bindingIter.next(); + + if (nextBinding.id != TypeIds.NoId) { + insertIntoIdMap(nextBinding.id, this.identityMap.get(nextBinding)); + bindingIter.remove(); + } + } + + // Now look again to see if this binding can be found + if (targetId < this.mapIdToValue.length) { + value = (V)this.mapIdToValue[targetId]; + } + } + + return value; + } + + private void insertIntoIdMap(int targetId, V value) { + int requiredSize = targetId + 1; + if (this.mapIdToValue.length < requiredSize) { + int newSize = requiredSize * 2; + Object[] newArray = new Object[newSize]; + System.arraycopy(this.mapIdToValue, 0, newArray, 0, this.mapIdToValue.length); + this.mapIdToValue = newArray; + } + this.mapIdToValue[targetId] = value; + } + + public void clear() { + this.identityMap.clear(); + this.bindingsWithoutAnId.clear(); + this.mapIdToValue = new Object[0]; + } +} diff --git a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/hierarchy/HierarchyBinaryType.java b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/hierarchy/HierarchyBinaryType.java index 932c0494f..0427355e3 100644 --- a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/hierarchy/HierarchyBinaryType.java +++ b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/hierarchy/HierarchyBinaryType.java @@ -53,6 +53,23 @@ public HierarchyBinaryType(int modifiers, char[] qualification, char[] sourceNam this.typeParameterSignatures = typeParameterSignatures; CharOperation.replace(this.name, '.', '/'); } + +public HierarchyBinaryType(int modifiers, char[] binaryName, char[] sourceName, char[] enclosingTypeBinaryName, char[][] typeParameterSignatures) { + this.modifiers = modifiers; + this.sourceName = sourceName; + this.name = binaryName; + this.enclosingTypeName = enclosingTypeBinaryName; + this.typeParameterSignatures = typeParameterSignatures; + + if (typeParameterSignatures != null) { + for (char[] next : typeParameterSignatures) { + if (next == null) { + throw new IllegalArgumentException("Parameter's type signature must not be null"); //$NON-NLS-1$ + } + } + } +} + /** * @see org.eclipse.jdt.internal.compiler.env.IBinaryType */ @@ -197,6 +214,7 @@ public boolean isMember() { return false; // index did not record this information (since unused for hierarchies) } + public void recordSuperType(char[] superTypeName, char[] superQualification, char superClassOrInterface){ // index encoding of p.A$B was B/p.A$, rebuild the proper name @@ -215,17 +233,25 @@ public void recordSuperType(char[] superTypeName, char[] superQualification, cha if (TypeDeclaration.kind(this.modifiers) == TypeDeclaration.INTERFACE_DECL) return; char[] encodedName = CharOperation.concat(superQualification, superTypeName, '/'); CharOperation.replace(encodedName, '.', '/'); - this.superclass = encodedName; + recordSuperclass(encodedName); } else { char[] encodedName = CharOperation.concat(superQualification, superTypeName, '/'); CharOperation.replace(encodedName, '.', '/'); - if (this.superInterfaces == NoInterface){ - this.superInterfaces = new char[][] { encodedName }; - } else { - int length = this.superInterfaces.length; - System.arraycopy(this.superInterfaces, 0, this.superInterfaces = new char[length+1][], 0, length); - this.superInterfaces[length] = encodedName; - } + recordInterface(encodedName); + } +} + +public void recordSuperclass(char[] binaryName) { + this.superclass = binaryName; +} + +public void recordInterface(char[] binaryName) { + if (this.superInterfaces == NoInterface){ + this.superInterfaces = new char[][] { binaryName }; + } else { + int length = this.superInterfaces.length; + System.arraycopy(this.superInterfaces, 0, this.superInterfaces = new char[length+1][], 0, length); + this.superInterfaces[length] = binaryName; } } @@ -235,6 +261,7 @@ public void recordSuperType(char[] superTypeName, char[] superQualification, cha public char[] sourceFileName() { return null; } +@Override public String toString() { StringBuffer buffer = new StringBuffer(); if (this.modifiers == ClassFileConstants.AccPublic) { diff --git a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/hierarchy/HierarchyBuilder.java b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/hierarchy/HierarchyBuilder.java index 41fb754a1..46fd72d2e 100644 --- a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/hierarchy/HierarchyBuilder.java +++ b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/hierarchy/HierarchyBuilder.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2000, 2015 IBM Corporation and others. + * Copyright (c) 2000, 2016 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 @@ -18,15 +18,25 @@ import org.eclipse.core.resources.IResource; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.OperationCanceledException; +import org.eclipse.jdt.core.IClassFile; import org.eclipse.jdt.core.IType; import org.eclipse.jdt.core.JavaModelException; import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration; +import org.eclipse.jdt.internal.compiler.classfmt.ClassFormatException; import org.eclipse.jdt.internal.compiler.env.IBinaryType; import org.eclipse.jdt.internal.compiler.env.ICompilationUnit; import org.eclipse.jdt.internal.compiler.env.IGenericType; import org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding; import org.eclipse.jdt.internal.compiler.problem.DefaultProblemFactory; -import org.eclipse.jdt.internal.core.*; +import org.eclipse.jdt.internal.core.ClassFile; +import org.eclipse.jdt.internal.core.JavaElement; +import org.eclipse.jdt.internal.core.JavaProject; +import org.eclipse.jdt.internal.core.NameLookup; +import org.eclipse.jdt.internal.core.Openable; +import org.eclipse.jdt.internal.core.ResolvedBinaryType; +import org.eclipse.jdt.internal.core.SearchableEnvironment; +import org.eclipse.jdt.internal.core.SourceTypeElementInfo; +import org.eclipse.jdt.internal.core.nd.java.model.BinaryTypeFactory; import org.eclipse.jdt.internal.core.util.ResourceCompilationUnit; import org.eclipse.jdt.internal.core.util.Util; @@ -280,6 +290,7 @@ public abstract class HierarchyBuilder { protected ICompilationUnit createCompilationUnitFromPath(Openable handle, IFile file) { final char[] elementName = handle.getElementName().toCharArray(); return new ResourceCompilationUnit(file) { + @Override public char[] getFileName() { return elementName; } @@ -316,33 +327,17 @@ protected IBinaryType createInfoFromClassFile(Openable handle, IResource file) { * Create a type info from the given class file in a jar and adds it to the given list of infos. */ protected IBinaryType createInfoFromClassFileInJar(Openable classFile) { - PackageFragment pkg = (PackageFragment) classFile.getParent(); - String classFilePath = Util.concatWith(pkg.names, classFile.getElementName(), '/'); - IBinaryType info = null; - java.util.zip.ZipFile zipFile = null; + IClassFile cf = (IClassFile)classFile; + IBinaryType info; try { - zipFile = ((JarPackageFragmentRoot)pkg.getParent()).getJar(); - info = org.eclipse.jdt.internal.compiler.classfmt.ClassFileReader.read( - zipFile, - classFilePath); - } catch (org.eclipse.jdt.internal.compiler.classfmt.ClassFormatException e) { - if (TypeHierarchy.DEBUG) { - e.printStackTrace(); - } - return null; - } catch (java.io.IOException e) { + info = BinaryTypeFactory.create(cf, null); + } catch (JavaModelException | ClassFormatException e) { if (TypeHierarchy.DEBUG) { e.printStackTrace(); } return null; - } catch (CoreException e) { - if (TypeHierarchy.DEBUG) { - e.printStackTrace(); - } - return null; - } finally { - JavaModelManager.getJavaModelManager().closeZipFile(zipFile); } + this.infoToHandle.put(info, classFile); return info; } diff --git a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/hierarchy/HierarchyResolver.java b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/hierarchy/HierarchyResolver.java index b57e2d5ec..d9a99ff85 100644 --- a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/hierarchy/HierarchyResolver.java +++ b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/hierarchy/HierarchyResolver.java @@ -87,6 +87,7 @@ public class HierarchyResolver implements ITypeRequestor { private CompilerOptions options; HierarchyBuilder builder; private ReferenceBinding[] typeBindings; + private BindingMap<IGenericType> bindingMap = new BindingMap<>(); private int typeIndex; private IGenericType[] typeModels; @@ -230,10 +231,9 @@ private IType findSuperClass(IGenericType type, ReferenceBinding typeBinding) { } } } - for (int t = this.typeIndex; t >= 0; t--) { - if (TypeBinding.equalsEquals(this.typeBindings[t], superBinding)) { - return this.builder.getHandle(this.typeModels[t], superBinding); - } + IGenericType typeModel = this.bindingMap.get(superBinding); + if (typeModel != null) { + return this.builder.getHandle(typeModel, superBinding); } } return null; @@ -336,13 +336,12 @@ private IType[] findSuperInterfaces(IGenericType type, ReferenceBinding typeBind // ensure that the binding corresponds to the interface defined by the user if (CharOperation.equals(simpleName, interfaceBinding.sourceName)) { bindingIndex++; - for (int t = this.typeIndex; t >= 0; t--) { - if (TypeBinding.equalsEquals(this.typeBindings[t], interfaceBinding)) { - IType handle = this.builder.getHandle(this.typeModels[t], interfaceBinding); - if (handle != null) { - superinterfaces[index++] = handle; - continue next; - } + IGenericType genericType = this.bindingMap.get(interfaceBinding); + if (genericType != null) { + IType handle = this.builder.getHandle(genericType, interfaceBinding); + if (handle != null) { + superinterfaces[index++] = handle; + continue next; } } } @@ -438,6 +437,7 @@ private void remember(IGenericType suppliedType, ReferenceBinding typeBinding) { } this.typeModels[this.typeIndex] = suppliedType; this.typeBindings[this.typeIndex] = typeBinding; + this.bindingMap.put(typeBinding, suppliedType); } private void remember(IType type, ReferenceBinding typeBinding) { //{ObjectTeams: for phantom roles avoid hitting the JME (phantom has no info) but proceed into else as to record what we have @@ -740,6 +740,7 @@ private void reset(){ this.typeIndex = -1; this.typeModels = new IGenericType[5]; this.typeBindings = new ReferenceBinding[5]; + this.bindingMap.clear(); } /** @@ -1062,6 +1063,7 @@ private void setEnvironment(LookupEnvironment lookupEnvironment, HierarchyBuilde this.typeIndex = -1; this.typeModels = new IGenericType[5]; this.typeBindings = new ReferenceBinding[5]; + this.bindingMap.clear(); } /* diff --git a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/hierarchy/IndexBasedHierarchyBuilder.java b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/hierarchy/IndexBasedHierarchyBuilder.java index 4c9d1e7f4..a2342ebc1 100644 --- a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/hierarchy/IndexBasedHierarchyBuilder.java +++ b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/hierarchy/IndexBasedHierarchyBuilder.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2000, 2015 IBM Corporation and others. + * Copyright (c) 2000, 2016 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 @@ -10,6 +10,7 @@ *******************************************************************************/ package org.eclipse.jdt.internal.core.hierarchy; +import java.util.ArrayDeque; import java.util.ArrayList; import java.util.Arrays; import java.util.Comparator; @@ -17,9 +18,12 @@ import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.Map; +import java.util.Set; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IResource; +import org.eclipse.core.resources.IWorkspaceRoot; +import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.SubMonitor; @@ -50,9 +54,19 @@ import org.eclipse.jdt.internal.core.Member; import org.eclipse.jdt.internal.core.Openable; import org.eclipse.jdt.internal.core.PackageFragment; import org.eclipse.jdt.internal.core.SearchableEnvironment; +import org.eclipse.jdt.internal.core.nd.IReader; +import org.eclipse.jdt.internal.core.nd.Nd; +import org.eclipse.jdt.internal.core.nd.indexer.Indexer; +import org.eclipse.jdt.internal.core.nd.java.JavaIndex; +import org.eclipse.jdt.internal.core.nd.java.JavaNames; +import org.eclipse.jdt.internal.core.nd.java.NdType; +import org.eclipse.jdt.internal.core.nd.java.NdTypeId; +import org.eclipse.jdt.internal.core.nd.java.NdTypeInterface; +import org.eclipse.jdt.internal.core.nd.java.NdTypeSignature; import org.eclipse.jdt.internal.core.search.IndexQueryRequestor; import org.eclipse.jdt.internal.core.search.JavaSearchParticipant; import org.eclipse.jdt.internal.core.search.SubTypeSearchJob; +import org.eclipse.jdt.internal.core.search.UnindexedSearchScope; import org.eclipse.jdt.internal.core.search.indexing.IIndexConstants; import org.eclipse.jdt.internal.core.search.indexing.IndexManager; import org.eclipse.jdt.internal.core.search.matching.MatchLocator; @@ -467,7 +481,103 @@ public static void searchAllPossibleSubTypes( int waitingPolicy, // WaitUntilReadyToSearch | ForceImmediateSearch | CancelIfNotReadyToSearch final IProgressMonitor monitor) { - SubMonitor subMonitor = SubMonitor.convert(monitor); + if (JavaIndex.isEnabled()) { + SubMonitor subMonitor = SubMonitor.convert(monitor, 2); + newSearchAllPossibleSubTypes(type, scope, binariesFromIndexMatches, pathRequestor, waitingPolicy, + subMonitor.split(1)); + legacySearchAllPossibleSubTypes(type, UnindexedSearchScope.filterEntriesCoveredByTheNewIndex(scope), + binariesFromIndexMatches, pathRequestor, waitingPolicy, subMonitor.split(1)); + } else { + legacySearchAllPossibleSubTypes(type, scope, binariesFromIndexMatches, pathRequestor, waitingPolicy, + monitor); + } +} + +private static void newSearchAllPossibleSubTypes(IType type, IJavaSearchScope scope2, Map binariesFromIndexMatches2, + IPathRequestor pathRequestor, int waitingPolicy, IProgressMonitor progressMonitor) { + SubMonitor subMonitor = SubMonitor.convert(progressMonitor, 2); + JavaIndex index = JavaIndex.getIndex(); + + Indexer.getInstance().waitForIndex(waitingPolicy, subMonitor.split(1)); + + Nd nd = index.getNd(); + char[] fieldDefinition = JavaNames.fullyQualifiedNameToFieldDescriptor(type.getFullyQualifiedName().toCharArray()); + + IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot(); + + try (IReader reader = nd.acquireReadLock()) { + NdTypeId foundType = index.findType(fieldDefinition); + + if (foundType == null) { + return; + } + + ArrayDeque<NdType> typesToVisit = new ArrayDeque<>(); + Set<NdType> discoveredTypes = new HashSet<>(); + typesToVisit.addAll(foundType.getTypes()); + discoveredTypes.addAll(typesToVisit); + + while (!typesToVisit.isEmpty()) { + NdType nextType = typesToVisit.removeFirst(); + NdTypeId typeId = nextType.getTypeId(); + + String typePath = new String(JavaNames.getIndexPathFor(nextType, root)); + if (!scope2.encloses(typePath)) { + continue; + } + + subMonitor.setWorkRemaining(Math.max(typesToVisit.size(), 3000)).split(1); + + boolean isLocalClass = nextType.isLocal() || nextType.isAnonymous(); + pathRequestor.acceptPath(typePath, isLocalClass); + + HierarchyBinaryType binaryType = (HierarchyBinaryType)binariesFromIndexMatches2.get(typePath); + if (binaryType == null) { + binaryType = createBinaryTypeFrom(nextType); + binariesFromIndexMatches2.put(typePath, binaryType); + } + + for (NdType subType : typeId.getSubTypes()) { + if (discoveredTypes.add(subType)) { + typesToVisit.add(subType); + } + } + } + } +} + +private static HierarchyBinaryType createBinaryTypeFrom(NdType type) { + char[] enclosingTypeName = null; + NdTypeSignature enclosingType = type.getDeclaringType(); + if (enclosingType != null) { + enclosingTypeName = enclosingType.getRawType().getBinaryName(); + } + char[][] typeParameters = type.getTypeParameterSignatures(); + NdTypeId typeId = type.getTypeId(); + HierarchyBinaryType result = new HierarchyBinaryType(type.getModifiers(), typeId.getBinaryName(), + type.getSourceName(), enclosingTypeName, typeParameters.length == 0 ? null : typeParameters); + + NdTypeSignature superClass = type.getSuperclass(); + if (superClass != null) { + result.recordSuperclass(superClass.getRawType().getBinaryName()); + } + + for (NdTypeInterface interf : type.getInterfaces()) { + result.recordInterface(interf.getInterface().getRawType().getBinaryName()); + } + return result; +} + +private static void legacySearchAllPossibleSubTypes( + IType type, + IJavaSearchScope scope, + final Map binariesFromIndexMatches, + final IPathRequestor pathRequestor, + int waitingPolicy, // WaitUntilReadyToSearch | ForceImmediateSearch | CancelIfNotReadyToSearch + final IProgressMonitor progressMonitor) { + + SubMonitor subMonitor = SubMonitor.convert(progressMonitor, 100); + /* embed constructs inside arrays so as to pass them to (inner) collector */ final Queue queue = new Queue(); final HashtableOfObject foundSuperNames = new HashtableOfObject(5); |