3a97ad77b52db0db309d01ac61a371eaaa245e27
[phpeclipse.git] / net.sourceforge.phpeclipse / src / net / sourceforge / phpdt / internal / core / builder / NameEnvironment.java
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
7  * 
8  * Contributors:
9  *     IBM Corporation - initial API and implementation
10  *******************************************************************************/
11 package net.sourceforge.phpdt.internal.core.builder;
12
13 import java.util.ArrayList;
14
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;
26
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;
35
36 public class NameEnvironment implements INameEnvironment {
37
38         boolean isIncrementalBuild;
39
40         ClasspathMultiDirectory[] sourceLocations;
41
42         // ClasspathLocation[] binaryLocations;
43
44         String[] initialTypeNames; // assumed that each name is of the form
45                                                                 // "a/b/ClassName"
46
47         SourceFile[] additionalUnits;
48
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);
54                 setNames(null, null);
55         }
56
57         public NameEnvironment(IJavaProject javaProject) {
58                 this.isIncrementalBuild = false;
59                 try {
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];
65                 }
66                 setNames(null, null);
67         }
68
69         /*
70          * Some examples of resolved class path entries. Remember to search class
71          * path in the order that it was defined.
72          * 
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
81          * 
82          * 2. zip files:
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
85          * 
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
89          */
90         private void computeClasspathLocations(IWorkspaceRoot root,
91                         JavaProject javaProject, SimpleLookupTable binaryLocationsPerProject)
92                         throws CoreException {
93
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);
103                 }
104
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 */);
112
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);
119                         if (target == null)
120                                 continue nextEntry;
121
122                         switch (entry.getEntryKind()) {
123                         case IClasspathEntry.CPE_SOURCE:
124                                 if (!(target instanceof IContainer))
125                                         continue nextEntry;
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();
132                                 // } else {
133                                 // outputFolder = root.getFolder(outputPath);
134                                 // if (!outputFolder.exists())
135                                 // createFolder(outputFolder);
136                                 // }
137                                 sLocations.add(ClasspathLocation.forSourceFolder(
138                                                 (IContainer) target, outputFolder, entry
139                                                                 .fullExclusionPatternChars()));
140                                 continue nextEntry;
141
142                         case IClasspathEntry.CPE_PROJECT:
143                                 if (!(target instanceof IProject))
144                                         continue nextEntry;
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
149
150                                 JavaProject prereqJavaProject = (JavaProject) JavaCore
151                                                 .create(prereqProject);
152                                 IClasspathEntry[] prereqClasspathEntries = prereqJavaProject
153                                                 .getRawClasspath();
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
176                                                                                                                                                 // builder
177                                                                                                                                                 // mode
178                                                                 ClasspathLocation[] existingLocations = (ClasspathLocation[]) binaryLocationsPerProject
179                                                                                 .get(prereqProject);
180                                                                 if (existingLocations == null) {
181                                                                         existingLocations = new ClasspathLocation[] { bLocation };
182                                                                 } else {
183                                                                         int size = existingLocations.length;
184                                                                         System
185                                                                                         .arraycopy(
186                                                                                                         existingLocations,
187                                                                                                         0,
188                                                                                                         existingLocations = new ClasspathLocation[size + 1],
189                                                                                                         0, size);
190                                                                         existingLocations[size] = bLocation;
191                                                                 }
192                                                                 binaryLocationsPerProject.put(prereqProject,
193                                                                                 existingLocations);
194                                                         }
195                                                 }
196                                         }
197                                 }
198                                 continue nextEntry;
199
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
211                                 // }
212                                 // bLocations.add(bLocation);
213                                 // if (binaryLocationsPerProject != null) { // normal builder
214                                 // mode
215                                 // IProject p = resource.getProject(); // can be the project
216                                 // being built
217                                 // ClasspathLocation[] existingLocations = (ClasspathLocation[])
218                                 // binaryLocationsPerProject.get(p);
219                                 // if (existingLocations == null) {
220                                 // existingLocations = new ClasspathLocation[] {bLocation};
221                                 // } else {
222                                 // int size = existingLocations.length;
223                                 // System.arraycopy(existingLocations, 0, existingLocations =
224                                 // new ClasspathLocation[size + 1], 0, size);
225                                 // existingLocations[size] = bLocation;
226                                 // }
227                                 // binaryLocationsPerProject.put(p, existingLocations);
228                                 // }
229                                 // } else if (target instanceof File) {
230                                 // if (!(ProjectPrefUtil.isArchiveFileName(path.lastSegment())))
231                                 // continue nextEntry;
232                                 // bLocations.add(ClasspathLocation.forLibrary(path.toString()));
233                                 // }
234                                 // continue nextEntry;
235                         }
236                 }
237
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);
244
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
251                                 // if
252                                 // (outputPath.equals(sourceLocations[j].binaryFolder.getFullPath()))
253                                 // {
254                                 // md.hasIndependentOutputFolder =
255                                 // sourceLocations[j].hasIndependentOutputFolder;
256                                 // continue next;
257                                 // }
258                                 // }
259                                 outputFolders.add(md);
260
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++)
264                                 // if
265                                 // (outputPath.equals(sourceLocations[j].sourceFolder.getFullPath()))
266                                 // continue next;
267                                 md.hasIndependentOutputFolder = true;
268                         }
269                 }
270
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()];
275                 // int index = 0;
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);
282         }
283
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();
291         }
292
293 //      private void createFolder(IContainer folder) throws CoreException {
294 //              if (!folder.exists()) {
295 //                      createFolder(folder.getParent());
296 //                      ((IFolder) folder).create(true, true, null);
297 //              }
298 //      }
299
300         private NameEnvironmentAnswer findClass(String qualifiedTypeName,
301                         char[] typeName) {
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(
310                                                                                 qualifiedTypeName));
311                                         return null; // looking for a file which we know was
312                                                                         // provided at the beginning of the
313                                                                         // compilation
314                                 }
315                         }
316                 }
317
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)))
336                                                         continue next;
337                                         return new NameEnvironmentAnswer(additionalUnit);
338                                 }
339                         }
340                 }
341
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);
350                 // }
351                 //
352                 // // NOTE: the output folders are added at the beginning of the
353                 // binaryLocations
354                 // for (int i = 0, l = binaryLocations.length; i < l; i++) {
355                 // NameEnvironmentAnswer answer =
356                 // binaryLocations[i].findClass(binaryFileName, qPackageName,
357                 // qBinaryFileName);
358                 // if (answer != null) return answer;
359                 // }
360                 return null;
361         }
362
363         public NameEnvironmentAnswer findType(char[][] compoundName) {
364                 if (compoundName != null)
365                         return findClass(new String(CharOperation.concatWith(compoundName,
366                                         '/')), compoundName[compoundName.length - 1]);
367                 return null;
368         }
369
370         public NameEnvironmentAnswer findType(char[] typeName, char[][] packageName) {
371                 if (typeName != null)
372                         return findClass(new String(CharOperation.concatWith(packageName,
373                                         typeName, '/')), typeName);
374                 return null;
375         }
376
377         public boolean isPackage(char[][] compoundName, char[] packageName) {
378                 return isPackage(new String(CharOperation.concatWith(compoundName,
379                                 packageName, '/')));
380         }
381
382         public boolean isPackage(String qualifiedPackageName) {
383                 // NOTE: the output folders are added at the beginning of the
384                 // binaryLocations
385                 // for (int i = 0, l = binaryLocations.length; i < l; i++)
386                 // if (binaryLocations[i].isPackage(qualifiedPackageName))
387                 // return true;
388                 return false;
389         }
390
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();
398         }
399 }