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
35 * compilation mechanism in common with the batch and 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(
74 JavaCore.CORE_ENCODING, true);
75 this.compiler = newCompiler();
76 this.workQueue = new WorkQueue();
77 this.problemSourceFiles = new ArrayList(3);
80 public void acceptResult(CompilationResult result) {
81 // In Batch mode, we write out the class files, hold onto the dependency
83 // & additional types and report problems.
85 // In Incremental mode, when writing out a class file we need to compare
87 // against the previous file, remembering if structural changes occured.
88 // Before reporting the new problems, we need to update the problem
90 // remove the old problems. Plus delete additional class files that no
93 SourceFile compilationUnit = (SourceFile) result.getCompilationUnit(); // go
99 if (!workQueue.isCompiled(compilationUnit)) {
101 workQueue.finished(compilationUnit);
103 updateProblemsFor(compilationUnit, result); // record
109 updateTasksFor(compilationUnit, result); // record tasks
110 } catch (CoreException e) {
111 throw internalException(e);
114 // String typeLocator = compilationUnit.typeLocator();
115 // ClassFile[] classFiles = result.getClassFiles();
116 // int length = classFiles.length;
117 // ArrayList duplicateTypeNames = null;
118 // ArrayList definedTypeNames = new ArrayList(length);
119 // for (int i = 0; i < length; i++) {
120 // ClassFile classFile = classFiles[i];
121 // char[][] compoundName = classFile.getCompoundName();
122 // char[] typeName = compoundName[compoundName.length - 1];
123 // boolean isNestedType = CharOperation.contains('$', typeName);
125 // // Look for a possible collision, if one exists, report an error
126 // but do not write the class file
127 // if (isNestedType) {
128 // String qualifiedTypeName = new
129 // String(classFile.outerMostEnclosingClassFile().fileName());
130 // if (newState.isDuplicateLocator(qualifiedTypeName, typeLocator))
133 // String qualifiedTypeName = new String(classFile.fileName()); //
134 // the qualified type name "p1/p2/A"
135 // if (newState.isDuplicateLocator(qualifiedTypeName, typeLocator))
137 // if (duplicateTypeNames == null)
138 // duplicateTypeNames = new ArrayList();
139 // duplicateTypeNames.add(compoundName);
140 // createErrorFor(compilationUnit.resource,
141 // ProjectPrefUtil.bind("build.duplicateClassFile", new
142 // String(typeName)));
146 // newState.recordLocatorForType(qualifiedTypeName, typeLocator);
148 // definedTypeNames.add(writeClassFile(classFile,
149 // compilationUnit.sourceLocation.binaryFolder, !isNestedType));
152 // finishedWith(typeLocator, result,
153 // compilationUnit.getMainTypeName(), definedTypeNames,
154 // duplicateTypeNames);
155 notifier.compiled(compilationUnit);
156 // } catch (CoreException e) {
157 // Util.log(e, "JavaBuilder handling CoreException"); //$NON-NLS-1$
158 // createErrorFor(compilationUnit.resource,
159 // Util.bind("build.inconsistentClassFile")); //$NON-NLS-1$
164 protected void cleanUp() {
165 this.nameEnvironment.cleanup();
167 this.javaBuilder = null;
168 this.nameEnvironment = null;
169 this.sourceLocations = null;
170 this.notifier = null;
171 this.compiler = null;
172 this.workQueue = null;
173 this.problemSourceFiles = null;
177 * Compile the given elements, adding more elements to the work queue if
178 * they are affected by the changes.
180 protected void compile(SourceFile[] units) {
181 int toDo = units.length;
182 if (this.compiledAllAtOnce = toDo <= MAX_AT_ONCE) {
184 if (PHPBuilder.DEBUG)
185 for (int i = 0; i < toDo; i++)
187 .println("About to compile " + units[i].typeLocator()); //$NON-NLS-1$
188 compile(units, null);
191 boolean compilingFirstGroup = true;
193 int doNow = toDo < MAX_AT_ONCE ? toDo : MAX_AT_ONCE;
195 SourceFile[] toCompile = new SourceFile[doNow];
196 while (i < toDo && index < doNow) {
197 // Although it needed compiling when this method was called,
199 // already been compiled when it was referenced by another
201 SourceFile unit = units[i++];
202 if (compilingFirstGroup || workQueue.isWaiting(unit)) {
203 if (PHPBuilder.DEBUG)
205 .println("About to compile " + unit.typeLocator()); //$NON-NLS-1$
206 toCompile[index++] = unit;
210 System.arraycopy(toCompile, 0,
211 toCompile = new SourceFile[index], 0, index);
212 SourceFile[] additionalUnits = new SourceFile[toDo - i];
213 System.arraycopy(units, i, additionalUnits, 0,
214 additionalUnits.length);
215 compilingFirstGroup = false;
216 compile(toCompile, additionalUnits);
221 void compile(SourceFile[] units, SourceFile[] additionalUnits) {
222 if (units.length == 0)
224 notifier.aboutToCompile(units[0]); // just to change the message
226 // extend additionalFilenames with all hierarchical problem types found
227 // during this entire build
228 if (!problemSourceFiles.isEmpty()) {
229 int toAdd = problemSourceFiles.size();
230 int length = additionalUnits == null ? 0 : additionalUnits.length;
232 additionalUnits = new SourceFile[toAdd];
234 System.arraycopy(additionalUnits, 0,
235 additionalUnits = new SourceFile[length + toAdd], 0,
237 for (int i = 0; i < toAdd; i++)
238 additionalUnits[length + i] = (SourceFile) problemSourceFiles
241 String[] initialTypeNames = new String[units.length];
242 for (int i = 0, l = units.length; i < l; i++)
243 initialTypeNames[i] = units[i].initialTypeName;
244 nameEnvironment.setNames(initialTypeNames, additionalUnits);
245 notifier.checkCancel();
248 compiler.compile(units);
249 } catch (AbortCompilation ignored) {
250 // ignore the AbortCompilcation coming from
251 // BuildNotifier.checkCancelWithinCompiler()
252 // the Compiler failed after the user has chose to cancel... likely
253 // due to an OutOfMemory error
257 // Check for cancel immediately after a compile, because the compiler
259 // have been cancelled but without propagating the correct exception
260 notifier.checkCancel();
263 protected void createErrorFor(IResource resource, String message) {
265 IMarker marker = resource
266 .createMarker(IJavaModelMarker.JAVA_MODEL_PROBLEM_MARKER);
267 int severity = IMarker.SEVERITY_ERROR;
268 if (message.equals(Util.bind("build.duplicateResource"))) //$NON-NLS-1$
269 if (JavaCore.WARNING.equals(javaBuilder.javaProject.getOption(
270 JavaCore.CORE_JAVA_BUILD_DUPLICATE_RESOURCE, true)))
271 severity = IMarker.SEVERITY_WARNING;
272 marker.setAttributes(new String[] { IMarker.MESSAGE,
273 IMarker.SEVERITY, IMarker.CHAR_START, IMarker.CHAR_END },
274 new Object[] { message, new Integer(severity),
275 new Integer(0), new Integer(1) });
276 } catch (CoreException e) {
277 throw internalException(e);
281 // protected void finishedWith(String sourceLocator, CompilationResult
282 // result, char[] mainTypeName) throws CoreException {//,
283 // ArrayList definedTypeNames, ArrayList duplicateTypeNames) throws
285 // if (duplicateTypeNames == null) {
286 // newState.record(sourceLocator, result.qualifiedReferences,
287 // result.simpleNameReferences, mainTypeName, definedTypeNames);
291 // char[][][] qualifiedRefs = result.qualifiedReferences;
292 // char[][] simpleRefs = result.simpleNameReferences;
293 // // for each duplicate type p1.p2.A, add the type name A (package was
295 // next : for (int i = 0, l = duplicateTypeNames.size(); i < l; i++) {
296 // char[][] compoundName = (char[][]) duplicateTypeNames.get(i);
297 // char[] typeName = compoundName[compoundName.length - 1];
298 // int sLength = simpleRefs.length;
299 // for (int j = 0; j < sLength; j++)
300 // if (CharOperation.equals(simpleRefs[j], typeName))
302 // System.arraycopy(simpleRefs, 0, simpleRefs = new char[sLength + 1][], 0,
304 // simpleRefs[sLength] = typeName;
306 // newState.record(sourceLocator, qualifiedRefs, simpleRefs, mainTypeName,
307 // definedTypeNames);
310 // protected IContainer createFolder(IPath packagePath, IContainer
311 // outputFolder) throws CoreException {
312 // if (packagePath.isEmpty()) return outputFolder;
313 // IFolder folder = outputFolder.getFolder(packagePath);
314 // if (!folder.exists()) {
315 // createFolder(packagePath.removeLastSegments(1), outputFolder);
316 // folder.create(true, true, null);
317 // folder.setDerived(true);
322 protected RuntimeException internalException(CoreException t) {
323 ImageBuilderInternalException imageBuilderException = new ImageBuilderInternalException(
326 return new AbortCompilation(true, imageBuilderException);
327 return imageBuilderException;
330 protected Compiler newCompiler() {
331 // called once when the builder is initialized... can override if needed
332 return new Compiler(nameEnvironment, DefaultErrorHandlingPolicies
333 .proceedWithAllProblems(), javaBuilder.javaProject
334 .getOptions(true), this, ProblemFactory
335 .getProblemFactory(Locale.getDefault()));
338 protected boolean isExcludedFromProject(IPath childPath)
339 throws JavaModelException {
340 // answer whether the folder should be ignored when walking the project
341 // as a source folder
342 if (childPath.segmentCount() > 2)
343 return false; // is a subfolder of a package
345 for (int j = 0, k = sourceLocations.length; j < k; j++) {
347 // (childPath.equals(sourceLocations[j].binaryFolder.getFullPath()))
349 if (childPath.equals(sourceLocations[j].sourceFolder.getFullPath()))
352 // skip default output folder which may not be used by any source folder
353 return false; // childPath.equals(javaBuilder.javaProject.getOutputLocation());
357 * Creates a marker from each problem and adds it to the resource. The
358 * marker is as follows: - its type is T_PROBLEM - its plugin ID is the
359 * JavaBuilder's plugin ID - its message is the problem's message - its
360 * priority reflects the severity of the problem - its range is the
361 * problem's range - it has an extra attribute "ID" which holds the
364 protected void storeProblemsFor(SourceFile sourceFile, IProblem[] problems)
365 throws CoreException {
366 if (sourceFile == null || problems == null || problems.length == 0)
369 // String missingClassFile = null;
370 IResource resource = sourceFile.resource;
371 for (int i = 0, l = problems.length; i < l; i++) {
372 IProblem problem = problems[i];
373 int id = problem.getID();
375 case IProblem.IsClassPathCorrect:
376 // PHPBuilder.removeProblemsAndTasksFor(javaBuilder.currentProject);
377 // // make this the only problem for this project
378 // String[] args = problem.getArguments();
379 // missingClassFile = args[0];
381 case IProblem.SuperclassMustBeAClass:
382 case IProblem.SuperInterfaceMustBeAnInterface:
383 case IProblem.HierarchyCircularitySelfReference:
384 case IProblem.HierarchyCircularity:
385 case IProblem.HierarchyHasProblems:
386 case IProblem.SuperclassNotFound:
387 case IProblem.SuperclassNotVisible:
388 case IProblem.SuperclassAmbiguous:
389 case IProblem.SuperclassInternalNameProvided:
390 case IProblem.SuperclassInheritedNameHidesEnclosingName:
391 case IProblem.InterfaceNotFound:
392 case IProblem.InterfaceNotVisible:
393 case IProblem.InterfaceAmbiguous:
394 case IProblem.InterfaceInternalNameProvided:
395 case IProblem.InterfaceInheritedNameHidesEnclosingName:
396 // ensure that this file is always retrieved from source for the
398 if (!problemSourceFiles.contains(sourceFile))
399 problemSourceFiles.add(sourceFile);
403 if (id != IProblem.Task) {
404 IMarker marker = resource
405 .createMarker(IJavaModelMarker.JAVA_MODEL_PROBLEM_MARKER);
408 new String[] { IMarker.MESSAGE,
409 IMarker.SEVERITY, IJavaModelMarker.ID,
410 IMarker.CHAR_START, IMarker.CHAR_END,
412 IJavaModelMarker.ARGUMENTS },
414 problem.getMessage(),
416 problem.isError() ? IMarker.SEVERITY_ERROR
417 : IMarker.SEVERITY_WARNING),
419 new Integer(problem.getSourceStart()),
420 new Integer(problem.getSourceEnd() + 1),
422 .getSourceLineNumber()),
424 .getProblemArgumentsForMarker(problem
429 * Do NOT want to populate the Java Model just to find the matching
430 * Java element. Also cannot query compilation units located in
431 * folders with invalid package names such as 'a/b.c.d/e'. //
432 * compute a user-friendly location IJavaElement element =
433 * JavaCore.create(resource); if (element instanceof
434 * net.sourceforge.phpdt.core.ICompilationUnit) { // try to find a
435 * finer grain element net.sourceforge.phpdt.core.ICompilationUnit
436 * unit = (net.sourceforge.phpdt.core.ICompilationUnit) element;
437 * IJavaElement fragment =
438 * unit.getElementAt(problem.getSourceStart()); if (fragment !=
439 * null) element = fragment; } String location = null; if (element
440 * instanceof JavaElement) location = ((JavaElement)
441 * element).readableName(); if (location != null)
442 * marker.setAttribute(IMarker.LOCATION, location);
445 // if (missingClassFile != null)
446 // throw new MissingClassFileException(missingClassFile);
450 protected void storeTasksFor(SourceFile sourceFile, IProblem[] tasks)
451 throws CoreException {
452 if (sourceFile == null || tasks == null || tasks.length == 0)
455 IResource resource = sourceFile.resource;
456 for (int i = 0, l = tasks.length; i < l; i++) {
457 IProblem task = tasks[i];
458 if (task.getID() == IProblem.Task) {
459 IMarker marker = resource
460 .createMarker(IJavaModelMarker.TASK_MARKER);
461 int priority = IMarker.PRIORITY_NORMAL;
462 String compilerPriority = task.getArguments()[2];
463 if (JavaCore.COMPILER_TASK_PRIORITY_HIGH
464 .equals(compilerPriority))
465 priority = IMarker.PRIORITY_HIGH;
466 else if (JavaCore.COMPILER_TASK_PRIORITY_LOW
467 .equals(compilerPriority))
468 priority = IMarker.PRIORITY_LOW;
469 marker.setAttributes(new String[] { IMarker.MESSAGE,
470 IMarker.PRIORITY, IMarker.DONE, IMarker.CHAR_START,
471 IMarker.CHAR_END, IMarker.LINE_NUMBER,
472 IMarker.USER_EDITABLE, }, new Object[] {
473 task.getMessage(), new Integer(priority),
474 new Boolean(false), new Integer(task.getSourceStart()),
475 new Integer(task.getSourceEnd() + 1),
476 new Integer(task.getSourceLineNumber()),
477 new Boolean(false), });
482 protected void updateProblemsFor(SourceFile sourceFile,
483 CompilationResult result) throws CoreException {
484 IProblem[] problems = result.getProblems();
485 if (problems == null || problems.length == 0)
487 // axelcl start insert - calculate line numbers
488 if (problems != null) {
489 for (int i = 0; i < problems.length; i++) {
490 if (problems[i].getSourceLineNumber() == 1) {
491 problems[i].setSourceLineNumber(ProblemHandler
492 .searchLineNumber(result.lineSeparatorPositions,
493 problems[i].getSourceStart()));
498 notifier.updateProblemCounts(problems);
499 storeProblemsFor(sourceFile, problems);
502 protected void updateTasksFor(SourceFile sourceFile,
503 CompilationResult result) throws CoreException {
504 IProblem[] tasks = result.getTasks();
505 if (tasks == null || tasks.length == 0)
508 storeTasksFor(sourceFile, tasks);
511 // protected char[] writeClassFile(ClassFile classFile, IContainer
512 // outputFolder, boolean isSecondaryType) throws CoreException {
513 // String fileName = new String(classFile.fileName()); // the qualified type
515 // IPath filePath = new Path(fileName);
516 // IContainer container = outputFolder;
517 // if (filePath.segmentCount() > 1) {
518 // container = createFolder(filePath.removeLastSegments(1), outputFolder);
519 // filePath = new Path(filePath.lastSegment());
523 // container.getFile(filePath.addFileExtension(JavaBuilder.CLASS_EXTENSION));
524 // writeClassFileBytes(classFile.getBytes(), file, fileName,
526 // // answer the name of the class file as in Y or Y$M
527 // return filePath.lastSegment().toCharArray();
530 // protected void writeClassFileBytes(byte[] bytes, IFile file, String
531 // qualifiedFileName, boolean isSecondaryType) throws
533 // if (file.exists()) {
534 // // Deal with shared output folders... last one wins... no collision cases
536 // if (JavaBuilder.DEBUG)
537 // System.out.println("Writing changed class file " +
538 // file.getName());//$NON-NLS-1$
539 // file.setContents(new ByteArrayInputStream(bytes), true, false, null);
540 // if (!file.isDerived())
541 // file.setDerived(true);
543 // // Default implementation just writes out the bytes for the new class
545 // if (JavaBuilder.DEBUG)
546 // System.out.println("Writing new class file " +
547 // file.getName());//$NON-NLS-1$
548 // file.create(new ByteArrayInputStream(bytes), IResource.FORCE, null);
549 // file.setDerived(true);