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.compiler;
011:
012: /**
013: * A compilation result consists of all information returned by the compiler for
014: * a single compiled compilation source unit. This includes:
015: * <ul>
016: * <li> the compilation unit that was compiled
017: * <li> for each type produced by compiling the compilation unit, its binary and optionally its principal structure
018: * <li> any problems (errors or warnings) produced
019: * <li> dependency info
020: * </ul>
021: *
022: * The principle structure and binary may be null if the compiler could not produce them.
023: * If neither could be produced, there is no corresponding entry for the type.
024: *
025: * The dependency info includes type references such as supertypes, field types, method
026: * parameter and return types, local variable types, types of intermediate expressions, etc.
027: * It also includes the namespaces (packages) in which names were looked up.
028: * It does <em>not</em> include finer grained dependencies such as information about
029: * specific fields and methods which were referenced, but does contain their
030: * declaring types and any other types used to locate such fields or methods.
031: */
032: import java.util.Arrays;
033: import java.util.Comparator;
034: import java.util.HashMap;
035: import java.util.HashSet;
036: import java.util.Hashtable;
037: import java.util.Iterator;
038: import java.util.Map;
039: import java.util.Set;
040:
041: import org.eclipse.jdt.core.compiler.CategorizedProblem;
042: import org.eclipse.jdt.core.compiler.IProblem;
043: import org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration;
044: import org.eclipse.jdt.internal.compiler.env.ICompilationUnit;
045: import org.eclipse.jdt.internal.compiler.impl.ReferenceContext;
046: import org.eclipse.jdt.internal.compiler.lookup.SourceTypeBinding;
047: import org.eclipse.jdt.internal.compiler.parser.RecoveryScannerData;
048: import org.eclipse.jdt.internal.compiler.problem.ProblemReporter;
049: import org.eclipse.jdt.internal.compiler.util.Util;
050:
051: public class CompilationResult {
052:
053: public CategorizedProblem problems[];
054: public CategorizedProblem tasks[];
055: public int problemCount;
056: public int taskCount;
057: public ICompilationUnit compilationUnit;
058: private Map problemsMap;
059: private Set firstErrors;
060: private int maxProblemPerUnit;
061: public char[][][] qualifiedReferences;
062: public char[][] simpleNameReferences;
063: public boolean hasAnnotations = false;
064: public int lineSeparatorPositions[];
065: public RecoveryScannerData recoveryScannerData;
066: public Map compiledTypes = new Hashtable(11);
067: public int unitIndex, totalUnitsKnown;
068: public boolean hasBeenAccepted = false;
069: public char[] fileName;
070: public boolean hasInconsistentToplevelHierarchies = false; // record the fact some toplevel types have inconsistent hierarchies
071: public boolean hasSyntaxError = false;
072: long[] suppressWarningIrritants; // irritant for suppressed warnings
073: long[] suppressWarningScopePositions; // (start << 32) + end
074: int suppressWarningsCount;
075: public char[][] packageName;
076:
077: private static final int[] EMPTY_LINE_ENDS = Util.EMPTY_INT_ARRAY;
078: private static final Comparator PROBLEM_COMPARATOR = new Comparator() {
079: public int compare(Object o1, Object o2) {
080: return ((CategorizedProblem) o1).getSourceStart()
081: - ((CategorizedProblem) o2).getSourceStart();
082: }
083: };
084:
085: public CompilationResult(char[] fileName, int unitIndex,
086: int totalUnitsKnown, int maxProblemPerUnit) {
087:
088: this .fileName = fileName;
089: this .unitIndex = unitIndex;
090: this .totalUnitsKnown = totalUnitsKnown;
091: this .maxProblemPerUnit = maxProblemPerUnit;
092: }
093:
094: public CompilationResult(ICompilationUnit compilationUnit,
095: int unitIndex, int totalUnitsKnown, int maxProblemPerUnit) {
096:
097: this .fileName = compilationUnit.getFileName();
098: this .compilationUnit = compilationUnit;
099: this .unitIndex = unitIndex;
100: this .totalUnitsKnown = totalUnitsKnown;
101: this .maxProblemPerUnit = maxProblemPerUnit;
102: }
103:
104: private int computePriority(CategorizedProblem problem) {
105: final int P_STATIC = 10000;
106: final int P_OUTSIDE_METHOD = 40000;
107: final int P_FIRST_ERROR = 20000;
108: final int P_ERROR = 100000;
109:
110: int priority = 10000 - problem.getSourceLineNumber(); // early problems first
111: if (priority < 0)
112: priority = 0;
113: if (problem.isError()) {
114: priority += P_ERROR;
115: }
116: ReferenceContext context = this .problemsMap == null ? null
117: : (ReferenceContext) this .problemsMap.get(problem);
118: if (context != null) {
119: if (context instanceof AbstractMethodDeclaration) {
120: AbstractMethodDeclaration method = (AbstractMethodDeclaration) context;
121: if (method.isStatic()) {
122: priority += P_STATIC;
123: }
124: } else {
125: priority += P_OUTSIDE_METHOD;
126: }
127: if (this .firstErrors.contains(problem)) { // if context is null, firstErrors is null too
128: priority += P_FIRST_ERROR;
129: }
130: } else {
131: priority += P_OUTSIDE_METHOD;
132: }
133: return priority;
134: }
135:
136: public void discardSuppressedWarnings() {
137: if (this .suppressWarningsCount == 0)
138: return;
139: int removed = 0;
140: nextProblem: for (int i = 0, length = this .problemCount; i < length; i++) {
141: CategorizedProblem problem = this .problems[i];
142: int problemID = problem.getID();
143: if (!problem.isWarning()) {
144: continue nextProblem;
145: }
146: int start = problem.getSourceStart();
147: int end = problem.getSourceEnd();
148: nextSuppress: for (int j = 0, max = this .suppressWarningsCount; j < max; j++) {
149: long position = this .suppressWarningScopePositions[j];
150: int startSuppress = (int) (position >>> 32);
151: int endSuppress = (int) position;
152: if (start < startSuppress)
153: continue nextSuppress;
154: if (end > endSuppress)
155: continue nextSuppress;
156: if ((ProblemReporter.getIrritant(problemID) & this .suppressWarningIrritants[j]) == 0)
157: continue nextSuppress;
158: // discard suppressed warning
159: removed++;
160: this .problems[i] = null;
161: if (this .problemsMap != null)
162: this .problemsMap.remove(problem);
163: if (this .firstErrors != null)
164: this .firstErrors.remove(problem);
165: continue nextProblem;
166: }
167: }
168: if (removed > 0) {
169: for (int i = 0, index = 0; i < this .problemCount; i++) {
170: CategorizedProblem problem;
171: if ((problem = this .problems[i]) != null) {
172: if (i > index) {
173: this .problems[index++] = problem;
174: } else {
175: index++;
176: }
177: }
178: }
179: this .problemCount -= removed;
180: }
181: }
182:
183: public CategorizedProblem[] getAllProblems() {
184: CategorizedProblem[] onlyProblems = this .getProblems();
185: int onlyProblemCount = onlyProblems != null ? onlyProblems.length
186: : 0;
187: CategorizedProblem[] onlyTasks = this .getTasks();
188: int onlyTaskCount = onlyTasks != null ? onlyTasks.length : 0;
189: if (onlyTaskCount == 0) {
190: return onlyProblems;
191: }
192: if (onlyProblemCount == 0) {
193: return onlyTasks;
194: }
195:
196: int totalNumberOfProblem = onlyProblemCount + onlyTaskCount;
197: CategorizedProblem[] allProblems = new CategorizedProblem[totalNumberOfProblem];
198: int allProblemIndex = 0;
199: int taskIndex = 0;
200: int problemIndex = 0;
201: while (taskIndex + problemIndex < totalNumberOfProblem) {
202: CategorizedProblem nextTask = null;
203: CategorizedProblem nextProblem = null;
204: if (taskIndex < onlyTaskCount) {
205: nextTask = onlyTasks[taskIndex];
206: }
207: if (problemIndex < onlyProblemCount) {
208: nextProblem = onlyProblems[problemIndex];
209: }
210: // select the next problem
211: CategorizedProblem currentProblem = null;
212: if (nextProblem != null) {
213: if (nextTask != null) {
214: if (nextProblem.getSourceStart() < nextTask
215: .getSourceStart()) {
216: currentProblem = nextProblem;
217: problemIndex++;
218: } else {
219: currentProblem = nextTask;
220: taskIndex++;
221: }
222: } else {
223: currentProblem = nextProblem;
224: problemIndex++;
225: }
226: } else {
227: if (nextTask != null) {
228: currentProblem = nextTask;
229: taskIndex++;
230: }
231: }
232: allProblems[allProblemIndex++] = currentProblem;
233: }
234: return allProblems;
235: }
236:
237: public ClassFile[] getClassFiles() {
238: ClassFile[] classFiles = new ClassFile[this .compiledTypes
239: .size()];
240: this .compiledTypes.values().toArray(classFiles);
241: return classFiles;
242: }
243:
244: /**
245: * Answer the initial compilation unit corresponding to the present compilation result
246: */
247: public ICompilationUnit getCompilationUnit() {
248: return this .compilationUnit;
249: }
250:
251: /**
252: * Answer the errors encountered during compilation.
253: */
254: public CategorizedProblem[] getErrors() {
255: CategorizedProblem[] reportedProblems = getProblems();
256: int errorCount = 0;
257: for (int i = 0; i < this .problemCount; i++) {
258: if (reportedProblems[i].isError())
259: errorCount++;
260: }
261: if (errorCount == this .problemCount)
262: return reportedProblems;
263: CategorizedProblem[] errors = new CategorizedProblem[errorCount];
264: int index = 0;
265: for (int i = 0; i < this .problemCount; i++) {
266: if (reportedProblems[i].isError())
267: errors[index++] = reportedProblems[i];
268: }
269: return errors;
270: }
271:
272: /**
273: * Answer the initial file name
274: */
275: public char[] getFileName() {
276: return this .fileName;
277: }
278:
279: public int[] getLineSeparatorPositions() {
280: return this .lineSeparatorPositions == null ? CompilationResult.EMPTY_LINE_ENDS
281: : this .lineSeparatorPositions;
282: }
283:
284: /**
285: * Answer the problems (errors and warnings) encountered during compilation.
286: *
287: * This is not a compiler internal API - it has side-effects !
288: * It is intended to be used only once all problems have been detected,
289: * and makes sure the problems slot as the exact size of the number of
290: * problems.
291: */
292: public CategorizedProblem[] getProblems() {
293: // Re-adjust the size of the problems if necessary.
294: if (this .problems != null) {
295: discardSuppressedWarnings();
296:
297: if (this .problemCount != this .problems.length) {
298: System
299: .arraycopy(
300: this .problems,
301: 0,
302: (this .problems = new CategorizedProblem[this .problemCount]),
303: 0, this .problemCount);
304: }
305:
306: if (this .maxProblemPerUnit > 0
307: && this .problemCount > this .maxProblemPerUnit) {
308: quickPrioritize(this .problems, 0, this .problemCount - 1);
309: this .problemCount = this .maxProblemPerUnit;
310: System
311: .arraycopy(
312: this .problems,
313: 0,
314: (this .problems = new CategorizedProblem[this .problemCount]),
315: 0, this .problemCount);
316: }
317:
318: // Stable sort problems per source positions.
319: Arrays.sort(this .problems, 0, this .problems.length,
320: CompilationResult.PROBLEM_COMPARATOR);
321: //quickSort(problems, 0, problems.length-1);
322: }
323: return this .problems;
324: }
325:
326: /**
327: * Answer the tasks (TO-DO, ...) encountered during compilation.
328: *
329: * This is not a compiler internal API - it has side-effects !
330: * It is intended to be used only once all problems have been detected,
331: * and makes sure the problems slot as the exact size of the number of
332: * problems.
333: */
334: public CategorizedProblem[] getTasks() {
335: // Re-adjust the size of the tasks if necessary.
336: if (this .tasks != null) {
337:
338: if (this .taskCount != this .tasks.length) {
339: System
340: .arraycopy(
341: this .tasks,
342: 0,
343: (this .tasks = new CategorizedProblem[this .taskCount]),
344: 0, this .taskCount);
345: }
346: // Stable sort problems per source positions.
347: Arrays.sort(this .tasks, 0, this .tasks.length,
348: CompilationResult.PROBLEM_COMPARATOR);
349: //quickSort(tasks, 0, tasks.length-1);
350: }
351: return this .tasks;
352: }
353:
354: public boolean hasErrors() {
355: if (this .problems != null)
356: for (int i = 0; i < this .problemCount; i++) {
357: if (this .problems[i].isError())
358: return true;
359: }
360: return false;
361: }
362:
363: public boolean hasProblems() {
364: return this .problemCount != 0;
365: }
366:
367: public boolean hasTasks() {
368: return this .taskCount != 0;
369: }
370:
371: public boolean hasWarnings() {
372: if (this .problems != null)
373: for (int i = 0; i < this .problemCount; i++) {
374: if (this .problems[i].isWarning())
375: return true;
376: }
377: return false;
378: }
379:
380: private void quickPrioritize(CategorizedProblem[] problemList,
381: int left, int right) {
382: if (left >= right)
383: return;
384:
385: // sort the problems by their priority... starting with the highest priority
386: int original_left = left;
387: int original_right = right;
388: int mid = computePriority(problemList[left + (right - left) / 2]);
389: do {
390: while (computePriority(problemList[right]) < mid)
391: right--;
392: while (mid < computePriority(problemList[left]))
393: left++;
394: if (left <= right) {
395: CategorizedProblem tmp = problemList[left];
396: problemList[left] = problemList[right];
397: problemList[right] = tmp;
398: left++;
399: right--;
400: }
401: } while (left <= right);
402: if (original_left < right)
403: quickPrioritize(problemList, original_left, right);
404: if (left < original_right)
405: quickPrioritize(problemList, left, original_right);
406: }
407:
408: /*
409: * Record the compilation unit result's package name
410: */
411: public void recordPackageName(char[][] packName) {
412: this .packageName = packName;
413: }
414:
415: public void record(CategorizedProblem newProblem,
416: ReferenceContext referenceContext) {
417: //new Exception("VERBOSE PROBLEM REPORTING").printStackTrace();
418: if (newProblem.getID() == IProblem.Task) {
419: recordTask(newProblem);
420: return;
421: }
422: if (this .problemCount == 0) {
423: this .problems = new CategorizedProblem[5];
424: } else if (this .problemCount == this .problems.length) {
425: System
426: .arraycopy(
427: this .problems,
428: 0,
429: (this .problems = new CategorizedProblem[this .problemCount * 2]),
430: 0, this .problemCount);
431: }
432: this .problems[this .problemCount++] = newProblem;
433: if (referenceContext != null) {
434: if (this .problemsMap == null)
435: this .problemsMap = new HashMap(5);
436: if (this .firstErrors == null)
437: this .firstErrors = new HashSet(5);
438: if (newProblem.isError() && !referenceContext.hasErrors())
439: this .firstErrors.add(newProblem);
440: this .problemsMap.put(newProblem, referenceContext);
441: }
442: if ((newProblem.getID() & IProblem.Syntax) != 0
443: && newProblem.isError())
444: this .hasSyntaxError = true;
445: }
446:
447: /**
448: * For now, remember the compiled type using its compound name.
449: */
450: public void record(char[] typeName, ClassFile classFile) {
451: SourceTypeBinding sourceType = classFile.referenceBinding;
452: if (!sourceType.isLocalType()
453: && sourceType.isHierarchyInconsistent()) {
454: this .hasInconsistentToplevelHierarchies = true;
455: }
456: this .compiledTypes.put(typeName, classFile);
457: }
458:
459: public void recordSuppressWarnings(long irritant, int scopeStart,
460: int scopeEnd) {
461: if (this .suppressWarningIrritants == null) {
462: this .suppressWarningIrritants = new long[3];
463: this .suppressWarningScopePositions = new long[3];
464: } else if (this .suppressWarningIrritants.length == this .suppressWarningsCount) {
465: System
466: .arraycopy(
467: this .suppressWarningIrritants,
468: 0,
469: this .suppressWarningIrritants = new long[2 * this .suppressWarningsCount],
470: 0, this .suppressWarningsCount);
471: System
472: .arraycopy(
473: this .suppressWarningScopePositions,
474: 0,
475: this .suppressWarningScopePositions = new long[2 * this .suppressWarningsCount],
476: 0, this .suppressWarningsCount);
477: }
478: this .suppressWarningIrritants[this .suppressWarningsCount] = irritant;
479: this .suppressWarningScopePositions[this .suppressWarningsCount++] = ((long) scopeStart << 32)
480: + scopeEnd;
481: }
482:
483: private void recordTask(CategorizedProblem newProblem) {
484: if (this .taskCount == 0) {
485: this .tasks = new CategorizedProblem[5];
486: } else if (this .taskCount == this .tasks.length) {
487: System
488: .arraycopy(
489: this .tasks,
490: 0,
491: (this .tasks = new CategorizedProblem[this .taskCount * 2]),
492: 0, this .taskCount);
493: }
494: this .tasks[this .taskCount++] = newProblem;
495: }
496:
497: public CompilationResult tagAsAccepted() {
498: this .hasBeenAccepted = true;
499: this .problemsMap = null; // flush
500: this .firstErrors = null; // flush
501: return this ;
502: }
503:
504: public String toString() {
505: StringBuffer buffer = new StringBuffer();
506: if (this .fileName != null) {
507: buffer
508: .append("Filename : ").append(this .fileName).append('\n'); //$NON-NLS-1$
509: }
510: if (this .compiledTypes != null) {
511: buffer.append("COMPILED type(s) \n"); //$NON-NLS-1$
512: Iterator keys = this .compiledTypes.keySet().iterator();
513: while (keys.hasNext()) {
514: char[] typeName = (char[]) keys.next();
515: buffer.append("\t - ").append(typeName).append('\n'); //$NON-NLS-1$
516:
517: }
518: } else {
519: buffer.append("No COMPILED type\n"); //$NON-NLS-1$
520: }
521: if (this .problems != null) {
522: buffer.append(this .problemCount).append(
523: " PROBLEM(s) detected \n"); //$NON-NLS-1$
524: for (int i = 0; i < this .problemCount; i++) {
525: buffer
526: .append("\t - ").append(this .problems[i]).append('\n'); //$NON-NLS-1$
527: }
528: } else {
529: buffer.append("No PROBLEM\n"); //$NON-NLS-1$
530: }
531: return buffer.toString();
532: }
533: }
|