/******************************************************************************* * 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.internal.eval; import org.eclipse.jdt.core.CompletionContext; import org.eclipse.jdt.core.CompletionProposal; import org.eclipse.jdt.core.CompletionRequestor; import org.eclipse.jdt.core.Flags; import org.eclipse.jdt.core.Signature; import org.eclipse.jdt.core.compiler.CategorizedProblem; import org.eclipse.jdt.core.compiler.CharOperation; import org.eclipse.jdt.core.compiler.IProblem; import org.eclipse.jdt.internal.codeassist.ISelectionRequestor; /** * Maps back and forth a code snippet to a compilation unit. The structure of the compilation unit * is as follows: * * <pre> * [package <package name>;] * [import <import name>;]* * public class <code snippet class name> extends <global variable class name> { * [<declaring type> val$this;] * public void run() { * <code snippet> * } * } * </pre> */ class CodeSnippetToCuMapper implements EvaluationConstants { /** * The generated compilation unit. */ public char[] cuSource; /** * Where the code snippet starts in the generated compilation unit. */ public int lineNumberOffset= 0; public int startPosOffset= 0; // Internal fields char[] codeSnippet; char[] snippetPackageName; char[][] snippetImports; char[] snippetClassName; char[] snippetVarClassName; char[] snippetDeclaringTypeName; // Mapping of external local variables char[][] localVarNames; char[][] localVarTypeNames; /** * Rebuild source in presence of external local variables */ public CodeSnippetToCuMapper(char[] codeSnippet, char[] packageName, char[][] imports, char[] className, char[] varClassName, char[][] localVarNames, char[][] localVarTypeNames, int[] localVarModifiers, char[] declaringTypeName, String lineSeparator) { this.codeSnippet= codeSnippet; this.snippetPackageName= packageName; this.snippetImports= imports; this.snippetClassName= className; this.snippetVarClassName= varClassName; this.localVarNames= localVarNames; this.localVarTypeNames= localVarTypeNames; this.snippetDeclaringTypeName= declaringTypeName; buildCUSource(lineSeparator); } private void buildCUSource(String lineSeparator) { StringBuffer buffer= new StringBuffer(); // package declaration if (this.snippetPackageName != null && this.snippetPackageName.length != 0) { buffer.append("package "); //$NON-NLS-1$ buffer.append(this.snippetPackageName); buffer.append(";").append(lineSeparator); //$NON-NLS-1$ this.lineNumberOffset++; } // import declarations char[][] imports= this.snippetImports; for (int i= 0; i < imports.length; i++) { buffer.append("import "); //$NON-NLS-1$ buffer.append(imports[i]); buffer.append(';').append(lineSeparator); this.lineNumberOffset++; } // class declaration buffer.append("public class "); //$NON-NLS-1$ buffer.append(this.snippetClassName); // super class is either a global variable class or the CodeSnippet class if (this.snippetVarClassName != null) { buffer.append(" extends "); //$NON-NLS-1$ buffer.append(this.snippetVarClassName); } else { buffer.append(" extends "); //$NON-NLS-1$ buffer.append(PACKAGE_NAME); buffer.append("."); //$NON-NLS-1$ buffer.append(ROOT_CLASS_NAME); } buffer.append(" {").append(lineSeparator); //$NON-NLS-1$ this.lineNumberOffset++; if (this.snippetDeclaringTypeName != null) { buffer.append(" "); //$NON-NLS-1$ buffer.append(this.snippetDeclaringTypeName); buffer.append(" "); //$NON-NLS-1$ buffer.append(DELEGATE_THIS); // val$this buffer.append(';').append(lineSeparator); this.lineNumberOffset++; } // add some storage location for local variable persisted state if (this.localVarNames != null) { for (int i= 0, max= this.localVarNames.length; i < max; i++) { buffer.append(" "); //$NON-NLS-1$ buffer.append(this.localVarTypeNames[i]); buffer.append(" "); //$NON-NLS-1$ buffer.append(LOCAL_VAR_PREFIX); // val$... buffer.append(this.localVarNames[i]); buffer.append(';').append(lineSeparator); this.lineNumberOffset++; } } // run() method declaration buffer.append("public void run() throws Throwable {").append(lineSeparator); //$NON-NLS-1$ this.lineNumberOffset++; this.startPosOffset= buffer.length(); buffer.append(this.codeSnippet); // a line separator is required after the code snippet source code // in case the code snippet source code ends with a line comment // http://dev.eclipse.org/bugs/show_bug.cgi?id=14838 buffer.append(lineSeparator).append('}').append(lineSeparator); // end of class declaration buffer.append('}').append(lineSeparator); // store result int length= buffer.length(); this.cuSource= new char[length]; buffer.getChars(0, length, this.cuSource, 0); } /** * Returns a completion requestor that wraps the given requestor and shift the results according * to the start offset and line number offset of the code snippet in the generated compilation * unit. */ public CompletionRequestor getCompletionRequestor(final CompletionRequestor originalRequestor) { return new CompletionRequestor() { public void accept(CompletionProposal proposal) { switch (proposal.getKind()) { case CompletionProposal.TYPE_REF: int flags= proposal.getFlags(); if ((flags & Flags.AccEnum) == 0 && (flags & Flags.AccInterface) == 0) { // Remove completion on generated class name or generated global variable class name char[] packageName= proposal.getDeclarationSignature(); char[] className= Signature.getSignatureSimpleName(proposal.getSignature()); if (CharOperation.equals(packageName, CodeSnippetToCuMapper.this.snippetPackageName) && (CharOperation.equals(className, CodeSnippetToCuMapper.this.snippetClassName) || CharOperation.equals(className, CodeSnippetToCuMapper.this.snippetVarClassName))) return; if (CharOperation.equals(packageName, PACKAGE_NAME) && CharOperation.equals(className, ROOT_CLASS_NAME)) return; } break; case CompletionProposal.METHOD_REF: case CompletionProposal.METHOD_DECLARATION: case CompletionProposal.METHOD_REF_WITH_CASTED_RECEIVER: // Remove completion on generated method char[] declaringTypePackageName= Signature.getSignatureQualifier(proposal.getDeclarationSignature()); char[] declaringTypeName= Signature.getSignatureSimpleName(proposal.getDeclarationSignature()); if (CharOperation.equals(declaringTypePackageName, CodeSnippetToCuMapper.this.snippetPackageName) && CharOperation.equals(declaringTypeName, CodeSnippetToCuMapper.this.snippetClassName)) return; if (CharOperation.equals(declaringTypePackageName, PACKAGE_NAME) && CharOperation.equals(declaringTypeName, ROOT_CLASS_NAME)) return; break; } originalRequestor.accept(proposal); } public void completionFailure(IProblem problem) { problem.setSourceStart(problem.getSourceStart() - CodeSnippetToCuMapper.this.startPosOffset); problem.setSourceEnd(problem.getSourceEnd() - CodeSnippetToCuMapper.this.startPosOffset); problem.setSourceLineNumber(problem.getSourceLineNumber() - CodeSnippetToCuMapper.this.lineNumberOffset); originalRequestor.completionFailure(problem); } public void acceptContext(CompletionContext context) { originalRequestor.acceptContext(context); } public void beginReporting() { originalRequestor.beginReporting(); } public void endReporting() { originalRequestor.endReporting(); } public boolean isIgnored(int completionProposalKind) { return originalRequestor.isIgnored(completionProposalKind); } public void setIgnored(int completionProposalKind, boolean ignore) { originalRequestor.setIgnored(completionProposalKind, ignore); } public boolean isAllowingRequiredProposals(int mainKind, int requiredKind) { return originalRequestor.isAllowingRequiredProposals(mainKind, requiredKind); } public void setAllowsRequiredProposals(int mainKind, int requiredKind, boolean allow) { originalRequestor.setAllowsRequiredProposals(mainKind, requiredKind, allow); } }; } public char[] getCUSource(String lineSeparator) { if (this.cuSource == null) { buildCUSource(lineSeparator); } return this.cuSource; } /** * Returns the type of evaluation that corresponds to the given line number in the generated * compilation unit. */ public int getEvaluationType(int lineNumber) { int currentLine= 1; // check package declaration if (this.snippetPackageName != null && this.snippetPackageName.length != 0) { if (lineNumber == 1) { return EvaluationResult.T_PACKAGE; } currentLine++; } // check imports char[][] imports= this.snippetImports; if ((currentLine <= lineNumber) && (lineNumber < (currentLine + imports.length))) { return EvaluationResult.T_IMPORT; } currentLine+= imports.length + 1; // + 1 to skip the class declaration line // check generated fields currentLine+= (this.snippetDeclaringTypeName == null ? 0 : 1) + (this.localVarNames == null ? 0 : this.localVarNames.length); if (currentLine > lineNumber) { return EvaluationResult.T_INTERNAL; } currentLine++; // + 1 to skip the method declaration line // check code snippet if (currentLine >= this.lineNumberOffset) { return EvaluationResult.T_CODE_SNIPPET; } // default return EvaluationResult.T_INTERNAL; } /** * Returns the import defined at the given line number. */ public char[] getImport(int lineNumber) { int importStartLine= this.lineNumberOffset - 1 - this.snippetImports.length; return this.snippetImports[lineNumber - importStartLine]; } /** * Returns a selection requestor that wraps the given requestor and shift the problems according * to the start offset and line number offset of the code snippet in the generated compilation * unit. */ public ISelectionRequestor getSelectionRequestor(final ISelectionRequestor originalRequestor) { return new ISelectionRequestor() { public void acceptType(char[] packageName, char[] typeName, int modifiers, boolean isDeclaration, char[] uniqueKey, int start, int end) { originalRequestor.acceptType(packageName, typeName, modifiers, isDeclaration, uniqueKey, start, end); } public void acceptError(CategorizedProblem error) { error.setSourceLineNumber(error.getSourceLineNumber() - CodeSnippetToCuMapper.this.lineNumberOffset); error.setSourceStart(error.getSourceStart() - CodeSnippetToCuMapper.this.startPosOffset); error.setSourceEnd(error.getSourceEnd() - CodeSnippetToCuMapper.this.startPosOffset); originalRequestor.acceptError(error); } public void acceptField(char[] declaringTypePackageName, char[] declaringTypeName, char[] name, boolean isDeclaration, char[] uniqueKey, int start, int end) { originalRequestor.acceptField(declaringTypePackageName, declaringTypeName, name, isDeclaration, uniqueKey, start, end); } public void acceptMethod(char[] declaringTypePackageName, char[] declaringTypeName, String enclosingDeclaringTypeSignature, char[] selector, char[][] parameterPackageNames, char[][] parameterTypeNames, String[] parameterSignatures, char[][] typeParameterNames, char[][][] typeParameterBoundNames, boolean isConstructor, boolean isDeclaration, char[] uniqueKey, int start, int end) { originalRequestor.acceptMethod(declaringTypePackageName, declaringTypeName, enclosingDeclaringTypeSignature, selector, parameterPackageNames, parameterTypeNames, parameterSignatures, typeParameterNames, typeParameterBoundNames, isConstructor, isDeclaration, uniqueKey, start, end); } public void acceptPackage(char[] packageName) { originalRequestor.acceptPackage(packageName); } public void acceptTypeParameter(char[] declaringTypePackageName, char[] declaringTypeName, char[] typeParameterName, boolean isDeclaration, int start, int end) { originalRequestor.acceptTypeParameter(declaringTypePackageName, declaringTypeName, typeParameterName, isDeclaration, start, end); } public void acceptMethodTypeParameter(char[] declaringTypePackageName, char[] declaringTypeName, char[] selector, int selectorStart, int selectorEnd, char[] typeParameterName, boolean isDeclaration, int start, int end) { originalRequestor.acceptMethodTypeParameter(declaringTypePackageName, declaringTypeName, selector, selectorStart, selectorEnd, typeParameterName, isDeclaration, start, end); } }; } }