/*******************************************************************************
* Copyright (c) 2000, 2010 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.core;
import java.util.Hashtable;
import java.util.Map;
import org.eclipse.core.resources.IMarker;
import org.eclipse.jdt.core.compiler.CharOperation;
import org.eclipse.jdt.core.compiler.IProblem;
import org.eclipse.jdt.core.compiler.InvalidInputException;
import org.eclipse.jdt.internal.compiler.impl.CompilerOptions;
import org.eclipse.jdt.internal.compiler.parser.Scanner;
import org.eclipse.jdt.internal.compiler.parser.TerminalTokens;
import org.eclipse.jdt.internal.compiler.problem.ProblemReporter;
import org.eclipse.jdt.internal.core.util.Messages;
import org.eclipse.jdt.internal.core.util.Util;
/**
* This class is the entry point for source corrections.
*
* This class is intended to be instantiated by clients.
*
* @since 2.0
* @noextend This class is not intended to be subclassed by clients.
*/
public class CorrectionEngine {
/**
* This field is not intended to be used by client.
*/
protected int correctionStart;
/**
* This field is not intended to be used by client.
*/
protected int correctionEnd;
/**
* This field is not intended to be used by client.
*/
protected int prefixLength;
/**
* This field is not intended to be used by client.
*/
protected ICompilationUnit compilationUnit;
/**
* This field is not intended to be used by client.
*/
protected ICorrectionRequestor correctionRequestor;
/**
* This field is not intended to be used by client.
*/
protected static final int CLASSES= 0x00000001;
/**
* This field is not intended to be used by client.
*/
protected static final int INTERFACES= 0x00000002;
/**
* This field is not intended to be used by client.
*/
protected static final int IMPORT= 0x00000004;
/**
* This field is not intended to be used by client.
*/
protected static final int METHOD= 0x00000008;
/**
* This field is not intended to be used by client.
*/
protected static final int FIELD= 0x00000010;
/**
* This field is not intended to be used by client.
*/
protected static final int LOCAL= 0x00000020;
/**
* This field is not intended to be used by client.
*/
protected int filter;
/**
* The CorrectionEngine is responsible for computing problem corrections.
*
* @param setting java.util.Map set of options used to configure the code correction engine.
* CURRENTLY THERE IS NO CORRECTION SPECIFIC SETTINGS.
*/
public CorrectionEngine(Map setting) {
// settings ignored for now
}
/**
* Performs code correction for the given marker, reporting results to the given correction
* requestor.
*
* Correction results are answered through a requestor.
*
* @param marker the marker which describe the problem to correct.
* @param targetUnit replace the compilation unit given by the marker. Ignored if null.
* @param positionOffset the offset of position given by the marker.
* @param requestor the given correction requestor
* @exception IllegalArgumentException if <code>requestor</code> is <code>null</code>
* @exception JavaModelException currently this exception is never thrown, but the opportunity
* to thrown an exception when the correction failed is kept for later.
* @since 2.0
*/
public void computeCorrections(IMarker marker, ICompilationUnit targetUnit, int positionOffset, ICorrectionRequestor requestor) throws JavaModelException {
IJavaElement element= targetUnit == null ? JavaCore.create(marker.getResource()) : targetUnit;
if (!(element instanceof ICompilationUnit))
return;
ICompilationUnit unit= (ICompilationUnit)element;
int id= marker.getAttribute(IJavaModelMarker.ID, -1);
String[] args= Util.getProblemArgumentsFromMarker(marker.getAttribute(IJavaModelMarker.ARGUMENTS, "")); //$NON-NLS-1$
int start= marker.getAttribute(IMarker.CHAR_START, -1);
int end= marker.getAttribute(IMarker.CHAR_END, -1);
computeCorrections(unit, id, start + positionOffset, end + positionOffset, args, requestor);
}
/**
* Performs code correction for the given IProblem, reporting results to the given correction
* requestor.
*
* Correction results are answered through a requestor.
*
* @param problem the problem which describe the problem to correct.
* @param targetUnit denote the compilation unit in which correction occurs. Cannot be null.
* @param requestor the given correction requestor
* @exception IllegalArgumentException if <code>targetUnit</code> or <code>requestor</code> is
* <code>null</code>
* @exception JavaModelException currently this exception is never thrown, but the opportunity
* to thrown an exception when the correction failed is kept for later.
* @since 2.0
*/
public void computeCorrections(IProblem problem, ICompilationUnit targetUnit, ICorrectionRequestor requestor) throws JavaModelException {
if (requestor == null) {
throw new IllegalArgumentException(Messages.correction_nullUnit);
}
this.computeCorrections(
targetUnit, problem.getID(),
problem.getSourceStart(),
problem.getSourceEnd(),
problem.getArguments(),
requestor);
}
/*
* Ask the engine to compute a correction for the specified problem
* of the given compilation unit.
* Correction results are answered through a requestor.
*
* @param unit org.eclipse.jdt.internal.core.ICompilationUnit
* the compilation unit.
*
* @param id int
* the id of the problem.
*
* @param start int
* a position in the source where the error begin.
*
* @param end int
* a position in the source where the error finish.
*
* @param arguments String[]
* arguments of the problem.
*
* @exception IllegalArgumentException if <code>requestor</code> is <code>null</code>
* @exception JavaModelException currently this exception is never thrown, but the opportunity to thrown an exception
* when the correction failed is kept for later.
* @since 2.0
*/
private void computeCorrections(ICompilationUnit unit, int id, int start, int end, String[] arguments, ICorrectionRequestor requestor) {
if (id == -1 || arguments == null || start == -1 || end == -1)
return;
if (requestor == null) {
throw new IllegalArgumentException(Messages.correction_nullRequestor);
}
this.correctionRequestor= requestor;
this.correctionStart= start;
this.correctionEnd= end;
this.compilationUnit= unit;
String argument= null;
try {
switch (id) {
// Type correction
case IProblem.ImportNotFound:
this.filter= IMPORT;
argument= arguments[0];
break;
case IProblem.UndefinedType:
this.filter= CLASSES | INTERFACES;
argument= arguments[0];
break;
// Method correction
case IProblem.UndefinedMethod:
this.filter= METHOD;
argument= arguments[1];
break;
// Field and local variable correction
case IProblem.UndefinedField:
this.filter= FIELD;
argument= arguments[0];
break;
case IProblem.UndefinedName:
case IProblem.UnresolvedVariable:
this.filter= FIELD | LOCAL;
argument= arguments[0];
break;
}
} catch (ArrayIndexOutOfBoundsException e) {
return;
}
if (argument != null) {
correct(argument.toCharArray());
}
}
private void correct(char[] argument) {
try {
String source= this.compilationUnit.getSource();
Map currentProjectOptions= this.compilationUnit.getJavaProject().getOptions(true);
long sourceLevel= CompilerOptions.versionToJdkLevel(currentProjectOptions.get(JavaCore.COMPILER_SOURCE));
long complianceLevel= CompilerOptions.versionToJdkLevel(currentProjectOptions.get(JavaCore.COMPILER_COMPLIANCE));
Scanner scanner=
new Scanner(
false /*comment*/,
false /*whitespace*/,
false /*nls*/,
sourceLevel,
complianceLevel,
null/*taskTag*/,
null/*taskPriorities*/,
true /*taskCaseSensitive*/);
scanner.setSource(source.toCharArray());
scanner.resetTo(this.correctionStart, this.correctionEnd);
int token= 0;
char[] argumentSource= CharOperation.NO_CHAR;
// search last segment position
while (true) {
token= scanner.getNextToken();
if (token == TerminalTokens.TokenNameEOF)
return;
char[] tokenSource= scanner.getCurrentTokenSource();
argumentSource= CharOperation.concat(argumentSource, tokenSource);
if (!CharOperation.prefixEquals(argumentSource, argument))
return;
if (CharOperation.equals(argument, argumentSource)) {
this.correctionStart= scanner.startPosition;
this.correctionEnd= scanner.currentPosition;
this.prefixLength= CharOperation.lastIndexOf('.', argument) + 1;
break;
}
}
// search completion position
int completionPosition= this.correctionStart;
scanner.resetTo(completionPosition, this.correctionEnd);
int position= completionPosition;
for (int i= 0; i < 4; i++) {
if (scanner.getNextCharAsJavaIdentifierPart()) {
completionPosition= position;
position= scanner.currentPosition;
} else {
break;
}
}
Hashtable oldOptions= JavaCore.getOptions();
try {
Hashtable options= new Hashtable(oldOptions);
options.put(JavaCore.CODEASSIST_CAMEL_CASE_MATCH, JavaCore.DISABLED);
JavaCore.setOptions(options);
this.compilationUnit.codeComplete(
completionPosition,
this.completionRequestor
);
} finally {
JavaCore.setOptions(oldOptions);
}
} catch (JavaModelException e) {
return;
} catch (InvalidInputException e) {
return;
}
}
/**
* This field is not intended to be used by client.
*/
protected CompletionRequestor completionRequestor= new CompletionRequestor() {
public void accept(CompletionProposal proposal) {
switch (proposal.getKind()) {
case CompletionProposal.TYPE_REF:
int flags= proposal.getFlags();
if (!(Flags.isEnum(flags) || Flags.isAnnotation(flags))) {
if ((CorrectionEngine.this.filter & (CLASSES | INTERFACES)) != 0) {
char[] completionName= proposal.getCompletion();
CorrectionEngine.this.correctionRequestor.acceptClass(
proposal.getDeclarationSignature(),
Signature.getSignatureSimpleName(proposal.getSignature()),
CharOperation.subarray(completionName, CorrectionEngine.this.prefixLength, completionName.length),
proposal.getFlags(),
CorrectionEngine.this.correctionStart,
CorrectionEngine.this.correctionEnd);
} else if ((CorrectionEngine.this.filter & IMPORT) != 0) {
char[] packageName= proposal.getDeclarationSignature();
char[] className= Signature.getSignatureSimpleName(proposal.getSignature());
char[] fullName= CharOperation.concat(packageName, className, '.');
CorrectionEngine.this.correctionRequestor.acceptClass(
packageName,
className,
CharOperation.subarray(fullName, CorrectionEngine.this.prefixLength, fullName.length),
proposal.getFlags(),
CorrectionEngine.this.correctionStart,
CorrectionEngine.this.correctionEnd);
}
}
break;
case CompletionProposal.FIELD_REF:
if ((CorrectionEngine.this.filter & FIELD) != 0) {
char[] declaringSignature= proposal.getDeclarationSignature();
char[] signature= proposal.getSignature();
CorrectionEngine.this.correctionRequestor.acceptField(
Signature.getSignatureQualifier(declaringSignature),
Signature.getSignatureSimpleName(declaringSignature),
proposal.getName(),
Signature.getSignatureQualifier(signature),
Signature.getSignatureSimpleName(signature),
proposal.getName(),
proposal.getFlags(),
CorrectionEngine.this.correctionStart,
CorrectionEngine.this.correctionEnd);
}
break;
case CompletionProposal.LOCAL_VARIABLE_REF:
if ((CorrectionEngine.this.filter & LOCAL) != 0) {
char[] signature= proposal.getSignature();
CorrectionEngine.this.correctionRequestor.acceptLocalVariable(
proposal.getName(),
Signature.getSignatureQualifier(signature),
Signature.getSignatureSimpleName(signature),
proposal.getFlags(),
CorrectionEngine.this.correctionStart,
CorrectionEngine.this.correctionEnd);
}
break;
case CompletionProposal.METHOD_REF:
if ((CorrectionEngine.this.filter & METHOD) != 0) {
char[] declaringSignature= proposal.getDeclarationSignature();
char[] signature= proposal.getSignature();
char[][] parameterTypeSignatures= Signature.getParameterTypes(signature);
int length= parameterTypeSignatures.length;
char[][] parameterPackageNames= new char[length][];
char[][] parameterTypeNames= new char[length][];
for (int i= 0; i < length; i++) {
parameterPackageNames[i]= Signature.getSignatureQualifier(parameterTypeSignatures[i]);
parameterTypeNames[i]= Signature.getSignatureSimpleName(parameterTypeSignatures[i]);
}
char[] returnTypeSignature= Signature.getReturnType(signature);
CorrectionEngine.this.correctionRequestor.acceptMethod(
Signature.getSignatureQualifier(declaringSignature),
Signature.getSignatureSimpleName(declaringSignature),
proposal.getName(),
parameterPackageNames,
parameterTypeNames,
proposal.findParameterNames(null),
Signature.getSignatureQualifier(returnTypeSignature),
Signature.getSignatureSimpleName(returnTypeSignature),
proposal.getName(),
proposal.getFlags(),
CorrectionEngine.this.correctionStart,
CorrectionEngine.this.correctionEnd);
}
break;
case CompletionProposal.PACKAGE_REF:
if ((CorrectionEngine.this.filter & (CLASSES | INTERFACES | IMPORT)) != 0) {
char[] packageName= proposal.getDeclarationSignature();
CorrectionEngine.this.correctionRequestor.acceptPackage(
packageName,
CharOperation.subarray(packageName, CorrectionEngine.this.prefixLength, packageName.length),
CorrectionEngine.this.correctionStart,
CorrectionEngine.this.correctionEnd);
}
break;
}
}
};
/**
* Return an array of strings which contains one entry per warning token accepted by the
* <code>@SuppressWarnings</code> annotation. This array is neither null nor empty, it contains
* at least the String <code>all</code>. It should not be modified by the caller (please take a
* copy if modifications are needed).<br>
* <b>Note:</b> The tokens returned are not necessarily standardized across Java compilers. If
* you were to use one of these tokens in a <code>@SuppressWarnings</code> annotation in the
* Java source code, the effects (if any) may vary from compiler to compiler.
*
* @return an array of strings which contains one entry per warning token accepted by the
* <code>@SuppressWarnings</code> annotation.
* @since 3.2
*/
public static String[] getAllWarningTokens() {
return CompilerOptions.warningTokens;
}
/**
* Helper method for decoding problem marker attributes. Returns an array of String arguments
* extracted from the problem marker "arguments" attribute, or <code>null</code> if the marker
* "arguments" attribute is missing or ill-formed.
*
* @param problemMarker the problem marker to decode arguments from.
* @return an array of String arguments, or <code>null</code> if unable to extract arguments
* @since 2.1
*/
public static String[] getProblemArguments(IMarker problemMarker) {
String argumentsString= problemMarker.getAttribute(IJavaModelMarker.ARGUMENTS, null);
return Util.getProblemArgumentsFromMarker(argumentsString);
}
/**
* Returns a token which can be used to suppress a given warning using
* <code>@SuppressWarnings</code> annotation, for a given problem ID ({@link IProblem }). If a
* particular problem is not suppressable, <code>null</code> will be returned.
* <p>
* <b>Note:</b> <code>@SuppressWarnings</code> can only suppress warnings, which means that if
* some problems got promoted to ERROR using custom compiler settings (
* {@link IJavaProject#setOption(String, String)}), the <code>@SuppressWarnings</code>
* annotation will be ineffective.
* </p>
* <p>
* <b>Note:</b> <code>@SuppressWarnings</code> can be argumented with <code>"all"</code> so as
* to suppress all possible warnings at once.
* </p>
* <p>
* <b>Note:</b> The tokens returned are not necessarily standardized across Java compilers. If
* you were to use one of these tokens in an @SuppressWarnings annotation in the Java source
* code, the effects (if any) may vary from compiler to compiler.
* </p>
*
* @param problemID the ID of a given warning to suppress
* @return a String which can be used in <code>@SuppressWarnings</code> annotation, or
* <code>null</code> if unable to suppress this warning.
* @since 3.1
*/
public static String getWarningToken(int problemID) {
int irritant= ProblemReporter.getIrritant(problemID);
if (irritant != 0) {
return CompilerOptions.warningTokenFromIrritant(irritant);
}
return null;
}
}