/******************************************************************************* * 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.wst.jsdt.core; import java.util.Hashtable; import java.util.Map; import org.eclipse.core.resources.IMarker; import org.eclipse.wst.jsdt.core.compiler.CharOperation; import org.eclipse.wst.jsdt.core.compiler.IProblem; import org.eclipse.wst.jsdt.core.compiler.InvalidInputException; import org.eclipse.wst.jsdt.internal.compiler.impl.CompilerOptions; import org.eclipse.wst.jsdt.internal.compiler.lookup.ProblemReasons; import org.eclipse.wst.jsdt.internal.compiler.parser.Scanner; import org.eclipse.wst.jsdt.internal.compiler.parser.TerminalTokens; import org.eclipse.wst.jsdt.internal.compiler.problem.ProblemReporter; import org.eclipse.wst.jsdt.internal.core.util.Messages; import org.eclipse.wst.jsdt.internal.core.util.Util; /** * This class is the entry point for source corrections. * * This class is not intended to be subclassed by clients. This class is intended to be instantiated by clients. * * * Provisional API: This class/interface is part of an interim API that is still under development and expected to * change significantly before reaching stability. It is being made available at this early stage to solicit feedback * from pioneering adopters on the understanding that any code that uses this API will almost certainly be broken * (repeatedly) as the API evolves. */ public class CorrectionEngine implements ProblemReasons { /** * 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 IJavaScriptUnit 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 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 JavaScriptModelException currently this exception is never thrown, but the opportunity to thrown an exception * when the correction failed is kept for later. */ public void computeCorrections(IMarker marker, IJavaScriptUnit targetUnit, int positionOffset, ICorrectionRequestor requestor) throws JavaScriptModelException { IJavaScriptElement element = targetUnit == null ? JavaScriptCore.create(marker.getResource()) : targetUnit; if(!(element instanceof IJavaScriptUnit)) return; IJavaScriptUnit unit = (IJavaScriptUnit) element; int id = marker.getAttribute(IJavaScriptModelMarker.ID, -1); String[] args = Util.getProblemArgumentsFromMarker(marker.getAttribute(IJavaScriptModelMarker.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 JavaScriptModelException currently this exception is never thrown, but the opportunity to thrown an exception * when the correction failed is kept for later. */ public void computeCorrections(IProblem problem, IJavaScriptUnit targetUnit, ICorrectionRequestor requestor) throws JavaScriptModelException { 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.wst.jsdt.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 JavaScriptModelException currently this exception is never thrown, but the opportunity to thrown an exception * when the correction failed is kept for later. */ private void computeCorrections(IJavaScriptUnit 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; argument = arguments[0]; break; // Method correction case IProblem.UndefinedMethod : case IProblem.UndefinedFunction : 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 : 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(); Scanner scanner = new Scanner(); 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 = JavaScriptCore.getOptions(); try { Hashtable options = new Hashtable(oldOptions); options.put(JavaScriptCore.CODEASSIST_CAMEL_CASE_MATCH, JavaScriptCore.DISABLED); JavaScriptCore.setOptions(options); this.compilationUnit.codeComplete( completionPosition, this.completionRequestor ); } finally { JavaScriptCore.setOptions(oldOptions); } } catch (JavaScriptModelException 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: if((CorrectionEngine.this.filter & CLASSES) != 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 | 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 JavaScript * validators. If you were to use one of these tokens in a <code>@SuppressWarnings</code> * annotation in the JavaScript source code, the effects (if any) may vary from * validator to validator. * * @return an array of strings which contains one entry per warning token * accepted by the <code>@SuppressWarnings</code> annotation. */ 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 */ public static String[] getProblemArguments(IMarker problemMarker){ String argumentsString = problemMarker.getAttribute(IJavaScriptModelMarker.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 IJavaScriptProject#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 JavaScript * validators. If you were to use one of these tokens in an @SuppressWarnings * annotation in the JavaScript source code, the effects (if any) may vary from * validator to validator. * </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. */ public static String getWarningToken(int problemID){ long irritant = ProblemReporter.getIrritant(problemID); if (irritant != 0) { return CompilerOptions.warningTokenFromIrritant(irritant); } return null; } }