Docjar: A Java Source and Docuemnt Enginecom.*    java.*    javax.*    org.*    all    new    plug-in

Quick Search    Search Deep

Source code: org/eclipse/jdt/internal/core/builder/AbstractImageBuilder.java


1   /*******************************************************************************
2    * Copyright (c) 2000, 2004 IBM Corporation and others.
3    * All rights reserved. This program and the accompanying materials 
4    * are made available under the terms of the Common Public License v1.0
5    * which accompanies this distribution, and is available at
6    * http://www.eclipse.org/legal/cpl-v10.html
7    * 
8    * Contributors:
9    *     IBM Corporation - initial API and implementation
10   *******************************************************************************/
11  package org.eclipse.jdt.internal.core.builder;
12  
13  import org.eclipse.core.runtime.*;
14  import org.eclipse.core.resources.*;
15  
16  import org.eclipse.jdt.core.*;
17  import org.eclipse.jdt.core.compiler.*;
18  import org.eclipse.jdt.core.compiler.IProblem;
19  import org.eclipse.jdt.internal.compiler.*;
20  import org.eclipse.jdt.internal.compiler.ClassFile;
21  import org.eclipse.jdt.internal.compiler.Compiler;
22  import org.eclipse.jdt.internal.compiler.problem.*;
23  import org.eclipse.jdt.internal.compiler.util.SuffixConstants;
24  import org.eclipse.jdt.internal.core.util.Util;
25  
26  import java.io.*;
27  import java.util.*;
28  
29  /**
30   * The abstract superclass of Java builders.
31   * Provides the building and compilation mechanism
32   * in common with the batch and incremental builders.
33   */
34  public abstract class AbstractImageBuilder implements ICompilerRequestor {
35  
36  protected JavaBuilder javaBuilder;
37  protected State newState;
38  
39  // local copies
40  protected NameEnvironment nameEnvironment;
41  protected ClasspathMultiDirectory[] sourceLocations;
42  protected BuildNotifier notifier;
43  
44  protected Compiler compiler;
45  protected WorkQueue workQueue;
46  protected ArrayList problemSourceFiles;
47  protected boolean compiledAllAtOnce;
48  
49  private boolean inCompiler;
50  
51  public static int MAX_AT_ONCE = 1000;
52  
53  protected AbstractImageBuilder(JavaBuilder javaBuilder) {
54    this.javaBuilder = javaBuilder;
55    this.newState = new State(javaBuilder);
56  
57    // local copies
58    this.nameEnvironment = javaBuilder.nameEnvironment;
59    this.sourceLocations = this.nameEnvironment.sourceLocations;
60    this.notifier = javaBuilder.notifier;
61  
62    this.compiler = newCompiler();
63    this.workQueue = new WorkQueue();
64    this.problemSourceFiles = new ArrayList(3);
65  }
66  
67  public void acceptResult(CompilationResult result) {
68    // In Batch mode, we write out the class files, hold onto the dependency info
69    // & additional types and report problems.
70  
71    // In Incremental mode, when writing out a class file we need to compare it
72    // against the previous file, remembering if structural changes occured.
73    // Before reporting the new problems, we need to update the problem count &
74    // remove the old problems. Plus delete additional class files that no longer exist.
75  
76    SourceFile compilationUnit = (SourceFile) result.getCompilationUnit(); // go directly back to the sourceFile
77    if (!workQueue.isCompiled(compilationUnit)) {
78      workQueue.finished(compilationUnit);
79  
80      try {
81        updateProblemsFor(compilationUnit, result); // record compilation problems before potentially adding duplicate errors
82        updateTasksFor(compilationUnit, result); // record tasks
83      } catch (CoreException e) {
84        throw internalException(e);
85      }
86  
87      String typeLocator = compilationUnit.typeLocator();
88      ClassFile[] classFiles = result.getClassFiles();
89      int length = classFiles.length;
90      ArrayList duplicateTypeNames = null;
91      ArrayList definedTypeNames = new ArrayList(length);
92      for (int i = 0; i < length; i++) {
93        ClassFile classFile = classFiles[i];
94        char[][] compoundName = classFile.getCompoundName();
95        char[] typeName = compoundName[compoundName.length - 1];
96        boolean isNestedType = classFile.enclosingClassFile != null;
97  
98        // Look for a possible collision, if one exists, report an error but do not write the class file
99        if (isNestedType) {
100         String qualifiedTypeName = new String(classFile.outerMostEnclosingClassFile().fileName());
101         if (newState.isDuplicateLocator(qualifiedTypeName, typeLocator))
102           continue;
103       } else {
104         String qualifiedTypeName = new String(classFile.fileName()); // the qualified type name "p1/p2/A"
105         if (newState.isDuplicateLocator(qualifiedTypeName, typeLocator)) {
106           if (duplicateTypeNames == null)
107             duplicateTypeNames = new ArrayList();
108           duplicateTypeNames.add(compoundName);
109           createProblemFor(compilationUnit.resource, Util.bind("build.duplicateClassFile", new String(typeName)), JavaCore.ERROR); //$NON-NLS-1$
110           continue;
111         }
112         newState.recordLocatorForType(qualifiedTypeName, typeLocator);
113       }
114       try {
115         definedTypeNames.add(writeClassFile(classFile, compilationUnit, !isNestedType));
116       } catch (CoreException e) {
117         Util.log(e, "JavaBuilder handling CoreException"); //$NON-NLS-1$
118         if (e.getStatus().getCode() == IResourceStatus.CASE_VARIANT_EXISTS)
119           createProblemFor(compilationUnit.resource, Util.bind("build.classFileCollision", e.getMessage()), JavaCore.ERROR); //$NON-NLS-1$
120         else
121           createProblemFor(compilationUnit.resource, Util.bind("build.inconsistentClassFile"), JavaCore.ERROR); //$NON-NLS-1$
122       }
123     }
124     finishedWith(typeLocator, result, compilationUnit.getMainTypeName(), definedTypeNames, duplicateTypeNames);
125     notifier.compiled(compilationUnit);
126   }
127 }
128 
129 protected void cleanUp() {
130   this.nameEnvironment.cleanup();
131 
132   this.javaBuilder = null;
133   this.nameEnvironment = null;
134   this.sourceLocations = null;
135   this.notifier = null;
136   this.compiler = null;
137   this.workQueue = null;
138   this.problemSourceFiles = null;
139 }
140 
141 /* Compile the given elements, adding more elements to the work queue 
142 * if they are affected by the changes.
143 */
144 protected void compile(SourceFile[] units) {
145   int unitsLength = units.length;
146 
147   this.compiledAllAtOnce = unitsLength <= MAX_AT_ONCE;
148   if (this.compiledAllAtOnce) {
149     // do them all now
150     if (JavaBuilder.DEBUG)
151       for (int i = 0; i < unitsLength; i++)
152         System.out.println("About to compile " + units[i].typeLocator()); //$NON-NLS-1$
153     compile(units, null);
154   } else {
155     int i = 0;
156     boolean compilingFirstGroup = true;
157     while (i < unitsLength) {
158       int doNow = unitsLength < MAX_AT_ONCE ? unitsLength : MAX_AT_ONCE;
159       int index = 0;
160       SourceFile[] toCompile = new SourceFile[doNow];
161       while (i < unitsLength && index < doNow) {
162         // Although it needed compiling when this method was called, it may have
163         // already been compiled when it was referenced by another unit.
164         SourceFile unit = units[i++];
165         if (compilingFirstGroup || workQueue.isWaiting(unit)) {
166           if (JavaBuilder.DEBUG)
167             System.out.println("About to compile " + unit.typeLocator()); //$NON-NLS-1$
168           toCompile[index++] = unit;
169         }
170       }
171       if (index < doNow)
172         System.arraycopy(toCompile, 0, toCompile = new SourceFile[index], 0, index);
173       SourceFile[] additionalUnits = new SourceFile[unitsLength - i];
174       System.arraycopy(units, i, additionalUnits, 0, additionalUnits.length);
175       compilingFirstGroup = false;
176       compile(toCompile, additionalUnits);
177     }
178   }
179 }
180 
181 void compile(SourceFile[] units, SourceFile[] additionalUnits) {
182   if (units.length == 0) return;
183   notifier.aboutToCompile(units[0]); // just to change the message
184 
185   // extend additionalFilenames with all hierarchical problem types found during this entire build
186   if (!problemSourceFiles.isEmpty()) {
187     int toAdd = problemSourceFiles.size();
188     int length = additionalUnits == null ? 0 : additionalUnits.length;
189     if (length == 0)
190       additionalUnits = new SourceFile[toAdd];
191     else
192       System.arraycopy(additionalUnits, 0, additionalUnits = new SourceFile[length + toAdd], 0, length);
193     for (int i = 0; i < toAdd; i++)
194       additionalUnits[length + i] = (SourceFile) problemSourceFiles.get(i);
195   }
196   String[] initialTypeNames = new String[units.length];
197   for (int i = 0, l = units.length; i < l; i++)
198     initialTypeNames[i] = units[i].initialTypeName;
199   nameEnvironment.setNames(initialTypeNames, additionalUnits);
200   notifier.checkCancel();
201   try {
202     inCompiler = true;
203     compiler.compile(units);
204   } catch (AbortCompilation ignored) {
205     // ignore the AbortCompilcation coming from BuildNotifier.checkCancelWithinCompiler()
206     // the Compiler failed after the user has chose to cancel... likely due to an OutOfMemory error
207   } finally {
208     inCompiler = false;
209   }
210   // Check for cancel immediately after a compile, because the compiler may
211   // have been cancelled but without propagating the correct exception
212   notifier.checkCancel();
213 }
214 
215 protected void createProblemFor(IResource resource, String message, String problemSeverity) {
216   try {
217     IMarker marker = resource.createMarker(IJavaModelMarker.JAVA_MODEL_PROBLEM_MARKER);
218     int severity = problemSeverity.equals(JavaCore.WARNING) ? IMarker.SEVERITY_WARNING : IMarker.SEVERITY_ERROR;
219 
220     marker.setAttributes(
221       new String[] {IMarker.MESSAGE, IMarker.SEVERITY, IMarker.CHAR_START, IMarker.CHAR_END},
222       new Object[] {message, new Integer(severity), new Integer(0), new Integer(1)});
223   } catch (CoreException e) {
224     throw internalException(e);
225   }
226 }
227 
228 protected void finishedWith(String sourceLocator, CompilationResult result, char[] mainTypeName, ArrayList definedTypeNames, ArrayList duplicateTypeNames) {
229   if (duplicateTypeNames == null) {
230     newState.record(sourceLocator, result.qualifiedReferences, result.simpleNameReferences, mainTypeName, definedTypeNames);
231     return;
232   }
233 
234   char[][][] qualifiedRefs = result.qualifiedReferences;
235   char[][] simpleRefs = result.simpleNameReferences;
236   // for each duplicate type p1.p2.A, add the type name A (package was already added)
237   next : for (int i = 0, l = duplicateTypeNames.size(); i < l; i++) {
238     char[][] compoundName = (char[][]) duplicateTypeNames.get(i);
239     char[] typeName = compoundName[compoundName.length - 1];
240     int sLength = simpleRefs.length;
241     for (int j = 0; j < sLength; j++)
242       if (CharOperation.equals(simpleRefs[j], typeName))
243         continue next;
244     System.arraycopy(simpleRefs, 0, simpleRefs = new char[sLength + 1][], 0, sLength);
245     simpleRefs[sLength] = typeName;
246   }
247   newState.record(sourceLocator, qualifiedRefs, simpleRefs, mainTypeName, definedTypeNames);
248 }
249 
250 protected IContainer createFolder(IPath packagePath, IContainer outputFolder) throws CoreException {
251   if (packagePath.isEmpty()) return outputFolder;
252   IFolder folder = outputFolder.getFolder(packagePath);
253   if (!folder.exists()) {
254     createFolder(packagePath.removeLastSegments(1), outputFolder);
255     folder.create(true, true, null);
256     folder.setDerived(true);
257   }
258   return folder;
259 }
260 
261 protected RuntimeException internalException(CoreException t) {
262   ImageBuilderInternalException imageBuilderException = new ImageBuilderInternalException(t);
263   if (inCompiler)
264     return new AbortCompilation(true, imageBuilderException);
265   return imageBuilderException;
266 }
267 
268 protected Compiler newCompiler() {
269   // called once when the builder is initialized... can override if needed
270   Compiler newCompiler = new Compiler(
271     nameEnvironment,
272     DefaultErrorHandlingPolicies.proceedWithAllProblems(),
273     javaBuilder.javaProject.getOptions(true),
274     this,
275     ProblemFactory.getProblemFactory(Locale.getDefault()));
276   // enable the compiler reference info support
277   newCompiler.options.produceReferenceInfo = true;
278 
279   org.eclipse.jdt.internal.compiler.lookup.LookupEnvironment env = newCompiler.lookupEnvironment;
280   synchronized (env) {
281     // enable shared byte[]'s used by ClassFile to avoid allocating MBs during a build
282     env.sharedArraysUsed = false;
283     env.sharedClassFileHeader = new byte[30000];
284     env.sharedClassFileContents = new byte[30000];
285   }
286 
287   return newCompiler;
288 }
289 
290 protected boolean isExcludedFromProject(IPath childPath) throws JavaModelException {
291   // answer whether the folder should be ignored when walking the project as a source folder
292   if (childPath.segmentCount() > 2) return false; // is a subfolder of a package
293 
294   for (int j = 0, k = sourceLocations.length; j < k; j++) {
295     if (childPath.equals(sourceLocations[j].binaryFolder.getFullPath())) return true;
296     if (childPath.equals(sourceLocations[j].sourceFolder.getFullPath())) return true;
297   }
298   // skip default output folder which may not be used by any source folder
299   return childPath.equals(javaBuilder.javaProject.getOutputLocation());
300 }
301 
302 /**
303  * Creates a marker from each problem and adds it to the resource.
304  * The marker is as follows:
305  *   - its type is T_PROBLEM
306  *   - its plugin ID is the JavaBuilder's plugin ID
307  *   - its message is the problem's message
308  *   - its priority reflects the severity of the problem
309  *   - its range is the problem's range
310  *   - it has an extra attribute "ID" which holds the problem's id
311  */
312 protected void storeProblemsFor(SourceFile sourceFile, IProblem[] problems) throws CoreException {
313   if (sourceFile == null || problems == null || problems.length == 0) return;
314 
315   String missingClassFile = null;
316   IResource resource = sourceFile.resource;
317   for (int i = 0, l = problems.length; i < l; i++) {
318     IProblem problem = problems[i];
319     int id = problem.getID();
320     switch (id) {
321       case IProblem.IsClassPathCorrect :
322         JavaBuilder.removeProblemsAndTasksFor(javaBuilder.currentProject); // make this the only problem for this project
323         String[] args = problem.getArguments();
324         missingClassFile = args[0];
325         break;
326       case IProblem.SuperclassMustBeAClass :
327       case IProblem.SuperInterfaceMustBeAnInterface :
328       case IProblem.HierarchyCircularitySelfReference :
329       case IProblem.HierarchyCircularity :
330       case IProblem.HierarchyHasProblems :
331       case IProblem.SuperclassNotFound :
332       case IProblem.SuperclassNotVisible :
333       case IProblem.SuperclassAmbiguous :
334       case IProblem.SuperclassInternalNameProvided :
335       case IProblem.SuperclassInheritedNameHidesEnclosingName :
336       case IProblem.InterfaceNotFound :
337       case IProblem.InterfaceNotVisible :
338       case IProblem.InterfaceAmbiguous :
339       case IProblem.InterfaceInternalNameProvided :
340       case IProblem.InterfaceInheritedNameHidesEnclosingName :
341         // ensure that this file is always retrieved from source for the rest of the build
342         if (!problemSourceFiles.contains(sourceFile))
343           problemSourceFiles.add(sourceFile);
344         break;
345     }
346 
347     if (id != IProblem.Task) {
348       IMarker marker = resource.createMarker(IJavaModelMarker.JAVA_MODEL_PROBLEM_MARKER);
349       marker.setAttributes(
350         new String[] {
351           IMarker.MESSAGE, 
352           IMarker.SEVERITY, 
353           IJavaModelMarker.ID, 
354           IMarker.CHAR_START, 
355           IMarker.CHAR_END, 
356           IMarker.LINE_NUMBER, 
357           IJavaModelMarker.ARGUMENTS},
358         new Object[] { 
359           problem.getMessage(),
360           new Integer(problem.isError() ? IMarker.SEVERITY_ERROR : IMarker.SEVERITY_WARNING), 
361           new Integer(id),
362           new Integer(problem.getSourceStart()),
363           new Integer(problem.getSourceEnd() + 1),
364           new Integer(problem.getSourceLineNumber()),
365           Util.getProblemArgumentsForMarker(problem.getArguments())
366         });
367     }
368 
369 /* Do NOT want to populate the Java Model just to find the matching Java element.
370  * Also cannot query compilation units located in folders with invalid package
371  * names such as 'a/b.c.d/e'.
372 
373     // compute a user-friendly location
374     IJavaElement element = JavaCore.create(resource);
375     if (element instanceof org.eclipse.jdt.core.ICompilationUnit) { // try to find a finer grain element
376       org.eclipse.jdt.core.ICompilationUnit unit = (org.eclipse.jdt.core.ICompilationUnit) element;
377       IJavaElement fragment = unit.getElementAt(problem.getSourceStart());
378       if (fragment != null) element = fragment;
379     }
380     String location = null;
381     if (element instanceof JavaElement)
382       location = ((JavaElement) element).readableName();
383     if (location != null)
384       marker.setAttribute(IMarker.LOCATION, location);
385 */
386 
387     if (missingClassFile != null)
388       throw new MissingClassFileException(missingClassFile);
389   }
390 }
391 
392 protected void storeTasksFor(SourceFile sourceFile, IProblem[] tasks) throws CoreException {
393   if (sourceFile == null || tasks == null || tasks.length == 0) return;
394 
395   IResource resource = sourceFile.resource;
396   for (int i = 0, l = tasks.length; i < l; i++) {
397     IProblem task = tasks[i];
398     if (task.getID() == IProblem.Task) {
399       IMarker marker = resource.createMarker(IJavaModelMarker.TASK_MARKER);
400       int priority = IMarker.PRIORITY_NORMAL;
401       String compilerPriority = task.getArguments()[2];
402       if (JavaCore.COMPILER_TASK_PRIORITY_HIGH.equals(compilerPriority))
403         priority = IMarker.PRIORITY_HIGH;
404       else if (JavaCore.COMPILER_TASK_PRIORITY_LOW.equals(compilerPriority))
405         priority = IMarker.PRIORITY_LOW;
406       marker.setAttributes(
407         new String[] {
408           IMarker.MESSAGE, 
409           IMarker.PRIORITY, 
410           IMarker.DONE, 
411           IMarker.CHAR_START, 
412           IMarker.CHAR_END, 
413           IMarker.LINE_NUMBER,
414           IMarker.USER_EDITABLE, 
415         }, 
416         new Object[] { 
417           task.getMessage(),
418           new Integer(priority),
419           org.eclipse.jdt.internal.compiler.util.Util.toBoolean(false),
420           new Integer(task.getSourceStart()),
421           new Integer(task.getSourceEnd() + 1),
422           new Integer(task.getSourceLineNumber()),
423           new Boolean(false),
424         });
425     }
426   }
427 }
428 
429 protected void updateProblemsFor(SourceFile sourceFile, CompilationResult result) throws CoreException {
430   IProblem[] problems = result.getProblems();
431   if (problems == null || problems.length == 0) return;
432 
433   notifier.updateProblemCounts(problems);
434   storeProblemsFor(sourceFile, problems);
435 }
436 
437 protected void updateTasksFor(SourceFile sourceFile, CompilationResult result) throws CoreException {
438   IProblem[] tasks = result.getTasks();
439   if (tasks == null || tasks.length == 0) return;
440 
441   storeTasksFor(sourceFile, tasks);
442 }
443 
444 protected char[] writeClassFile(ClassFile classFile, SourceFile compilationUnit, boolean isSecondaryType) throws CoreException {
445   String fileName = new String(classFile.fileName()); // the qualified type name "p1/p2/A"
446   IPath filePath = new Path(fileName);
447   IContainer outputFolder = compilationUnit.sourceLocation.binaryFolder; 
448   IContainer container = outputFolder;
449   if (filePath.segmentCount() > 1) {
450     container = createFolder(filePath.removeLastSegments(1), outputFolder);
451     filePath = new Path(filePath.lastSegment());
452   }
453 
454   IFile file = container.getFile(filePath.addFileExtension(SuffixConstants.EXTENSION_class));
455   writeClassFileBytes(classFile.getBytes(), file, fileName, isSecondaryType, compilationUnit.updateClassFile);
456   if (classFile.ownSharedArrays) {
457     org.eclipse.jdt.internal.compiler.lookup.LookupEnvironment env = this.compiler.lookupEnvironment;
458     synchronized (env) {
459       env.sharedArraysUsed = false;
460     }
461   }
462 
463   // answer the name of the class file as in Y or Y$M
464   return filePath.lastSegment().toCharArray();
465 }
466 
467 protected void writeClassFileBytes(byte[] bytes, IFile file, String qualifiedFileName, boolean isSecondaryType, boolean updateClassFile) throws CoreException {
468   if (file.exists()) {
469     // Deal with shared output folders... last one wins... no collision cases detected
470     if (JavaBuilder.DEBUG)
471       System.out.println("Writing changed class file " + file.getName());//$NON-NLS-1$
472     file.setContents(new ByteArrayInputStream(bytes), true, false, null);
473     if (!file.isDerived())
474       file.setDerived(true);
475   } else {
476     // Default implementation just writes out the bytes for the new class file...
477     if (JavaBuilder.DEBUG)
478       System.out.println("Writing new class file " + file.getName());//$NON-NLS-1$
479     file.create(new ByteArrayInputStream(bytes), IResource.FORCE, null);
480     file.setDerived(true);
481   }
482 }
483 }