new version with WorkingCopy Management
[phpeclipse.git] / net.sourceforge.phpeclipse / src / net / sourceforge / phpdt / internal / core / builder / NameEnvironment.java
diff --git a/net.sourceforge.phpeclipse/src/net/sourceforge/phpdt/internal/core/builder/NameEnvironment.java b/net.sourceforge.phpeclipse/src/net/sourceforge/phpdt/internal/core/builder/NameEnvironment.java
new file mode 100644 (file)
index 0000000..9265c4c
--- /dev/null
@@ -0,0 +1,332 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2003 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials 
+ * are made available under the terms of the Common Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/cpl-v10.html
+ * 
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package net.sourceforge.phpdt.internal.core.builder;
+
+import java.util.ArrayList;
+
+import net.sourceforge.phpdt.core.IClasspathEntry;
+import net.sourceforge.phpdt.core.IJavaProject;
+import net.sourceforge.phpdt.core.compiler.CharOperation;
+import net.sourceforge.phpdt.internal.compiler.env.INameEnvironment;
+import net.sourceforge.phpdt.internal.compiler.env.NameEnvironmentAnswer;
+import net.sourceforge.phpdt.internal.compiler.problem.AbortCompilation;
+import net.sourceforge.phpdt.internal.core.ClasspathEntry;
+import net.sourceforge.phpdt.internal.core.JavaModel;
+import net.sourceforge.phpdt.internal.core.JavaProject;
+import net.sourceforge.phpdt.internal.core.util.SimpleLookupTable;
+import net.sourceforge.phpeclipse.PHPCore;
+
+import org.eclipse.core.resources.IContainer;
+import org.eclipse.core.resources.IFolder;
+import org.eclipse.core.resources.IMarker;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.IWorkspaceRoot;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.Path;
+
+public class NameEnvironment implements INameEnvironment {
+
+  boolean isIncrementalBuild;
+  ClasspathMultiDirectory[] sourceLocations;
+  //ClasspathLocation[] binaryLocations;
+
+  String[] initialTypeNames; // assumed that each name is of the form "a/b/ClassName"
+  SourceFile[] additionalUnits;
+
+  NameEnvironment(IWorkspaceRoot root, JavaProject javaProject, SimpleLookupTable binaryLocationsPerProject) throws CoreException {
+    this.isIncrementalBuild = false;
+//    this.sourceLocations = new ClasspathMultiDirectory[0];
+    computeClasspathLocations(root, javaProject, binaryLocationsPerProject);
+    setNames(null, null);
+  }
+
+  public NameEnvironment(IJavaProject javaProject) {
+    this.isIncrementalBuild = false;
+       try {
+               computeClasspathLocations(javaProject.getProject().getWorkspace().getRoot(), (JavaProject) javaProject, null);
+       } catch(CoreException e) {
+//    this.sourceLocations = new ClasspathMultiDirectory[0];
+    //         this.binaryLocations = new ClasspathLocation[0];
+       }
+    setNames(null, null);
+  }
+
+  /* Some examples of resolved class path entries.
+  * Remember to search class path in the order that it was defined.
+  *
+  * 1a. typical project with no source folders:
+  *   /Test[CPE_SOURCE][K_SOURCE] -> D:/eclipse.test/Test
+  * 1b. project with source folders:
+  *   /Test/src1[CPE_SOURCE][K_SOURCE] -> D:/eclipse.test/Test/src1
+  *   /Test/src2[CPE_SOURCE][K_SOURCE] -> D:/eclipse.test/Test/src2
+  *  NOTE: These can be in any order & separated by prereq projects or libraries
+  * 1c. project external to workspace (only detectable using getLocation()):
+  *   /Test/src[CPE_SOURCE][K_SOURCE] -> d:/eclipse.zzz/src
+  *  Need to search source folder & output folder
+  *
+  * 2. zip files:
+  *   D:/j9/lib/jclMax/classes.zip[CPE_LIBRARY][K_BINARY][sourcePath:d:/j9/lib/jclMax/source/source.zip]
+  *      -> D:/j9/lib/jclMax/classes.zip
+  *  ALWAYS want to take the library path as is
+  *
+  * 3a. prereq project (regardless of whether it has a source or output folder):
+  *   /Test[CPE_PROJECT][K_SOURCE] -> D:/eclipse.test/Test
+  *  ALWAYS want to append the output folder & ONLY search for .class files
+  */
+  private void computeClasspathLocations(
+       IWorkspaceRoot root,
+       JavaProject javaProject,
+       SimpleLookupTable binaryLocationsPerProject) throws CoreException {
+  
+       /* Update incomplete classpath marker */
+       IClasspathEntry[] classpathEntries = javaProject.getExpandedClasspath(true, true);
+  
+       /* Update cycle marker */
+       IMarker cycleMarker = javaProject.getCycleMarker();
+       if (cycleMarker != null) {
+               int severity = PHPCore.ERROR.equals(javaProject.getOption(PHPCore.CORE_CIRCULAR_CLASSPATH, true))
+                       ? IMarker.SEVERITY_ERROR
+                       : IMarker.SEVERITY_WARNING;
+               if (severity != ((Integer) cycleMarker.getAttribute(IMarker.SEVERITY)).intValue())
+                       cycleMarker.setAttribute(IMarker.SEVERITY, severity);
+       }
+  
+       ArrayList sLocations = new ArrayList(classpathEntries.length);
+       ArrayList bLocations = new ArrayList(classpathEntries.length);
+       nextEntry : for (int i = 0, l = classpathEntries.length; i < l; i++) {
+               ClasspathEntry entry = (ClasspathEntry) classpathEntries[i];
+               IPath path = entry.getPath();
+               Object target = JavaModel.getTarget(root, path, true);
+               if (target == null) continue nextEntry;
+  
+               switch(entry.getEntryKind()) {
+                       case IClasspathEntry.CPE_SOURCE :
+                               if (!(target instanceof IContainer)) continue nextEntry;
+                               IPath outputPath = entry.getOutputLocation() != null 
+                                       ? entry.getOutputLocation() 
+                                       : javaProject.getOutputLocation();
+                               IContainer outputFolder;
+                               if (outputPath.segmentCount() == 1) {
+                                       outputFolder = javaProject.getProject();
+                               } else {
+                                       outputFolder = root.getFolder(outputPath);
+                                       if (!outputFolder.exists())
+                                               createFolder(outputFolder);
+                               }
+                               sLocations.add(
+                                       ClasspathLocation.forSourceFolder((IContainer) target, outputFolder, entry.fullExclusionPatternChars()));
+                               continue nextEntry;
+  
+                       case IClasspathEntry.CPE_PROJECT :
+                               if (!(target instanceof IProject)) continue nextEntry;
+                               IProject prereqProject = (IProject) target;
+                               if (!JavaProject.hasJavaNature(prereqProject)) continue nextEntry; // if project doesn't have java nature or is not accessible
+  
+                               JavaProject prereqJavaProject = (JavaProject) PHPCore.create(prereqProject);
+                               IClasspathEntry[] prereqClasspathEntries = prereqJavaProject.getRawClasspath();
+                               ArrayList seen = new ArrayList();
+                               nextPrereqEntry: for (int j = 0, m = prereqClasspathEntries.length; j < m; j++) {
+                                       IClasspathEntry prereqEntry = (IClasspathEntry) prereqClasspathEntries[j];
+                                       if (prereqEntry.getEntryKind() == IClasspathEntry.CPE_SOURCE) {
+                                               Object prereqTarget = JavaModel.getTarget(root, prereqEntry.getPath(), true);
+                                               if (!(prereqTarget instanceof IContainer)) continue nextPrereqEntry;
+                                               IPath prereqOutputPath = prereqEntry.getOutputLocation() != null 
+                                                       ? prereqEntry.getOutputLocation() 
+                                                       : prereqJavaProject.getOutputLocation();
+                                               IContainer binaryFolder = prereqOutputPath.segmentCount() == 1
+                                                       ? (IContainer) prereqProject
+                                                       : (IContainer) root.getFolder(prereqOutputPath);
+                                               if (binaryFolder.exists() && !seen.contains(binaryFolder)) {
+                                                       seen.add(binaryFolder);
+                                                       ClasspathLocation bLocation = ClasspathLocation.forBinaryFolder(binaryFolder, true);
+                                                       bLocations.add(bLocation);
+                                                       if (binaryLocationsPerProject != null) { // normal builder mode
+                                                               ClasspathLocation[] existingLocations = (ClasspathLocation[]) binaryLocationsPerProject.get(prereqProject);
+                                                               if (existingLocations == null) {
+                                                                       existingLocations = new ClasspathLocation[] {bLocation};
+                                                               } else {
+                                                                       int size = existingLocations.length;
+                                                                       System.arraycopy(existingLocations, 0, existingLocations = new ClasspathLocation[size + 1], 0, size);
+                                                                       existingLocations[size] = bLocation;
+                                                               }
+                                                               binaryLocationsPerProject.put(prereqProject, existingLocations);
+                                                       }
+                                               }
+                                       }
+                               }
+                               continue nextEntry;
+  
+//                     case IClasspathEntry.CPE_LIBRARY :
+//                             if (target instanceof IResource) {
+//                                     IResource resource = (IResource) target;
+//                                     ClasspathLocation bLocation = null;
+//                                     if (resource instanceof IFile) {
+//                                             if (!(Util.isArchiveFileName(path.lastSegment())))
+//                                                     continue nextEntry;
+//                                             bLocation = ClasspathLocation.forLibrary((IFile) resource);
+//                                     } else if (resource instanceof IContainer) {
+//                                             bLocation = ClasspathLocation.forBinaryFolder((IContainer) target, false); // is library folder not output folder
+//                                     }
+//                                     bLocations.add(bLocation);
+//                                     if (binaryLocationsPerProject != null) { // normal builder mode
+//                                             IProject p = resource.getProject(); // can be the project being built
+//                                             ClasspathLocation[] existingLocations = (ClasspathLocation[]) binaryLocationsPerProject.get(p);
+//                                             if (existingLocations == null) {
+//                                                     existingLocations = new ClasspathLocation[] {bLocation};
+//                                             } else {
+//                                                     int size = existingLocations.length;
+//                                                     System.arraycopy(existingLocations, 0, existingLocations = new ClasspathLocation[size + 1], 0, size);
+//                                                     existingLocations[size] = bLocation;
+//                                             }
+//                                             binaryLocationsPerProject.put(p, existingLocations);
+//                                     }
+//                             } else if (target instanceof File) {
+//                                     if (!(Util.isArchiveFileName(path.lastSegment())))
+//                                             continue nextEntry;
+//                                     bLocations.add(ClasspathLocation.forLibrary(path.toString()));
+//                             }
+//                             continue nextEntry;
+               }
+       }
+  
+       // now split the classpath locations... place the output folders ahead of the other .class file folders & jars
+       ArrayList outputFolders = new ArrayList(1);
+       this.sourceLocations = new ClasspathMultiDirectory[sLocations.size()];
+       if (!sLocations.isEmpty()) {
+               sLocations.toArray(this.sourceLocations);
+  
+               // collect the output folders, skipping duplicates
+               next : for (int i = 0, l = sourceLocations.length; i < l; i++) {
+                       ClasspathMultiDirectory md = sourceLocations[i];
+                       IPath outputPath = md.binaryFolder.getFullPath();
+                       for (int j = 0; j < i; j++) { // compare against previously walked source folders
+                               if (outputPath.equals(sourceLocations[j].binaryFolder.getFullPath())) {
+                                       md.hasIndependentOutputFolder = sourceLocations[j].hasIndependentOutputFolder;
+                                       continue next;
+                               }
+                       }
+                       outputFolders.add(md);
+  
+                       // also tag each source folder whose output folder is an independent folder & is not also a source folder
+                       for (int j = 0, m = sourceLocations.length; j < m; j++)
+                               if (outputPath.equals(sourceLocations[j].sourceFolder.getFullPath()))
+                                       continue next;
+                       md.hasIndependentOutputFolder = true;
+               }
+       }
+
+//   combine the output folders with the binary folders & jars... place the output folders before other .class file folders & jars
+//     this.binaryLocations = new ClasspathLocation[outputFolders.size() + bLocations.size()];
+//     int index = 0;
+//     for (int i = 0, l = outputFolders.size(); i < l; i++)
+//             this.binaryLocations[index++] = (ClasspathLocation) outputFolders.get(i);
+//     for (int i = 0, l = bLocations.size(); i < l; i++)
+//             this.binaryLocations[index++] = (ClasspathLocation) bLocations.get(i);
+  }
+
+  public void cleanup() {
+    this.initialTypeNames = null;
+    this.additionalUnits = null;
+    for (int i = 0, l = sourceLocations.length; i < l; i++)
+      sourceLocations[i].cleanup();
+    // for (int i = 0, l = binaryLocations.length; i < l; i++)
+    //         binaryLocations[i].cleanup();
+  }
+
+  private void createFolder(IContainer folder) throws CoreException {
+    if (!folder.exists()) {
+      createFolder(folder.getParent());
+      ((IFolder) folder).create(true, true, null);
+    }
+  }
+
+  private NameEnvironmentAnswer findClass(String qualifiedTypeName, char[] typeName) {
+    if (initialTypeNames != null) {
+      for (int i = 0, l = initialTypeNames.length; i < l; i++) {
+        if (qualifiedTypeName.equals(initialTypeNames[i])) {
+          if (isIncrementalBuild)
+            // catch the case that a type inside a source file has been renamed but other class files are looking for it
+            throw new AbortCompilation(true, new AbortIncrementalBuildException(qualifiedTypeName));
+          return null; // looking for a file which we know was provided at the beginning of the compilation
+        }
+      }
+    }
+
+    if (additionalUnits != null && sourceLocations.length > 0) {
+      // if an additional source file is waiting to be compiled, answer it BUT not if this is a secondary type search
+      // if we answer X.java & it no longer defines Y then the binary type looking for Y will think the class path is wrong
+      // let the recompile loop fix up dependents when the secondary type Y has been deleted from X.java
+      IPath qSourceFilePath = new Path(qualifiedTypeName + ".java"); //$NON-NLS-1$
+      int qSegmentCount = qSourceFilePath.segmentCount();
+      next : for (int i = 0, l = additionalUnits.length; i < l; i++) {
+        SourceFile additionalUnit = additionalUnits[i];
+        IPath fullPath = additionalUnit.resource.getFullPath();
+        int prefixCount = additionalUnit.sourceLocation.sourceFolder.getFullPath().segmentCount();
+        if (qSegmentCount == fullPath.segmentCount() - prefixCount) {
+          for (int j = 0; j < qSegmentCount; j++)
+            if (!qSourceFilePath.segment(j).equals(fullPath.segment(j + prefixCount)))
+              continue next;
+          return new NameEnvironmentAnswer(additionalUnit);
+        }
+      }
+    }
+
+    // String qBinaryFileName = qualifiedTypeName + ".class"; //$NON-NLS-1$
+    // String binaryFileName = qBinaryFileName;
+    // String qPackageName =  ""; //$NON-NLS-1$
+    // if (qualifiedTypeName.length() > typeName.length) {
+    //         int typeNameStart = qBinaryFileName.length() - typeName.length - 6; // size of ".class"
+    //         qPackageName =  qBinaryFileName.substring(0, typeNameStart - 1);
+    //         binaryFileName = qBinaryFileName.substring(typeNameStart);
+    // }
+    //
+    // // NOTE: the output folders are added at the beginning of the binaryLocations
+    // for (int i = 0, l = binaryLocations.length; i < l; i++) {
+    //         NameEnvironmentAnswer answer = binaryLocations[i].findClass(binaryFileName, qPackageName, qBinaryFileName);
+    //         if (answer != null) return answer;
+    // }
+    return null;
+  }
+
+  public NameEnvironmentAnswer findType(char[][] compoundName) {
+    if (compoundName != null)
+      return findClass(new String(CharOperation.concatWith(compoundName, '/')), compoundName[compoundName.length - 1]);
+    return null;
+  }
+
+  public NameEnvironmentAnswer findType(char[] typeName, char[][] packageName) {
+    if (typeName != null)
+      return findClass(new String(CharOperation.concatWith(packageName, typeName, '/')), typeName);
+    return null;
+  }
+
+  public boolean isPackage(char[][] compoundName, char[] packageName) {
+    return isPackage(new String(CharOperation.concatWith(compoundName, packageName, '/')));
+  }
+
+  public boolean isPackage(String qualifiedPackageName) {
+    // NOTE: the output folders are added at the beginning of the binaryLocations
+    // for (int i = 0, l = binaryLocations.length; i < l; i++)
+    //         if (binaryLocations[i].isPackage(qualifiedPackageName))
+    //                 return true;
+    return false;
+  }
+
+  void setNames(String[] initialTypeNames, SourceFile[] additionalUnits) {
+    this.initialTypeNames = initialTypeNames;
+    this.additionalUnits = additionalUnits;
+    for (int i = 0, l = sourceLocations.length; i < l; i++)
+      sourceLocations[i].reset();
+    // for (int i = 0, l = binaryLocations.length; i < l; i++)
+    //         binaryLocations[i].reset();
+  }
+}