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
9 * IBM Corporation - initial API and implementation
10 *******************************************************************************/
11 package net.sourceforge.phpdt.internal.core.builder;
13 import java.util.ArrayList;
14 import java.util.Locale;
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.compiler.problem.ProblemHandler;
26 import net.sourceforge.phpdt.internal.core.util.Util;
28 import org.eclipse.core.resources.IMarker;
29 import org.eclipse.core.resources.IResource;
30 import org.eclipse.core.runtime.CoreException;
31 import org.eclipse.core.runtime.IPath;
34 * The abstract superclass of Java builders. Provides the building and compilation mechanism in common with the batch and
35 * incremental builders.
37 public abstract class AbstractImageBuilder implements ICompilerRequestor {
39 protected PHPBuilder javaBuilder;
41 protected State newState;
44 protected NameEnvironment nameEnvironment;
46 protected ClasspathMultiDirectory[] sourceLocations;
48 protected BuildNotifier notifier;
50 protected String encoding;
52 protected Compiler compiler;
54 protected WorkQueue workQueue;
56 protected ArrayList problemSourceFiles;
58 protected boolean compiledAllAtOnce;
60 private boolean inCompiler;
62 public static int MAX_AT_ONCE = 1000;
64 protected AbstractImageBuilder(PHPBuilder javaBuilder) {
65 this.javaBuilder = javaBuilder;
66 this.newState = new State(javaBuilder);
69 this.nameEnvironment = javaBuilder.nameEnvironment;
70 this.sourceLocations = this.nameEnvironment.sourceLocations;
71 this.notifier = javaBuilder.notifier;
73 this.encoding = javaBuilder.javaProject.getOption(JavaCore.CORE_ENCODING, true);
74 this.compiler = newCompiler();
75 this.workQueue = new WorkQueue();
76 this.problemSourceFiles = new ArrayList(3);
79 public void acceptResult(CompilationResult result) {
80 // In Batch mode, we write out the class files, hold onto the dependency info
81 // & additional types and report problems.
83 // In Incremental mode, when writing out a class file we need to compare it
84 // against the previous file, remembering if structural changes occured.
85 // Before reporting the new problems, we need to update the problem count &
86 // remove the old problems. Plus delete additional class files that no longer exist.
88 SourceFile compilationUnit = (SourceFile) result.getCompilationUnit(); // go directly back to the sourceFile
89 if (!workQueue.isCompiled(compilationUnit)) {
91 workQueue.finished(compilationUnit);
93 updateProblemsFor(compilationUnit, result); // record compilation problems before potentially adding duplicate errors
94 updateTasksFor(compilationUnit, result); // record tasks
95 } catch (CoreException e) {
96 throw internalException(e);
99 // String typeLocator = compilationUnit.typeLocator();
100 // ClassFile[] classFiles = result.getClassFiles();
101 // int length = classFiles.length;
102 // ArrayList duplicateTypeNames = null;
103 // ArrayList definedTypeNames = new ArrayList(length);
104 // for (int i = 0; i < length; i++) {
105 // ClassFile classFile = classFiles[i];
106 // char[][] compoundName = classFile.getCompoundName();
107 // char[] typeName = compoundName[compoundName.length - 1];
108 // boolean isNestedType = CharOperation.contains('$', typeName);
110 // // Look for a possible collision, if one exists, report an error but do not write the class file
111 // if (isNestedType) {
112 // String qualifiedTypeName = new String(classFile.outerMostEnclosingClassFile().fileName());
113 // if (newState.isDuplicateLocator(qualifiedTypeName, typeLocator))
116 // String qualifiedTypeName = new String(classFile.fileName()); // the qualified type name "p1/p2/A"
117 // if (newState.isDuplicateLocator(qualifiedTypeName, typeLocator)) {
118 // if (duplicateTypeNames == null)
119 // duplicateTypeNames = new ArrayList();
120 // duplicateTypeNames.add(compoundName);
121 // createErrorFor(compilationUnit.resource, ProjectPrefUtil.bind("build.duplicateClassFile", new String(typeName)));
125 // newState.recordLocatorForType(qualifiedTypeName, typeLocator);
127 // definedTypeNames.add(writeClassFile(classFile, compilationUnit.sourceLocation.binaryFolder, !isNestedType));
130 // finishedWith(typeLocator, result, compilationUnit.getMainTypeName(), definedTypeNames, duplicateTypeNames);
131 notifier.compiled(compilationUnit);
132 // } catch (CoreException e) {
133 // Util.log(e, "JavaBuilder handling CoreException"); //$NON-NLS-1$
134 // createErrorFor(compilationUnit.resource, Util.bind("build.inconsistentClassFile")); //$NON-NLS-1$
139 protected void cleanUp() {
140 this.nameEnvironment.cleanup();
142 this.javaBuilder = null;
143 this.nameEnvironment = null;
144 this.sourceLocations = null;
145 this.notifier = null;
146 this.compiler = null;
147 this.workQueue = null;
148 this.problemSourceFiles = null;
152 * Compile the given elements, adding more elements to the work queue if they are affected by the changes.
154 protected void compile(SourceFile[] units) {
155 int toDo = units.length;
156 if (this.compiledAllAtOnce = toDo <= MAX_AT_ONCE) {
158 if (PHPBuilder.DEBUG)
159 for (int i = 0; i < toDo; i++)
160 System.out.println("About to compile " + units[i].typeLocator()); //$NON-NLS-1$
161 compile(units, null);
164 boolean compilingFirstGroup = true;
166 int doNow = toDo < MAX_AT_ONCE ? toDo : MAX_AT_ONCE;
168 SourceFile[] toCompile = new SourceFile[doNow];
169 while (i < toDo && index < doNow) {
170 // Although it needed compiling when this method was called, it may have
171 // already been compiled when it was referenced by another unit.
172 SourceFile unit = units[i++];
173 if (compilingFirstGroup || workQueue.isWaiting(unit)) {
174 if (PHPBuilder.DEBUG)
175 System.out.println("About to compile " + unit.typeLocator()); //$NON-NLS-1$
176 toCompile[index++] = unit;
180 System.arraycopy(toCompile, 0, toCompile = new SourceFile[index], 0, index);
181 SourceFile[] additionalUnits = new SourceFile[toDo - i];
182 System.arraycopy(units, i, additionalUnits, 0, additionalUnits.length);
183 compilingFirstGroup = false;
184 compile(toCompile, additionalUnits);
189 void compile(SourceFile[] units, SourceFile[] additionalUnits) {
190 if (units.length == 0)
192 notifier.aboutToCompile(units[0]); // just to change the message
194 // extend additionalFilenames with all hierarchical problem types found during this entire build
195 if (!problemSourceFiles.isEmpty()) {
196 int toAdd = problemSourceFiles.size();
197 int length = additionalUnits == null ? 0 : additionalUnits.length;
199 additionalUnits = new SourceFile[toAdd];
201 System.arraycopy(additionalUnits, 0, additionalUnits = new SourceFile[length + toAdd], 0, length);
202 for (int i = 0; i < toAdd; i++)
203 additionalUnits[length + i] = (SourceFile) problemSourceFiles.get(i);
205 String[] initialTypeNames = new String[units.length];
206 for (int i = 0, l = units.length; i < l; i++)
207 initialTypeNames[i] = units[i].initialTypeName;
208 nameEnvironment.setNames(initialTypeNames, additionalUnits);
209 notifier.checkCancel();
212 compiler.compile(units);
213 } catch (AbortCompilation ignored) {
214 // ignore the AbortCompilcation coming from BuildNotifier.checkCancelWithinCompiler()
215 // the Compiler failed after the user has chose to cancel... likely due to an OutOfMemory error
219 // Check for cancel immediately after a compile, because the compiler may
220 // have been cancelled but without propagating the correct exception
221 notifier.checkCancel();
224 protected void createErrorFor(IResource resource, String message) {
226 IMarker marker = resource.createMarker(IJavaModelMarker.JAVA_MODEL_PROBLEM_MARKER);
227 int severity = IMarker.SEVERITY_ERROR;
228 if (message.equals(Util.bind("build.duplicateResource"))) //$NON-NLS-1$
229 if (JavaCore.WARNING.equals(javaBuilder.javaProject.getOption(JavaCore.CORE_JAVA_BUILD_DUPLICATE_RESOURCE, true)))
230 severity = IMarker.SEVERITY_WARNING;
231 marker.setAttributes(new String[] { IMarker.MESSAGE, IMarker.SEVERITY, IMarker.CHAR_START, IMarker.CHAR_END }, new Object[] {
233 new Integer(severity),
236 } catch (CoreException e) {
237 throw internalException(e);
241 //protected void finishedWith(String sourceLocator, CompilationResult result, char[] mainTypeName) throws CoreException {//,
242 // ArrayList definedTypeNames, ArrayList duplicateTypeNames) throws CoreException {
243 // if (duplicateTypeNames == null) {
244 // newState.record(sourceLocator, result.qualifiedReferences, result.simpleNameReferences, mainTypeName, definedTypeNames);
248 // char[][][] qualifiedRefs = result.qualifiedReferences;
249 // char[][] simpleRefs = result.simpleNameReferences;
250 // // for each duplicate type p1.p2.A, add the type name A (package was already added)
251 // next : for (int i = 0, l = duplicateTypeNames.size(); i < l; i++) {
252 // char[][] compoundName = (char[][]) duplicateTypeNames.get(i);
253 // char[] typeName = compoundName[compoundName.length - 1];
254 // int sLength = simpleRefs.length;
255 // for (int j = 0; j < sLength; j++)
256 // if (CharOperation.equals(simpleRefs[j], typeName))
258 // System.arraycopy(simpleRefs, 0, simpleRefs = new char[sLength + 1][], 0, sLength);
259 // simpleRefs[sLength] = typeName;
261 // newState.record(sourceLocator, qualifiedRefs, simpleRefs, mainTypeName, definedTypeNames);
264 //protected IContainer createFolder(IPath packagePath, IContainer outputFolder) throws CoreException {
265 // if (packagePath.isEmpty()) return outputFolder;
266 // IFolder folder = outputFolder.getFolder(packagePath);
267 // if (!folder.exists()) {
268 // createFolder(packagePath.removeLastSegments(1), outputFolder);
269 // folder.create(true, true, null);
270 // folder.setDerived(true);
275 protected RuntimeException internalException(CoreException t) {
276 ImageBuilderInternalException imageBuilderException = new ImageBuilderInternalException(t);
278 return new AbortCompilation(true, imageBuilderException);
279 return imageBuilderException;
282 protected Compiler newCompiler() {
283 // called once when the builder is initialized... can override if needed
284 return new Compiler(nameEnvironment, DefaultErrorHandlingPolicies.proceedWithAllProblems(), javaBuilder.javaProject
285 .getOptions(true), this, ProblemFactory.getProblemFactory(Locale.getDefault()));
288 protected boolean isExcludedFromProject(IPath childPath) throws JavaModelException {
289 // answer whether the folder should be ignored when walking the project as a source folder
290 if (childPath.segmentCount() > 2)
291 return false; // is a subfolder of a package
293 for (int j = 0, k = sourceLocations.length; j < k; j++) {
294 // if (childPath.equals(sourceLocations[j].binaryFolder.getFullPath())) return true;
295 if (childPath.equals(sourceLocations[j].sourceFolder.getFullPath()))
298 // skip default output folder which may not be used by any source folder
299 return false; //childPath.equals(javaBuilder.javaProject.getOutputLocation());
303 * Creates a marker from each problem and adds it to the resource. The marker is as follows: - its type is T_PROBLEM - its plugin
304 * ID is the JavaBuilder's plugin ID - its message is the problem's message - its priority reflects the severity of the problem -
305 * its range is the problem's range - it has an extra attribute "ID" which holds the problem's id
307 protected void storeProblemsFor(SourceFile sourceFile, IProblem[] problems) throws CoreException {
308 if (sourceFile == null || problems == null || problems.length == 0)
311 // String missingClassFile = null;
312 IResource resource = sourceFile.resource;
313 for (int i = 0, l = problems.length; i < l; i++) {
314 IProblem problem = problems[i];
315 int id = problem.getID();
317 case IProblem.IsClassPathCorrect:
318 // PHPBuilder.removeProblemsAndTasksFor(javaBuilder.currentProject); // make this the only problem for this project
319 // String[] args = problem.getArguments();
320 // missingClassFile = args[0];
322 case IProblem.SuperclassMustBeAClass:
323 case IProblem.SuperInterfaceMustBeAnInterface:
324 case IProblem.HierarchyCircularitySelfReference:
325 case IProblem.HierarchyCircularity:
326 case IProblem.HierarchyHasProblems:
327 case IProblem.SuperclassNotFound:
328 case IProblem.SuperclassNotVisible:
329 case IProblem.SuperclassAmbiguous:
330 case IProblem.SuperclassInternalNameProvided:
331 case IProblem.SuperclassInheritedNameHidesEnclosingName:
332 case IProblem.InterfaceNotFound:
333 case IProblem.InterfaceNotVisible:
334 case IProblem.InterfaceAmbiguous:
335 case IProblem.InterfaceInternalNameProvided:
336 case IProblem.InterfaceInheritedNameHidesEnclosingName:
337 // ensure that this file is always retrieved from source for the rest of the build
338 if (!problemSourceFiles.contains(sourceFile))
339 problemSourceFiles.add(sourceFile);
343 if (id != IProblem.Task) {
344 IMarker marker = resource.createMarker(IJavaModelMarker.JAVA_MODEL_PROBLEM_MARKER);
345 marker.setAttributes(new String[] {
352 IJavaModelMarker.ARGUMENTS }, new Object[] {
353 problem.getMessage(),
354 new Integer(problem.isError() ? IMarker.SEVERITY_ERROR : IMarker.SEVERITY_WARNING),
356 new Integer(problem.getSourceStart()),
357 new Integer(problem.getSourceEnd() + 1),
358 new Integer(problem.getSourceLineNumber()),
359 Util.getProblemArgumentsForMarker(problem.getArguments()) });
363 * Do NOT want to populate the Java Model just to find the matching Java element. Also cannot query compilation units located
364 * in folders with invalid package names such as 'a/b.c.d/e'.
365 * // compute a user-friendly location IJavaElement element = JavaCore.create(resource); if (element instanceof
366 * net.sourceforge.phpdt.core.ICompilationUnit) { // try to find a finer grain element
367 * net.sourceforge.phpdt.core.ICompilationUnit unit = (net.sourceforge.phpdt.core.ICompilationUnit) element; IJavaElement
368 * fragment = unit.getElementAt(problem.getSourceStart()); if (fragment != null) element = fragment; } String location = null;
369 * if (element instanceof JavaElement) location = ((JavaElement) element).readableName(); if (location != null)
370 * marker.setAttribute(IMarker.LOCATION, location);
373 // if (missingClassFile != null)
374 // throw new MissingClassFileException(missingClassFile);
378 protected void storeTasksFor(SourceFile sourceFile, IProblem[] tasks) throws CoreException {
379 if (sourceFile == null || tasks == null || tasks.length == 0)
382 IResource resource = sourceFile.resource;
383 for (int i = 0, l = tasks.length; i < l; i++) {
384 IProblem task = tasks[i];
385 if (task.getID() == IProblem.Task) {
386 IMarker marker = resource.createMarker(IJavaModelMarker.TASK_MARKER);
387 int priority = IMarker.PRIORITY_NORMAL;
388 String compilerPriority = task.getArguments()[2];
389 if (JavaCore.COMPILER_TASK_PRIORITY_HIGH.equals(compilerPriority))
390 priority = IMarker.PRIORITY_HIGH;
391 else if (JavaCore.COMPILER_TASK_PRIORITY_LOW.equals(compilerPriority))
392 priority = IMarker.PRIORITY_LOW;
393 marker.setAttributes(new String[] {
400 IMarker.USER_EDITABLE, }, new Object[] {
402 new Integer(priority),
404 new Integer(task.getSourceStart()),
405 new Integer(task.getSourceEnd() + 1),
406 new Integer(task.getSourceLineNumber()),
407 new Boolean(false), });
412 protected void updateProblemsFor(SourceFile sourceFile, CompilationResult result) throws CoreException {
413 IProblem[] problems = result.getProblems();
414 if (problems == null || problems.length == 0)
416 //axelcl start insert - calculate line numbers
417 for (int i = 0; i < problems.length; i++) {
418 if (problems[i].getSourceLineNumber() == 1) {
419 problems[i].setSourceLineNumber(ProblemHandler
420 .searchLineNumber(result.lineSeparatorPositions, problems[i].getSourceStart()));
424 notifier.updateProblemCounts(problems);
425 storeProblemsFor(sourceFile, problems);
428 protected void updateTasksFor(SourceFile sourceFile, CompilationResult result) throws CoreException {
429 IProblem[] tasks = result.getTasks();
430 if (tasks == null || tasks.length == 0)
433 storeTasksFor(sourceFile, tasks);
436 //protected char[] writeClassFile(ClassFile classFile, IContainer outputFolder, boolean isSecondaryType) throws CoreException {
437 // String fileName = new String(classFile.fileName()); // the qualified type name "p1/p2/A"
438 // IPath filePath = new Path(fileName);
439 // IContainer container = outputFolder;
440 // if (filePath.segmentCount() > 1) {
441 // container = createFolder(filePath.removeLastSegments(1), outputFolder);
442 // filePath = new Path(filePath.lastSegment());
445 // IFile file = container.getFile(filePath.addFileExtension(JavaBuilder.CLASS_EXTENSION));
446 // writeClassFileBytes(classFile.getBytes(), file, fileName, isSecondaryType);
447 // // answer the name of the class file as in Y or Y$M
448 // return filePath.lastSegment().toCharArray();
451 //protected void writeClassFileBytes(byte[] bytes, IFile file, String qualifiedFileName, boolean isSecondaryType) throws
453 // if (file.exists()) {
454 // // Deal with shared output folders... last one wins... no collision cases detected
455 // if (JavaBuilder.DEBUG)
456 // System.out.println("Writing changed class file " + file.getName());//$NON-NLS-1$
457 // file.setContents(new ByteArrayInputStream(bytes), true, false, null);
458 // if (!file.isDerived())
459 // file.setDerived(true);
461 // // Default implementation just writes out the bytes for the new class file...
462 // if (JavaBuilder.DEBUG)
463 // System.out.println("Writing new class file " + file.getName());//$NON-NLS-1$
464 // file.create(new ByteArrayInputStream(bytes), IResource.FORCE, null);
465 // file.setDerived(true);