001: /*******************************************************************************
002: * Copyright (c) 2000, 2007 IBM Corporation and others.
003: * All rights reserved. This program and the accompanying materials
004: * are made available under the terms of the Eclipse Public License v1.0
005: * which accompanies this distribution, and is available at
006: * http://www.eclipse.org/legal/epl-v10.html
007: *
008: * Contributors:
009: * IBM Corporation - initial API and implementation
010: *******************************************************************************/package org.eclipse.jdt.internal.core.builder;
011:
012: import org.eclipse.core.resources.*;
013: import org.eclipse.core.runtime.*;
014:
015: import org.eclipse.jdt.core.*;
016: import org.eclipse.jdt.core.compiler.CharOperation;
017: import org.eclipse.jdt.internal.compiler.env.*;
018: import org.eclipse.jdt.internal.compiler.problem.AbortCompilation;
019: import org.eclipse.jdt.internal.compiler.util.SimpleLookupTable;
020: import org.eclipse.jdt.internal.compiler.util.SimpleSet;
021: import org.eclipse.jdt.internal.compiler.util.SuffixConstants;
022: import org.eclipse.jdt.internal.core.*;
023:
024: import java.io.*;
025: import java.util.*;
026:
027: public class NameEnvironment implements INameEnvironment,
028: SuffixConstants {
029:
030: boolean isIncrementalBuild;
031: ClasspathMultiDirectory[] sourceLocations;
032: ClasspathLocation[] binaryLocations;
033: BuildNotifier notifier;
034:
035: SimpleSet initialTypeNames; // assumed that each name is of the form "a/b/ClassName"
036: SimpleLookupTable additionalUnits;
037:
038: NameEnvironment(IWorkspaceRoot root, JavaProject javaProject,
039: SimpleLookupTable binaryLocationsPerProject,
040: BuildNotifier notifier) throws CoreException {
041: this .isIncrementalBuild = false;
042: this .notifier = notifier;
043: computeClasspathLocations(root, javaProject,
044: binaryLocationsPerProject);
045: setNames(null, null);
046: }
047:
048: public NameEnvironment(IJavaProject javaProject) {
049: this .isIncrementalBuild = false;
050: try {
051: computeClasspathLocations(javaProject.getProject()
052: .getWorkspace().getRoot(),
053: (JavaProject) javaProject, null);
054: } catch (CoreException e) {
055: this .sourceLocations = new ClasspathMultiDirectory[0];
056: this .binaryLocations = new ClasspathLocation[0];
057: }
058: setNames(null, null);
059: }
060:
061: /* Some examples of resolved class path entries.
062: * Remember to search class path in the order that it was defined.
063: *
064: * 1a. typical project with no source folders:
065: * /Test[CPE_SOURCE][K_SOURCE] -> D:/eclipse.test/Test
066: * 1b. project with source folders:
067: * /Test/src1[CPE_SOURCE][K_SOURCE] -> D:/eclipse.test/Test/src1
068: * /Test/src2[CPE_SOURCE][K_SOURCE] -> D:/eclipse.test/Test/src2
069: * NOTE: These can be in any order & separated by prereq projects or libraries
070: * 1c. project external to workspace (only detectable using getLocation()):
071: * /Test/src[CPE_SOURCE][K_SOURCE] -> d:/eclipse.zzz/src
072: * Need to search source folder & output folder
073: *
074: * 2. zip files:
075: * D:/j9/lib/jclMax/classes.zip[CPE_LIBRARY][K_BINARY][sourcePath:d:/j9/lib/jclMax/source/source.zip]
076: * -> D:/j9/lib/jclMax/classes.zip
077: * ALWAYS want to take the library path as is
078: *
079: * 3a. prereq project (regardless of whether it has a source or output folder):
080: * /Test[CPE_PROJECT][K_SOURCE] -> D:/eclipse.test/Test
081: * ALWAYS want to append the output folder & ONLY search for .class files
082: */
083: private void computeClasspathLocations(IWorkspaceRoot root,
084: JavaProject javaProject,
085: SimpleLookupTable binaryLocationsPerProject)
086: throws CoreException {
087:
088: /* Update cycle marker */
089: IMarker cycleMarker = javaProject.getCycleMarker();
090: if (cycleMarker != null) {
091: int severity = JavaCore.ERROR.equals(javaProject.getOption(
092: JavaCore.CORE_CIRCULAR_CLASSPATH, true)) ? IMarker.SEVERITY_ERROR
093: : IMarker.SEVERITY_WARNING;
094: if (severity != cycleMarker.getAttribute(IMarker.SEVERITY,
095: severity))
096: cycleMarker.setAttribute(IMarker.SEVERITY, severity);
097: }
098:
099: IClasspathEntry[] classpathEntries = javaProject
100: .getExpandedClasspath();
101: ArrayList sLocations = new ArrayList(classpathEntries.length);
102: ArrayList bLocations = new ArrayList(classpathEntries.length);
103: nextEntry: for (int i = 0, l = classpathEntries.length; i < l; i++) {
104: ClasspathEntry entry = (ClasspathEntry) classpathEntries[i];
105: IPath path = entry.getPath();
106: Object target = JavaModel.getTarget(root, path, true);
107: if (target == null)
108: continue nextEntry;
109:
110: switch (entry.getEntryKind()) {
111: case IClasspathEntry.CPE_SOURCE:
112: if (!(target instanceof IContainer))
113: continue nextEntry;
114: IPath outputPath = entry.getOutputLocation() != null ? entry
115: .getOutputLocation()
116: : javaProject.getOutputLocation();
117: IContainer outputFolder;
118: if (outputPath.segmentCount() == 1) {
119: outputFolder = javaProject.getProject();
120: } else {
121: outputFolder = root.getFolder(outputPath);
122: if (!outputFolder.exists())
123: createOutputFolder(outputFolder);
124: }
125: sLocations.add(ClasspathLocation.forSourceFolder(
126: (IContainer) target, outputFolder, entry
127: .fullInclusionPatternChars(), entry
128: .fullExclusionPatternChars()));
129: continue nextEntry;
130:
131: case IClasspathEntry.CPE_PROJECT:
132: if (!(target instanceof IProject))
133: continue nextEntry;
134: IProject prereqProject = (IProject) target;
135: if (!JavaProject.hasJavaNature(prereqProject))
136: continue nextEntry; // if project doesn't have java nature or is not accessible
137:
138: JavaProject prereqJavaProject = (JavaProject) JavaCore
139: .create(prereqProject);
140: IClasspathEntry[] prereqClasspathEntries = prereqJavaProject
141: .getRawClasspath();
142: ArrayList seen = new ArrayList();
143: nextPrereqEntry: for (int j = 0, m = prereqClasspathEntries.length; j < m; j++) {
144: IClasspathEntry prereqEntry = prereqClasspathEntries[j];
145: if (prereqEntry.getEntryKind() == IClasspathEntry.CPE_SOURCE) {
146: Object prereqTarget = JavaModel.getTarget(root,
147: prereqEntry.getPath(), true);
148: if (!(prereqTarget instanceof IContainer))
149: continue nextPrereqEntry;
150: IPath prereqOutputPath = prereqEntry
151: .getOutputLocation() != null ? prereqEntry
152: .getOutputLocation()
153: : prereqJavaProject.getOutputLocation();
154: IContainer binaryFolder = prereqOutputPath
155: .segmentCount() == 1 ? (IContainer) prereqProject
156: : (IContainer) root
157: .getFolder(prereqOutputPath);
158: if (binaryFolder.exists()
159: && !seen.contains(binaryFolder)) {
160: seen.add(binaryFolder);
161: ClasspathLocation bLocation = ClasspathLocation
162: .forBinaryFolder(binaryFolder,
163: true, entry
164: .getAccessRuleSet());
165: bLocations.add(bLocation);
166: if (binaryLocationsPerProject != null) { // normal builder mode
167: ClasspathLocation[] existingLocations = (ClasspathLocation[]) binaryLocationsPerProject
168: .get(prereqProject);
169: if (existingLocations == null) {
170: existingLocations = new ClasspathLocation[] { bLocation };
171: } else {
172: int size = existingLocations.length;
173: System
174: .arraycopy(
175: existingLocations,
176: 0,
177: existingLocations = new ClasspathLocation[size + 1],
178: 0, size);
179: existingLocations[size] = bLocation;
180: }
181: binaryLocationsPerProject.put(
182: prereqProject,
183: existingLocations);
184: }
185: }
186: }
187: }
188: continue nextEntry;
189:
190: case IClasspathEntry.CPE_LIBRARY:
191: if (target instanceof IResource) {
192: IResource resource = (IResource) target;
193: ClasspathLocation bLocation = null;
194: if (resource instanceof IFile) {
195: if (!(org.eclipse.jdt.internal.compiler.util.Util
196: .isArchiveFileName(path.lastSegment())))
197: continue nextEntry;
198: AccessRuleSet accessRuleSet = (JavaCore.IGNORE
199: .equals(javaProject
200: .getOption(
201: JavaCore.COMPILER_PB_FORBIDDEN_REFERENCE,
202: true)) && JavaCore.IGNORE
203: .equals(javaProject
204: .getOption(
205: JavaCore.COMPILER_PB_DISCOURAGED_REFERENCE,
206: true))) ? null : entry
207: .getAccessRuleSet();
208: bLocation = ClasspathLocation.forLibrary(
209: (IFile) resource, accessRuleSet);
210: } else if (resource instanceof IContainer) {
211: AccessRuleSet accessRuleSet = (JavaCore.IGNORE
212: .equals(javaProject
213: .getOption(
214: JavaCore.COMPILER_PB_FORBIDDEN_REFERENCE,
215: true)) && JavaCore.IGNORE
216: .equals(javaProject
217: .getOption(
218: JavaCore.COMPILER_PB_DISCOURAGED_REFERENCE,
219: true))) ? null : entry
220: .getAccessRuleSet();
221: bLocation = ClasspathLocation.forBinaryFolder(
222: (IContainer) target, false,
223: accessRuleSet); // is library folder not output folder
224: }
225: bLocations.add(bLocation);
226: if (binaryLocationsPerProject != null) { // normal builder mode
227: IProject p = resource.getProject(); // can be the project being built
228: ClasspathLocation[] existingLocations = (ClasspathLocation[]) binaryLocationsPerProject
229: .get(p);
230: if (existingLocations == null) {
231: existingLocations = new ClasspathLocation[] { bLocation };
232: } else {
233: int size = existingLocations.length;
234: System
235: .arraycopy(
236: existingLocations,
237: 0,
238: existingLocations = new ClasspathLocation[size + 1],
239: 0, size);
240: existingLocations[size] = bLocation;
241: }
242: binaryLocationsPerProject.put(p,
243: existingLocations);
244: }
245: } else if (target instanceof File) {
246: if (!(org.eclipse.jdt.internal.compiler.util.Util
247: .isArchiveFileName(path.lastSegment())))
248: continue nextEntry;
249: AccessRuleSet accessRuleSet = (JavaCore.IGNORE
250: .equals(javaProject
251: .getOption(
252: JavaCore.COMPILER_PB_FORBIDDEN_REFERENCE,
253: true)) && JavaCore.IGNORE
254: .equals(javaProject
255: .getOption(
256: JavaCore.COMPILER_PB_DISCOURAGED_REFERENCE,
257: true))) ? null : entry
258: .getAccessRuleSet();
259: bLocations.add(ClasspathLocation.forLibrary(path
260: .toString(), accessRuleSet));
261: }
262: continue nextEntry;
263: }
264: }
265:
266: // now split the classpath locations... place the output folders ahead of the other .class file folders & jars
267: ArrayList outputFolders = new ArrayList(1);
268: this .sourceLocations = new ClasspathMultiDirectory[sLocations
269: .size()];
270: if (!sLocations.isEmpty()) {
271: sLocations.toArray(this .sourceLocations);
272:
273: // collect the output folders, skipping duplicates
274: next: for (int i = 0, l = sourceLocations.length; i < l; i++) {
275: ClasspathMultiDirectory md = sourceLocations[i];
276: IPath outputPath = md.binaryFolder.getFullPath();
277: for (int j = 0; j < i; j++) { // compare against previously walked source folders
278: if (outputPath
279: .equals(sourceLocations[j].binaryFolder
280: .getFullPath())) {
281: md.hasIndependentOutputFolder = sourceLocations[j].hasIndependentOutputFolder;
282: continue next;
283: }
284: }
285: outputFolders.add(md);
286:
287: // also tag each source folder whose output folder is an independent folder & is not also a source folder
288: for (int j = 0, m = sourceLocations.length; j < m; j++)
289: if (outputPath
290: .equals(sourceLocations[j].sourceFolder
291: .getFullPath()))
292: continue next;
293: md.hasIndependentOutputFolder = true;
294: }
295: }
296:
297: // combine the output folders with the binary folders & jars... place the output folders before other .class file folders & jars
298: this .binaryLocations = new ClasspathLocation[outputFolders
299: .size()
300: + bLocations.size()];
301: int index = 0;
302: for (int i = 0, l = outputFolders.size(); i < l; i++)
303: this .binaryLocations[index++] = (ClasspathLocation) outputFolders
304: .get(i);
305: for (int i = 0, l = bLocations.size(); i < l; i++)
306: this .binaryLocations[index++] = (ClasspathLocation) bLocations
307: .get(i);
308: }
309:
310: public void cleanup() {
311: this .initialTypeNames = null;
312: this .additionalUnits = null;
313: for (int i = 0, l = sourceLocations.length; i < l; i++)
314: sourceLocations[i].cleanup();
315: for (int i = 0, l = binaryLocations.length; i < l; i++)
316: binaryLocations[i].cleanup();
317: }
318:
319: private void createOutputFolder(IContainer outputFolder)
320: throws CoreException {
321: createParentFolder(outputFolder.getParent());
322: ((IFolder) outputFolder).create(IResource.FORCE
323: | IResource.DERIVED, true, null);
324: }
325:
326: private void createParentFolder(IContainer parent)
327: throws CoreException {
328: if (!parent.exists()) {
329: createParentFolder(parent.getParent());
330: ((IFolder) parent).create(true, true, null);
331: }
332: }
333:
334: private NameEnvironmentAnswer findClass(String qualifiedTypeName,
335: char[] typeName) {
336: if (this .notifier != null)
337: this .notifier.checkCancelWithinCompiler();
338:
339: if (this .initialTypeNames != null
340: && this .initialTypeNames.includes(qualifiedTypeName)) {
341: if (isIncrementalBuild)
342: // catch the case that a type inside a source file has been renamed but other class files are looking for it
343: throw new AbortCompilation(true,
344: new AbortIncrementalBuildException(
345: qualifiedTypeName));
346: return null; // looking for a file which we know was provided at the beginning of the compilation
347: }
348:
349: if (this .additionalUnits != null
350: && this .sourceLocations.length > 0) {
351: // if an additional source file is waiting to be compiled, answer it BUT not if this is a secondary type search
352: // if we answer X.java & it no longer defines Y then the binary type looking for Y will think the class path is wrong
353: // let the recompile loop fix up dependents when the secondary type Y has been deleted from X.java
354: SourceFile unit = (SourceFile) this .additionalUnits
355: .get(qualifiedTypeName); // doesn't have file extension
356: if (unit != null)
357: return new NameEnvironmentAnswer(unit, null /*no access restriction*/);
358: }
359:
360: String qBinaryFileName = qualifiedTypeName
361: + SUFFIX_STRING_class;
362: String binaryFileName = qBinaryFileName;
363: String qPackageName = ""; //$NON-NLS-1$
364: if (qualifiedTypeName.length() > typeName.length) {
365: int typeNameStart = qBinaryFileName.length()
366: - typeName.length - 6; // size of ".class"
367: qPackageName = qBinaryFileName.substring(0,
368: typeNameStart - 1);
369: binaryFileName = qBinaryFileName.substring(typeNameStart);
370: }
371:
372: // NOTE: the output folders are added at the beginning of the binaryLocations
373: NameEnvironmentAnswer suggestedAnswer = null;
374: for (int i = 0, l = binaryLocations.length; i < l; i++) {
375: NameEnvironmentAnswer answer = binaryLocations[i]
376: .findClass(binaryFileName, qPackageName,
377: qBinaryFileName);
378: if (answer != null) {
379: if (!answer.ignoreIfBetter()) {
380: if (answer.isBetter(suggestedAnswer))
381: return answer;
382: } else if (answer.isBetter(suggestedAnswer))
383: // remember suggestion and keep looking
384: suggestedAnswer = answer;
385: }
386: }
387: if (suggestedAnswer != null)
388: // no better answer was found
389: return suggestedAnswer;
390: return null;
391: }
392:
393: public NameEnvironmentAnswer findType(char[][] compoundName) {
394: if (compoundName != null)
395: return findClass(new String(CharOperation.concatWith(
396: compoundName, '/')),
397: compoundName[compoundName.length - 1]);
398: return null;
399: }
400:
401: public NameEnvironmentAnswer findType(char[] typeName,
402: char[][] packageName) {
403: if (typeName != null)
404: return findClass(new String(CharOperation.concatWith(
405: packageName, typeName, '/')), typeName);
406: return null;
407: }
408:
409: public boolean isPackage(char[][] compoundName, char[] packageName) {
410: return isPackage(new String(CharOperation.concatWith(
411: compoundName, packageName, '/')));
412: }
413:
414: public boolean isPackage(String qualifiedPackageName) {
415: // NOTE: the output folders are added at the beginning of the binaryLocations
416: for (int i = 0, l = binaryLocations.length; i < l; i++)
417: if (binaryLocations[i].isPackage(qualifiedPackageName))
418: return true;
419: return false;
420: }
421:
422: void setNames(String[] typeNames, SourceFile[] additionalFiles) {
423: // convert the initial typeNames to a set
424: if (typeNames == null) {
425: this .initialTypeNames = null;
426: } else {
427: this .initialTypeNames = new SimpleSet(typeNames.length);
428: for (int i = 0, l = typeNames.length; i < l; i++)
429: this .initialTypeNames.add(typeNames[i]);
430: }
431: // map the additional source files by qualified type name
432: if (additionalFiles == null) {
433: this .additionalUnits = null;
434: } else {
435: this .additionalUnits = new SimpleLookupTable(
436: additionalFiles.length);
437: for (int i = 0, l = additionalFiles.length; i < l; i++) {
438: SourceFile additionalUnit = additionalFiles[i];
439: if (additionalUnit != null)
440: this .additionalUnits.put(
441: additionalUnit.initialTypeName,
442: additionalFiles[i]);
443: }
444: }
445:
446: for (int i = 0, l = sourceLocations.length; i < l; i++)
447: sourceLocations[i].reset();
448: for (int i = 0, l = binaryLocations.length; i < l; i++)
449: binaryLocations[i].reset();
450: }
451: }
|