/******************************************************************************* * 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.CategorizedProblem; import org.eclipse.jdt.core.compiler.CharOperation; 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; } }