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;
40 ClasspathMultiDirectory[] sourceLocations;
42 // ClasspathLocation[] binaryLocations;
44 String[] initialTypeNames; // assumed that each name is of the form
47 SourceFile[] additionalUnits;
49 NameEnvironment(IWorkspaceRoot root, JavaProject javaProject,
50 SimpleLookupTable binaryLocationsPerProject) throws CoreException {
51 this.isIncrementalBuild = false;
52 // this.sourceLocations = new ClasspathMultiDirectory[0];
53 computeClasspathLocations(root, javaProject, binaryLocationsPerProject);
57 public NameEnvironment(IJavaProject javaProject) {
58 this.isIncrementalBuild = false;
60 computeClasspathLocations(javaProject.getProject().getWorkspace()
61 .getRoot(), (JavaProject) javaProject, null);
62 } catch (CoreException e) {
63 // this.sourceLocations = new ClasspathMultiDirectory[0];
64 // this.binaryLocations = new ClasspathLocation[0];
70 * Some examples of resolved class path entries. Remember to search class
71 * path in the order that it was defined.
73 * 1a. typical project with no source folders: /Test[CPE_SOURCE][K_SOURCE] ->
74 * D:/eclipse.test/Test 1b. project with source folders:
75 * /Test/src1[CPE_SOURCE][K_SOURCE] -> D:/eclipse.test/Test/src1
76 * /Test/src2[CPE_SOURCE][K_SOURCE] -> D:/eclipse.test/Test/src2 NOTE: These
77 * can be in any order & separated by prereq projects or libraries 1c.
78 * project external to workspace (only detectable using getLocation()):
79 * /Test/src[CPE_SOURCE][K_SOURCE] -> d:/eclipse.zzz/src Need to search
80 * source folder & output folder
83 * D:/j9/lib/jclMax/classes.zip[CPE_LIBRARY][K_BINARY][sourcePath:d:/j9/lib/jclMax/source/source.zip] ->
84 * D:/j9/lib/jclMax/classes.zip ALWAYS want to take the library path as is
86 * 3a. prereq project (regardless of whether it has a source or output
87 * folder): /Test[CPE_PROJECT][K_SOURCE] -> D:/eclipse.test/Test ALWAYS want
88 * to append the output folder & ONLY search for .class files
90 private void computeClasspathLocations(IWorkspaceRoot root,
91 JavaProject javaProject, SimpleLookupTable binaryLocationsPerProject)
92 throws CoreException {
94 /* Update cycle marker */
95 IMarker cycleMarker = javaProject.getCycleMarker();
96 if (cycleMarker != null) {
97 int severity = JavaCore.ERROR.equals(javaProject.getOption(
98 JavaCore.CORE_CIRCULAR_CLASSPATH, true)) ? IMarker.SEVERITY_ERROR
99 : IMarker.SEVERITY_WARNING;
100 if (severity != ((Integer) cycleMarker
101 .getAttribute(IMarker.SEVERITY)).intValue())
102 cycleMarker.setAttribute(IMarker.SEVERITY, severity);
105 /* Update incomplete classpath marker */
106 // IClasspathEntry[] classpathEntries =
107 // javaProject.getExpandedClasspath(true, true);
108 IClasspathEntry[] classpathEntries = javaProject
109 .getExpandedClasspath(true/* ignore unresolved variable */,
110 false/* don't create markers */, null/* preferred cp */,
111 null/* preferred output */);
113 ArrayList sLocations = new ArrayList(classpathEntries.length);
114 ArrayList bLocations = new ArrayList(classpathEntries.length);
115 nextEntry: for (int i = 0, l = classpathEntries.length; i < l; i++) {
116 ClasspathEntry entry = (ClasspathEntry) classpathEntries[i];
117 IPath path = entry.getPath();
118 Object target = JavaModel.getTarget(root, path, true);
122 switch (entry.getEntryKind()) {
123 case IClasspathEntry.CPE_SOURCE:
124 if (!(target instanceof IContainer))
126 // IPath outputPath = entry.getOutputLocation() != null
127 // ? entry.getOutputLocation()
128 // : javaProject.getOutputLocation();
129 IContainer outputFolder = null;
130 // if (outputPath.segmentCount() == 1) {
131 // outputFolder = javaProject.getProject();
133 // outputFolder = root.getFolder(outputPath);
134 // if (!outputFolder.exists())
135 // createFolder(outputFolder);
137 sLocations.add(ClasspathLocation.forSourceFolder(
138 (IContainer) target, outputFolder, entry
139 .fullExclusionPatternChars()));
142 case IClasspathEntry.CPE_PROJECT:
143 if (!(target instanceof IProject))
145 IProject prereqProject = (IProject) target;
146 if (!JavaProject.hasJavaNature(prereqProject))
147 continue nextEntry; // if project doesn't have java nature
148 // or is not accessible
150 JavaProject prereqJavaProject = (JavaProject) JavaCore
151 .create(prereqProject);
152 IClasspathEntry[] prereqClasspathEntries = prereqJavaProject
154 ArrayList seen = new ArrayList();
155 nextPrereqEntry: for (int j = 0, m = prereqClasspathEntries.length; j < m; j++) {
156 IClasspathEntry prereqEntry = (IClasspathEntry) prereqClasspathEntries[j];
157 if (prereqEntry.getEntryKind() == IClasspathEntry.CPE_SOURCE) {
158 Object prereqTarget = JavaModel.getTarget(root,
159 prereqEntry.getPath(), true);
160 if (!(prereqTarget instanceof IContainer))
161 continue nextPrereqEntry;
162 IPath prereqOutputPath = prereqEntry
163 .getOutputLocation() != null ? prereqEntry
164 .getOutputLocation() : prereqJavaProject
165 .getOutputLocation();
166 IContainer binaryFolder = prereqOutputPath
167 .segmentCount() == 1 ? (IContainer) prereqProject
168 : (IContainer) root.getFolder(prereqOutputPath);
169 if (binaryFolder.exists()
170 && !seen.contains(binaryFolder)) {
171 seen.add(binaryFolder);
172 ClasspathLocation bLocation = ClasspathLocation
173 .forBinaryFolder(binaryFolder, true);
174 bLocations.add(bLocation);
175 if (binaryLocationsPerProject != null) { // normal
178 ClasspathLocation[] existingLocations = (ClasspathLocation[]) binaryLocationsPerProject
180 if (existingLocations == null) {
181 existingLocations = new ClasspathLocation[] { bLocation };
183 int size = existingLocations.length;
188 existingLocations = new ClasspathLocation[size + 1],
190 existingLocations[size] = bLocation;
192 binaryLocationsPerProject.put(prereqProject,
200 // case IClasspathEntry.CPE_LIBRARY :
201 // if (target instanceof IResource) {
202 // IResource resource = (IResource) target;
203 // ClasspathLocation bLocation = null;
204 // if (resource instanceof IFile) {
205 // if (!(ProjectPrefUtil.isArchiveFileName(path.lastSegment())))
206 // continue nextEntry;
207 // bLocation = ClasspathLocation.forLibrary((IFile) resource);
208 // } else if (resource instanceof IContainer) {
209 // bLocation = ClasspathLocation.forBinaryFolder((IContainer)
210 // target, false); // is library folder not output folder
212 // bLocations.add(bLocation);
213 // if (binaryLocationsPerProject != null) { // normal builder
215 // IProject p = resource.getProject(); // can be the project
217 // ClasspathLocation[] existingLocations = (ClasspathLocation[])
218 // binaryLocationsPerProject.get(p);
219 // if (existingLocations == null) {
220 // existingLocations = new ClasspathLocation[] {bLocation};
222 // int size = existingLocations.length;
223 // System.arraycopy(existingLocations, 0, existingLocations =
224 // new ClasspathLocation[size + 1], 0, size);
225 // existingLocations[size] = bLocation;
227 // binaryLocationsPerProject.put(p, existingLocations);
229 // } else if (target instanceof File) {
230 // if (!(ProjectPrefUtil.isArchiveFileName(path.lastSegment())))
231 // continue nextEntry;
232 // bLocations.add(ClasspathLocation.forLibrary(path.toString()));
234 // continue nextEntry;
238 // now split the classpath locations... place the output folders ahead
239 // of the other .class file folders & jars
240 ArrayList outputFolders = new ArrayList(1);
241 this.sourceLocations = new ClasspathMultiDirectory[sLocations.size()];
242 if (!sLocations.isEmpty()) {
243 sLocations.toArray(this.sourceLocations);
245 // collect the output folders, skipping duplicates
246 next: for (int i = 0, l = sourceLocations.length; i < l; i++) {
247 ClasspathMultiDirectory md = sourceLocations[i];
248 // IPath outputPath = md.binaryFolder.getFullPath();
249 // for (int j = 0; j < i; j++) { // compare against previously
250 // walked source folders
252 // (outputPath.equals(sourceLocations[j].binaryFolder.getFullPath()))
254 // md.hasIndependentOutputFolder =
255 // sourceLocations[j].hasIndependentOutputFolder;
259 outputFolders.add(md);
261 // also tag each source folder whose output folder is an
262 // independent folder & is not also a source folder
263 // for (int j = 0, m = sourceLocations.length; j < m; j++)
265 // (outputPath.equals(sourceLocations[j].sourceFolder.getFullPath()))
267 md.hasIndependentOutputFolder = true;
271 // combine the output folders with the binary folders & jars... place
272 // the output folders before other .class file folders & jars
273 // this.binaryLocations = new ClasspathLocation[outputFolders.size() +
274 // bLocations.size()];
276 // for (int i = 0, l = outputFolders.size(); i < l; i++)
277 // this.binaryLocations[index++] = (ClasspathLocation)
278 // outputFolders.get(i);
279 // for (int i = 0, l = bLocations.size(); i < l; i++)
280 // this.binaryLocations[index++] = (ClasspathLocation)
281 // bLocations.get(i);
284 public void cleanup() {
285 this.initialTypeNames = null;
286 this.additionalUnits = null;
287 for (int i = 0, l = sourceLocations.length; i < l; i++)
288 sourceLocations[i].cleanup();
289 // for (int i = 0, l = binaryLocations.length; i < l; i++)
290 // binaryLocations[i].cleanup();
293 private void createFolder(IContainer folder) throws CoreException {
294 if (!folder.exists()) {
295 createFolder(folder.getParent());
296 ((IFolder) folder).create(true, true, null);
300 private NameEnvironmentAnswer findClass(String qualifiedTypeName,
302 if (initialTypeNames != null) {
303 for (int i = 0, l = initialTypeNames.length; i < l; i++) {
304 if (qualifiedTypeName.equals(initialTypeNames[i])) {
305 if (isIncrementalBuild)
306 // catch the case that a type inside a source file has
307 // been renamed but other class files are looking for it
308 throw new AbortCompilation(true,
309 new AbortIncrementalBuildException(
311 return null; // looking for a file which we know was
312 // provided at the beginning of the
318 if (additionalUnits != null && sourceLocations.length > 0) {
319 // if an additional source file is waiting to be compiled, answer it
320 // BUT not if this is a secondary type search
321 // if we answer X.java & it no longer defines Y then the binary type
322 // looking for Y will think the class path is wrong
323 // let the recompile loop fix up dependents when the secondary type
324 // Y has been deleted from X.java
325 IPath qSourceFilePath = new Path(qualifiedTypeName + ".java"); //$NON-NLS-1$
326 int qSegmentCount = qSourceFilePath.segmentCount();
327 next: for (int i = 0, l = additionalUnits.length; i < l; i++) {
328 SourceFile additionalUnit = additionalUnits[i];
329 IPath fullPath = additionalUnit.resource.getFullPath();
330 int prefixCount = additionalUnit.sourceLocation.sourceFolder
331 .getFullPath().segmentCount();
332 if (qSegmentCount == fullPath.segmentCount() - prefixCount) {
333 for (int j = 0; j < qSegmentCount; j++)
334 if (!qSourceFilePath.segment(j).equals(
335 fullPath.segment(j + prefixCount)))
337 return new NameEnvironmentAnswer(additionalUnit);
342 // String qBinaryFileName = qualifiedTypeName + ".class"; //$NON-NLS-1$
343 // String binaryFileName = qBinaryFileName;
344 // String qPackageName = ""; //$NON-NLS-1$
345 // if (qualifiedTypeName.length() > typeName.length) {
346 // int typeNameStart = qBinaryFileName.length() - typeName.length - 6;
347 // // size of ".class"
348 // qPackageName = qBinaryFileName.substring(0, typeNameStart - 1);
349 // binaryFileName = qBinaryFileName.substring(typeNameStart);
352 // // NOTE: the output folders are added at the beginning of the
354 // for (int i = 0, l = binaryLocations.length; i < l; i++) {
355 // NameEnvironmentAnswer answer =
356 // binaryLocations[i].findClass(binaryFileName, qPackageName,
358 // if (answer != null) return answer;
363 public NameEnvironmentAnswer findType(char[][] compoundName) {
364 if (compoundName != null)
365 return findClass(new String(CharOperation.concatWith(compoundName,
366 '/')), compoundName[compoundName.length - 1]);
370 public NameEnvironmentAnswer findType(char[] typeName, char[][] packageName) {
371 if (typeName != null)
372 return findClass(new String(CharOperation.concatWith(packageName,
373 typeName, '/')), typeName);
377 public boolean isPackage(char[][] compoundName, char[] packageName) {
378 return isPackage(new String(CharOperation.concatWith(compoundName,
382 public boolean isPackage(String qualifiedPackageName) {
383 // NOTE: the output folders are added at the beginning of the
385 // for (int i = 0, l = binaryLocations.length; i < l; i++)
386 // if (binaryLocations[i].isPackage(qualifiedPackageName))
391 void setNames(String[] initialTypeNames, SourceFile[] additionalUnits) {
392 this.initialTypeNames = initialTypeNames;
393 this.additionalUnits = additionalUnits;
394 for (int i = 0, l = sourceLocations.length; i < l; i++)
395 sourceLocations[i].reset();
396 // for (int i = 0, l = binaryLocations.length; i < l; i++)
397 // binaryLocations[i].reset();