| /******************************************************************************* |
| * Copyright (c) 2015 GK Software AG 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: |
| * Stephan Herrmann - initial API and implementation |
| *******************************************************************************/ |
| package org.eclipse.jdt.internal.ui.viewsupport; |
| |
| import static org.eclipse.jdt.internal.ui.viewsupport.JavaElementLinks.JAVADOC_SCHEME; |
| import static org.eclipse.jdt.internal.ui.viewsupport.JavaElementLinks.createHeaderLink; |
| import static org.eclipse.jdt.internal.ui.viewsupport.JavaElementLinks.createURI; |
| |
| import java.net.URISyntaxException; |
| |
| import org.eclipse.jface.viewers.StyledString; |
| |
| import org.eclipse.jdt.core.Flags; |
| import org.eclipse.jdt.core.IJavaElement; |
| import org.eclipse.jdt.core.IMethod; |
| import org.eclipse.jdt.core.IType; |
| import org.eclipse.jdt.core.JavaModelException; |
| import org.eclipse.jdt.core.dom.IAnnotationBinding; |
| import org.eclipse.jdt.core.dom.IBinding; |
| import org.eclipse.jdt.core.dom.IMemberValuePairBinding; |
| import org.eclipse.jdt.core.dom.IMethodBinding; |
| import org.eclipse.jdt.core.dom.IPackageBinding; |
| import org.eclipse.jdt.core.dom.ITypeBinding; |
| import org.eclipse.jdt.core.dom.IVariableBinding; |
| |
| import org.eclipse.jdt.internal.corext.dom.ASTNodes; |
| import org.eclipse.jdt.internal.corext.util.JavaModelUtil; |
| import org.eclipse.jdt.internal.corext.util.Messages; |
| |
| import org.eclipse.jdt.ui.JavaElementLabels; |
| |
| import org.eclipse.jdt.internal.ui.JavaPlugin; |
| import org.eclipse.jdt.internal.ui.JavaUIMessages; |
| import org.eclipse.jdt.internal.ui.viewsupport.JavaElementLinks.JavaElementLinkedLabelComposer; |
| |
| public class BindingLinkedLabelComposer extends JavaElementLinkedLabelComposer { |
| |
| private static final String PARAMETER_ELLIPSIS_LABEL= "(...)"; //$NON-NLS-1$ |
| private static final String ANON_TYPE_TAIL= "() {...}"; //$NON-NLS-1$ |
| private static final String LAMBDA_LABEL= "() -> {...}"; //$NON-NLS-1$ |
| private static final String MISSING_LABEL= "MISSING"; //$NON-NLS-1$ |
| |
| private static final String INIT_NAME= ""; //$NON-NLS-1$ |
| |
| private static final long M_ALL_QUALIFIED= JavaElementLabels.M_PARAMETER_TYPES | JavaElementLabels.M_FULLY_QUALIFIED; |
| |
| private static final long IS_POST_QUALIFICATION= 1L << 63; |
| private static final long TP_BOUNDS= 1L << 62; |
| |
| private IJavaElement fEnclosingElement; |
| private boolean fIsFromSource; |
| |
| public BindingLinkedLabelComposer(IJavaElement enclosingElement, StringBuffer buffer, boolean isFromSource) { |
| super(enclosingElement, buffer); |
| fEnclosingElement= enclosingElement; |
| fIsFromSource= isFromSource; |
| } |
| |
| /** |
| * <p>Supported flags: |
| * <ul> |
| * <li>{@link JavaElementLabels#ALL_FULLY_QUALIFIED} (set)</li> |
| * <li>{@link JavaElementLabels#M_PRE_RETURNTYPE}</li> |
| * <li>{@link JavaElementLabels#M_PARAMETER_ANNOTATIONS}</li> |
| * <li>{@link JavaElementLabels#M_PARAMETER_TYPES}</li> |
| * <li>{@link JavaElementLabels#M_PARAMETER_NAMES}</li> |
| * <li>{@link JavaElementLabels#M_EXCEPTIONS}</li> |
| * <li>{@link JavaElementLabels#M_PRE_TYPE_PARAMETERS}</li> |
| * <li>{@link JavaElementLabels#F_PRE_TYPE_SIGNATURE}</li> |
| * <li>{@link JavaElementLabels#T_TYPE_PARAMETERS}</li> |
| * <li>{@link JavaElementLabels#USE_RESOLVED}</li> |
| * <li>{@link JavaElementLabels#M_POST_QUALIFIED}</li> |
| * <li>{@link JavaElementLabels#F_POST_QUALIFIED}</li> |
| * <li>{@link JavaElementLabels#T_POST_QUALIFIED}</li> |
| * <li>{@link JavaElementLabels#TP_POST_QUALIFIED}</li> |
| * </ul> |
| * @param binding a binding to be rendered |
| * @param flags rendering flags, see above for supported values. |
| */ |
| public void appendBindingLabel(IBinding binding, long flags) { |
| switch (binding.getKind()) { |
| case IBinding.METHOD: |
| appendMethodBindingLabel((IMethodBinding) binding, flags); |
| return; |
| case IBinding.TYPE: |
| appendTypeBindingLabel((ITypeBinding) binding, flags | TP_BOUNDS); |
| return; |
| case IBinding.VARIABLE: |
| appendVariableLabel((IVariableBinding) binding, flags); |
| return; |
| case IBinding.PACKAGE: |
| appendPackageLabel((IPackageBinding) binding, flags); |
| break; |
| case IBinding.ANNOTATION: |
| case IBinding.MEMBER_VALUE_PAIR: |
| // not used for hovers |
| } |
| } |
| |
| private void appendMethodBindingLabel(IMethodBinding method, long flags) { |
| long qualificationFlags = flags & (QUALIFIER_FLAGS | JavaElementLabels.ALL_FULLY_QUALIFIED | JavaElementLabels.T_CONTAINER_QUALIFIED); |
| qualificationFlags |= TP_BOUNDS; |
| if (getFlag(flags, JavaElementLabels.T_POST_QUALIFIED)) |
| flags |= JavaElementLabels.M_POST_QUALIFIED; |
| flags &= ~(JavaElementLabels.T_POST_QUALIFIED | TP_BOUNDS); |
| IMethodBinding origMethod= method; |
| IBinding declaringMember= method.getDeclaringMember(); |
| if (declaringMember != null) { |
| if (isEnclosingElement(method.getJavaElement())) { |
| // don't render top-level as lambda |
| method= method.getMethodDeclaration(); // for the main part show the SAM instead |
| } else { |
| appendLambdaLabel(method, flags); |
| return; |
| } |
| } |
| |
| if (!getFlag(flags, JavaElementLabels.USE_RESOLVED)) { |
| method= method.getMethodDeclaration(); |
| } |
| if (fIsFromSource) { |
| flags &= ~JavaElementLabels.T_FULLY_QUALIFIED; |
| } |
| boolean isInitializer= method.getName().equals(INIT_NAME); |
| |
| // type parameters |
| if (getFlag(flags, JavaElementLabels.M_PRE_TYPE_PARAMETERS)) { |
| ITypeBinding[] typeParameters= method.getTypeParameters(); |
| if (typeParameters.length > 0) { |
| appendTypeArgumentsBindingLabel(typeParameters, null, flags | TP_BOUNDS); |
| fBuffer.append(' '); |
| } |
| } |
| |
| // return type |
| if (getFlag(flags, JavaElementLabels.M_PRE_RETURNTYPE) && !method.isConstructor() && !isInitializer) { |
| appendTypeBindingLabel(method.getReturnType(), flags); |
| fBuffer.append(' '); |
| } |
| |
| // qualification |
| if (getFlag(flags, JavaElementLabels.M_FULLY_QUALIFIED | JavaElementLabels.T_CONTAINER_QUALIFIED)) { |
| appendTypeBindingLabel(method.getDeclaringClass(), qualificationFlags); |
| fBuffer.append('.'); |
| } |
| |
| if (isInitializer) { |
| fBuffer.append(JavaUIMessages.JavaElementLabels_initializer); |
| } else { |
| appendNameLink(method, origMethod); |
| } |
| |
| if (!isInitializer) { |
| // constructor type arguments |
| if (getFlag(flags, JavaElementLabels.T_TYPE_PARAMETERS) && method.isConstructor()) { |
| appendTypeArgumentsBindingLabel(method.getTypeArguments(), null, flags); |
| } |
| |
| // parameters |
| fBuffer.append('('); |
| if (getFlag(flags, JavaElementLabels.M_PARAMETER_TYPES | JavaElementLabels.M_PARAMETER_NAMES)) { |
| ITypeBinding[] types= null; |
| int nParams= 0; |
| boolean renderVarargs= false; |
| boolean isPolymorphic= false; |
| IMethod iMethod= getIMethod(method); |
| if (getFlag(flags, JavaElementLabels.M_PARAMETER_TYPES)) { |
| types= method.getParameterTypes(); |
| nParams= types.length; |
| renderVarargs= method.isVarargs(); |
| // retrieval of flag isPolymorphic uses strategy from JavaElementLabelComposer: |
| if (getFlag(flags, JavaElementLabels.USE_RESOLVED) |
| && iMethod != null |
| && iMethod.isResolved() |
| && iMethod.getParameterTypes().length == 1 |
| && JavaModelUtil.isPolymorphicSignature(iMethod)) { |
| renderVarargs= false; |
| isPolymorphic= true; |
| } |
| } |
| String[] names= null; |
| if (getFlag(flags, JavaElementLabels.M_PARAMETER_NAMES) && iMethod != null) { |
| // mostly from JavaElementLabelComposer: |
| try { |
| names= iMethod.getParameterNames(); |
| } catch (JavaModelException e) { |
| JavaPlugin.log(e); |
| } |
| if (isPolymorphic) { |
| // handled specially below |
| } else if (types == null) { |
| nParams= names.length; |
| } else { // types != null |
| if (nParams != names.length) { |
| if (types.length > names.length) { |
| // see https://bugs.eclipse.org/bugs/show_bug.cgi?id=99137 |
| nParams= names.length; |
| ITypeBinding[] typesWithoutSyntheticParams= new ITypeBinding[nParams]; |
| System.arraycopy(types, types.length - nParams, typesWithoutSyntheticParams, 0, nParams); |
| types= typesWithoutSyntheticParams; |
| } else { |
| // https://bugs.eclipse.org/bugs/show_bug.cgi?id=101029 |
| // JavaPlugin.logErrorMessage("JavaElementLabels: Number of param types(" + nParams + ") != number of names(" + names.length + "): " + method.getElementName()); //$NON-NLS-1$//$NON-NLS-2$//$NON-NLS-3$ |
| names= null; // no names rendered |
| } |
| } |
| } |
| } |
| IAnnotationBinding[][] parameterAnnotations= null; |
| if (nParams > 0 && getFlag(flags, JavaElementLabels.M_PARAMETER_ANNOTATIONS)) { |
| parameterAnnotations= new IAnnotationBinding[nParams][]; |
| for (int i= 0; i < nParams; i++) { |
| parameterAnnotations[i]= method.getParameterAnnotations(i); |
| } |
| } |
| |
| for (int i= 0; i < nParams; i++) { |
| if (i > 0) |
| fBuffer.append(JavaElementLabels.COMMA_STRING); |
| if (parameterAnnotations != null && i < parameterAnnotations.length) { |
| appendAnnotationLabels(parameterAnnotations[i], flags); |
| } |
| |
| if (types != null) { |
| ITypeBinding paramSig= types[i]; |
| if (renderVarargs && (i == nParams - 1)) { |
| int newDim= paramSig.getDimensions() - 1; |
| appendTypeBindingLabel(paramSig.getElementType(), flags); |
| for (int k= 0; k < newDim; k++) { |
| fBuffer.append('[').append(']'); |
| } |
| fBuffer.append(JavaElementLabels.ELLIPSIS_STRING); |
| } else { |
| appendTypeBindingLabel(paramSig, flags); |
| } |
| } |
| if (names != null) { |
| if (types != null) { |
| fBuffer.append(' '); |
| } |
| if (isPolymorphic) { |
| fBuffer.append(names[0] + i); |
| } else { |
| fBuffer.append(names[i]); |
| } |
| } |
| } |
| } else { |
| if (method.getParameterTypes().length > 0) { |
| fBuffer.append(JavaElementLabels.ELLIPSIS_STRING); |
| } |
| } |
| fBuffer.append(')'); |
| |
| |
| if (getFlag(flags, JavaElementLabels.M_EXCEPTIONS)) { |
| ITypeBinding[] types= method.getExceptionTypes(); |
| if (types.length > 0) { |
| fBuffer.append(" throws "); //$NON-NLS-1$ |
| for (int i= 0; i < types.length; i++) { |
| if (i > 0) |
| fBuffer.append(JavaElementLabels.COMMA_STRING); |
| appendTypeBindingLabel(types[i], flags); |
| } |
| } |
| } |
| |
| if (getFlag(flags, JavaElementLabels.M_APP_TYPE_PARAMETERS)) { |
| int offset= fBuffer.length(); |
| if (method.isParameterizedMethod()) { |
| appendTypeArgumentsBindingLabel(method.getTypeArguments(), String.valueOf(' '), flags); |
| } else { |
| appendTypeArgumentsBindingLabel(method.getTypeParameters(), String.valueOf(' '), flags|TP_BOUNDS); |
| } |
| if (getFlag(flags, JavaElementLabels.COLORIZE) && offset != fBuffer.length()) { |
| fBuffer.setStyle(offset, fBuffer.length() - offset, StyledString.DECORATIONS_STYLER); |
| } |
| } |
| } |
| |
| // post qualification |
| if (getFlag(flags, JavaElementLabels.M_POST_QUALIFIED)) { |
| fBuffer.append(JavaElementLabels.CONCAT_STRING); |
| if (declaringMember != null && origMethod != method) { |
| // show lambda as qualification of a SAM |
| appendLambdaLabel(origMethod, qualificationFlags | JavaElementLabels.ALL_FULLY_QUALIFIED); |
| fBuffer.append(' '); |
| appendTypeBindingLabel(origMethod.getMethodDeclaration().getDeclaringClass(), M_ALL_QUALIFIED | IS_POST_QUALIFICATION); |
| } else { |
| appendTypeBindingLabel(method.getDeclaringClass(), getPostQualificationFlags(flags)); |
| } |
| } |
| } |
| |
| private void appendVariableLabel(IVariableBinding variable, long flags) { |
| long qualificationFlags = flags & (QUALIFIER_FLAGS | JavaElementLabels.ALL_FULLY_QUALIFIED | JavaElementLabels.T_CONTAINER_QUALIFIED); |
| qualificationFlags |= TP_BOUNDS; |
| if (fIsFromSource) { |
| flags &= ~JavaElementLabels.T_FULLY_QUALIFIED; |
| } |
| if (getFlag(flags, JavaElementLabels.T_POST_QUALIFIED)) |
| flags |= JavaElementLabels.F_POST_QUALIFIED; |
| flags &= ~(JavaElementLabels.T_POST_QUALIFIED | TP_BOUNDS); |
| |
| if (getFlag(flags, JavaElementLabels.F_PRE_TYPE_SIGNATURE) && !Flags.isEnum(variable.getModifiers())) { |
| appendTypeBindingLabel(variable.getType(), flags); |
| fBuffer.append(' '); |
| } |
| |
| // qualification |
| if (getFlag(flags, JavaElementLabels.F_FULLY_QUALIFIED)) { |
| appendVariableQualification(variable, qualificationFlags); |
| fBuffer.append('.'); |
| } |
| if (variable.isField()) { |
| appendNameLink(variable, variable); |
| } else { |
| fBuffer.append(variable.getName()); |
| } |
| |
| if (getFlag(flags, JavaElementLabels.F_POST_QUALIFIED)) { |
| fBuffer.append(JavaElementLabels.CONCAT_STRING); |
| appendVariableQualification(variable, getPostQualificationFlags(qualificationFlags)); |
| } |
| } |
| |
| private void appendVariableQualification(IVariableBinding variable, long flags) { |
| if (variable.isField()) { |
| appendTypeBindingLabel(variable.getDeclaringClass(), flags); |
| } else { |
| IMethodBinding declaringMethod= variable.getDeclaringMethod(); |
| if (declaringMethod != null) { |
| IBinding declaringMember= declaringMethod.getDeclaringMember(); |
| if (declaringMember != null) { |
| appendLambdaLabel(declaringMethod, flags); |
| fBuffer.append(' '); |
| appendMethodBindingLabel(declaringMethod.getMethodDeclaration(), (flags & QUALIFIER_FLAGS) | M_ALL_QUALIFIED); |
| } else { |
| appendMethodBindingLabel(declaringMethod, flags | M_ALL_QUALIFIED); |
| } |
| } else { |
| // workaround for: local variable inside initializer doesn't yet expose the #getDeclaringMethod(); |
| IJavaElement element= variable.getJavaElement(); |
| if (element != null && element.getParent() != null) |
| appendElementLabel(element.getParent(), flags); |
| else |
| fBuffer.append(MISSING_LABEL); |
| } |
| } |
| } |
| |
| private void appendLambdaLabel(IMethodBinding lambdaBinding, long flags) { |
| long qualificationFlags = flags & (QUALIFIER_FLAGS | JavaElementLabels.ALL_FULLY_QUALIFIED); |
| if (fIsFromSource) { |
| flags &= ~JavaElementLabels.T_FULLY_QUALIFIED; |
| } |
| if (getFlag(flags, JavaElementLabels.M_FULLY_QUALIFIED | JavaElementLabels.T_FULLY_QUALIFIED)) { |
| appendBindingLabel(lambdaBinding.getDeclaringMember(), qualificationFlags); |
| fBuffer.append('.'); |
| fBuffer.append(LAMBDA_LABEL); |
| } else { |
| IMethodBinding sam= lambdaBinding.getMethodDeclaration(); |
| appendMethodBindingLabel(sam, flags & ~JavaElementLabels.ALL_POST_QUALIFIED); |
| } |
| if (getFlag(flags, JavaElementLabels.M_POST_QUALIFIED|JavaElementLabels.T_POST_QUALIFIED)) { |
| fBuffer.append(JavaElementLabels.CONCAT_STRING); |
| qualificationFlags |= JavaElementLabels.ALL_FULLY_QUALIFIED; |
| appendBindingLabel(lambdaBinding.getDeclaringMember(), qualificationFlags); |
| fBuffer.append('.'); |
| fBuffer.append(LAMBDA_LABEL); |
| fBuffer.append(' '); |
| appendTypeBindingLabel(lambdaBinding.getDeclaringClass(), flags & (QUALIFIER_FLAGS | JavaElementLabels.ALL_FULLY_QUALIFIED) | IS_POST_QUALIFICATION); |
| } |
| } |
| |
| private void appendTypeBindingLabel(ITypeBinding typeBinding, long flags) { |
| long typeRefFlags= flags & ~JavaElementLabels.ALL_POST_QUALIFIED; |
| if (fIsFromSource) { |
| typeRefFlags &= ~(JavaElementLabels.ALL_FULLY_QUALIFIED | TP_BOUNDS); |
| } |
| // qualification of anonymous (class or lambda): |
| IBinding declaringMember= typeBinding.getDeclaringMember(); |
| if (declaringMember != null |
| && getFlag(flags, JavaElementLabels.T_FULLY_QUALIFIED | JavaElementLabels.T_CONTAINER_QUALIFIED)) |
| { |
| long qualificationFlags= flags; |
| if (getFlag(flags, JavaElementLabels.T_FULLY_QUALIFIED)) |
| qualificationFlags |= JavaElementLabels.ALL_FULLY_QUALIFIED; |
| else if (getFlag(flags, JavaElementLabels.T_CONTAINER_QUALIFIED)) |
| qualificationFlags |= (JavaElementLabels.F_FULLY_QUALIFIED | JavaElementLabels.M_FULLY_QUALIFIED); |
| appendBindingLabel(declaringMember, qualificationFlags); |
| fBuffer.append('.'); |
| flags &= ~(JavaElementLabels.T_FULLY_QUALIFIED | JavaElementLabels.M_FULLY_QUALIFIED | JavaElementLabels.T_CONTAINER_QUALIFIED); // qualification is done |
| } |
| if (!typeBinding.isArray()) { // different textual order for type annotations on array types |
| appendAnnotationLabels(typeBinding.getTypeAnnotations(), typeRefFlags); |
| } |
| if (typeBinding.isPrimitive()) { |
| fBuffer.append(typeBinding.getName()); |
| } else if (typeBinding.isArray()) { |
| appendTypeBindingLabel(typeBinding.getElementType(), flags); |
| IAnnotationBinding[] typeAnnotations= typeBinding.getTypeAnnotations(); |
| if (typeAnnotations.length > 0) { |
| fBuffer.append(' '); |
| appendAnnotationLabels(typeAnnotations, typeRefFlags); |
| } |
| for (int dim= typeBinding.getDimensions(); dim > 0; dim--) { |
| fBuffer.append('[').append(']'); |
| } |
| } else if (typeBinding.isClass() || typeBinding.isInterface() || typeBinding.isEnum()) { |
| fBuffer.append(getTypeLink(typeBinding, flags)); |
| if (getFlag(flags, JavaElementLabels.T_TYPE_PARAMETERS)) { |
| ITypeBinding[] typeArguments= typeBinding.getTypeArguments(); |
| if (typeArguments.length > 0) { |
| appendTypeArgumentsBindingLabel(typeArguments, null, typeRefFlags); |
| } else { |
| ITypeBinding[] typeParameters= typeBinding.getTypeParameters(); |
| appendTypeArgumentsBindingLabel(typeParameters, null, typeRefFlags | TP_BOUNDS); |
| } |
| } |
| } else if (typeBinding.isParameterizedType()) { |
| fBuffer.append(getTypeLink(typeBinding.getTypeDeclaration(), flags)); |
| fBuffer.append(getLT()); |
| ITypeBinding[] typeArguments= typeBinding.getTypeArguments(); |
| for (int i= 0; i < typeArguments.length; i++) { |
| if (i > 0) |
| fBuffer.append(JavaElementLabels.COMMA_STRING); |
| appendTypeBindingLabel(typeArguments[i], typeRefFlags); |
| } |
| fBuffer.append(getGT()); |
| } else if (typeBinding.isTypeVariable()) { |
| appendNameLink(typeBinding, typeBinding); |
| if (getFlag(flags, TP_BOUNDS)) { |
| ITypeBinding[] bounds= typeBinding.getTypeBounds(); |
| if (hasRelevantBound(bounds)) { |
| fBuffer.append(" extends "); //$NON-NLS-1$ |
| for (int i= 0; i < bounds.length; i++) { |
| if (i > 0) |
| fBuffer.append(" & "); //$NON-NLS-1$ |
| appendTypeBindingLabel(bounds[i], (typeRefFlags | JavaElementLabels.T_TYPE_PARAMETERS) & ~TP_BOUNDS); |
| } |
| } |
| } |
| // post qualification |
| if (getFlag(flags, JavaElementLabels.TP_POST_QUALIFIED)) { |
| fBuffer.append(JavaElementLabels.CONCAT_STRING); |
| if (typeBinding.getDeclaringClass() != null) |
| appendTypeBindingLabel(typeBinding.getDeclaringClass(), getPostQualificationFlags(flags) & ~JavaElementLabels.T_TYPE_PARAMETERS); |
| else |
| appendMethodBindingLabel(typeBinding.getDeclaringMethod(), getPostQualificationFlags(flags) & ~JavaElementLabels.M_APP_TYPE_PARAMETERS); |
| } |
| return; |
| } else if (typeBinding.isAnnotation()) { |
| fBuffer.append(getTypeLink(typeBinding, flags)); |
| } else if (typeBinding.isWildcardType()) { |
| ITypeBinding bound= typeBinding.getBound(); |
| if (bound == null || bound.getSuperclass() == null) { // no relevant bound / only j.l.Object |
| fBuffer.append('?'); |
| } else { |
| if (typeBinding.isUpperbound()) |
| fBuffer.append("? extends "); //$NON-NLS-1$ |
| else |
| fBuffer.append("? super "); //$NON-NLS-1$ |
| appendTypeBindingLabel(bound, typeRefFlags); |
| } |
| } else if (typeBinding.isCapture()) { |
| appendTypeBindingLabel(typeBinding.getWildcard(), flags); |
| } |
| // post qualification |
| if (getFlag(flags, JavaElementLabels.T_POST_QUALIFIED)) { |
| // search the innermost declaring thing: |
| IBinding declaringBinding= typeBinding.getDeclaringMember(); |
| if (declaringBinding == null) { |
| declaringBinding= typeBinding.getDeclaringMethod(); |
| if (declaringBinding == null) |
| declaringBinding= typeBinding.getDeclaringClass(); |
| if (declaringBinding == null) |
| declaringBinding= typeBinding.getPackage(); |
| } |
| if (declaringBinding != null) { |
| fBuffer.append(JavaElementLabels.CONCAT_STRING); |
| // heuristics: JavaElementsLabelComposer does not include method parameters in post qualification of local type: |
| appendBindingLabel(declaringBinding, getPostQualificationFlags(flags) & ~JavaElementLabels.M_PARAMETER_TYPES); |
| } |
| } |
| } |
| |
| private void appendTypeArgumentsBindingLabel(ITypeBinding[] parameters, String separator, long flags) { |
| if (parameters.length > 0) { |
| if (separator != null) |
| fBuffer.append(separator); |
| fBuffer.append(getLT()); |
| for (int i = 0; i < parameters.length; i++) { |
| if (i > 0) |
| fBuffer.append(JavaElementLabels.COMMA_STRING); |
| appendTypeBindingLabel(parameters[i], flags); |
| } |
| fBuffer.append(getGT()); |
| } |
| } |
| |
| private void appendAnnotationLabels(IAnnotationBinding[] annotationBindings, long flags) { |
| for (int i= 0; i < annotationBindings.length; i++) { |
| appendAnnotationLabel(annotationBindings[i], flags); |
| fBuffer.append(' '); |
| } |
| } |
| |
| private void appendAnnotationLabel(IAnnotationBinding annotation, long flags) { |
| fBuffer.append('@'); |
| appendTypeBindingLabel(annotation.getAnnotationType(), flags); |
| IMemberValuePairBinding[] memberValuePairs= annotation.getDeclaredMemberValuePairs(); |
| if (memberValuePairs.length == 0) |
| return; |
| if (fIsFromSource) { |
| flags &= ~JavaElementLabels.T_FULLY_QUALIFIED; |
| } |
| fBuffer.append('('); |
| for (int i= 0; i < memberValuePairs.length; i++) { |
| if (i > 0) |
| fBuffer.append(JavaElementLabels.COMMA_STRING); |
| IMemberValuePairBinding memberValuePair= memberValuePairs[i]; |
| fBuffer.append(getMemberName(fEnclosingElement, annotation.getName(), memberValuePair.getName())); |
| fBuffer.append('='); |
| long valueFlags= flags & ~(JavaElementLabels.F_PRE_TYPE_SIGNATURE|JavaElementLabels.M_PRE_RETURNTYPE|JavaElementLabels.ALL_POST_QUALIFIED); |
| appendAnnotationValue(annotation, memberValuePair.getValue(), valueFlags); |
| } |
| fBuffer.append(')'); |
| } |
| |
| private void appendAnnotationValue(IAnnotationBinding annotation, Object value, long flags) { |
| // 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, flags); |
| } |
| fBuffer.append('}'); |
| } else { |
| if (value instanceof ITypeBinding) { |
| appendTypeBindingLabel((ITypeBinding) value, flags); |
| fBuffer.append(".class"); //$NON-NLS-1$ |
| } else if (value instanceof String) { |
| fBuffer.append(htmlEscape(ASTNodes.getEscapedStringLiteral((String) value))); |
| } else if (value instanceof IVariableBinding) { |
| appendVariableLabel((IVariableBinding) value, flags); |
| } else if (value instanceof IAnnotationBinding) { |
| appendAnnotationLabel((IAnnotationBinding) value, flags); |
| } else if (value instanceof Character) { |
| fBuffer.append(ASTNodes.getEscapedCharacterLiteral(((Character) value).charValue())); |
| } else { // other primitive literals |
| fBuffer.append(String.valueOf(value)); |
| } |
| } |
| } |
| |
| private void appendPackageLabel(IPackageBinding binding, long flags) { |
| appendAnnotationLabels(binding.getAnnotations(), flags); |
| String qualifiedName= binding.getName(); |
| if (qualifiedName.length() > 0) { |
| IJavaElement packageElement= binding.getJavaElement(); |
| try { |
| String uri= createURI(JAVADOC_SCHEME, packageElement); |
| fBuffer.append(createHeaderLink(uri, qualifiedName)); |
| } catch (URISyntaxException e) { |
| JavaPlugin.log(e); |
| fBuffer.append(qualifiedName); |
| } |
| } else { |
| fBuffer.append("(default package)"); //$NON-NLS-1$ |
| } |
| } |
| |
| // consider only relevant bounds / ignore j.l.Object |
| private boolean hasRelevantBound(ITypeBinding[] bounds) { |
| if (bounds != null) { |
| for (int i= 0; i < bounds.length; i++) |
| if (bounds[i].isInterface() || bounds[i].getSuperclass() != null) |
| return true; |
| } |
| return false; |
| } |
| |
| private long getPostQualificationFlags(long flags) { |
| flags |= JavaElementLabels.ALL_FULLY_QUALIFIED | JavaElementLabels.M_PARAMETER_TYPES | IS_POST_QUALIFICATION; |
| flags &= ~JavaElementLabels.ALL_POST_QUALIFIED; |
| flags &= ~(JavaElementLabels.T_TYPE_PARAMETERS | |
| JavaElementLabels.M_PRE_TYPE_PARAMETERS | JavaElementLabels.M_PRE_RETURNTYPE | JavaElementLabels.M_PARAMETER_NAMES); |
| return flags; |
| } |
| |
| // applicable for types, methods & fields |
| private void appendNameLink(IBinding binding, IBinding origBinding) { |
| try { |
| IJavaElement javaElement= origBinding.getJavaElement(); |
| if (!isEnclosingElement(javaElement)) { |
| IJavaElement element= binding.getJavaElement(); |
| if (element == null) // extra safety, not seen during tests |
| element= fEnclosingElement; |
| String uri= createURI(JAVADOC_SCHEME, element); |
| String title= getLinkTitle(binding); |
| if (title != null) { |
| title= Messages.format(JavaUIMessages.JavaElementLinks_title, title); |
| } else { |
| title= ""; //$NON-NLS-1$ |
| } |
| fBuffer.append(createHeaderLink(uri, binding.getName(), title)); |
| return; |
| } |
| } catch (URISyntaxException e) { |
| JavaPlugin.log(e); |
| } |
| fBuffer.append(binding.getName()); |
| } |
| |
| private String getLinkTitle(IBinding binding) { |
| if (binding instanceof ITypeBinding) { |
| IMethodBinding declaringMethod= ((ITypeBinding) binding).getDeclaringMethod(); |
| String title= null; |
| ITypeBinding declaringClass= null; |
| if (declaringMethod != null) { |
| title= '.'+declaringMethod.getName()+PARAMETER_ELLIPSIS_LABEL; |
| declaringClass= declaringMethod.getDeclaringClass(); |
| } else { |
| declaringClass= ((ITypeBinding) binding).getDeclaringClass(); |
| } |
| if (declaringClass != null) { |
| String typeName= getTypeName(declaringClass, JavaElementLabels.ALL_FULLY_QUALIFIED); |
| return title != null ? typeName+title : typeName; |
| } |
| } else if (binding instanceof IMethodBinding) { |
| return getTypeName(((IMethodBinding) binding).getDeclaringClass(), JavaElementLabels.ALL_FULLY_QUALIFIED); |
| } else if (binding instanceof IVariableBinding) { |
| return getTypeName(((IVariableBinding) binding).getDeclaringClass(), JavaElementLabels.ALL_FULLY_QUALIFIED); |
| } |
| return null; |
| } |
| |
| private String getTypeLink(ITypeBinding typeBinding, long flags) { |
| if (isEnclosingElement(typeBinding.getJavaElement())) { |
| return getTypeName(typeBinding, flags); |
| } |
| typeBinding= typeBinding.getTypeDeclaration(); |
| String typeName = getTypeName(typeBinding, flags); |
| String qualifiedName = getTypeName(typeBinding, JavaElementLabels.T_FULLY_QUALIFIED); |
| String title= ""; //$NON-NLS-1$ |
| int qualifierLength= qualifiedName.length() - typeName.length() - 1; |
| if (qualifierLength > 0) { |
| if (qualifiedName.endsWith(typeName)) { |
| title= qualifiedName.substring(0, qualifierLength); |
| title= Messages.format(JavaUIMessages.JavaElementLinks_title, title); |
| } else { |
| title= qualifiedName; // Not expected. Just show the whole qualifiedName. |
| } |
| } |
| |
| try { |
| String uri= createURI(JAVADOC_SCHEME, fEnclosingElement, qualifiedName, null, null); |
| return createHeaderLink(uri, typeName, title); |
| } catch (URISyntaxException e) { |
| JavaPlugin.log(e); |
| return typeName; |
| } |
| } |
| |
| private String getTypeName(ITypeBinding typeBinding, long flags) { |
| if (typeBinding.isLocal()) { |
| StringBuilder buf= new StringBuilder(); |
| IMethodBinding declaringMethod= typeBinding.getDeclaringMethod(); |
| if (getFlag(flags, JavaElementLabels.T_FULLY_QUALIFIED|JavaElementLabels.T_CONTAINER_QUALIFIED)) { |
| if (declaringMethod != null) { |
| buf.append(getTypeName(declaringMethod.getDeclaringClass(), flags)); |
| buf.append('.'); |
| buf.append(declaringMethod.getName()); |
| if (declaringMethod.getParameterTypes().length > 0) { |
| buf.append(PARAMETER_ELLIPSIS_LABEL); |
| } else { |
| buf.append("()"); //$NON-NLS-1$ |
| } |
| } else { |
| buf.append(getTypeName(typeBinding.getDeclaringClass(), flags)); |
| IBinding declaringMember= typeBinding.getDeclaringMember(); |
| if (declaringMember != null) { |
| buf.append('.'); |
| if (declaringMember instanceof ITypeBinding) { |
| buf.append(getTypeName((ITypeBinding) declaringMember, flags)); |
| } else { // field or method |
| String name= declaringMember.getName(); |
| if (name.equals(INIT_NAME)) |
| buf.append(JavaUIMessages.JavaElementLabels_initializer); |
| else |
| buf.append(name); |
| } |
| } |
| } |
| buf.append('.'); |
| } |
| if (typeBinding.isAnonymous()) { |
| buf.append("new "); //$NON-NLS-1$ |
| ITypeBinding[] interfaces= typeBinding.getInterfaces(); |
| if (interfaces.length > 0) { |
| buf.append(interfaces[0].getName()); |
| } else if (typeBinding.getSuperclass() != null){ |
| buf.append(typeBinding.getSuperclass().getName()); |
| } else { |
| buf.append(MISSING_LABEL); |
| } |
| buf.append(ANON_TYPE_TAIL); |
| } else { |
| buf.append(typeBinding.getName()); |
| } |
| return buf.toString(); |
| } |
| if (getFlag(flags, JavaElementLabels.T_FULLY_QUALIFIED)) { |
| return typeBinding.getQualifiedName(); |
| } else if (getFlag(flags, JavaElementLabels.T_CONTAINER_QUALIFIED)) { |
| ITypeBinding declaringClass= typeBinding.getDeclaringClass(); |
| if (declaringClass != null) { |
| StringBuilder buf= new StringBuilder(getTypeName(declaringClass, flags)); |
| buf.append('.'); |
| buf.append(typeBinding.getName()); |
| return buf.toString(); |
| } |
| } |
| return typeBinding.getName(); |
| } |
| |
| private IMethod getIMethod(IMethodBinding method) { |
| IJavaElement element= method.getJavaElement(); |
| if (element == null || element.getElementType() != IJavaElement.METHOD) |
| return null; // extra safety, not seen in tests (triggered from ctor of local class inside initializer) |
| IMethod iMethod= (IMethod) element; |
| if (isEnclosingElement(iMethod)) { |
| return (IMethod) fEnclosingElement; // benefit from better details on LambdaMethod |
| } |
| return iMethod; |
| } |
| |
| private boolean isEnclosingElement(IJavaElement element) { |
| if (element == null) |
| return false; |
| if (element.equals(fEnclosingElement)) |
| return true; |
| String enclosingKey= null; |
| // unify different representations of a lambda: LambdaMethod, LambdaExpression, IMethod (SAM) |
| switch (fEnclosingElement.getElementType()) { |
| case IJavaElement.TYPE: |
| if (((IType)fEnclosingElement).isLambda() && element.getElementType() == IJavaElement.METHOD) { |
| // navigate from LambdaExpression to LambdaMethod to be comparable to IMethod: |
| try { |
| IJavaElement[] children= ((IType) fEnclosingElement).getChildren(); |
| if (children != null && children.length == 1) { |
| if (children[0].getElementType() == IJavaElement.METHOD) { |
| enclosingKey= ((IMethod) children[0]).getKey(); |
| } |
| } |
| } catch (JavaModelException e) { |
| // continue without key |
| } |
| } else { |
| enclosingKey= ((IType) fEnclosingElement).getKey(); |
| } |
| break; |
| case IJavaElement.METHOD: |
| enclosingKey= ((IMethod) fEnclosingElement).getKey(); |
| break; |
| } |
| if (enclosingKey != null) { |
| switch (element.getElementType()) { |
| case IJavaElement.TYPE: |
| return enclosingKey.equals(((IType) element).getKey()); |
| case IJavaElement.METHOD: |
| return enclosingKey.equals(((IMethod) element).getKey()); |
| } |
| } |
| return false; |
| } |
| |
| private String htmlEscape(String escaped) { |
| return escaped.replace("&", "&").replace("<", "<").replace(">", ">");//$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ |
| } |
| } |