/******************************************************************************* * Copyright (c) 2000, 2009 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 * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * IBM Corporation - initial API and implementation *******************************************************************************/ package org.eclipse.jdt.internal.codeassist.complete; import org.eclipse.jdt.internal.compiler.ast.*; import org.eclipse.jdt.internal.compiler.lookup.*; /** * Node representing a Javadoc comment including code selection. */ public class CompletionJavadoc extends Javadoc { Expression completionNode; public CompletionJavadoc(int sourceStart, int sourceEnd) { super(sourceStart, sourceEnd); } /** * @return Returns the completionNode. */ public Expression getCompletionNode() { return this.completionNode; } /** * Resolve selected node if not null and throw exception to let clients know * that it has been found. * * @throws CompletionNodeFound */ private void internalResolve(Scope scope) { if (this.completionNode != null) { if (this.completionNode instanceof CompletionOnJavadocTag) { ((CompletionOnJavadocTag)this.completionNode).filterPossibleTags(scope); } else { boolean resolve = true; if (this.completionNode instanceof CompletionOnJavadocParamNameReference) { resolve = ((CompletionOnJavadocParamNameReference)this.completionNode).token != null; } else if (this.completionNode instanceof CompletionOnJavadocTypeParamReference) { resolve = ((CompletionOnJavadocTypeParamReference)this.completionNode).token != null; } if (resolve) { switch (scope.kind) { case Scope.CLASS_SCOPE: this.completionNode.resolveType((ClassScope)scope); break; case Scope.METHOD_SCOPE: this.completionNode.resolveType((MethodScope) scope); break; } } if (this.completionNode instanceof CompletionOnJavadocParamNameReference) { CompletionOnJavadocParamNameReference paramNameReference = (CompletionOnJavadocParamNameReference) this.completionNode; if (scope.kind == Scope.METHOD_SCOPE) { paramNameReference.missingParams = missingParamTags(paramNameReference.binding, (MethodScope)scope); } if (paramNameReference.token == null || paramNameReference.token.length == 0) { paramNameReference.missingTypeParams = missingTypeParameterTags(paramNameReference.binding, scope); } } else if (this.completionNode instanceof CompletionOnJavadocTypeParamReference) { CompletionOnJavadocTypeParamReference typeParamReference = (CompletionOnJavadocTypeParamReference) this.completionNode; typeParamReference.missingParams = missingTypeParameterTags(typeParamReference.resolvedType, scope); } } Binding qualifiedBinding = null; if (this.completionNode instanceof CompletionOnJavadocQualifiedTypeReference) { CompletionOnJavadocQualifiedTypeReference typeRef = (CompletionOnJavadocQualifiedTypeReference) this.completionNode; if (typeRef.packageBinding == null) { qualifiedBinding = typeRef.resolvedType; } else { qualifiedBinding = typeRef.packageBinding; } } else if (this.completionNode instanceof CompletionOnJavadocMessageSend) { CompletionOnJavadocMessageSend msg = (CompletionOnJavadocMessageSend) this.completionNode; if (!msg.receiver.isThis()) qualifiedBinding = msg.receiver.resolvedType; } else if (this.completionNode instanceof CompletionOnJavadocAllocationExpression) { CompletionOnJavadocAllocationExpression alloc = (CompletionOnJavadocAllocationExpression) this.completionNode; qualifiedBinding = alloc.type.resolvedType; } throw new CompletionNodeFound(this.completionNode, qualifiedBinding, scope); } } /* * @see org.eclipse.jdt.internal.compiler.ast.ASTNode#print(int, java.lang.StringBuffer) */ public StringBuffer print(int indent, StringBuffer output) { printIndent(indent, output).append("/**\n"); //$NON-NLS-1$ boolean nodePrinted = false; if (this.paramReferences != null) { for (int i = 0, length = this.paramReferences.length; i < length; i++) { printIndent(indent, output).append(" * @param "); //$NON-NLS-1$ this.paramReferences[i].print(indent, output).append('\n'); if (!nodePrinted && this.completionNode != null) { nodePrinted = this.completionNode == this.paramReferences[i]; } } } if (this.paramTypeParameters != null) { for (int i = 0, length = this.paramTypeParameters.length; i < length; i++) { printIndent(indent, output).append(" * @param <"); //$NON-NLS-1$ this.paramTypeParameters[i].print(indent, output).append(">\n"); //$NON-NLS-1$ if (!nodePrinted && this.completionNode != null) { nodePrinted = this.completionNode == this.paramTypeParameters[i]; } } } if (this.returnStatement != null) { printIndent(indent, output).append(" * @"); //$NON-NLS-1$ this.returnStatement.print(indent, output).append('\n'); } if (this.exceptionReferences != null) { for (int i = 0, length = this.exceptionReferences.length; i < length; i++) { printIndent(indent, output).append(" * @throws "); //$NON-NLS-1$ this.exceptionReferences[i].print(indent, output).append('\n'); if (!nodePrinted && this.completionNode != null) { nodePrinted = this.completionNode == this.exceptionReferences[i]; } } } if (this.seeReferences != null) { for (int i = 0, length = this.seeReferences.length; i < length; i++) { printIndent(indent, output).append(" * @see "); //$NON-NLS-1$ this.seeReferences[i].print(indent, output).append('\n'); if (!nodePrinted && this.completionNode != null) { nodePrinted = this.completionNode == this.seeReferences[i]; } } } if (!nodePrinted && this.completionNode != null) { printIndent(indent, output).append(" * "); //$NON-NLS-1$ this.completionNode.print(indent, output).append('\n'); } printIndent(indent, output).append(" */\n"); //$NON-NLS-1$ return output; } /** * Resolve completion node if not null and throw exception to let clients know * that it has been found. * * @throws CompletionNodeFound */ public void resolve(ClassScope scope) { super.resolve(scope); internalResolve(scope); } /** * Resolve completion node if not null and throw exception to let clients know * that it has been found. * * @throws CompletionNodeFound */ public void resolve(CompilationUnitScope scope) { internalResolve(scope); } /** * Resolve completion node if not null and throw exception to let clients know * that it has been found. * * @throws CompletionNodeFound */ public void resolve(MethodScope scope) { super.resolve(scope); internalResolve(scope); } /* * Look for missing method @param tags */ private char[][] missingParamTags(Binding paramNameRefBinding, MethodScope methScope) { // Verify if there's some possible param tag AbstractMethodDeclaration md = methScope.referenceMethod(); int paramTagsSize = this.paramReferences == null ? 0 : this.paramReferences.length; if (md == null) return null; int argumentsSize = md.arguments == null ? 0 : md.arguments.length; if (argumentsSize == 0) return null; // Store all method arguments if there's no @param in javadoc if (paramTagsSize == 0) { char[][] missingParams = new char[argumentsSize][]; for (int i = 0; i < argumentsSize; i++) { missingParams[i] = md.arguments[i].name; } return missingParams; } // Look for missing arguments char[][] missingParams = new char[argumentsSize][]; int size = 0; for (int i = 0; i < argumentsSize; i++) { Argument arg = md.arguments[i]; boolean found = false; int paramNameRefCount = 0; for (int j = 0; j < paramTagsSize && !found; j++) { JavadocSingleNameReference param = this.paramReferences[j]; if (arg.binding == param.binding) { if (param.binding == paramNameRefBinding) { // do not count first occurence of param name reference paramNameRefCount++; found = paramNameRefCount > 1; } else { found = true; } } } if (!found) { missingParams[size++] = arg.name; } } if (size > 0) { if (size != argumentsSize) { System.arraycopy(missingParams, 0, missingParams = new char[size][], 0, size); } return missingParams; } return null; } /* * Look for missing type parameters @param tags */ private char[][] missingTypeParameterTags(Binding paramNameRefBinding, Scope scope) { int paramTypeParamLength = this.paramTypeParameters == null ? 0 : this.paramTypeParameters.length; // Verify if there's any type parameter to tag TypeParameter[] parameters = null; TypeVariableBinding[] typeVariables = null; switch (scope.kind) { case Scope.METHOD_SCOPE: AbstractMethodDeclaration methodDeclaration = ((MethodScope)scope).referenceMethod(); if (methodDeclaration == null) return null; parameters = methodDeclaration.typeParameters(); typeVariables = methodDeclaration.binding.typeVariables; break; case Scope.CLASS_SCOPE: TypeDeclaration typeDeclaration = ((ClassScope) scope).referenceContext; parameters = typeDeclaration.typeParameters; typeVariables = typeDeclaration.binding.typeVariables; break; } if (typeVariables == null || typeVariables.length == 0) return null; // Store all type parameters if there's no @param in javadoc if (parameters != null) { int typeParametersLength = parameters.length; if (paramTypeParamLength == 0) { char[][] missingParams = new char[typeParametersLength][]; for (int i = 0; i < typeParametersLength; i++) { missingParams[i] = parameters[i].name; } return missingParams; } // Look for missing type parameter char[][] missingParams = new char[typeParametersLength][]; int size = 0; for (int i = 0; i < typeParametersLength; i++) { TypeParameter parameter = parameters[i]; boolean found = false; int paramNameRefCount = 0; for (int j = 0; j < paramTypeParamLength && !found; j++) { if (parameter.binding == this.paramTypeParameters[j].resolvedType) { if (parameter.binding == paramNameRefBinding) { // do not count first occurence of param nmae reference paramNameRefCount++; found = paramNameRefCount > 1; } else { found = true; } } } if (!found) { missingParams[size++] = parameter.name; } } if (size > 0) { if (size != typeParametersLength) { System.arraycopy(missingParams, 0, missingParams = new char[size][], 0, size); } return missingParams; } } return null; } }