/*******************************************************************************
* 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
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.che.ide.ext.java.jdt.internal.text.correction;
import org.eclipse.che.ide.ext.java.jdt.Images;
import org.eclipse.che.ide.ext.java.jdt.codeassistant.api.IProblemLocation;
import org.eclipse.che.ide.ext.java.jdt.core.CorrectionEngine;
import org.eclipse.che.ide.ext.java.jdt.core.JavaCore;
import org.eclipse.che.ide.ext.java.jdt.core.dom.AST;
import org.eclipse.che.ide.ext.java.jdt.core.dom.ASTNode;
import org.eclipse.che.ide.ext.java.jdt.core.dom.Annotation;
import org.eclipse.che.ide.ext.java.jdt.core.dom.AnnotationTypeDeclaration;
import org.eclipse.che.ide.ext.java.jdt.core.dom.AnnotationTypeMemberDeclaration;
import org.eclipse.che.ide.ext.java.jdt.core.dom.ArrayInitializer;
import org.eclipse.che.ide.ext.java.jdt.core.dom.ChildListPropertyDescriptor;
import org.eclipse.che.ide.ext.java.jdt.core.dom.CompilationUnit;
import org.eclipse.che.ide.ext.java.jdt.core.dom.EnumConstantDeclaration;
import org.eclipse.che.ide.ext.java.jdt.core.dom.EnumDeclaration;
import org.eclipse.che.ide.ext.java.jdt.core.dom.Expression;
import org.eclipse.che.ide.ext.java.jdt.core.dom.FieldDeclaration;
import org.eclipse.che.ide.ext.java.jdt.core.dom.ITypeBinding;
import org.eclipse.che.ide.ext.java.jdt.core.dom.ImportDeclaration;
import org.eclipse.che.ide.ext.java.jdt.core.dom.MemberValuePair;
import org.eclipse.che.ide.ext.java.jdt.core.dom.MethodDeclaration;
import org.eclipse.che.ide.ext.java.jdt.core.dom.NormalAnnotation;
import org.eclipse.che.ide.ext.java.jdt.core.dom.SingleMemberAnnotation;
import org.eclipse.che.ide.ext.java.jdt.core.dom.SingleVariableDeclaration;
import org.eclipse.che.ide.ext.java.jdt.core.dom.StringLiteral;
import org.eclipse.che.ide.ext.java.jdt.core.dom.TypeDeclaration;
import org.eclipse.che.ide.ext.java.jdt.core.dom.VariableDeclarationExpression;
import org.eclipse.che.ide.ext.java.jdt.core.dom.VariableDeclarationFragment;
import org.eclipse.che.ide.ext.java.jdt.core.dom.VariableDeclarationStatement;
import org.eclipse.che.ide.ext.java.jdt.core.dom.rewrite.ASTRewrite;
import org.eclipse.che.ide.ext.java.jdt.core.dom.rewrite.ListRewrite;
import org.eclipse.che.ide.ext.java.jdt.internal.corext.dom.ASTNodes;
import org.eclipse.che.ide.ext.java.jdt.internal.text.correction.proposals.ASTRewriteCorrectionProposal;
import org.eclipse.che.ide.ext.java.jdt.quickassist.api.InvocationContext;
import org.eclipse.che.ide.ext.java.jdt.text.Document;
import org.eclipse.che.ide.runtime.CoreException;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
/**
*
*/
public class SuppressWarningsSubProcessor {
private static final String ADD_SUPPRESSWARNINGS_ID = "org.eclipse.jdt.ui.correction.addSuppressWarnings"; //$NON-NLS-1$
public static final boolean hasSuppressWarningsProposal(int problemId) {
// if (CorrectionEngine.getWarningToken(problemId) != null && JavaModelUtil.is50OrHigher(javaProject)) {
// String optionId= JavaCore.getOptionForConfigurableSeverity(problemId);
// if (optionId != null) {
// String optionValue= javaProject.getOption(optionId, true);
// return JavaCore.WARNING.equals(optionValue);
// }
// }
return true;
}
public static void addSuppressWarningsProposals(InvocationContext context, IProblemLocation problem,
Collection<ICommandAccess> proposals) {
// if (problem.isError()
// && !JavaCore.ENABLED.equals(context.getCompilationUnit().getJavaProject()
// .getOption(JavaCore.COMPILER_PB_SUPPRESS_OPTIONAL_ERRORS, true)))
// {
// return;
// }
if (JavaCore.DISABLED.equals(JavaCore.getOption(JavaCore.COMPILER_PB_SUPPRESS_WARNINGS))) {
return;
}
String warningToken = CorrectionEngine.getWarningToken(problem.getProblemId());
if (warningToken == null) {
return;
}
for (Iterator<ICommandAccess> iter = proposals.iterator(); iter.hasNext(); ) {
Object element = iter.next();
if (element instanceof SuppressWarningsProposal
&& warningToken.equals(((SuppressWarningsProposal)element).getWarningToken())) {
return; // only one at a time
}
}
ASTNode node = problem.getCoveringNode(context.getASTRoot());
if (node == null) {
return;
}
ASTNode target = node;
int relevance = -2;
do {
relevance =
addSuppressWarningsProposalIfPossible(context.getDocument(), target, warningToken, relevance, proposals);
if (relevance == 0)
return;
target = target.getParent();
}
while (target != null);
ASTNode importStatement = ASTNodes.getParent(node, ImportDeclaration.IMPORT_DECLARATION);
if (importStatement != null && !context.getASTRoot().types().isEmpty()) {
target = (ASTNode)context.getASTRoot().types().get(0);
if (target != null) {
addSuppressWarningsProposalIfPossible(context.getDocument(), target, warningToken, -2, proposals);
}
}
}
private static String getFirstFragmentName(List<VariableDeclarationFragment> fragments) {
if (fragments.size() > 0) {
return fragments.get(0).getName().getIdentifier();
}
return new String();
}
private static class SuppressWarningsProposal extends ASTRewriteCorrectionProposal {
private final String fWarningToken;
private final ASTNode fNode;
private final ChildListPropertyDescriptor fProperty;
public SuppressWarningsProposal(String warningToken, String label, ASTNode node,
ChildListPropertyDescriptor property, int relevance, Document document) {
super(label, null, relevance, document, Images.javadoc);
fWarningToken = warningToken;
fNode = node;
fProperty = property;
setCommandId(ADD_SUPPRESSWARNINGS_ID);
}
/** @return Returns the warningToken. */
public String getWarningToken() {
return fWarningToken;
}
/* (non-Javadoc)
* @see org.eclipse.jdt.internal.ui.text.correction.ASTRewriteCorrectionProposal#getRewrite()
*/
@Override
protected ASTRewrite getRewrite() throws CoreException {
AST ast = fNode.getAST();
ASTRewrite rewrite = ASTRewrite.create(ast);
StringLiteral newStringLiteral = ast.newStringLiteral();
newStringLiteral.setLiteralValue(fWarningToken);
Annotation existing = findExistingAnnotation((List<? extends ASTNode>)fNode.getStructuralProperty(fProperty));
if (existing == null) {
ListRewrite listRewrite = rewrite.getListRewrite(fNode, fProperty);
SingleMemberAnnotation newAnnot = ast.newSingleMemberAnnotation();
String importString =
createImportRewrite((CompilationUnit)fNode.getRoot()).addImport("java.lang.SuppressWarnings"); //$NON-NLS-1$
newAnnot.setTypeName(ast.newName(importString));
newAnnot.setValue(newStringLiteral);
listRewrite.insertFirst(newAnnot, null);
} else if (existing instanceof SingleMemberAnnotation) {
SingleMemberAnnotation annotation = (SingleMemberAnnotation)existing;
Expression value = annotation.getValue();
if (!addSuppressArgument(rewrite, value, newStringLiteral)) {
rewrite.set(existing, SingleMemberAnnotation.VALUE_PROPERTY, newStringLiteral, null);
}
} else if (existing instanceof NormalAnnotation) {
NormalAnnotation annotation = (NormalAnnotation)existing;
Expression value = findValue(annotation.values());
if (!addSuppressArgument(rewrite, value, newStringLiteral)) {
ListRewrite listRewrite = rewrite.getListRewrite(annotation, NormalAnnotation.VALUES_PROPERTY);
MemberValuePair pair = ast.newMemberValuePair();
pair.setName(ast.newSimpleName("value")); //$NON-NLS-1$
pair.setValue(newStringLiteral);
listRewrite.insertFirst(pair, null);
}
}
return rewrite;
}
private static boolean addSuppressArgument(ASTRewrite rewrite, Expression value, StringLiteral newStringLiteral) {
if (value instanceof ArrayInitializer) {
ListRewrite listRewrite = rewrite.getListRewrite(value, ArrayInitializer.EXPRESSIONS_PROPERTY);
listRewrite.insertLast(newStringLiteral, null);
} else if (value instanceof StringLiteral) {
ArrayInitializer newArr = rewrite.getAST().newArrayInitializer();
newArr.expressions().add(rewrite.createMoveTarget(value));
newArr.expressions().add(newStringLiteral);
rewrite.replace(value, newArr, null);
} else {
return false;
}
return true;
}
private static Expression findValue(List<MemberValuePair> keyValues) {
for (int i = 0, len = keyValues.size(); i < len; i++) {
MemberValuePair curr = keyValues.get(i);
if ("value".equals(curr.getName().getIdentifier())) { //$NON-NLS-1$
return curr.getValue();
}
}
return null;
}
private static Annotation findExistingAnnotation(List<? extends ASTNode> modifiers) {
for (int i = 0, len = modifiers.size(); i < len; i++) {
Object curr = modifiers.get(i);
if (curr instanceof NormalAnnotation || curr instanceof SingleMemberAnnotation) {
Annotation annotation = (Annotation)curr;
ITypeBinding typeBinding = annotation.resolveTypeBinding();
if (typeBinding != null) {
if ("java.lang.SuppressWarnings".equals(typeBinding.getQualifiedName())) { //$NON-NLS-1$
return annotation;
}
} else {
String fullyQualifiedName = annotation.getTypeName().getFullyQualifiedName();
if ("SuppressWarnings".equals(fullyQualifiedName) ||
"java.lang.SuppressWarnings".equals(fullyQualifiedName)) { //$NON-NLS-1$ //$NON-NLS-2$
return annotation;
}
}
}
}
return null;
}
}
/**
* Adds a SuppressWarnings proposal if possible and returns whether parent nodes should be processed or not (and with what relevance).
*
* @param cu
* the compilation unit
* @param node
* the node on which to add a SuppressWarning token
* @param warningToken
* the warning token to add
* @param relevance
* the proposal's relevance
* @param proposals
* collector to which the proposal should be added
* @return <code>0</code> if no further proposals should be added to parent nodes, or the relevance of the next proposal
* @since 3.6
*/
private static int addSuppressWarningsProposalIfPossible(Document document, ASTNode node, String warningToken,
int relevance, Collection<ICommandAccess> proposals) {
ChildListPropertyDescriptor property;
String name;
boolean isLocalVariable = false;
switch (node.getNodeType()) {
case ASTNode.SINGLE_VARIABLE_DECLARATION:
property = SingleVariableDeclaration.MODIFIERS2_PROPERTY;
name = ((SingleVariableDeclaration)node).getName().getIdentifier();
isLocalVariable = true;
break;
case ASTNode.VARIABLE_DECLARATION_STATEMENT:
property = VariableDeclarationStatement.MODIFIERS2_PROPERTY;
name = getFirstFragmentName(((VariableDeclarationStatement)node).fragments());
isLocalVariable = true;
break;
case ASTNode.VARIABLE_DECLARATION_EXPRESSION:
property = VariableDeclarationExpression.MODIFIERS2_PROPERTY;
name = getFirstFragmentName(((VariableDeclarationExpression)node).fragments());
isLocalVariable = true;
break;
case ASTNode.TYPE_DECLARATION:
property = TypeDeclaration.MODIFIERS2_PROPERTY;
name = ((TypeDeclaration)node).getName().getIdentifier();
break;
case ASTNode.ANNOTATION_TYPE_DECLARATION:
property = AnnotationTypeDeclaration.MODIFIERS2_PROPERTY;
name = ((AnnotationTypeDeclaration)node).getName().getIdentifier();
break;
case ASTNode.ENUM_DECLARATION:
property = EnumDeclaration.MODIFIERS2_PROPERTY;
name = ((EnumDeclaration)node).getName().getIdentifier();
break;
case ASTNode.FIELD_DECLARATION:
property = FieldDeclaration.MODIFIERS2_PROPERTY;
name = getFirstFragmentName(((FieldDeclaration)node).fragments());
break;
// case ASTNode.INITIALIZER: not used, because Initializer cannot have annotations
case ASTNode.METHOD_DECLARATION:
property = MethodDeclaration.MODIFIERS2_PROPERTY;
name = ((MethodDeclaration)node).getName().getIdentifier() + "()"; //$NON-NLS-1$
break;
case ASTNode.ANNOTATION_TYPE_MEMBER_DECLARATION:
property = AnnotationTypeMemberDeclaration.MODIFIERS2_PROPERTY;
name = ((AnnotationTypeMemberDeclaration)node).getName().getIdentifier() + "()"; //$NON-NLS-1$
break;
case ASTNode.ENUM_CONSTANT_DECLARATION:
property = EnumConstantDeclaration.MODIFIERS2_PROPERTY;
name = ((EnumConstantDeclaration)node).getName().getIdentifier();
break;
default:
return relevance;
}
String label =
CorrectionMessages.INSTANCE.SuppressWarningsSubProcessor_suppress_warnings_label(warningToken, name);
ASTRewriteCorrectionProposal proposal =
new SuppressWarningsProposal(warningToken, label, node, property, relevance, document);
proposals.add(proposal);
return isLocalVariable ? relevance - 1 : 0;
}
/**
* Adds a proposal to correct the name of the SuppressWarning annotation
*
* @param context
* the context
* @param problem
* the problem
* @param proposals
* the resulting proposals
*/
public static void addUnknownSuppressWarningProposals(InvocationContext context, IProblemLocation problem,
Collection<ICommandAccess> proposals) {
ASTNode coveringNode = context.getCoveringNode();
if (!(coveringNode instanceof StringLiteral))
return;
AST ast = coveringNode.getAST();
StringLiteral literal = (StringLiteral)coveringNode;
String literalValue = literal.getLiteralValue();
String[] allWarningTokens = CorrectionEngine.getAllWarningTokens();
for (int i = 0; i < allWarningTokens.length; i++) {
String curr = allWarningTokens[i];
if (NameMatcher.isSimilarName(literalValue, curr)) {
StringLiteral newLiteral = ast.newStringLiteral();
newLiteral.setLiteralValue(curr);
ASTRewrite rewrite = ASTRewrite.create(ast);
rewrite.replace(literal, newLiteral, null);
String label = CorrectionMessages.INSTANCE.SuppressWarningsSubProcessor_fix_suppress_token_label(curr);
Images image = Images.correction_change;
ASTRewriteCorrectionProposal proposal =
new ASTRewriteCorrectionProposal(label, rewrite, 5, context.getDocument(), image);
proposals.add(proposal);
}
}
addRemoveUnusedSuppressWarningProposals(context, problem, proposals);
}
public static void addRemoveUnusedSuppressWarningProposals(InvocationContext context, IProblemLocation problem,
Collection<ICommandAccess> proposals) {
ASTNode coveringNode = problem.getCoveringNode(context.getASTRoot());
if (!(coveringNode instanceof StringLiteral))
return;
StringLiteral literal = (StringLiteral)coveringNode;
if (coveringNode.getParent() instanceof MemberValuePair) {
coveringNode = coveringNode.getParent();
}
ASTNode parent = coveringNode.getParent();
ASTRewrite rewrite = ASTRewrite.create(coveringNode.getAST());
if (parent instanceof SingleMemberAnnotation) {
rewrite.remove(parent, null);
} else if (parent instanceof NormalAnnotation) {
NormalAnnotation annot = (NormalAnnotation)parent;
if (annot.values().size() == 1) {
rewrite.remove(annot, null);
} else {
rewrite.remove(coveringNode, null);
}
} else if (parent instanceof ArrayInitializer) {
rewrite.remove(coveringNode, null);
} else {
return;
}
String label =
CorrectionMessages.INSTANCE.SuppressWarningsSubProcessor_remove_annotation_label(literal.getLiteralValue());
Images image = Images.delete_obj;
ASTRewriteCorrectionProposal proposal =
new ASTRewriteCorrectionProposal(label, rewrite, 5, context.getDocument(), image);
proposals.add(proposal);
}
}