### Eclipse Workspace Patch 1.0 #P org.eclipse.jdt.core Index: codeassist/org/eclipse/jdt/internal/codeassist/SelectionEngine.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.jdt.core/codeassist/org/eclipse/jdt/internal/codeassist/SelectionEngine.java,v retrieving revision 1.158 diff -u -r1.158 SelectionEngine.java --- codeassist/org/eclipse/jdt/internal/codeassist/SelectionEngine.java 7 Apr 2010 17:22:33 -0000 1.158 +++ codeassist/org/eclipse/jdt/internal/codeassist/SelectionEngine.java 22 Feb 2011 12:35:51 -0000 @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2000, 2010 IBM Corporation and others. + * Copyright (c) 2000, 2011 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,12 +10,18 @@ *******************************************************************************/ package org.eclipse.jdt.internal.codeassist; +import java.util.ArrayList; +import java.util.Iterator; import java.util.Locale; import java.util.Map; import org.eclipse.core.resources.IFile; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.OperationCanceledException; +import org.eclipse.jdt.core.IBuffer; +import org.eclipse.jdt.core.IMember; +import org.eclipse.jdt.core.IOpenable; +import org.eclipse.jdt.core.ISourceRange; import org.eclipse.jdt.core.IType; import org.eclipse.jdt.core.JavaModelException; import org.eclipse.jdt.core.Signature; @@ -1106,7 +1112,7 @@ } this.acceptedAnswer = true; } else if (binding instanceof MethodBinding) { - MethodBinding methodBinding = (MethodBinding) binding; + MethodBinding methodBinding = getCorrectMethodBinding((MethodBinding) binding); this.noProposal = false; boolean isValuesOrValueOf = false; @@ -1608,4 +1614,192 @@ return false; } + + /* + * Returns the correct method binding according to whether the selection is on the method declaration + * or on the inheritDoc tag in its javadoc. + */ + private MethodBinding getCorrectMethodBinding(MethodBinding binding) { + if (this.parser.javadocParser instanceof SelectionJavadocParser) { + if (((SelectionJavadocParser)this.parser.javadocParser).inheritDocTagSelected){ + try { + Object res = findMethodWithAttachedDocInHierarchy(binding); + if (res instanceof MethodBinding) { + return (MethodBinding) res; + } + } catch (JavaModelException e) { + return null; + } + } + } + return binding; + } + + protected MethodBinding findOverriddenMethodInType(ReferenceBinding overriddenType, MethodBinding overriding) throws JavaModelException { + if (overriddenType == null) + return null; + MethodBinding[] overriddenMethods= overriddenType.availableMethods(); + LookupEnvironment lookupEnv = this.lookupEnvironment; + if (lookupEnv != null && overriddenMethods != null) { + for (int i= 0; i < overriddenMethods.length; i++) { + if (lookupEnv.methodVerifier().isMethodSubsignature(overriding, overriddenMethods[i])) { + return overriddenMethods[i]; + } + } + } + return null; + } + + private Object findMethodWithAttachedDocInHierarchy(final MethodBinding method) throws JavaModelException { + ReferenceBinding type= method.declaringClass; + final SelectionRequestor requestor1 = (SelectionRequestor) this.requestor; + return new InheritDocVisitor() { + public Object visit(ReferenceBinding currType) throws JavaModelException { + MethodBinding overridden = findOverriddenMethodInType(currType, method); + if (overridden == null) + return InheritDocVisitor.CONTINUE; + TypeBinding args[] = overridden.parameters; + String names[] = new String[args.length]; + for (int i = 0; i < args.length; i++) { + names[i] = Signature.createTypeSignature(args[i].sourceName(), false); + } + IMember member = (IMember) requestor1.findMethodFromBinding(overridden, names, overridden.declaringClass); + if (member == null) + return InheritDocVisitor.CONTINUE; + if (member.getAttachedJavadoc(null) != null ) { + // for binary methods with attached javadoc and no source attached + return overridden; + } + IOpenable openable = member.getOpenable(); + if (openable == null) + return InheritDocVisitor.CONTINUE; + IBuffer buf= openable.getBuffer(); + if (buf == null) { + // no source attachment found. This method maybe the one. Stop. + return InheritDocVisitor.STOP_BRANCH; + } + + ISourceRange javadocRange= member.getJavadocRange(); + if (javadocRange == null) + return InheritDocVisitor.CONTINUE; // this method doesn't have javadoc, continue to look. + String rawJavadoc= buf.getText(javadocRange.getOffset(), javadocRange.getLength()); + if (rawJavadoc != null) { + return overridden; + } + return InheritDocVisitor.CONTINUE; + } + }.visitInheritDoc(type); + } + + /** + * Implements the "Algorithm for Inheriting Method Comments" as specified for + * 1.4.2, + * 1.5, and + * 1.6. + * + *

