/*******************************************************************************
* 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;
}
}