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.JavaModelException;
18 import net.sourceforge.phpdt.core.JavaCore;
19 import net.sourceforge.phpdt.core.compiler.IProblem;
20 import net.sourceforge.phpdt.internal.compiler.Compiler;
21 import net.sourceforge.phpdt.internal.compiler.CompilationResult;
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;
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;
35 * The abstract superclass of Java builders.
36 * Provides the building and compilation mechanism
37 * in common with the batch and incremental builders.
39 public abstract class AbstractImageBuilder implements ICompilerRequestor {
41 protected PHPBuilder javaBuilder;
42 protected State newState;
45 protected NameEnvironment nameEnvironment;
46 protected ClasspathMultiDirectory[] sourceLocations;
47 protected BuildNotifier notifier;
49 protected String encoding;
50 protected Compiler compiler;
51 protected WorkQueue workQueue;
52 protected ArrayList problemSourceFiles;
53 protected boolean compiledAllAtOnce;
55 private boolean inCompiler;
57 public static int MAX_AT_ONCE = 1000;
59 protected AbstractImageBuilder(PHPBuilder javaBuilder) {
60 this.javaBuilder = javaBuilder;
61 this.newState = new State(javaBuilder);
64 this.nameEnvironment = javaBuilder.nameEnvironment;
65 this.sourceLocations = this.nameEnvironment.sourceLocations;
66 this.notifier = javaBuilder.notifier;
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);
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.
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.
83 SourceFile compilationUnit = (SourceFile) result.getCompilationUnit(); // go directly back to the sourceFile
84 if (!workQueue.isCompiled(compilationUnit)) {
86 workQueue.finished(compilationUnit);
87 updateProblemsFor(compilationUnit, result); // record compilation problems before potentially adding duplicate errors
88 updateTasksFor(compilationUnit, result); // record tasks
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);
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))
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, Util.bind("build.duplicateClassFile", new String(typeName))); //$NON-NLS-1$
115 // newState.recordLocatorForType(qualifiedTypeName, typeLocator);
117 // definedTypeNames.add(writeClassFile(classFile, compilationUnit.sourceLocation.binaryFolder, !isNestedType));
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$
129 protected void cleanUp() {
130 this.nameEnvironment.cleanup();
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;
141 /* Compile the given elements, adding more elements to the work queue
142 * if they are affected by the changes.
144 protected void compile(SourceFile[] units) {
145 int toDo = units.length;
146 if (this.compiledAllAtOnce = toDo <= MAX_AT_ONCE) {
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);
154 boolean compilingFirstGroup = true;
156 int doNow = toDo < MAX_AT_ONCE ? toDo : MAX_AT_ONCE;
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;
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);
179 void compile(SourceFile[] units, SourceFile[] additionalUnits) {
180 if (units.length == 0) return;
181 notifier.aboutToCompile(units[0]); // just to change the message
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;
188 additionalUnits = new SourceFile[toAdd];
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);
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();
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
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();
213 protected void createErrorFor(IResource resource, String message) {
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);
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);
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))
244 // System.arraycopy(simpleRefs, 0, simpleRefs = new char[sLength + 1][], 0, sLength);
245 // simpleRefs[sLength] = typeName;
247 // newState.record(sourceLocator, qualifiedRefs, simpleRefs, mainTypeName, definedTypeNames);
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);
261 protected RuntimeException internalException(CoreException t) {
262 ImageBuilderInternalException imageBuilderException = new ImageBuilderInternalException(t);
264 return new AbortCompilation(true, imageBuilderException);
265 return imageBuilderException;
268 protected Compiler newCompiler() {
269 // called once when the builder is initialized... can override if needed
272 DefaultErrorHandlingPolicies.proceedWithAllProblems(),
273 javaBuilder.javaProject.getOptions(true),
275 ProblemFactory.getProblemFactory(Locale.getDefault()));
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
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;
286 // skip default output folder which may not be used by any source folder
287 return childPath.equals(javaBuilder.javaProject.getOutputLocation());
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
300 protected void storeProblemsFor(SourceFile sourceFile, IProblem[] problems) throws CoreException {
301 if (sourceFile == null || problems == null || problems.length == 0) return;
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();
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];
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);
335 if (id != IProblem.Task) {
336 IMarker marker = resource.createMarker(IJavaModelMarker.JAVA_MODEL_PROBLEM_MARKER);
337 marker.setAttributes(
345 IJavaModelMarker.ARGUMENTS},
347 problem.getMessage(),
348 new Integer(problem.isError() ? IMarker.SEVERITY_ERROR : IMarker.SEVERITY_WARNING),
350 new Integer(problem.getSourceStart()),
351 new Integer(problem.getSourceEnd() + 1),
352 new Integer(problem.getSourceLineNumber()),
353 Util.getProblemArgumentsForMarker(problem.getArguments())
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'.
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;
368 String location = null;
369 if (element instanceof JavaElement)
370 location = ((JavaElement) element).readableName();
371 if (location != null)
372 marker.setAttribute(IMarker.LOCATION, location);
375 // if (missingClassFile != null)
376 // throw new MissingClassFileException(missingClassFile);
380 protected void storeTasksFor(SourceFile sourceFile, IProblem[] tasks) throws CoreException {
381 if (sourceFile == null || tasks == null || tasks.length == 0) return;
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(
402 IMarker.USER_EDITABLE,
406 new Integer(priority),
408 new Integer(task.getSourceStart()),
409 new Integer(task.getSourceEnd() + 1),
410 new Integer(task.getSourceLineNumber()),
417 protected void updateProblemsFor(SourceFile sourceFile, CompilationResult result) throws CoreException {
418 IProblem[] problems = result.getProblems();
419 if (problems == null || problems.length == 0) return;
421 notifier.updateProblemCounts(problems);
422 storeProblemsFor(sourceFile, problems);
425 protected void updateTasksFor(SourceFile sourceFile, CompilationResult result) throws CoreException {
426 IProblem[] tasks = result.getTasks();
427 if (tasks == null || tasks.length == 0) return;
429 storeTasksFor(sourceFile, tasks);
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());
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();
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);
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);