Bug 357325: [render] Method parameter annotations are not shown in
Javadoc hover/view
diff --git a/org.eclipse.jdt.ui.tests/ui/org/eclipse/jdt/ui/tests/core/JavaElementLabelsTest.java b/org.eclipse.jdt.ui.tests/ui/org/eclipse/jdt/ui/tests/core/JavaElementLabelsTest.java
index f03e08a..163a38d 100644
--- a/org.eclipse.jdt.ui.tests/ui/org/eclipse/jdt/ui/tests/core/JavaElementLabelsTest.java
+++ b/org.eclipse.jdt.ui.tests/ui/org/eclipse/jdt/ui/tests/core/JavaElementLabelsTest.java
@@ -612,4 +612,43 @@
 		lab= JavaElementLabels.getTextLabel(elem, JavaElementLabels.M_PARAMETER_TYPES | JavaElementLabels.USE_RESOLVED);
 		assertEqualString(lab, "asList(Integer...)");
 	}
+	
+	
+	public void testMethodLabelAnnotatedParameters() throws Exception {
+		IPackageFragmentRoot sourceFolder= JavaProjectHelper.addSourceContainer(fJProject1, "src");
+		IPackageFragment pack1= sourceFolder.createPackageFragment("org.test", false, null);
+		StringBuffer buf= new StringBuffer();
+		buf.append("package org.test;\n");
+		buf.append("\n");
+		buf.append("import java.lang.annotation.Retention;\n");
+		buf.append("import java.lang.annotation.RetentionPolicy;\n");
+		buf.append("\n");
+		buf.append("public class Annotations {\n");
+		buf.append("    void foo(@Outer(a=@Ann(\"Hello world\\r\\n\\t\\\"<'#@%^&\")) String param) { }\n");
+		buf.append("    \n");
+		buf.append("    void foo2(@Ann(value=\"\", cl=Annotations.class, ints={1, 2, -19},\n");
+		buf.append("            ch='\\0', sh= 0x7FFF, r= @Retention(RetentionPolicy.SOURCE)) String param) { }\n");
+		buf.append("}\n");
+		buf.append("@interface Ann {\n");
+		buf.append("    String value();\n");
+		buf.append("    Class<?> cl() default Ann.class;\n");
+		buf.append("    int[] ints() default {1, 2};\n");
+		buf.append("    char ch() default 'a';\n");
+		buf.append("    short sh() default 1;\n");
+		buf.append("    Retention r() default @Retention(RetentionPolicy.CLASS);\n");
+		buf.append("}\n");
+		buf.append("@interface Outer {\n");
+		buf.append("    Ann a();\n");
+		buf.append("}\n");
+		String content= buf.toString();
+		ICompilationUnit cu= pack1.createCompilationUnit("Annotations.java", content, false, null);
+
+		IJavaElement foo= cu.getElementAt(content.indexOf("foo"));
+		String lab= JavaElementLabels.getTextLabel(foo, JavaElementLabels.ALL_DEFAULT | JavaElementLabels.ALL_FULLY_QUALIFIED | JavaElementLabels.M_PARAMETER_ANNOTATIONS);
+		assertEqualString(lab, "org.test.Annotations.foo(@Outer(a=@Ann(value=\"Hello world\\r\\n\\t\\\"<'#@%^&\")) String)");
+
+		IJavaElement foo2= cu.getElementAt(content.indexOf("foo2"));
+		lab= JavaElementLabels.getTextLabel(foo2, JavaElementLabels.ALL_DEFAULT | JavaElementLabels.ALL_FULLY_QUALIFIED | JavaElementLabels.M_PARAMETER_ANNOTATIONS);
+		assertEqualString(lab, "org.test.Annotations.foo2(@Ann(value=\"\", cl=Annotations.class, ints={1, 2, -19}, ch='\\0', sh=32767, r=@Retention(value=RetentionPolicy.SOURCE)) String)");
+	}
 }