+ * Unfortunately, the implementation is broken in Javadoc implementations since 1.5, see + * Sun's bug. + *

+ * + *

+ * We adhere to the spec. + *

+ */ + private static abstract class InheritDocVisitor { + public static final Object STOP_BRANCH= new Object() { + public String toString() { return "STOP_BRANCH"; } //$NON-NLS-1$ + }; + public static final Object CONTINUE= new Object() { + public String toString() { return "CONTINUE"; } //$NON-NLS-1$ + }; + + /** + * Visits a type and decides how the visitor should proceed. + * + * @param currType the current type + * @return + * @throws JavaModelException unexpected problem + * @see #visitInheritDoc(ReferenceBinding) + */ + public abstract Object visit(ReferenceBinding currType) throws JavaModelException; + + /** + * Visits the super types of the given currentType. + * + * @param currentType the starting type + * @return the result from a call to {@link #visit(ReferenceBinding)}, or null if none of + * the calls returned a result + * @throws JavaModelException unexpected problem + */ + public Object visitInheritDoc(ReferenceBinding currentType) throws JavaModelException { + ArrayList visited= new ArrayList(); + visited.add(currentType); + Object result= visitInheritDocInterfaces(visited, currentType); + if (result != InheritDocVisitor.CONTINUE) + return result; + + ReferenceBinding superClass= currentType.superclass(); + + while (superClass != null && ! visited.contains(superClass)) { + result= visit(superClass); + if (result == InheritDocVisitor.STOP_BRANCH) { + return null; + } else if (result == InheritDocVisitor.CONTINUE) { + visited.add(superClass); + result= visitInheritDocInterfaces(visited, superClass); + if (result != InheritDocVisitor.CONTINUE) + return result; + else + superClass= superClass.superclass(); + } else { + return result; + } + } + + return null; + } + + /** + * Visits the super interfaces of the given type in the given hierarchy, thereby skipping already visited types. + * + * @param visited set of visited types + * @param currentType type whose super interfaces should be visited + * @return the result, or {@link #CONTINUE} if no result has been found + * @throws JavaModelException unexpected problem + */ + private Object visitInheritDocInterfaces(ArrayList visited, ReferenceBinding currentType) throws JavaModelException { + ArrayList toVisitChildren= new ArrayList(); + ReferenceBinding[] superInterfaces= currentType.superInterfaces(); + for (int i= 0; i < superInterfaces.length; i++) { + ReferenceBinding superInterface= superInterfaces[i]; + if (visited.contains(superInterface)) + continue; + visited.add(superInterface); + Object result= visit(superInterface); + if (result == InheritDocVisitor.STOP_BRANCH) { + //skip + } else if (result == InheritDocVisitor.CONTINUE) { + toVisitChildren.add(superInterface); + } else { + return result; + } + } + for (Iterator iter= toVisitChildren.iterator(); iter.hasNext(); ) { + ReferenceBinding child= (ReferenceBinding) iter.next(); + Object result= visitInheritDocInterfaces(visited, child); + if (result != InheritDocVisitor.CONTINUE) + return result; + } + return InheritDocVisitor.CONTINUE; + } + } } Index: codeassist/org/eclipse/jdt/internal/codeassist/select/SelectionJavadoc.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.jdt.core/codeassist/org/eclipse/jdt/internal/codeassist/select/SelectionJavadoc.java,v retrieving revision 1.7 diff -u -r1.7 SelectionJavadoc.java --- codeassist/org/eclipse/jdt/internal/codeassist/select/SelectionJavadoc.java 7 Mar 2009 01:08:06 -0000 1.7 +++ codeassist/org/eclipse/jdt/internal/codeassist/select/SelectionJavadoc.java 22 Feb 2011 12:35:51 -0000 @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2000, 2009 IBM Corporation and others. + * Copyright (c) 2000, 2011 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 @@ -11,6 +11,7 @@ package org.eclipse.jdt.internal.codeassist.select; import org.eclipse.jdt.internal.compiler.ast.*; +import org.eclipse.jdt.internal.compiler.impl.ReferenceContext; import org.eclipse.jdt.internal.compiler.lookup.*; /** @@ -19,9 +20,11 @@ public class SelectionJavadoc extends Javadoc { Expression selectedNode; + boolean inheritDocSelected; public SelectionJavadoc(int sourceStart, int sourceEnd) { super(sourceStart, sourceEnd); + this.inheritDocSelected = false; } /* (non-Javadoc) @@ -106,6 +109,11 @@ binding = this.selectedNode.resolvedType; } throw new SelectionNodeFound(binding); + } else if (this.inheritDocSelected) { + ReferenceContext referenceContext = scope.referenceContext(); + if (referenceContext instanceof MethodDeclaration) { + throw new SelectionNodeFound(((MethodDeclaration) referenceContext).binding); + } } } Index: codeassist/org/eclipse/jdt/internal/codeassist/select/SelectionJavadocParser.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.jdt.core/codeassist/org/eclipse/jdt/internal/codeassist/select/SelectionJavadocParser.java,v retrieving revision 1.9 diff -u -r1.9 SelectionJavadocParser.java --- codeassist/org/eclipse/jdt/internal/codeassist/select/SelectionJavadocParser.java 7 Mar 2009 01:08:06 -0000 1.9 +++ codeassist/org/eclipse/jdt/internal/codeassist/select/SelectionJavadocParser.java 22 Feb 2011 12:35:51 -0000 @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2000, 2009 IBM Corporation and others. + * Copyright (c) 2000, 2011 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 @@ -25,12 +25,14 @@ int selectionStart; int selectionEnd; ASTNode selectedNode; + public boolean inheritDocTagSelected; public SelectionJavadocParser(SelectionParser sourceParser) { super(sourceParser); this.shouldReportProblems = false; this.reportProblems = false; this.kind = SELECTION_PARSER | TEXT_PARSE; + this.inheritDocTagSelected = false; } /* @@ -186,6 +188,16 @@ protected void updateDocComment() { if (this.selectedNode instanceof Expression) { ((SelectionJavadoc) this.docComment).selectedNode = (Expression) this.selectedNode; + } else if (this.inheritDocTagSelected) { + ((SelectionJavadoc) this.docComment).inheritDocSelected = true; } } + + /* + * Sets a flag to denote that selection has taken place on an inheritDoc tag + */ + protected void parseInheritDocTag() { + if (this.tagSourceStart == this.selectionStart && this.tagSourceEnd == this.selectionEnd) + this.inheritDocTagSelected = true; + } } Index: compiler/org/eclipse/jdt/internal/compiler/parser/JavadocParser.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/parser/JavadocParser.java,v retrieving revision 1.80 diff -u -r1.80 JavadocParser.java --- compiler/org/eclipse/jdt/internal/compiler/parser/JavadocParser.java 8 Feb 2011 05:16:20 -0000 1.80 +++ compiler/org/eclipse/jdt/internal/compiler/parser/JavadocParser.java 22 Feb 2011 12:35:51 -0000 @@ -568,6 +568,10 @@ if (this.reportProblems) { recordInheritedPosition((((long) this.tagSourceStart) << 32) + this.tagSourceEnd); } + if (this.inlineTagStarted) { + // parse a 'valid' inheritDoc tag + parseInheritDocTag(); + } break; default: valid = false; @@ -692,6 +696,10 @@ return valid; } + protected void parseInheritDocTag() { + // do nothing + } + /* * Parse @param tag declaration and flag missing description if corresponding option is enabled */ Index: model/org/eclipse/jdt/internal/core/SelectionRequestor.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/SelectionRequestor.java,v retrieving revision 1.79 diff -u -r1.79 SelectionRequestor.java --- model/org/eclipse/jdt/internal/core/SelectionRequestor.java 7 Sep 2010 19:14:26 -0000 1.79 +++ model/org/eclipse/jdt/internal/core/SelectionRequestor.java 22 Feb 2011 12:35:51 -0000 @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2000, 2010 IBM Corporation and others. + * Copyright (c) 2000, 2011 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 @@ -34,6 +34,7 @@ import org.eclipse.jdt.internal.compiler.lookup.LocalVariableBinding; import org.eclipse.jdt.internal.compiler.lookup.MethodBinding; import org.eclipse.jdt.internal.compiler.lookup.ParameterizedTypeBinding; +import org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding; import org.eclipse.jdt.internal.compiler.lookup.SourceTypeBinding; import org.eclipse.jdt.internal.compiler.lookup.TypeBinding; import org.eclipse.jdt.internal.compiler.lookup.TypeConstants; @@ -842,6 +843,60 @@ } return res; } +// TODO(Ayush): This can be an API in IType +private IMethod findMethod(IType type, char[] selector, String[] paramTypeNames) throws JavaModelException { + IMethod method = null; + int startingIndex = 0; + String[] args; + IType enclosingType = type.getDeclaringType(); + // If the method is a constructor of a non-static inner type, add the enclosing type as an + // additional parameter to the constructor + if (enclosingType != null + && CharOperation.equals(type.getElementName().toCharArray(), selector) + && !Flags.isStatic(type.getFlags())) { + args = new String[paramTypeNames.length+1]; + startingIndex = 1; + args[0] = Signature.createTypeSignature(enclosingType.getFullyQualifiedName(), true); + } else { + args = new String[paramTypeNames.length]; + } + int length = args.length; + for(int i = startingIndex; i< length ; i++){ + args[i] = new String(paramTypeNames[i-startingIndex]); + } + method = type.getMethod(new String(selector), args); + + IMethod[] methods = type.findMethods(method); + if (methods != null && methods.length > 0) { + method = methods[0]; + } + return method; +} + +/** + * This method returns an IMethod element from the given binding. However, + * unlike {@link #findMethod(IType, char[], String[])}, this does not require an IType to get + * the IMethod element. + * @param method the given method binding + * @param signatures the type signatures of the method arguments + * @param declaringClass the binding of the method's declaring class + * @return an IMethod corresponding to the method binding given, or null if none is found. + */ +public IJavaElement findMethodFromBinding(MethodBinding method, String[] signatures, ReferenceBinding declaringClass) { + IType foundType = this.resolveType(declaringClass.qualifiedPackageName(), declaringClass.qualifiedSourceName(), NameLookup.ACCEPT_CLASSES & NameLookup.ACCEPT_INTERFACES); + if (foundType != null) { + if (foundType instanceof BinaryType) { + try { + return this.findMethod(foundType, method.selector, signatures); + } catch (JavaModelException e) { + return null; + } + } else { + return foundType.getMethod(new String(method.selector), signatures); + } + } + return null; +} /** * Returns the resolved elements. */ #P org.eclipse.jdt.core.tests.model Index: src/org/eclipse/jdt/core/tests/model/SelectionJavadocModelTests.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/SelectionJavadocModelTests.java,v retrieving revision 1.16 diff -u -r1.16 SelectionJavadocModelTests.java --- src/org/eclipse/jdt/core/tests/model/SelectionJavadocModelTests.java 27 Jun 2008 16:02:40 -0000 1.16 +++ src/org/eclipse/jdt/core/tests/model/SelectionJavadocModelTests.java 22 Feb 2011 12:35:59 -0000 @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2000, 2008 IBM Corporation and others. + * Copyright (c) 2000, 2011 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 @@ -1311,4 +1311,174 @@ elements ); } + + public void testBug171019() throws CoreException { + this.wcOwner = new WorkingCopyOwner() {}; + this.workingCopies = new ICompilationUnit[1]; + this.workingCopies[0] = getWorkingCopy("/Tests/b171019/X.java", + "package b171019;\n" + + "interface X {\n" + + " /**\n" + + " * Main desc of foo..\n" + + " */\n" + + " void foo(int x);\n" + + "}\n" + + "interface Y extends X {\n" + + " /**\n" + + " * {@inheritDoc}\n" + // should navigate to X.foo(int) + " */\n" + + " void foo(int x);\n\n" + + " /**\n" + + " * {@inheritDoc}\n" + // should navigate to Y.foo(String) + " */\n" + + " void foo(String s);\n" + + "}\n" + ); + IJavaElement[] elements = new IJavaElement[2]; + elements[0] = selectMethod(this.workingCopies[0], "@inheritDoc", 1); + elements[1] = selectMethod(this.workingCopies[0], "@inheritDoc", 2); + assertElementsEqual("Invalid selection(s)", + "foo(int) [in X [in [Working copy] X.java [in b171019 [in [in Tests]]]]]\n" + + "foo(String) [in Y [in [Working copy] X.java [in b171019 [in [in Tests]]]]]", + elements + ); + } + + public void testBug171019b() throws CoreException { + this.wcOwner = new WorkingCopyOwner() {}; + this.workingCopies = new ICompilationUnit[1]; + this.workingCopies[0] = getWorkingCopy("/Tests/b171019/X.java", + "package b171019;\n" + + "interface X {\n" + + " /**\n" + + " * Main desc of foo..\n" + + " */\n" + + " void foo(int x);\n" + + "}\n" + + "class X1 implements X{\n" + + " void foo(int x){}\n" + + "}\n" + + "class Y extends X1 {\n" + + " /**\n" + + " * {@inheritDoc}\n" + // should navigate to X.foo(int) + " */\n" + + " void foo(int x);\n\n" + + " /**\n" + + " * {@inheritDoc}\n" + // should navigate to Y.foo(String) + " */\n" + + " void foo(String s);\n" + + "}\n" + ); + IJavaElement[] elements = new IJavaElement[2]; + elements[0] = selectMethod(this.workingCopies[0], "@inheritDoc", 1); + elements[1] = selectMethod(this.workingCopies[0], "@inheritDoc", 2); + assertElementsEqual("Invalid selection(s)", + "foo(int) [in X [in [Working copy] X.java [in b171019 [in [in Tests]]]]]\n" + + "foo(String) [in Y [in [Working copy] X.java [in b171019 [in [in Tests]]]]]", + elements + ); + } + + public void testBug171019c() throws CoreException { + this.wcOwner = new WorkingCopyOwner() {}; + this.workingCopies = new ICompilationUnit[1]; + this.workingCopies[0] = getWorkingCopy("/Tests/b171019/X.java", + "package b171019;\n" + + "interface X1 {\n" + + " /**\n" + + " * Main desc of foo in X1..\n" + + " */\n" + + " void foo(int x);\n" + + "}\n" + + "interface X2 {\n" + + " /**\n" + + " * Main desc of foo in X2..\n" + + " */\n" + + " void foo(int x);\n" + + "}\n" + + "class X implements X1 {\n" + + " /**\n" + + " * X desc of foo..\n" + + " */\n" + + " void foo(int x){}\n" + + "}\n" + + "class Y extends X implements X2 {\n" + + " /**\n" + + " * {@inheritDoc}\n" + // should navigate to X2.foo(int) + " */\n" + + " void foo(int x);\n\n" + + "}\n" + ); + IJavaElement[] elements = new IJavaElement[1]; + elements[0] = selectMethod(this.workingCopies[0], "@inheritDoc", 1); + assertElementsEqual("Invalid selection(s)", + "foo(int) [in X2 [in [Working copy] X.java [in b171019 [in [in Tests]]]]]", + elements + ); + } + + public void testBug171019d() throws CoreException { + this.wcOwner = new WorkingCopyOwner() {}; + this.workingCopies = new ICompilationUnit[1]; + this.workingCopies[0] = getWorkingCopy("/Tests/b171019/X.java", + "package b171019;\n" + + "interface X1 {\n" + + " /**\n" + + " * Main desc of foo in X1..\n" + + " */\n" + + " void foo(int x);\n" + + "}\n" + + "interface X2 {\n" + + " void foo(int x);\n" + + "}\n" + + "class X implements X1 {\n" + + " /**\n" + + " * X desc of foo..\n" + + " */\n" + + " void foo(int x){}\n" + + "}\n" + + "class Y extends X implements X2 {\n" + + " /**\n" + + " * {@inheritDoc}\n" + // should navigate to X.foo(int) + " */\n" + + " void foo(int x);\n\n" + + "}\n" + ); + IJavaElement[] elements = new IJavaElement[1]; + elements[0] = selectMethod(this.workingCopies[0], "@inheritDoc", 1); + assertElementsEqual("Invalid selection(s)", + "foo(int) [in X [in [Working copy] X.java [in b171019 [in [in Tests]]]]]", + elements + ); + } + + public void testBug171019e() throws CoreException { + this.wcOwner = new WorkingCopyOwner() {}; + this.workingCopies = new ICompilationUnit[1]; + this.workingCopies[0] = getWorkingCopy("/Tests/b171019/X.java", + "package b171019;\n" + + "interface X {\n" + + " /**\n" + + " * Main desc of foo..\n" + + " */\n" + + " void foo(int x);\n" + + "}\n" + + "interface Y {\n" + + " void foo(String str);\n" + + "}\n" + + "abstract class Z implements X, Y {\n" + + " /**\n" + + " * {@inheritDoc}\n" + // navigates to X.foo(int) + " */\n" + + " void foo(int x) {\n" + + " }\n" + + "}" + ); + IJavaElement[] elements = new IJavaElement[1]; + elements[0] = selectMethod(this.workingCopies[0], "@inheritDoc", 1); + assertElementsEqual("Invalid selection(s)", + "foo(int) [in X [in [Working copy] X.java [in b171019 [in [in Tests]]]]]", + elements + ); + } }