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.ast;
011:
012: import java.util.Arrays;
013: import java.util.Comparator;
014:
015: import org.eclipse.jdt.core.compiler.CategorizedProblem;
016: import org.eclipse.jdt.core.compiler.CharOperation;
017: import org.eclipse.jdt.internal.compiler.ASTVisitor;
018: import org.eclipse.jdt.internal.compiler.ClassFile;
019: import org.eclipse.jdt.internal.compiler.CompilationResult;
020: import org.eclipse.jdt.internal.compiler.impl.ReferenceContext;
021: import org.eclipse.jdt.internal.compiler.lookup.CompilationUnitScope;
022: import org.eclipse.jdt.internal.compiler.lookup.ImportBinding;
023: import org.eclipse.jdt.internal.compiler.lookup.LocalTypeBinding;
024: import org.eclipse.jdt.internal.compiler.lookup.MethodScope;
025: import org.eclipse.jdt.internal.compiler.lookup.TypeConstants;
026: import org.eclipse.jdt.internal.compiler.parser.NLSTag;
027: import org.eclipse.jdt.internal.compiler.problem.AbortCompilationUnit;
028: import org.eclipse.jdt.internal.compiler.problem.AbortMethod;
029: import org.eclipse.jdt.internal.compiler.problem.AbortType;
030: import org.eclipse.jdt.internal.compiler.problem.ProblemReporter;
031: import org.eclipse.jdt.internal.compiler.problem.ProblemSeverities;
032:
033: public class CompilationUnitDeclaration extends ASTNode implements
034: ProblemSeverities, ReferenceContext {
035:
036: private static final Comparator STRING_LITERAL_COMPARATOR = new Comparator() {
037: public int compare(Object o1, Object o2) {
038: StringLiteral literal1 = (StringLiteral) o1;
039: StringLiteral literal2 = (StringLiteral) o2;
040: return literal1.sourceStart - literal2.sourceStart;
041: }
042: };
043: private static final int STRING_LITERALS_INCREMENT = 10;
044:
045: public ImportReference currentPackage;
046: public ImportReference[] imports;
047: public TypeDeclaration[] types;
048: public int[][] comments;
049:
050: public boolean ignoreFurtherInvestigation = false; // once pointless to investigate due to errors
051: public boolean ignoreMethodBodies = false;
052: public CompilationUnitScope scope;
053: public ProblemReporter problemReporter;
054: public CompilationResult compilationResult;
055:
056: public LocalTypeBinding[] localTypes;
057: public int localTypeCount = 0;
058:
059: public boolean isPropagatingInnerClassEmulation;
060:
061: public Javadoc javadoc; // 1.5 addition for package-info.java
062:
063: public NLSTag[] nlsTags;
064: private StringLiteral[] stringLiterals;
065: private int stringLiteralsPtr;
066:
067: public CompilationUnitDeclaration(ProblemReporter problemReporter,
068: CompilationResult compilationResult, int sourceLength) {
069:
070: this .problemReporter = problemReporter;
071: this .compilationResult = compilationResult;
072:
073: //by definition of a compilation unit....
074: sourceStart = 0;
075: sourceEnd = sourceLength - 1;
076: }
077:
078: /*
079: * We cause the compilation task to abort to a given extent.
080: */
081: public void abort(int abortLevel, CategorizedProblem problem) {
082:
083: switch (abortLevel) {
084: case AbortType:
085: throw new AbortType(this .compilationResult, problem);
086: case AbortMethod:
087: throw new AbortMethod(this .compilationResult, problem);
088: default:
089: throw new AbortCompilationUnit(this .compilationResult,
090: problem);
091: }
092: }
093:
094: /*
095: * Dispatch code analysis AND request saturation of inner emulation
096: */
097: public void analyseCode() {
098:
099: if (ignoreFurtherInvestigation)
100: return;
101: try {
102: if (types != null) {
103: for (int i = 0, count = types.length; i < count; i++) {
104: types[i].analyseCode(scope);
105: }
106: }
107: // request inner emulation propagation
108: propagateInnerEmulationForAllLocalTypes();
109: } catch (AbortCompilationUnit e) {
110: this .ignoreFurtherInvestigation = true;
111: return;
112: }
113: }
114:
115: /*
116: * When unit result is about to be accepted, removed back pointers
117: * to compiler structures.
118: */
119: public void cleanUp() {
120: if (this .types != null) {
121: for (int i = 0, max = this .types.length; i < max; i++) {
122: cleanUp(this .types[i]);
123: }
124: for (int i = 0, max = this .localTypeCount; i < max; i++) {
125: LocalTypeBinding localType = localTypes[i];
126: // null out the type's scope backpointers
127: localType.scope = null; // local members are already in the list
128: localType.enclosingCase = null;
129: }
130: }
131:
132: compilationResult.recoveryScannerData = null; // recovery is already done
133:
134: ClassFile[] classFiles = compilationResult.getClassFiles();
135: for (int i = 0, max = classFiles.length; i < max; i++) {
136: // clear the classFile back pointer to the bindings
137: ClassFile classFile = classFiles[i];
138: // null out the classfile backpointer to a type binding
139: classFile.referenceBinding = null;
140: classFile.innerClassesBindings = null;
141: }
142: }
143:
144: private void cleanUp(TypeDeclaration type) {
145: if (type.memberTypes != null) {
146: for (int i = 0, max = type.memberTypes.length; i < max; i++) {
147: cleanUp(type.memberTypes[i]);
148: }
149: }
150: if (type.binding != null && type.binding.isAnnotationType())
151: compilationResult.hasAnnotations = true;
152: if (type.binding != null) {
153: // null out the type's scope backpointers
154: type.binding.scope = null;
155: }
156: }
157:
158: public void checkUnusedImports() {
159:
160: if (this .scope.imports != null) {
161: for (int i = 0, max = this .scope.imports.length; i < max; i++) {
162: ImportBinding importBinding = this .scope.imports[i];
163: ImportReference importReference = importBinding.reference;
164: if (importReference != null
165: && ((importReference.bits & ASTNode.Used) == 0)) {
166: scope.problemReporter().unusedImport(
167: importReference);
168: }
169: }
170: }
171: }
172:
173: public CompilationResult compilationResult() {
174: return this .compilationResult;
175: }
176:
177: /*
178: * Finds the matching type amoung this compilation unit types.
179: * Returns null if no type with this name is found.
180: * The type name is a compound name
181: * eg. if we're looking for X.A.B then a type name would be {X, A, B}
182: */
183: public TypeDeclaration declarationOfType(char[][] typeName) {
184:
185: for (int i = 0; i < this .types.length; i++) {
186: TypeDeclaration typeDecl = this .types[i]
187: .declarationOfType(typeName);
188: if (typeDecl != null) {
189: return typeDecl;
190: }
191: }
192: return null;
193: }
194:
195: /**
196: * Bytecode generation
197: */
198: public void generateCode() {
199: if (ignoreFurtherInvestigation) {
200: if (types != null) {
201: for (int i = 0, count = types.length; i < count; i++) {
202: types[i].ignoreFurtherInvestigation = true;
203: // propagate the flag to request problem type creation
204: types[i].generateCode(scope);
205: }
206: }
207: return;
208: }
209: if (this .isPackageInfo() && this .types != null
210: && this .currentPackage != null
211: && this .currentPackage.annotations != null) {
212: types[0].annotations = this .currentPackage.annotations;
213: }
214: try {
215: if (types != null) {
216: for (int i = 0, count = types.length; i < count; i++)
217: types[i].generateCode(scope);
218: }
219: } catch (AbortCompilationUnit e) {
220: // ignore
221: }
222: }
223:
224: public char[] getFileName() {
225:
226: return compilationResult.getFileName();
227: }
228:
229: public char[] getMainTypeName() {
230:
231: if (compilationResult.compilationUnit == null) {
232: char[] fileName = compilationResult.getFileName();
233:
234: int start = CharOperation.lastIndexOf('/', fileName) + 1;
235: if (start == 0
236: || start < CharOperation
237: .lastIndexOf('\\', fileName))
238: start = CharOperation.lastIndexOf('\\', fileName) + 1;
239:
240: int end = CharOperation.lastIndexOf('.', fileName);
241: if (end == -1)
242: end = fileName.length;
243:
244: return CharOperation.subarray(fileName, start, end);
245: } else {
246: return compilationResult.compilationUnit.getMainTypeName();
247: }
248: }
249:
250: public boolean isEmpty() {
251:
252: return (currentPackage == null) && (imports == null)
253: && (types == null);
254: }
255:
256: public boolean isPackageInfo() {
257: return CharOperation.equals(this .getMainTypeName(),
258: TypeConstants.PACKAGE_INFO_NAME);
259: }
260:
261: public boolean hasErrors() {
262: return this .ignoreFurtherInvestigation;
263: }
264:
265: public StringBuffer print(int indent, StringBuffer output) {
266:
267: if (currentPackage != null) {
268: printIndent(indent, output).append("package "); //$NON-NLS-1$
269: currentPackage.print(0, output, false).append(";\n"); //$NON-NLS-1$
270: }
271: if (imports != null)
272: for (int i = 0; i < imports.length; i++) {
273: printIndent(indent, output).append("import "); //$NON-NLS-1$
274: ImportReference currentImport = imports[i];
275: if (currentImport.isStatic()) {
276: output.append("static "); //$NON-NLS-1$
277: }
278: currentImport.print(0, output).append(";\n"); //$NON-NLS-1$
279: }
280:
281: if (types != null) {
282: for (int i = 0; i < types.length; i++) {
283: types[i].print(indent, output).append("\n"); //$NON-NLS-1$
284: }
285: }
286: return output;
287: }
288:
289: /*
290: * Force inner local types to update their innerclass emulation
291: */
292: public void propagateInnerEmulationForAllLocalTypes() {
293:
294: isPropagatingInnerClassEmulation = true;
295: for (int i = 0, max = this .localTypeCount; i < max; i++) {
296:
297: LocalTypeBinding localType = localTypes[i];
298: // only propagate for reachable local types
299: if ((localType.scope.referenceType().bits & IsReachable) != 0) {
300: localType.updateInnerEmulationDependents();
301: }
302: }
303: }
304:
305: public void recordStringLiteral(StringLiteral literal) {
306: if (this .stringLiterals == null) {
307: this .stringLiterals = new StringLiteral[STRING_LITERALS_INCREMENT];
308: this .stringLiteralsPtr = 0;
309: } else {
310: int stackLength = this .stringLiterals.length;
311: if (this .stringLiteralsPtr == stackLength) {
312: System
313: .arraycopy(
314: this .stringLiterals,
315: 0,
316: this .stringLiterals = new StringLiteral[stackLength
317: + STRING_LITERALS_INCREMENT],
318: 0, stackLength);
319: }
320: }
321: this .stringLiterals[this .stringLiteralsPtr++] = literal;
322: }
323:
324: /*
325: * Keep track of all local types, so as to update their innerclass
326: * emulation later on.
327: */
328: public void record(LocalTypeBinding localType) {
329:
330: if (this .localTypeCount == 0) {
331: this .localTypes = new LocalTypeBinding[5];
332: } else if (this .localTypeCount == this .localTypes.length) {
333: System
334: .arraycopy(
335: this .localTypes,
336: 0,
337: (this .localTypes = new LocalTypeBinding[this .localTypeCount * 2]),
338: 0, this .localTypeCount);
339: }
340: this .localTypes[this .localTypeCount++] = localType;
341: }
342:
343: public void resolve() {
344: int startingTypeIndex = 0;
345: boolean isPackageInfo = isPackageInfo();
346: if (this .types != null && isPackageInfo) {
347: // resolve synthetic type declaration
348: final TypeDeclaration syntheticTypeDeclaration = types[0];
349: // set empty javadoc to avoid missing warning (see bug https://bugs.eclipse.org/bugs/show_bug.cgi?id=95286)
350: if (syntheticTypeDeclaration.javadoc == null) {
351: syntheticTypeDeclaration.javadoc = new Javadoc(
352: syntheticTypeDeclaration.declarationSourceStart,
353: syntheticTypeDeclaration.declarationSourceStart);
354: }
355: syntheticTypeDeclaration.resolve(this .scope);
356: // resolve annotations if any
357: if (this .currentPackage != null
358: && this .currentPackage.annotations != null) {
359: resolveAnnotations(
360: syntheticTypeDeclaration.staticInitializerScope,
361: this .currentPackage.annotations,
362: this .scope.fPackage);
363: }
364: /*
365: * resolve javadoc package if any
366: * we do it now and the javadoc in the fake type won't be resolved
367: */
368: if (this .javadoc != null) {
369: this .javadoc
370: .resolve(syntheticTypeDeclaration.staticInitializerScope);
371: }
372: startingTypeIndex = 1;
373: } else {
374: // resolve compilation unit javadoc package if any
375: if (this .javadoc != null) {
376: this .javadoc.resolve(this .scope);
377: }
378: }
379: if (this .currentPackage != null
380: && this .currentPackage.annotations != null
381: && !isPackageInfo) {
382: scope.problemReporter()
383: .invalidFileNameForPackageAnnotations(
384: this .currentPackage.annotations[0]);
385: }
386: try {
387: if (types != null) {
388: for (int i = startingTypeIndex, count = types.length; i < count; i++) {
389: types[i].resolve(scope);
390: }
391: }
392: if (!this .compilationResult.hasErrors())
393: checkUnusedImports();
394: reportNLSProblems();
395: } catch (AbortCompilationUnit e) {
396: this .ignoreFurtherInvestigation = true;
397: return;
398: }
399: }
400:
401: private void reportNLSProblems() {
402: if (this .nlsTags != null || this .stringLiterals != null) {
403: final int stringLiteralsLength = this .stringLiteralsPtr;
404: final int nlsTagsLength = this .nlsTags == null ? 0
405: : this .nlsTags.length;
406: if (stringLiteralsLength == 0) {
407: if (nlsTagsLength != 0) {
408: for (int i = 0; i < nlsTagsLength; i++) {
409: NLSTag tag = this .nlsTags[i];
410: if (tag != null) {
411: scope.problemReporter().unnecessaryNLSTags(
412: tag.start, tag.end);
413: }
414: }
415: }
416: } else if (nlsTagsLength == 0) {
417: // resize string literals
418: if (this .stringLiterals.length != stringLiteralsLength) {
419: System
420: .arraycopy(
421: this .stringLiterals,
422: 0,
423: (stringLiterals = new StringLiteral[stringLiteralsLength]),
424: 0, stringLiteralsLength);
425: }
426: Arrays.sort(this .stringLiterals,
427: STRING_LITERAL_COMPARATOR);
428: for (int i = 0; i < stringLiteralsLength; i++) {
429: scope.problemReporter()
430: .nonExternalizedStringLiteral(
431: this .stringLiterals[i]);
432: }
433: } else {
434: // need to iterate both arrays to find non matching elements
435: if (this .stringLiterals.length != stringLiteralsLength) {
436: System
437: .arraycopy(
438: this .stringLiterals,
439: 0,
440: (stringLiterals = new StringLiteral[stringLiteralsLength]),
441: 0, stringLiteralsLength);
442: }
443: Arrays.sort(this .stringLiterals,
444: STRING_LITERAL_COMPARATOR);
445: int indexInLine = 1;
446: int lastLineNumber = -1;
447: StringLiteral literal = null;
448: int index = 0;
449: int i = 0;
450: stringLiteralsLoop: for (; i < stringLiteralsLength; i++) {
451: literal = this .stringLiterals[i];
452: final int literalLineNumber = literal.lineNumber;
453: if (lastLineNumber != literalLineNumber) {
454: indexInLine = 1;
455: lastLineNumber = literalLineNumber;
456: } else {
457: indexInLine++;
458: }
459: if (index < nlsTagsLength) {
460: nlsTagsLoop: for (; index < nlsTagsLength; index++) {
461: NLSTag tag = this .nlsTags[index];
462: if (tag == null)
463: continue nlsTagsLoop;
464: int tagLineNumber = tag.lineNumber;
465: if (literalLineNumber < tagLineNumber) {
466: scope.problemReporter()
467: .nonExternalizedStringLiteral(
468: literal);
469: continue stringLiteralsLoop;
470: } else if (literalLineNumber == tagLineNumber) {
471: if (tag.index == indexInLine) {
472: this .nlsTags[index] = null;
473: index++;
474: continue stringLiteralsLoop;
475: } else {
476: nlsTagsLoop2: for (int index2 = index + 1; index2 < nlsTagsLength; index2++) {
477: NLSTag tag2 = this .nlsTags[index2];
478: if (tag2 == null)
479: continue nlsTagsLoop2;
480: int tagLineNumber2 = tag2.lineNumber;
481: if (literalLineNumber == tagLineNumber2) {
482: if (tag2.index == indexInLine) {
483: this .nlsTags[index2] = null;
484: continue stringLiteralsLoop;
485: } else {
486: continue nlsTagsLoop2;
487: }
488: } else {
489: scope
490: .problemReporter()
491: .nonExternalizedStringLiteral(
492: literal);
493: continue stringLiteralsLoop;
494: }
495: }
496: scope
497: .problemReporter()
498: .nonExternalizedStringLiteral(
499: literal);
500: continue stringLiteralsLoop;
501: }
502: } else {
503: scope.problemReporter()
504: .unnecessaryNLSTags(tag.start,
505: tag.end);
506: continue nlsTagsLoop;
507: }
508: }
509: }
510: // all nls tags have been processed, so remaining string literals are not externalized
511: break stringLiteralsLoop;
512: }
513: for (; i < stringLiteralsLength; i++) {
514: scope.problemReporter()
515: .nonExternalizedStringLiteral(
516: this .stringLiterals[i]);
517: }
518: if (index < nlsTagsLength) {
519: for (; index < nlsTagsLength; index++) {
520: NLSTag tag = this .nlsTags[index];
521: if (tag != null) {
522: scope.problemReporter().unnecessaryNLSTags(
523: tag.start, tag.end);
524: }
525: }
526: }
527: }
528: }
529: }
530:
531: public void tagAsHavingErrors() {
532: ignoreFurtherInvestigation = true;
533: }
534:
535: public void traverse(ASTVisitor visitor,
536: CompilationUnitScope unitScope) {
537:
538: if (ignoreFurtherInvestigation)
539: return;
540: try {
541: if (visitor.visit(this , this .scope)) {
542: if (this .types != null && isPackageInfo()) {
543: // resolve synthetic type declaration
544: final TypeDeclaration syntheticTypeDeclaration = types[0];
545: // resolve javadoc package if any
546: final MethodScope methodScope = syntheticTypeDeclaration.staticInitializerScope;
547: if (this .javadoc != null) {
548: this .javadoc.traverse(visitor, methodScope);
549: }
550: if (this .currentPackage != null) {
551: final Annotation[] annotations = this .currentPackage.annotations;
552: if (annotations != null) {
553: int annotationsLength = annotations.length;
554: for (int i = 0; i < annotationsLength; i++) {
555: annotations[i].traverse(visitor,
556: methodScope);
557: }
558: }
559: }
560: }
561: if (this .currentPackage != null) {
562: this .currentPackage.traverse(visitor, this .scope);
563: }
564: if (imports != null) {
565: int importLength = imports.length;
566: for (int i = 0; i < importLength; i++) {
567: imports[i].traverse(visitor, this .scope);
568: }
569: }
570: if (types != null) {
571: int typesLength = types.length;
572: for (int i = 0; i < typesLength; i++) {
573: types[i].traverse(visitor, this .scope);
574: }
575: }
576: }
577: visitor.endVisit(this , this .scope);
578: } catch (AbortCompilationUnit e) {
579: // ignore
580: }
581: }
582: }
|