diff --git a/org.eclipse.jdt.ui/core extension/org/eclipse/jdt/internal/corext/dom/ASTNodes.java b/org.eclipse.jdt.ui/core extension/org/eclipse/jdt/internal/corext/dom/ASTNodes.java
index 1e7dcd3..c7ab214 100644
--- a/org.eclipse.jdt.ui/core extension/org/eclipse/jdt/internal/corext/dom/ASTNodes.java
+++ b/org.eclipse.jdt.ui/core extension/org/eclipse/jdt/internal/corext/dom/ASTNodes.java
@@ -38,6 +38,7 @@
 import org.eclipse.jdt.core.ITypeRoot;
 import org.eclipse.jdt.core.JavaModelException;
 import org.eclipse.jdt.core.compiler.IProblem;
+import org.eclipse.jdt.core.dom.AST;
 import org.eclipse.jdt.core.dom.ASTNode;
 import org.eclipse.jdt.core.dom.ASTVisitor;
 import org.eclipse.jdt.core.dom.AbstractTypeDeclaration;
@@ -45,6 +46,7 @@
 import org.eclipse.jdt.core.dom.ArrayType;
 import org.eclipse.jdt.core.dom.Assignment;
 import org.eclipse.jdt.core.dom.BodyDeclaration;
+import org.eclipse.jdt.core.dom.CharacterLiteral;
 import org.eclipse.jdt.core.dom.ChildListPropertyDescriptor;
 import org.eclipse.jdt.core.dom.ClassInstanceCreation;
 import org.eclipse.jdt.core.dom.CompilationUnit;
@@ -76,6 +78,7 @@
 import org.eclipse.jdt.core.dom.SimpleName;
 import org.eclipse.jdt.core.dom.SimpleType;
 import org.eclipse.jdt.core.dom.SingleVariableDeclaration;
+import org.eclipse.jdt.core.dom.StringLiteral;
 import org.eclipse.jdt.core.dom.StructuralPropertyDescriptor;
 import org.eclipse.jdt.core.dom.Type;
 import org.eclipse.jdt.core.dom.VariableDeclaration;
@@ -89,6 +92,7 @@
 import org.eclipse.jdt.internal.corext.util.Strings;
 
 import org.eclipse.jdt.internal.ui.JavaPlugin;
+import org.eclipse.jdt.internal.ui.javaeditor.ASTProvider;
 import org.eclipse.jdt.internal.ui.preferences.MembersOrderPreferenceCache;
 import org.eclipse.jdt.internal.ui.text.correction.ASTResolving;
 
@@ -947,4 +951,30 @@
 		return null;
 	}
 
+	/**
+	 * Escapes a string value to a literal that can be used in Java source.
+	 * 
+	 * @param stringValue the string value 
+	 * @return the escaped string
+	 * @see StringLiteral#getEscapedValue()
+	 */
+	public static String getEscapedStringLiteral(String stringValue) {
+		StringLiteral stringLiteral= AST.newAST(ASTProvider.SHARED_AST_LEVEL).newStringLiteral();
+		stringLiteral.setLiteralValue(stringValue);
+		return stringLiteral.getEscapedValue();
+	}
+
+	/**
+	 * Escapes a character value to a literal that can be used in Java source.
+	 * 
+	 * @param ch the character value 
+	 * @return the escaped string
+	 * @see CharacterLiteral#getEscapedValue()
+	 */
+	public static String getEscapedCharacterLiteral(char ch) {
+		CharacterLiteral characterLiteral= AST.newAST(ASTProvider.SHARED_AST_LEVEL).newCharacterLiteral();
+		characterLiteral.setCharValue(ch);
+		return characterLiteral.getEscapedValue();
+	}
+
 }
diff --git a/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/infoviews/JavadocView.java b/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/infoviews/JavadocView.java
index b783d2c..53f3c37 100644
--- a/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/infoviews/JavadocView.java
+++ b/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/infoviews/JavadocView.java
@@ -402,7 +402,7 @@
 
 	/** Flags used to render a label in the text widget. */
 	private static final long LABEL_FLAGS=  JavaElementLabels.ALL_FULLY_QUALIFIED
