/*******************************************************************************
* 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.AbstractMethodDeclaration;
import org.eclipse.jdt.internal.compiler.ast.Argument;
import org.eclipse.jdt.internal.compiler.ast.Expression;
import org.eclipse.jdt.internal.compiler.ast.Javadoc;
import org.eclipse.jdt.internal.compiler.ast.JavadocSingleNameReference;
import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration;
import org.eclipse.jdt.internal.compiler.ast.TypeParameter;
import org.eclipse.jdt.internal.compiler.lookup.Binding;
import org.eclipse.jdt.internal.compiler.lookup.ClassScope;
import org.eclipse.jdt.internal.compiler.lookup.CompilationUnitScope;
import org.eclipse.jdt.internal.compiler.lookup.MethodScope;
import org.eclipse.jdt.internal.compiler.lookup.Scope;
import org.eclipse.jdt.internal.compiler.lookup.TypeVariableBinding;
/**
* 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;
}
}