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;
15 import net.sourceforge.phpdt.core.IClasspathEntry;
16 import net.sourceforge.phpdt.core.IJavaProject;
17 import net.sourceforge.phpdt.core.JavaCore;
18 import net.sourceforge.phpdt.core.compiler.CharOperation;
19 import net.sourceforge.phpdt.internal.compiler.env.INameEnvironment;
20 import net.sourceforge.phpdt.internal.compiler.env.NameEnvironmentAnswer;
21 import net.sourceforge.phpdt.internal.compiler.problem.AbortCompilation;
22 import net.sourceforge.phpdt.internal.core.ClasspathEntry;
23 import net.sourceforge.phpdt.internal.core.JavaModel;
24 import net.sourceforge.phpdt.internal.core.JavaProject;
25 import net.sourceforge.phpdt.internal.core.util.SimpleLookupTable;
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.IProject;
31 import org.eclipse.core.resources.IWorkspaceRoot;
32 import org.eclipse.core.runtime.CoreException;
33 import org.eclipse.core.runtime.IPath;
34 import org.eclipse.core.runtime.Path;
36 public class NameEnvironment implements INameEnvironment {
38 boolean isIncrementalBuild;
39 ClasspathMultiDirectory[] sourceLocations;
40 //ClasspathLocation[] binaryLocations;
42 String[] initialTypeNames; // assumed that each name is of the form "a/b/ClassName"
43 SourceFile[] additionalUnits;
45 NameEnvironment(IWorkspaceRoot root, JavaProject javaProject, SimpleLookupTable binaryLocationsPerProject) throws CoreException {
46 this.isIncrementalBuild = false;
47 // this.sourceLocations = new ClasspathMultiDirectory[0];
48 computeClasspathLocations(root, javaProject, binaryLocationsPerProject);
52 public NameEnvironment(IJavaProject javaProject) {
53 this.isIncrementalBuild = false;
55 computeClasspathLocations(javaProject.getProject().getWorkspace().getRoot(), (JavaProject) javaProject, null);
56 } catch(CoreException e) {
57 // this.sourceLocations = new ClasspathMultiDirectory[0];
58 // this.binaryLocations = new ClasspathLocation[0];
63 /* Some examples of resolved class path entries.
64 * Remember to search class path in the order that it was defined.
66 * 1a. typical project with no source folders:
67 * /Test[CPE_SOURCE][K_SOURCE] -> D:/eclipse.test/Test
68 * 1b. project with source folders:
69 * /Test/src1[CPE_SOURCE][K_SOURCE] -> D:/eclipse.test/Test/src1
70 * /Test/src2[CPE_SOURCE][K_SOURCE] -> D:/eclipse.test/Test/src2
71 * NOTE: These can be in any order & separated by prereq projects or libraries
72 * 1c. project external to workspace (only detectable using getLocation()):
73 * /Test/src[CPE_SOURCE][K_SOURCE] -> d:/eclipse.zzz/src
74 * Need to search source folder & output folder
77 * D:/j9/lib/jclMax/classes.zip[CPE_LIBRARY][K_BINARY][sourcePath:d:/j9/lib/jclMax/source/source.zip]
78 * -> D:/j9/lib/jclMax/classes.zip
79 * ALWAYS want to take the library path as is
81 * 3a. prereq project (regardless of whether it has a source or output folder):
82 * /Test[CPE_PROJECT][K_SOURCE] -> D:/eclipse.test/Test
83 * ALWAYS want to append the output folder & ONLY search for .class files
85 private void computeClasspathLocations(
87 JavaProject javaProject,
88 SimpleLookupTable binaryLocationsPerProject) throws CoreException {
90 /* Update cycle marker */
91 IMarker cycleMarker = javaProject.getCycleMarker();
92 if (cycleMarker != null) {
93 int severity = JavaCore.ERROR.equals(javaProject.getOption(JavaCore.CORE_CIRCULAR_CLASSPATH, true))
94 ? IMarker.SEVERITY_ERROR
95 : IMarker.SEVERITY_WARNING;
96 if (severity != ((Integer) cycleMarker.getAttribute(IMarker.SEVERITY)).intValue())
97 cycleMarker.setAttribute(IMarker.SEVERITY, severity);
100 /* Update incomplete classpath marker */
101 // IClasspathEntry[] classpathEntries = javaProject.getExpandedClasspath(true, true);
102 IClasspathEntry[] classpathEntries = javaProject.getExpandedClasspath(true/*ignore unresolved variable*/, false/*don't create markers*/, null/*preferred cp*/, null/*preferred output*/);
104 ArrayList sLocations = new ArrayList(classpathEntries.length);
105 ArrayList bLocations = new ArrayList(classpathEntries.length);
106 nextEntry : for (int i = 0, l = classpathEntries.length; i < l; i++) {
107 ClasspathEntry entry = (ClasspathEntry) classpathEntries[i];
108 IPath path = entry.getPath();
109 Object target = JavaModel.getTarget(root, path, true);
110 if (target == null) continue nextEntry;
112 switch(entry.getEntryKind()) {
113 case IClasspathEntry.CPE_SOURCE :
114 if (!(target instanceof IContainer)) continue nextEntry;
115 // IPath outputPath = entry.getOutputLocation() != null
116 // ? entry.getOutputLocation()
117 // : javaProject.getOutputLocation();
118 IContainer outputFolder = null;
119 // if (outputPath.segmentCount() == 1) {
120 // outputFolder = javaProject.getProject();
122 // outputFolder = root.getFolder(outputPath);
123 // if (!outputFolder.exists())
124 // createFolder(outputFolder);
127 ClasspathLocation.forSourceFolder((IContainer) target, outputFolder, entry.fullExclusionPatternChars()));
130 case IClasspathEntry.CPE_PROJECT :
131 if (!(target instanceof IProject)) continue nextEntry;
132 IProject prereqProject = (IProject) target;
133 if (!JavaProject.hasJavaNature(prereqProject)) continue nextEntry; // if project doesn't have java nature or is not accessible
135 JavaProject prereqJavaProject = (JavaProject) JavaCore.create(prereqProject);
136 IClasspathEntry[] prereqClasspathEntries = prereqJavaProject.getRawClasspath();
137 ArrayList seen = new ArrayList();
138 nextPrereqEntry: for (int j = 0, m = prereqClasspathEntries.length; j < m; j++) {
139 IClasspathEntry prereqEntry = (IClasspathEntry) prereqClasspathEntries[j];
140 if (prereqEntry.getEntryKind() == IClasspathEntry.CPE_SOURCE) {
141 Object prereqTarget = JavaModel.getTarget(root, prereqEntry.getPath(), true);
142 if (!(prereqTarget instanceof IContainer)) continue nextPrereqEntry;
143 IPath prereqOutputPath = prereqEntry.getOutputLocation() != null
144 ? prereqEntry.getOutputLocation()
145 : prereqJavaProject.getOutputLocation();
146 IContainer binaryFolder = prereqOutputPath.segmentCount() == 1
147 ? (IContainer) prereqProject
148 : (IContainer) root.getFolder(prereqOutputPath);
149 if (binaryFolder.exists() && !seen.contains(binaryFolder)) {
150 seen.add(binaryFolder);
151 ClasspathLocation bLocation = ClasspathLocation.forBinaryFolder(binaryFolder, true);
152 bLocations.add(bLocation);
153 if (binaryLocationsPerProject != null) { // normal builder mode
154 ClasspathLocation[] existingLocations = (ClasspathLocation[]) binaryLocationsPerProject.get(prereqProject);
155 if (existingLocations == null) {
156 existingLocations = new ClasspathLocation[] {bLocation};
158 int size = existingLocations.length;
159 System.arraycopy(existingLocations, 0, existingLocations = new ClasspathLocation[size + 1], 0, size);
160 existingLocations[size] = bLocation;
162 binaryLocationsPerProject.put(prereqProject, existingLocations);
169 // case IClasspathEntry.CPE_LIBRARY :
170 // if (target instanceof IResource) {
171 // IResource resource = (IResource) target;
172 // ClasspathLocation bLocation = null;
173 // if (resource instanceof IFile) {
174 // if (!(ProjectPrefUtil.isArchiveFileName(path.lastSegment())))
175 // continue nextEntry;
176 // bLocation = ClasspathLocation.forLibrary((IFile) resource);
177 // } else if (resource instanceof IContainer) {
178 // bLocation = ClasspathLocation.forBinaryFolder((IContainer) target, false); // is library folder not output folder
180 // bLocations.add(bLocation);
181 // if (binaryLocationsPerProject != null) { // normal builder mode
182 // IProject p = resource.getProject(); // can be the project being built
183 // ClasspathLocation[] existingLocations = (ClasspathLocation[]) binaryLocationsPerProject.get(p);
184 // if (existingLocations == null) {
185 // existingLocations = new ClasspathLocation[] {bLocation};
187 // int size = existingLocations.length;
188 // System.arraycopy(existingLocations, 0, existingLocations = new ClasspathLocation[size + 1], 0, size);
189 // existingLocations[size] = bLocation;
191 // binaryLocationsPerProject.put(p, existingLocations);
193 // } else if (target instanceof File) {
194 // if (!(ProjectPrefUtil.isArchiveFileName(path.lastSegment())))
195 // continue nextEntry;
196 // bLocations.add(ClasspathLocation.forLibrary(path.toString()));
198 // continue nextEntry;
202 // now split the classpath locations... place the output folders ahead of the other .class file folders & jars
203 ArrayList outputFolders = new ArrayList(1);
204 this.sourceLocations = new ClasspathMultiDirectory[sLocations.size()];
205 if (!sLocations.isEmpty()) {
206 sLocations.toArray(this.sourceLocations);
208 // collect the output folders, skipping duplicates
209 next : for (int i = 0, l = sourceLocations.length; i < l; i++) {
210 ClasspathMultiDirectory md = sourceLocations[i];
211 // IPath outputPath = md.binaryFolder.getFullPath();
212 // for (int j = 0; j < i; j++) { // compare against previously walked source folders
213 // if (outputPath.equals(sourceLocations[j].binaryFolder.getFullPath())) {
214 // md.hasIndependentOutputFolder = sourceLocations[j].hasIndependentOutputFolder;
218 outputFolders.add(md);
220 // also tag each source folder whose output folder is an independent folder & is not also a source folder
221 // for (int j = 0, m = sourceLocations.length; j < m; j++)
222 // if (outputPath.equals(sourceLocations[j].sourceFolder.getFullPath()))
224 md.hasIndependentOutputFolder = true;
228 // combine the output folders with the binary folders & jars... place the output folders before other .class file folders & jars
229 // this.binaryLocations = new ClasspathLocation[outputFolders.size() + bLocations.size()];
231 // for (int i = 0, l = outputFolders.size(); i < l; i++)
232 // this.binaryLocations[index++] = (ClasspathLocation) outputFolders.get(i);
233 // for (int i = 0, l = bLocations.size(); i < l; i++)
234 // this.binaryLocations[index++] = (ClasspathLocation) bLocations.get(i);
237 public void cleanup() {
238 this.initialTypeNames = null;
239 this.additionalUnits = null;
240 for (int i = 0, l = sourceLocations.length; i < l; i++)
241 sourceLocations[i].cleanup();
242 // for (int i = 0, l = binaryLocations.length; i < l; i++)
243 // binaryLocations[i].cleanup();
246 private void createFolder(IContainer folder) throws CoreException {
247 if (!folder.exists()) {
248 createFolder(folder.getParent());
249 ((IFolder) folder).create(true, true, null);
253 private NameEnvironmentAnswer findClass(String qualifiedTypeName, char[] typeName) {
254 if (initialTypeNames != null) {
255 for (int i = 0, l = initialTypeNames.length; i < l; i++) {
256 if (qualifiedTypeName.equals(initialTypeNames[i])) {
257 if (isIncrementalBuild)
258 // catch the case that a type inside a source file has been renamed but other class files are looking for it
259 throw new AbortCompilation(true, new AbortIncrementalBuildException(qualifiedTypeName));
260 return null; // looking for a file which we know was provided at the beginning of the compilation
265 if (additionalUnits != null && sourceLocations.length > 0) {
266 // if an additional source file is waiting to be compiled, answer it BUT not if this is a secondary type search
267 // if we answer X.java & it no longer defines Y then the binary type looking for Y will think the class path is wrong
268 // let the recompile loop fix up dependents when the secondary type Y has been deleted from X.java
269 IPath qSourceFilePath = new Path(qualifiedTypeName + ".java"); //$NON-NLS-1$
270 int qSegmentCount = qSourceFilePath.segmentCount();
271 next : for (int i = 0, l = additionalUnits.length; i < l; i++) {
272 SourceFile additionalUnit = additionalUnits[i];
273 IPath fullPath = additionalUnit.resource.getFullPath();
274 int prefixCount = additionalUnit.sourceLocation.sourceFolder.getFullPath().segmentCount();
275 if (qSegmentCount == fullPath.segmentCount() - prefixCount) {
276 for (int j = 0; j < qSegmentCount; j++)
277 if (!qSourceFilePath.segment(j).equals(fullPath.segment(j + prefixCount)))
279 return new NameEnvironmentAnswer(additionalUnit);
284 // String qBinaryFileName = qualifiedTypeName + ".class"; //$NON-NLS-1$
285 // String binaryFileName = qBinaryFileName;
286 // String qPackageName = ""; //$NON-NLS-1$
287 // if (qualifiedTypeName.length() > typeName.length) {
288 // int typeNameStart = qBinaryFileName.length() - typeName.length - 6; // size of ".class"
289 // qPackageName = qBinaryFileName.substring(0, typeNameStart - 1);
290 // binaryFileName = qBinaryFileName.substring(typeNameStart);
293 // // NOTE: the output folders are added at the beginning of the binaryLocations
294 // for (int i = 0, l = binaryLocations.length; i < l; i++) {
295 // NameEnvironmentAnswer answer = binaryLocations[i].findClass(binaryFileName, qPackageName, qBinaryFileName);
296 // if (answer != null) return answer;
301 public NameEnvironmentAnswer findType(char[][] compoundName) {
302 if (compoundName != null)
303 return findClass(new String(CharOperation.concatWith(compoundName, '/')), compoundName[compoundName.length - 1]);
307 public NameEnvironmentAnswer findType(char[] typeName, char[][] packageName) {
308 if (typeName != null)
309 return findClass(new String(CharOperation.concatWith(packageName, typeName, '/')), typeName);
313 public boolean isPackage(char[][] compoundName, char[] packageName) {
314 return isPackage(new String(CharOperation.concatWith(compoundName, packageName, '/')));
317 public boolean isPackage(String qualifiedPackageName) {
318 // NOTE: the output folders are added at the beginning of the binaryLocations
319 // for (int i = 0, l = binaryLocations.length; i < l; i++)
320 // if (binaryLocations[i].isPackage(qualifiedPackageName))
325 void setNames(String[] initialTypeNames, SourceFile[] additionalUnits) {
326 this.initialTypeNames = initialTypeNames;
327 this.additionalUnits = additionalUnits;
328 for (int i = 0, l = sourceLocations.length; i < l; i++)
329 sourceLocations[i].reset();
330 // for (int i = 0, l = binaryLocations.length; i < l; i++)
331 // binaryLocations[i].reset();