-		| JavaElementLabels.M_PRE_RETURNTYPE | JavaElementLabels.M_PARAMETER_TYPES | JavaElementLabels.M_PARAMETER_NAMES | JavaElementLabels.M_EXCEPTIONS
+		| JavaElementLabels.M_PRE_RETURNTYPE | JavaElementLabels.M_PARAMETER_ANNOTATIONS | JavaElementLabels.M_PARAMETER_TYPES | JavaElementLabels.M_PARAMETER_NAMES | JavaElementLabels.M_EXCEPTIONS
 		| JavaElementLabels.F_PRE_TYPE_SIGNATURE | JavaElementLabels.T_TYPE_PARAMETERS;
 
 
diff --git a/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/text/java/hover/JavadocHover.java b/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/text/java/hover/JavadocHover.java
index beaba82..593cd2b 100644
--- a/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/text/java/hover/JavadocHover.java
+++ b/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/text/java/hover/JavadocHover.java
@@ -66,10 +66,8 @@
 import org.eclipse.jdt.core.ITypeRoot;
 import org.eclipse.jdt.core.JavaCore;
 import org.eclipse.jdt.core.JavaModelException;
-import org.eclipse.jdt.core.dom.AST;
 import org.eclipse.jdt.core.dom.ASTNode;
 import org.eclipse.jdt.core.dom.ASTParser;
-import org.eclipse.jdt.core.dom.CharacterLiteral;
 import org.eclipse.jdt.core.dom.ClassInstanceCreation;
 import org.eclipse.jdt.core.dom.CompilationUnit;
 import org.eclipse.jdt.core.dom.ConstructorInvocation;
@@ -85,11 +83,11 @@
 import org.eclipse.jdt.core.dom.QualifiedType;
 import org.eclipse.jdt.core.dom.SimpleName;
 import org.eclipse.jdt.core.dom.SimpleType;
-import org.eclipse.jdt.core.dom.StringLiteral;
 import org.eclipse.jdt.core.dom.StructuralPropertyDescriptor;
 import org.eclipse.jdt.core.dom.SuperConstructorInvocation;
 import org.eclipse.jdt.core.formatter.DefaultCodeFormatterConstants;
 
+import org.eclipse.jdt.internal.corext.dom.ASTNodes;
 import org.eclipse.jdt.internal.corext.javadoc.JavaDocLocations;
 import org.eclipse.jdt.internal.corext.util.JdtFlags;
 import org.eclipse.jdt.internal.corext.util.Messages;
@@ -421,7 +419,7 @@
 	}
 
 	private static final long LABEL_FLAGS=  JavaElementLabels.ALL_FULLY_QUALIFIED
-		| JavaElementLabels.M_PRE_RETURNTYPE | JavaElementLabels.M_PARAMETER_TYPES | JavaElementLabels.M_PARAMETER_NAMES | JavaElementLabels.M_EXCEPTIONS
+		| JavaElementLabels.M_PRE_RETURNTYPE | JavaElementLabels.M_PARAMETER_ANNOTATIONS | JavaElementLabels.M_PARAMETER_TYPES | JavaElementLabels.M_PARAMETER_NAMES | JavaElementLabels.M_EXCEPTIONS
 		| JavaElementLabels.F_PRE_TYPE_SIGNATURE | JavaElementLabels.M_PRE_TYPE_PARAMETERS | JavaElementLabels.T_TYPE_PARAMETERS
 		| JavaElementLabels.USE_RESOLVED;
 	private static final long LOCAL_VARIABLE_FLAGS= LABEL_FLAGS & ~JavaElementLabels.F_FULLY_QUALIFIED | JavaElementLabels.F_POST_QUALIFIED;
