Parser detects wrong include files now
[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   ClasspathMultiDirectory[] sourceLocations;
40   //ClasspathLocation[] binaryLocations;
41
42   String[] initialTypeNames; // assumed that each name is of the form "a/b/ClassName"
43   SourceFile[] additionalUnits;
44
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);
49     setNames(null, null);
50   }
51
52   public NameEnvironment(IJavaProject javaProject) {
53     this.isIncrementalBuild = false;
54         try {
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];
59         }
60     setNames(null, null);
61   }
62
63   /* Some examples of resolved class path entries.
64   * Remember to search class path in the order that it was defined.
65   *
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
75   *
76   * 2. zip files:
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
80   *
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
84   */
85   private void computeClasspathLocations(
86         IWorkspaceRoot root,
87         JavaProject javaProject,
88         SimpleLookupTable binaryLocationsPerProject) throws CoreException {
89   
90         /* Update incomplete classpath marker */
91         IClasspathEntry[] classpathEntries = javaProject.getExpandedClasspath(true, true);
92   
93         /* Update cycle marker */
94         IMarker cycleMarker = javaProject.getCycleMarker();
95         if (cycleMarker != null) {
96                 int severity = JavaCore.ERROR.equals(javaProject.getOption(JavaCore.CORE_CIRCULAR_CLASSPATH, true))
97                         ? IMarker.SEVERITY_ERROR
98                         : IMarker.SEVERITY_WARNING;
99                 if (severity != ((Integer) cycleMarker.getAttribute(IMarker.SEVERITY)).intValue())
100                         cycleMarker.setAttribute(IMarker.SEVERITY, severity);
101         }
102   
103         ArrayList sLocations = new ArrayList(classpathEntries.length);
104         ArrayList bLocations = new ArrayList(classpathEntries.length);
105         nextEntry : for (int i = 0, l = classpathEntries.length; i < l; i++) {
106                 ClasspathEntry entry = (ClasspathEntry) classpathEntries[i];
107                 IPath path = entry.getPath();
108                 Object target = JavaModel.getTarget(root, path, true);
109                 if (target == null) continue nextEntry;
110   
111                 switch(entry.getEntryKind()) {
112                         case IClasspathEntry.CPE_SOURCE :
113                                 if (!(target instanceof IContainer)) continue nextEntry;
114 //                              IPath outputPath = entry.getOutputLocation() != null 
115 //                                      ? entry.getOutputLocation() 
116 //                                      : javaProject.getOutputLocation();
117                                 IContainer outputFolder = null;
118 //                              if (outputPath.segmentCount() == 1) {
119 //                                      outputFolder = javaProject.getProject();
120 //                              } else {
121 //                                      outputFolder = root.getFolder(outputPath);
122 //                                      if (!outputFolder.exists())
123 //                                              createFolder(outputFolder);
124 //                              }
125                                 sLocations.add(
126                                         ClasspathLocation.forSourceFolder((IContainer) target, outputFolder, entry.fullExclusionPatternChars()));
127                                 continue nextEntry;
128   
129                         case IClasspathEntry.CPE_PROJECT :
130                                 if (!(target instanceof IProject)) continue nextEntry;
131                                 IProject prereqProject = (IProject) target;
132                                 if (!JavaProject.hasJavaNature(prereqProject)) continue nextEntry; // if project doesn't have java nature or is not accessible
133   
134                                 JavaProject prereqJavaProject = (JavaProject) JavaCore.create(prereqProject);
135                                 IClasspathEntry[] prereqClasspathEntries = prereqJavaProject.getRawClasspath();
136                                 ArrayList seen = new ArrayList();
137                                 nextPrereqEntry: for (int j = 0, m = prereqClasspathEntries.length; j < m; j++) {
138                                         IClasspathEntry prereqEntry = (IClasspathEntry) prereqClasspathEntries[j];
139                                         if (prereqEntry.getEntryKind() == IClasspathEntry.CPE_SOURCE) {
140                                                 Object prereqTarget = JavaModel.getTarget(root, prereqEntry.getPath(), true);
141                                                 if (!(prereqTarget instanceof IContainer)) continue nextPrereqEntry;
142                                                 IPath prereqOutputPath = prereqEntry.getOutputLocation() != null 
143                                                         ? prereqEntry.getOutputLocation() 
144                                                         : prereqJavaProject.getOutputLocation();
145                                                 IContainer binaryFolder = prereqOutputPath.segmentCount() == 1
146                                                         ? (IContainer) prereqProject
147                                                         : (IContainer) root.getFolder(prereqOutputPath);
148                                                 if (binaryFolder.exists() && !seen.contains(binaryFolder)) {
149                                                         seen.add(binaryFolder);
150                                                         ClasspathLocation bLocation = ClasspathLocation.forBinaryFolder(binaryFolder, true);
151                                                         bLocations.add(bLocation);
152                                                         if (binaryLocationsPerProject != null) { // normal builder mode
153                                                                 ClasspathLocation[] existingLocations = (ClasspathLocation[]) binaryLocationsPerProject.get(prereqProject);
154                                                                 if (existingLocations == null) {
155                                                                         existingLocations = new ClasspathLocation[] {bLocation};
156                                                                 } else {
157                                                                         int size = existingLocations.length;
158                                                                         System.arraycopy(existingLocations, 0, existingLocations = new ClasspathLocation[size + 1], 0, size);
159                                                                         existingLocations[size] = bLocation;
160                                                                 }
161                                                                 binaryLocationsPerProject.put(prereqProject, existingLocations);
162                                                         }
163                                                 }
164                                         }
165                                 }
166                                 continue nextEntry;
167   
168 //                      case IClasspathEntry.CPE_LIBRARY :
169 //                              if (target instanceof IResource) {
170 //                                      IResource resource = (IResource) target;
171 //                                      ClasspathLocation bLocation = null;
172 //                                      if (resource instanceof IFile) {
173 //                                              if (!(ProjectPrefUtil.isArchiveFileName(path.lastSegment())))
174 //                                                      continue nextEntry;
175 //                                              bLocation = ClasspathLocation.forLibrary((IFile) resource);
176 //                                      } else if (resource instanceof IContainer) {
177 //                                              bLocation = ClasspathLocation.forBinaryFolder((IContainer) target, false); // is library folder not output folder
178 //                                      }
179 //                                      bLocations.add(bLocation);
180 //                                      if (binaryLocationsPerProject != null) { // normal builder mode
181 //                                              IProject p = resource.getProject(); // can be the project being built
182 //                                              ClasspathLocation[] existingLocations = (ClasspathLocation[]) binaryLocationsPerProject.get(p);
183 //                                              if (existingLocations == null) {
184 //                                                      existingLocations = new ClasspathLocation[] {bLocation};
185 //                                              } else {
186 //                                                      int size = existingLocations.length;
187 //                                                      System.arraycopy(existingLocations, 0, existingLocations = new ClasspathLocation[size + 1], 0, size);
188 //                                                      existingLocations[size] = bLocation;
189 //                                              }
190 //                                              binaryLocationsPerProject.put(p, existingLocations);
191 //                                      }
192 //                              } else if (target instanceof File) {
193 //                                      if (!(ProjectPrefUtil.isArchiveFileName(path.lastSegment())))
194 //                                              continue nextEntry;
195 //                                      bLocations.add(ClasspathLocation.forLibrary(path.toString()));
196 //                              }
197 //                              continue nextEntry;
198                 }
199         }
200   
201         // now split the classpath locations... place the output folders ahead of the other .class file folders & jars
202         ArrayList outputFolders = new ArrayList(1);
203         this.sourceLocations = new ClasspathMultiDirectory[sLocations.size()];
204         if (!sLocations.isEmpty()) {
205                 sLocations.toArray(this.sourceLocations);
206   
207                 // collect the output folders, skipping duplicates
208                 next : for (int i = 0, l = sourceLocations.length; i < l; i++) {
209                         ClasspathMultiDirectory md = sourceLocations[i];
210 //                      IPath outputPath = md.binaryFolder.getFullPath();
211 //                      for (int j = 0; j < i; j++) { // compare against previously walked source folders
212 //                              if (outputPath.equals(sourceLocations[j].binaryFolder.getFullPath())) {
213 //                                      md.hasIndependentOutputFolder = sourceLocations[j].hasIndependentOutputFolder;
214 //                                      continue next;
215 //                              }
216 //                      }
217                         outputFolders.add(md);
218   
219                         // also tag each source folder whose output folder is an independent folder & is not also a source folder
220 //                      for (int j = 0, m = sourceLocations.length; j < m; j++)
221 //                              if (outputPath.equals(sourceLocations[j].sourceFolder.getFullPath()))
222 //                                      continue next;
223                         md.hasIndependentOutputFolder = true;
224                 }
225         }
226
227 //   combine the output folders with the binary folders & jars... place the output folders before other .class file folders & jars
228 //      this.binaryLocations = new ClasspathLocation[outputFolders.size() + bLocations.size()];
229 //      int index = 0;
230 //      for (int i = 0, l = outputFolders.size(); i < l; i++)
231 //              this.binaryLocations[index++] = (ClasspathLocation) outputFolders.get(i);
232 //      for (int i = 0, l = bLocations.size(); i < l; i++)
233 //              this.binaryLocations[index++] = (ClasspathLocation) bLocations.get(i);
234   }
235
236   public void cleanup() {
237     this.initialTypeNames = null;
238     this.additionalUnits = null;
239     for (int i = 0, l = sourceLocations.length; i < l; i++)
240       sourceLocations[i].cleanup();
241     //  for (int i = 0, l = binaryLocations.length; i < l; i++)
242     //          binaryLocations[i].cleanup();
243   }
244
245   private void createFolder(IContainer folder) throws CoreException {
246     if (!folder.exists()) {
247       createFolder(folder.getParent());
248       ((IFolder) folder).create(true, true, null);
249     }
250   }
251
252   private NameEnvironmentAnswer findClass(String qualifiedTypeName, char[] typeName) {
253     if (initialTypeNames != null) {
254       for (int i = 0, l = initialTypeNames.length; i < l; i++) {
255         if (qualifiedTypeName.equals(initialTypeNames[i])) {
256           if (isIncrementalBuild)
257             // catch the case that a type inside a source file has been renamed but other class files are looking for it
258             throw new AbortCompilation(true, new AbortIncrementalBuildException(qualifiedTypeName));
259           return null; // looking for a file which we know was provided at the beginning of the compilation
260         }
261       }
262     }
263
264     if (additionalUnits != null && sourceLocations.length > 0) {
265       // if an additional source file is waiting to be compiled, answer it BUT not if this is a secondary type search
266       // if we answer X.java & it no longer defines Y then the binary type looking for Y will think the class path is wrong
267       // let the recompile loop fix up dependents when the secondary type Y has been deleted from X.java
268       IPath qSourceFilePath = new Path(qualifiedTypeName + ".java"); //$NON-NLS-1$
269       int qSegmentCount = qSourceFilePath.segmentCount();
270       next : for (int i = 0, l = additionalUnits.length; i < l; i++) {
271         SourceFile additionalUnit = additionalUnits[i];
272         IPath fullPath = additionalUnit.resource.getFullPath();
273         int prefixCount = additionalUnit.sourceLocation.sourceFolder.getFullPath().segmentCount();
274         if (qSegmentCount == fullPath.segmentCount() - prefixCount) {
275           for (int j = 0; j < qSegmentCount; j++)
276             if (!qSourceFilePath.segment(j).equals(fullPath.segment(j + prefixCount)))
277               continue next;
278           return new NameEnvironmentAnswer(additionalUnit);
279         }
280       }
281     }
282
283     //  String qBinaryFileName = qualifiedTypeName + ".class"; //$NON-NLS-1$
284     //  String binaryFileName = qBinaryFileName;
285     //  String qPackageName =  ""; //$NON-NLS-1$
286     //  if (qualifiedTypeName.length() > typeName.length) {
287     //          int typeNameStart = qBinaryFileName.length() - typeName.length - 6; // size of ".class"
288     //          qPackageName =  qBinaryFileName.substring(0, typeNameStart - 1);
289     //          binaryFileName = qBinaryFileName.substring(typeNameStart);
290     //  }
291     //
292     //  // NOTE: the output folders are added at the beginning of the binaryLocations
293     //  for (int i = 0, l = binaryLocations.length; i < l; i++) {
294     //          NameEnvironmentAnswer answer = binaryLocations[i].findClass(binaryFileName, qPackageName, qBinaryFileName);
295     //          if (answer != null) return answer;
296     //  }
297     return null;
298   }
299
300   public NameEnvironmentAnswer findType(char[][] compoundName) {
301     if (compoundName != null)
302       return findClass(new String(CharOperation.concatWith(compoundName, '/')), compoundName[compoundName.length - 1]);
303     return null;
304   }
305
306   public NameEnvironmentAnswer findType(char[] typeName, char[][] packageName) {
307     if (typeName != null)
308       return findClass(new String(CharOperation.concatWith(packageName, typeName, '/')), typeName);
309     return null;
310   }
311
312   public boolean isPackage(char[][] compoundName, char[] packageName) {
313     return isPackage(new String(CharOperation.concatWith(compoundName, packageName, '/')));
314   }
315
316   public boolean isPackage(String qualifiedPackageName) {
317     // NOTE: the output folders are added at the beginning of the binaryLocations
318     //  for (int i = 0, l = binaryLocations.length; i < l; i++)
319     //          if (binaryLocations[i].isPackage(qualifiedPackageName))
320     //                  return true;
321     return false;
322   }
323
324   void setNames(String[] initialTypeNames, SourceFile[] additionalUnits) {
325     this.initialTypeNames = initialTypeNames;
326     this.additionalUnits = additionalUnits;
327     for (int i = 0, l = sourceLocations.length; i < l; i++)
328       sourceLocations[i].reset();
329     //  for (int i = 0, l = binaryLocations.length; i < l; i++)
330     //          binaryLocations[i].reset();
331   }
332 }