001: /*******************************************************************************
002: * Copyright (c) 2000, 2007 IBM Corporation and others.
003: * All rights reserved. This program and the accompanying materials
004: * are made available under the terms of the Eclipse Public License v1.0
005: * which accompanies this distribution, and is available at
006: * http://www.eclipse.org/legal/epl-v10.html
007: *
008: * Contributors:
009: * IBM Corporation - initial API and implementation
010: *******************************************************************************/package org.eclipse.jdt.internal.eval;
011:
012: import java.util.Map;
013:
014: import org.eclipse.jdt.core.compiler.*;
015: import org.eclipse.jdt.internal.compiler.ClassFile;
016: import org.eclipse.jdt.internal.compiler.Compiler;
017: import org.eclipse.jdt.internal.compiler.ICompilerRequestor;
018: import org.eclipse.jdt.internal.compiler.IProblemFactory;
019: import org.eclipse.jdt.internal.compiler.classfmt.ClassFileReader;
020: import org.eclipse.jdt.internal.compiler.classfmt.ClassFormatException;
021: import org.eclipse.jdt.internal.compiler.env.IBinaryType;
022: import org.eclipse.jdt.internal.compiler.env.INameEnvironment;
023:
024: /**
025: * A variables evaluator compiles the global variables of an evaluation context and returns
026: * the corresponding class files. Or it reports problems against these variables.
027: */
028: public class VariablesEvaluator extends Evaluator implements
029: EvaluationConstants {
030: /**
031: * Creates a new global variables evaluator.
032: */
033: VariablesEvaluator(EvaluationContext context,
034: INameEnvironment environment, Map options,
035: IRequestor requestor, IProblemFactory problemFactory) {
036: super (context, environment, options, requestor, problemFactory);
037: }
038:
039: /**
040: * @see org.eclipse.jdt.internal.eval.Evaluator
041: */
042: protected void addEvaluationResultForCompilationProblem(
043: Map resultsByIDs, CategorizedProblem problem,
044: char[] cuSource) {
045: // set evaluation id and type to an internal problem by default
046: char[] evaluationID = cuSource;
047: int evaluationType = EvaluationResult.T_INTERNAL;
048:
049: int pbLine = problem.getSourceLineNumber();
050: int currentLine = 1;
051:
052: // check package declaration
053: char[] packageName = getPackageName();
054: if (packageName.length > 0) {
055: if (pbLine == 1) {
056: // set evaluation id and type
057: evaluationID = packageName;
058: evaluationType = EvaluationResult.T_PACKAGE;
059:
060: // shift line number, source start and source end
061: problem.setSourceLineNumber(1);
062: problem.setSourceStart(0);
063: problem.setSourceEnd(evaluationID.length - 1);
064: }
065: currentLine++;
066: }
067:
068: // check imports
069: char[][] imports = this .context.imports;
070: if ((currentLine <= pbLine)
071: && (pbLine < (currentLine + imports.length))) {
072: // set evaluation id and type
073: evaluationID = imports[pbLine - currentLine];
074: evaluationType = EvaluationResult.T_IMPORT;
075:
076: // shift line number, source start and source end
077: problem.setSourceLineNumber(1);
078: problem.setSourceStart(0);
079: problem.setSourceEnd(evaluationID.length - 1);
080: }
081: currentLine += imports.length + 1; // + 1 to skip the class declaration line
082:
083: // check variable declarations
084: int varCount = this .context.variableCount;
085: if ((currentLine <= pbLine)
086: && (pbLine < currentLine + varCount)) {
087: GlobalVariable var = this .context.variables[pbLine
088: - currentLine];
089:
090: // set evaluation id and type
091: evaluationID = var.getName();
092: evaluationType = EvaluationResult.T_VARIABLE;
093:
094: // shift line number, source start and source end
095: int pbStart = problem.getSourceStart()
096: - var.declarationStart;
097: int pbEnd = problem.getSourceEnd() - var.declarationStart;
098: int typeLength = var.getTypeName().length;
099: if ((0 <= pbStart) && (pbEnd < typeLength)) {
100: // problem on the type of the variable
101: problem.setSourceLineNumber(-1);
102: } else {
103: // problem on the name of the variable
104: pbStart -= typeLength + 1; // type length + space
105: pbEnd -= typeLength + 1; // type length + space
106: problem.setSourceLineNumber(0);
107: }
108: problem.setSourceStart(pbStart);
109: problem.setSourceEnd(pbEnd);
110: }
111: currentLine = -1; // not needed any longer
112:
113: // check variable initializers
114: for (int i = 0; i < varCount; i++) {
115: GlobalVariable var = this .context.variables[i];
116: char[] initializer = var.getInitializer();
117: int initializerLength = initializer == null ? 0
118: : initializer.length;
119: if ((var.initializerStart <= problem.getSourceStart())
120: && (problem.getSourceEnd() < var.initializerStart
121: + var.name.length)) {
122: /* Problem with the variable name.
123: Ignore because it must have already been reported
124: when checking the declaration.
125: */
126: return;
127: } else if ((var.initExpressionStart <= problem
128: .getSourceStart())
129: && (problem.getSourceEnd() < var.initExpressionStart
130: + initializerLength)) {
131: // set evaluation id and type
132: evaluationID = var.name;
133: evaluationType = EvaluationResult.T_VARIABLE;
134:
135: // shift line number, source start and source end
136: problem.setSourceLineNumber(pbLine
137: - var.initializerLineStart + 1);
138: problem.setSourceStart(problem.getSourceStart()
139: - var.initExpressionStart);
140: problem.setSourceEnd(problem.getSourceEnd()
141: - var.initExpressionStart);
142:
143: break;
144: }
145: }
146:
147: EvaluationResult result = (EvaluationResult) resultsByIDs
148: .get(evaluationID);
149: if (result == null) {
150: resultsByIDs.put(evaluationID, new EvaluationResult(
151: evaluationID, evaluationType,
152: new CategorizedProblem[] { problem }));
153: } else {
154: result.addProblem(problem);
155: }
156: }
157:
158: /**
159: * @see org.eclipse.jdt.internal.eval.Evaluator
160: */
161: protected char[] getClassName() {
162: return CharOperation.concat(
163: EvaluationConstants.GLOBAL_VARS_CLASS_NAME_PREFIX,
164: Integer.toString(
165: EvaluationContext.VAR_CLASS_COUNTER + 1)
166: .toCharArray());
167: }
168:
169: /**
170: * Creates and returns a compiler for this evaluator.
171: */
172: Compiler getCompiler(ICompilerRequestor compilerRequestor) {
173: Compiler compiler = super .getCompiler(compilerRequestor);
174:
175: // Initialize the compiler's lookup environment with the already compiled super class
176: IBinaryType binaryType = this .context
177: .getRootCodeSnippetBinary();
178: if (binaryType != null) {
179: compiler.lookupEnvironment
180: .cacheBinaryType(binaryType, null /*no access restriction*/);
181: }
182:
183: // and the installed global variable classes
184: VariablesInfo installedVars = this .context.installedVars;
185: if (installedVars != null) {
186: ClassFile[] classFiles = installedVars.classFiles;
187: for (int i = 0; i < classFiles.length; i++) {
188: ClassFile classFile = classFiles[i];
189: IBinaryType binary = null;
190: try {
191: binary = new ClassFileReader(classFile.getBytes(),
192: null);
193: } catch (ClassFormatException e) {
194: e.printStackTrace(); // Should never happen since we compiled this type
195: }
196: compiler.lookupEnvironment
197: .cacheBinaryType(binary, null /*no access restriction*/);
198: }
199: }
200:
201: return compiler;
202: }
203:
204: /**
205: * Returns the name of package of the current compilation unit.
206: */
207: protected char[] getPackageName() {
208: return this .context.packageName;
209: }
210:
211: /**
212: * @see org.eclipse.jdt.internal.eval.Evaluator
213: */
214: protected char[] getSource() {
215: StringBuffer buffer = new StringBuffer();
216: int lineNumberOffset = 1;
217:
218: // package declaration
219: char[] packageName = getPackageName();
220: if (packageName.length != 0) {
221: buffer.append("package "); //$NON-NLS-1$
222: buffer.append(packageName);
223: buffer.append(';').append(this .context.lineSeparator);
224: lineNumberOffset++;
225: }
226:
227: // import declarations
228: char[][] imports = this .context.imports;
229: for (int i = 0; i < imports.length; i++) {
230: buffer.append("import "); //$NON-NLS-1$
231: buffer.append(imports[i]);
232: buffer.append(';').append(this .context.lineSeparator);
233: lineNumberOffset++;
234: }
235:
236: // class declaration
237: buffer.append("public class "); //$NON-NLS-1$
238: buffer.append(getClassName());
239: buffer.append(" extends "); //$NON-NLS-1$
240: buffer.append(PACKAGE_NAME);
241: buffer.append("."); //$NON-NLS-1$
242: buffer.append(ROOT_CLASS_NAME);
243: buffer.append(" {").append(this .context.lineSeparator); //$NON-NLS-1$
244: lineNumberOffset++;
245:
246: // field declarations
247: GlobalVariable[] vars = this .context.variables;
248: VariablesInfo installedVars = this .context.installedVars;
249: for (int i = 0; i < this .context.variableCount; i++) {
250: GlobalVariable var = vars[i];
251: buffer.append("\tpublic static "); //$NON-NLS-1$
252: var.declarationStart = buffer.length();
253: buffer.append(var.typeName);
254: buffer.append(" "); //$NON-NLS-1$
255: char[] varName = var.name;
256: buffer.append(varName);
257: buffer.append(';').append(this .context.lineSeparator);
258: lineNumberOffset++;
259: }
260:
261: // field initializations
262: buffer.append("\tstatic {").append(this .context.lineSeparator); //$NON-NLS-1$
263: lineNumberOffset++;
264: for (int i = 0; i < this .context.variableCount; i++) {
265: GlobalVariable var = vars[i];
266: char[] varName = var.name;
267: GlobalVariable installedVar = installedVars == null ? null
268: : installedVars.varNamed(varName);
269: if (installedVar == null
270: || !CharOperation.equals(installedVar.typeName,
271: var.typeName)) {
272: // Initialize with initializer if there was no previous value
273: char[] initializer = var.initializer;
274: if (initializer != null) {
275: buffer
276: .append("\t\ttry {").append(this .context.lineSeparator); //$NON-NLS-1$
277: lineNumberOffset++;
278: var.initializerLineStart = lineNumberOffset;
279: buffer.append("\t\t\t"); //$NON-NLS-1$
280: var.initializerStart = buffer.length();
281: buffer.append(varName);
282: buffer.append("= "); //$NON-NLS-1$
283: var.initExpressionStart = buffer.length();
284: buffer.append(initializer);
285: lineNumberOffset += numberOfCRs(initializer);
286: buffer.append(';').append(
287: this .context.lineSeparator);
288: buffer
289: .append("\t\t} catch (Throwable e) {").append(this .context.lineSeparator); //$NON-NLS-1$
290: buffer
291: .append("\t\t\te.printStackTrace();").append(this .context.lineSeparator); //$NON-NLS-1$
292: buffer
293: .append("\t\t}").append(this .context.lineSeparator); //$NON-NLS-1$
294: lineNumberOffset += 4; // 4 CRs
295: }
296: } else {
297: // Initialize with previous value if name and type are the same
298: buffer.append("\t\t"); //$NON-NLS-1$
299: buffer.append(varName);
300: buffer.append("= "); //$NON-NLS-1$
301: char[] installedPackageName = installedVars.packageName;
302: if (installedPackageName != null
303: && installedPackageName.length != 0) {
304: buffer.append(installedPackageName);
305: buffer.append("."); //$NON-NLS-1$
306: }
307: buffer.append(installedVars.className);
308: buffer.append("."); //$NON-NLS-1$
309: buffer.append(varName);
310: buffer.append(';').append(this .context.lineSeparator);
311: lineNumberOffset++;
312: }
313: }
314: buffer.append("\t}").append(this .context.lineSeparator); //$NON-NLS-1$
315:
316: // end of class declaration
317: buffer.append('}').append(this .context.lineSeparator);
318:
319: // return result
320: int length = buffer.length();
321: char[] result = new char[length];
322: buffer.getChars(0, length, result, 0);
323: return result;
324: }
325:
326: /**
327: * Returns the number of cariage returns included in the given source.
328: */
329: private int numberOfCRs(char[] source) {
330: int numberOfCRs = 0;
331: boolean lastWasCR = false;
332: for (int i = 0; i < source.length; i++) {
333: char currentChar = source[i];
334: switch (currentChar) {
335: case '\r':
336: lastWasCR = true;
337: numberOfCRs++;
338: break;
339: case '\n':
340: if (!lastWasCR)
341: numberOfCRs++; // merge CR-LF
342: lastWasCR = false;
343: break;
344: default:
345: lastWasCR = false;
346: }
347: }
348: return numberOfCRs;
349: }
350: }
|