@@ -766,10 +764,10 @@
 			return null;
 
 		if (constantValue instanceof String) {
-			return getEscapedStringLiteral((String) constantValue);
+			return ASTNodes.getEscapedStringLiteral((String) constantValue);
 
 		} else if (constantValue instanceof Character) {
-			String constantResult= getEscapedCharacterLiteral(((Character) constantValue).charValue());
+			String constantResult= ASTNodes.getEscapedCharacterLiteral(((Character) constantValue).charValue());
 
 			char charValue= ((Character) constantValue).charValue();
 			String hexString= Integer.toHexString(charValue);
@@ -812,23 +810,6 @@
 		return NodeFinder.perform(unit, hoverRegion.getOffset(),	hoverRegion.getLength());
 	}
 
-	private static String getEscapedStringLiteral(String stringValue) {
-		StringLiteral stringLiteral= AST.newAST(ASTProvider.SHARED_AST_LEVEL).newStringLiteral();
-		stringLiteral.setLiteralValue(stringValue);
-		String stringConstant= stringLiteral.getEscapedValue();
-		if (stringConstant.length() > 80) {
-			return stringConstant.substring(0, 80) + JavaElementLabels.ELLIPSIS_STRING;
-		} else {
-			return stringConstant;
-		}
-	}
-
-	private static String getEscapedCharacterLiteral(char ch) {
-		CharacterLiteral characterLiteral= AST.newAST(ASTProvider.SHARED_AST_LEVEL).newCharacterLiteral();
-		characterLiteral.setCharValue(ch);
-		return characterLiteral.getEscapedValue();
-	}
-	
 	/**
 	 * Creates and returns a formatted message for the given
 	 * constant with its hex value.
@@ -1062,6 +1043,7 @@
 	}
 
 	private static void addValue(StringBuffer buf, IJavaElement element, Object value) throws URISyntaxException {
+		// Note: To be bug-compatible with Javadoc from Java 5/6/7, we currently don't escape HTML tags in String-valued annotations.
 		if (value instanceof ITypeBinding) {
 			ITypeBinding typeBinding= (ITypeBinding)value;
 			IJavaElement type= typeBinding.getJavaElement();
@@ -1086,10 +1068,10 @@
 			addAnnotation(buf, element, annotationBinding);
 			
 		} else if (value instanceof String) {
-			buf.append(getEscapedStringLiteral((String)value));
+			buf.append(ASTNodes.getEscapedStringLiteral((String)value));
 			
 		} else if (value instanceof Character) {
-			buf.append(getEscapedCharacterLiteral(((Character)value).charValue()));
+			buf.append(ASTNodes.getEscapedCharacterLiteral(((Character)value).charValue()));
 			
 		} else if (value instanceof Object[]) {
 			Object[] values= (Object[])value;
diff --git a/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/viewsupport/JavaElementLabelComposer.java b/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/viewsupport/JavaElementLabelComposer.java
index 3c4ab9b..5f45c5e 100644
--- a/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/viewsupport/JavaElementLabelComposer.java
+++ b/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/viewsupport/JavaElementLabelComposer.java
@@ -27,6 +27,7 @@
 
 import org.eclipse.jdt.core.BindingKey;
 import org.eclipse.jdt.core.Flags;
+import org.eclipse.jdt.core.IAnnotation;
 import org.eclipse.jdt.core.IClassFile;
 import org.eclipse.jdt.core.IClasspathEntry;
 import org.eclipse.jdt.core.ICompilationUnit;
@@ -35,6 +36,7 @@
 import org.eclipse.jdt.core.IJavaElement;
 import org.eclipse.jdt.core.ILocalVariable;
 import org.eclipse.jdt.core.IMember;
+import org.eclipse.jdt.core.IMemberValuePair;
 import org.eclipse.jdt.core.IMethod;
 import org.eclipse.jdt.core.IPackageFragment;
 import org.eclipse.jdt.core.IPackageFragmentRoot;
@@ -43,6 +45,7 @@
 import org.eclipse.jdt.core.JavaModelException;
 import org.eclipse.jdt.core.Signature;
 
+import org.eclipse.jdt.internal.corext.dom.ASTNodes;
 import org.eclipse.jdt.internal.corext.util.JavaModelUtil;
 import org.eclipse.jdt.internal.corext.util.Messages;
 
@@ -212,7 +215,7 @@
 	private static String fgPkgNameAbbreviationPattern= ""; //$NON-NLS-1$
 	private static PackageNameAbbreviation[] fgPkgNameAbbreviation;
 
-	private final FlexibleBuffer fBuffer;
+	protected final FlexibleBuffer fBuffer;
 
 	private static final boolean getFlag(long flags, long flag) {
 		return (flags & flag) != 0;
@@ -429,11 +432,20 @@
 						}
 					}
 				}
+				
+				ILocalVariable[] annotatedParameters= null;
+				if (nParams > 0 && getFlag(flags, JavaElementLabels.M_PARAMETER_ANNOTATIONS)) {
+					annotatedParameters= method.getParameters();
+				}
 
 				for (int i= 0; i < nParams; i++) {
 					if (i > 0) {
 						fBuffer.append(JavaElementLabels.COMMA_STRING);
 					}
+					if (annotatedParameters != null && i < annotatedParameters.length) {
+						appendAnnotationLabels(annotatedParameters[i].getAnnotations(), flags);
+					}
+					
 					if (types != null) {
 						String paramSig= types[i];
 						if (renderVarargs && (i == nParams - 1)) {
@@ -541,6 +553,79 @@
 		}
 	}
 
+	protected void appendAnnotationLabels(IAnnotation[] annotations, long flags) throws JavaModelException {
+		for (int j= 0; j < annotations.length; j++) {
+			IAnnotation annotation= annotations[j];
+			appendAnnotationLabel(annotation, flags);
+			fBuffer.append(' ');
+		}
+	}
+
+	public void appendAnnotationLabel(IAnnotation annotation, long flags) throws JavaModelException {
+		fBuffer.append('@');
+		appendTypeSignatureLabel(annotation, Signature.createTypeSignature(annotation.getElementName(), false), flags);
+		IMemberValuePair[] memberValuePairs= annotation.getMemberValuePairs();
+		if (memberValuePairs.length == 0)
+			return;
+		fBuffer.append('(');
+		for (int i= 0; i < memberValuePairs.length; i++) {
+			if (i > 0)
+				fBuffer.append(JavaElementLabels.COMMA_STRING);
+			IMemberValuePair memberValuePair= memberValuePairs[i];
+			fBuffer.append(getMemberName(annotation, annotation.getElementName(), memberValuePair.getMemberName()));
+			fBuffer.append('=');
+			appendAnnotationValue(annotation, memberValuePair.getValue(), memberValuePair.getValueKind(), flags);
+		}
+		fBuffer.append(')');
+	}
+
+	private void appendAnnotationValue(IAnnotation annotation, Object value, int valueKind, long flags) throws JavaModelException {
+		// Note: To be bug-compatible with Javadoc from Java 5/6/7, we currently don't escape HTML tags in String-valued annotations.
+		if (value instanceof Object[]) {
+			fBuffer.append('{');
+			Object[] values= (Object[]) value;
+			for (int j= 0; j < values.length; j++) {
+				if (j > 0)
+					fBuffer.append(JavaElementLabels.COMMA_STRING);
+				value= values[j];
+				appendAnnotationValue(annotation, value, valueKind, flags);
+			}
+			fBuffer.append('}');
+		} else {
+			switch (valueKind) {
+				case IMemberValuePair.K_CLASS:
+					appendTypeSignatureLabel(annotation, Signature.createTypeSignature((String) value, false), flags);
+					fBuffer.append(".class"); //$NON-NLS-1$
+					break;
+				case IMemberValuePair.K_QUALIFIED_NAME:
+					String name = (String) value;
+					int lastDot= name.lastIndexOf('.');
+					if (lastDot != -1) {
+						String type= name.substring(0, lastDot);
+						String field= name.substring(lastDot + 1);
+						appendTypeSignatureLabel(annotation, Signature.createTypeSignature(type, false), flags);
+						fBuffer.append('.');
+						fBuffer.append(getMemberName(annotation, type, field));
+						break;
+					}
+//				case IMemberValuePair.K_SIMPLE_NAME: // can't implement, since parent type is not known
+					//$FALL-THROUGH$
+				case IMemberValuePair.K_ANNOTATION:
+					appendAnnotationLabel((IAnnotation) value, flags);
+					break;
+				case IMemberValuePair.K_STRING:
+					fBuffer.append(ASTNodes.getEscapedStringLiteral((String) value));
+					break;
+				case IMemberValuePair.K_CHAR:
+					fBuffer.append(ASTNodes.getEscapedCharacterLiteral(((Character) value).charValue()));
+					break;
+				default:
+					fBuffer.append(String.valueOf(value));
+					break;
+			}
+		}
+	}
+
 	private void appendCategoryLabel(IMember member, long flags) throws JavaModelException {
 		String[] categories= member.getCategories();
 		if (categories.length > 0) {
@@ -790,6 +875,18 @@
 		return Signature.getSimpleName(Signature.toString(Signature.getTypeErasure(typeSig)));
 	}
 
+	/**
+	 * Returns the simple name of the given member.
+	 *
+	 * @param enclosingElement the enclosing element
+	 * @param typeName the name of the member's declaring type
+	 * @param memberName the name of the member
+	 * @return the simple name of the member
+	 */
+	protected String getMemberName(IJavaElement enclosingElement, String typeName, String memberName) {
+		return memberName;
+	}
+
 	private void appendTypeArgumentSignaturesLabel(IJavaElement enclosingElement, String[] typeArgsSig, long flags) {
 		if (typeArgsSig.length > 0) {
 			fBuffer.append(getLT());
diff --git a/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/viewsupport/JavaElementLinks.java b/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/viewsupport/JavaElementLinks.java
index 7fc01fa..29175d0 100644
--- a/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/viewsupport/JavaElementLinks.java
+++ b/org.eclipse.jdt.ui/ui/org/eclipse/jdt/internal/ui/viewsupport/JavaElementLinks.java
@@ -144,6 +144,24 @@
 				return typeName;
 			}
 		}
