improved PHP parser
[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 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);
98         }
99         
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*/);
103         
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;
111   
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();
121 //                              } else {
122 //                                      outputFolder = root.getFolder(outputPath);
123 //                                      if (!outputFolder.exists())
124 //                                              createFolder(outputFolder);
125 //                              }
126                                 sLocations.add(
127                                         ClasspathLocation.forSourceFolder((IContainer) target, outputFolder, entry.fullExclusionPatternChars()));
128                                 continue nextEntry;
129   
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
134   
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};
157                                                                 } else {
158                                                                         int size = existingLocations.length;
159                                                                         System.arraycopy(existingLocations, 0, existingLocations = new ClasspathLocation[size + 1], 0, size);
160                                                                         existingLocations[size] = bLocation;
161                                                                 }
162                                                                 binaryLocationsPerProject.put(prereqProject, existingLocations);
163                                                         }
164                                                 }
165                                         }
166                                 }
167                                 continue nextEntry;
168   
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
179 //                                      }
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};
186 //                                              } else {
187 //                                                      int size = existingLocations.length;
188 //                                                      System.arraycopy(existingLocations, 0, existingLocations = new ClasspathLocation[size + 1], 0, size);
189 //                                                      existingLocations[size] = bLocation;
190 //                                              }
191 //                                              binaryLocationsPerProject.put(p, existingLocations);
192 //                                      }
193 //                              } else if (target instanceof File) {
194 //                                      if (!(ProjectPrefUtil.isArchiveFileName(path.lastSegment())))
195 //                                              continue nextEntry;
196 //                                      bLocations.add(ClasspathLocation.forLibrary(path.toString()));
197 //                              }
198 //                              continue nextEntry;
199                 }
200         }
201   
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);
207   
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;
215 //                                      continue next;
216 //                              }
217 //                      }
218                         outputFolders.add(md);
219   
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()))
223 //                                      continue next;
224                         md.hasIndependentOutputFolder = true;
225                 }
226         }
227
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()];
230 //      int index = 0;
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);
235   }
236
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();
244   }
245
246   private void createFolder(IContainer folder) throws CoreException {
247     if (!folder.exists()) {
248       createFolder(folder.getParent());
249       ((IFolder) folder).create(true, true, null);
250     }
251   }
252
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
261         }
262       }
263     }
264
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)))
278               continue next;
279           return new NameEnvironmentAnswer(additionalUnit);
280         }
281       }
282     }
283
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);
291     //  }
292     //
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;
297     //  }
298     return null;
299   }
300
301   public NameEnvironmentAnswer findType(char[][] compoundName) {
302     if (compoundName != null)
303       return findClass(new String(CharOperation.concatWith(compoundName, '/')), compoundName[compoundName.length - 1]);
304     return null;
305   }
306
307   public NameEnvironmentAnswer findType(char[] typeName, char[][] packageName) {
308     if (typeName != null)
309       return findClass(new String(CharOperation.concatWith(packageName, typeName, '/')), typeName);
310     return null;
311   }
312
313   public boolean isPackage(char[][] compoundName, char[] packageName) {
314     return isPackage(new String(CharOperation.concatWith(compoundName, packageName, '/')));
315   }
316
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))
321     //                  return true;
322     return false;
323   }
324
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();
332   }
333 }