/******************************************************************************* * 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.eval; import java.util.Map; import org.eclipse.jdt.core.compiler.*; import org.eclipse.jdt.internal.compiler.ClassFile; import org.eclipse.jdt.internal.compiler.Compiler; import org.eclipse.jdt.internal.compiler.ICompilerRequestor; import org.eclipse.jdt.internal.compiler.IProblemFactory; import org.eclipse.jdt.internal.compiler.classfmt.ClassFileReader; import org.eclipse.jdt.internal.compiler.classfmt.ClassFormatException; import org.eclipse.jdt.internal.compiler.env.IBinaryType; import org.eclipse.jdt.internal.compiler.env.INameEnvironment; /** * A variables evaluator compiles the global variables of an evaluation context and returns * the corresponding class files. Or it reports problems against these variables. */ public class VariablesEvaluator extends Evaluator implements EvaluationConstants { /** * Creates a new global variables evaluator. */ VariablesEvaluator(EvaluationContext context, INameEnvironment environment, Map options, IRequestor requestor, IProblemFactory problemFactory) { super(context, environment, options, requestor, problemFactory); } /** * @see org.eclipse.jdt.internal.eval.Evaluator */ protected void addEvaluationResultForCompilationProblem(Map resultsByIDs, CategorizedProblem problem, char[] cuSource) { // set evaluation id and type to an internal problem by default char[] evaluationID = cuSource; int evaluationType = EvaluationResult.T_INTERNAL; int pbLine = problem.getSourceLineNumber(); int currentLine = 1; // check package declaration char[] packageName = getPackageName(); if (packageName.length > 0) { if (pbLine == 1) { // set evaluation id and type evaluationID = packageName; evaluationType = EvaluationResult.T_PACKAGE; // shift line number, source start and source end problem.setSourceLineNumber(1); problem.setSourceStart(0); problem.setSourceEnd(evaluationID.length - 1); } currentLine++; } // check imports char[][] imports = this.context.imports; if ((currentLine <= pbLine) && (pbLine < (currentLine + imports.length))) { // set evaluation id and type evaluationID = imports[pbLine - currentLine]; evaluationType = EvaluationResult.T_IMPORT; // shift line number, source start and source end problem.setSourceLineNumber(1); problem.setSourceStart(0); problem.setSourceEnd(evaluationID.length - 1); } currentLine += imports.length + 1; // + 1 to skip the class declaration line // check variable declarations int varCount = this.context.variableCount; if ((currentLine <= pbLine) && (pbLine < currentLine + varCount)) { GlobalVariable var = this.context.variables[pbLine - currentLine]; // set evaluation id and type evaluationID = var.getName(); evaluationType = EvaluationResult.T_VARIABLE; // shift line number, source start and source end int pbStart = problem.getSourceStart() - var.declarationStart; int pbEnd = problem.getSourceEnd() - var.declarationStart; int typeLength = var.getTypeName().length; if ((0 <= pbStart) && (pbEnd < typeLength)) { // problem on the type of the variable problem.setSourceLineNumber(-1); } else { // problem on the name of the variable pbStart -= typeLength + 1; // type length + space pbEnd -= typeLength + 1; // type length + space problem.setSourceLineNumber(0); } problem.setSourceStart(pbStart); problem.setSourceEnd(pbEnd); } currentLine = -1; // not needed any longer // check variable initializers for (int i = 0; i < varCount; i++) { GlobalVariable var = this.context.variables[i]; char[] initializer = var.getInitializer(); int initializerLength = initializer == null ? 0 : initializer.length; if ((var.initializerStart <= problem.getSourceStart()) && (problem.getSourceEnd() < var.initializerStart + var.name.length)) { /* Problem with the variable name. Ignore because it must have already been reported when checking the declaration. */ return; } else if ((var.initExpressionStart <= problem.getSourceStart()) && (problem.getSourceEnd() < var.initExpressionStart + initializerLength)) { // set evaluation id and type evaluationID = var.name; evaluationType = EvaluationResult.T_VARIABLE; // shift line number, source start and source end problem.setSourceLineNumber(pbLine - var.initializerLineStart + 1); problem.setSourceStart(problem.getSourceStart() - var.initExpressionStart); problem.setSourceEnd(problem.getSourceEnd() - var.initExpressionStart); break; } } EvaluationResult result = (EvaluationResult)resultsByIDs.get(evaluationID); if (result == null) { resultsByIDs.put(evaluationID, new EvaluationResult(evaluationID, evaluationType, new CategorizedProblem[] {problem})); } else { result.addProblem(problem); } } /** * @see org.eclipse.jdt.internal.eval.Evaluator */ protected char[] getClassName() { return CharOperation.concat(EvaluationConstants.GLOBAL_VARS_CLASS_NAME_PREFIX, Integer.toString(EvaluationContext.VAR_CLASS_COUNTER + 1).toCharArray()); } /** * Creates and returns a compiler for this evaluator. */ Compiler getCompiler(ICompilerRequestor compilerRequestor) { Compiler compiler = super.getCompiler(compilerRequestor); // Initialize the compiler's lookup environment with the already compiled super class IBinaryType binaryType = this.context.getRootCodeSnippetBinary(); if (binaryType != null) { compiler.lookupEnvironment.cacheBinaryType(binaryType, null /*no access restriction*/); } // and the installed global variable classes VariablesInfo installedVars = this.context.installedVars; if (installedVars != null) { ClassFile[] classFiles = installedVars.classFiles; for (int i = 0; i < classFiles.length; i++) { ClassFile classFile = classFiles[i]; IBinaryType binary = null; try { binary = new ClassFileReader(classFile.getBytes(), null); } catch (ClassFormatException e) { e.printStackTrace(); // Should never happen since we compiled this type } compiler.lookupEnvironment.cacheBinaryType(binary, null /*no access restriction*/); } } return compiler; } /** * Returns the name of package of the current compilation unit. */ protected char[] getPackageName() { return this.context.packageName; } /** * @see org.eclipse.jdt.internal.eval.Evaluator */ protected char[] getSource() { StringBuffer buffer = new StringBuffer(); int lineNumberOffset = 1; // package declaration char[] packageName = getPackageName(); if (packageName.length != 0) { buffer.append("package "); //$NON-NLS-1$ buffer.append(packageName); buffer.append(';').append(this.context.lineSeparator); lineNumberOffset++; } // import declarations char[][] imports = this.context.imports; for (int i = 0; i < imports.length; i++) { buffer.append("import "); //$NON-NLS-1$ buffer.append(imports[i]); buffer.append(';').append(this.context.lineSeparator); lineNumberOffset++; } // class declaration buffer.append("public class "); //$NON-NLS-1$ buffer.append(getClassName()); buffer.append(" extends "); //$NON-NLS-1$ buffer.append(PACKAGE_NAME); buffer.append("."); //$NON-NLS-1$ buffer.append(ROOT_CLASS_NAME); buffer.append(" {").append(this.context.lineSeparator); //$NON-NLS-1$ lineNumberOffset++; // field declarations GlobalVariable[] vars = this.context.variables; VariablesInfo installedVars = this.context.installedVars; for (int i = 0; i < this.context.variableCount; i++){ GlobalVariable var = vars[i]; buffer.append("\tpublic static "); //$NON-NLS-1$ var.declarationStart = buffer.length(); buffer.append(var.typeName); buffer.append(" "); //$NON-NLS-1$ char[] varName = var.name; buffer.append(varName); buffer.append(';').append(this.context.lineSeparator); lineNumberOffset++; } // field initializations buffer.append("\tstatic {").append(this.context.lineSeparator); //$NON-NLS-1$ lineNumberOffset++; for (int i = 0; i < this.context.variableCount; i++){ GlobalVariable var = vars[i]; char[] varName = var.name; GlobalVariable installedVar = installedVars == null ? null : installedVars.varNamed(varName); if (installedVar == null || !CharOperation.equals(installedVar.typeName, var.typeName)) { // Initialize with initializer if there was no previous value char[] initializer = var.initializer; if (initializer != null) { buffer.append("\t\ttry {").append(this.context.lineSeparator); //$NON-NLS-1$ lineNumberOffset++; var.initializerLineStart = lineNumberOffset; buffer.append("\t\t\t"); //$NON-NLS-1$ var.initializerStart = buffer.length(); buffer.append(varName); buffer.append("= "); //$NON-NLS-1$ var.initExpressionStart = buffer.length(); buffer.append(initializer); lineNumberOffset += numberOfCRs(initializer); buffer.append(';').append(this.context.lineSeparator); buffer.append("\t\t} catch (Throwable e) {").append(this.context.lineSeparator); //$NON-NLS-1$ buffer.append("\t\t\te.printStackTrace();").append(this.context.lineSeparator); //$NON-NLS-1$ buffer.append("\t\t}").append(this.context.lineSeparator); //$NON-NLS-1$ lineNumberOffset += 4; // 4 CRs } } else { // Initialize with previous value if name and type are the same buffer.append("\t\t"); //$NON-NLS-1$ buffer.append(varName); buffer.append("= "); //$NON-NLS-1$ char[] installedPackageName = installedVars.packageName; if (installedPackageName != null && installedPackageName.length != 0) { buffer.append(installedPackageName); buffer.append("."); //$NON-NLS-1$ } buffer.append(installedVars.className); buffer.append("."); //$NON-NLS-1$ buffer.append(varName); buffer.append(';').append(this.context.lineSeparator); lineNumberOffset++; } } buffer.append("\t}").append(this.context.lineSeparator); //$NON-NLS-1$ // end of class declaration buffer.append('}').append(this.context.lineSeparator); // return result int length = buffer.length(); char[] result = new char[length]; buffer.getChars(0, length, result, 0); return result; } /** * Returns the number of cariage returns included in the given source. */ private int numberOfCRs(char[] source) { int numberOfCRs = 0; boolean lastWasCR = false; for (int i = 0; i < source.length; i++) { char currentChar = source[i]; switch(currentChar){ case '\r' : lastWasCR = true; numberOfCRs++; break; case '\n' : if (!lastWasCR) numberOfCRs++; // merge CR-LF lastWasCR = false; break; default : lastWasCR = false; } } return numberOfCRs; } }