+		
+		@Override
+		protected String getMemberName(IJavaElement enclosingElement, String typeName, String memberName) {
+			try {
+				String uri= createURI(JAVADOC_SCHEME, enclosingElement, typeName, memberName, null);
+				return createHeaderLink(uri, memberName);
+			} catch (URISyntaxException e) {
+				JavaPlugin.log(e);
+				return memberName;
+			}
+		}
+		
+		@Override
+		protected void appendAnnotationLabels(IAnnotation[] annotations, long flags) throws JavaModelException {
+			fBuffer.append("<span style='font-weight:normal;'>"); //$NON-NLS-1$
+			super.appendAnnotationLabels(annotations, flags);
+			fBuffer.append("</span>"); //$NON-NLS-1$
+		}
 	}
 
 	public static final String OPEN_LINK_SCHEME= "eclipse-open"; //$NON-NLS-1$
diff --git a/org.eclipse.jdt.ui/ui/org/eclipse/jdt/ui/JavaElementLabels.java b/org.eclipse.jdt.ui/ui/org/eclipse/jdt/ui/JavaElementLabels.java
index e422ec2..93f2772 100644
--- a/org.eclipse.jdt.ui/ui/org/eclipse/jdt/ui/JavaElementLabels.java
+++ b/org.eclipse.jdt.ui/ui/org/eclipse/jdt/ui/JavaElementLabels.java
@@ -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
@@ -74,6 +74,14 @@
 	public final static long M_PARAMETER_NAMES= 1L << 1;
 
 	/**
+	 * Method labels contain parameter annotations.
+	 * E.g. <code>foo(@NonNull int)</code>.
+	 * This flag is only valid if {@link #M_PARAMETER_NAMES} or {@link #M_PARAMETER_TYPES} is also set.
+	 * @since 3.8
+	 */
+	public final static long M_PARAMETER_ANNOTATIONS= 1L << 52;
+
+	/**
 	 * Method names contain type parameters prepended.
 	 * e.g. <code>&lt;A&gt; foo(A index)</code>
 	 */