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