protected void finishedWith(
      String sourceLocator,
      CompilationResult result,
      char[] mainTypeName,
      ArrayList definedTypeNames,
      ArrayList duplicateTypeNames) {
    char[][] previousTypeNames = this.newState.getDefinedTypeNamesFor(sourceLocator);
    if (previousTypeNames == null) previousTypeNames = new char[][] {mainTypeName};
    IPath packagePath = null;
    next:
    for (int i = 0, l = previousTypeNames.length; i < l; i++) {
      char[] previous = previousTypeNames[i];
      for (int j = 0, m = definedTypeNames.size(); j < m; j++)
        if (CharOperation.equals(previous, (char[]) definedTypeNames.get(j))) continue next;

      SourceFile sourceFile = (SourceFile) result.getCompilationUnit();
      if (packagePath == null) {
        int count = sourceFile.sourceLocation.sourceFolder.getFullPath().segmentCount();
        packagePath =
            sourceFile.resource.getFullPath().removeFirstSegments(count).removeLastSegments(1);
      }
      if (this.secondaryTypesToRemove == null)
        this.secondaryTypesToRemove = new SimpleLookupTable();
      ArrayList types =
          (ArrayList) this.secondaryTypesToRemove.get(sourceFile.sourceLocation.binaryFolder);
      if (types == null) types = new ArrayList(definedTypeNames.size());
      types.add(packagePath.append(new String(previous)));
      this.secondaryTypesToRemove.put(sourceFile.sourceLocation.binaryFolder, types);
    }
    super.finishedWith(sourceLocator, result, mainTypeName, definedTypeNames, duplicateTypeNames);
  }
  protected boolean checkForClassFileChanges(
      IResourceDelta binaryDelta, ClasspathMultiDirectory md, int segmentCount)
      throws CoreException {
    IResource resource = binaryDelta.getResource();
    // remember that if inclusion & exclusion patterns change then a full build is done
    boolean isExcluded =
        (md.exclusionPatterns != null || md.inclusionPatterns != null)
            && Util.isExcluded(resource, md.inclusionPatterns, md.exclusionPatterns);
    switch (resource.getType()) {
      case IResource.FOLDER:
        if (isExcluded && md.inclusionPatterns == null)
          return true; // no need to go further with this delta since its children cannot be
        // included

        IResourceDelta[] children = binaryDelta.getAffectedChildren();
        for (int i = 0, l = children.length; i < l; i++)
          if (!checkForClassFileChanges(children[i], md, segmentCount)) return false;
        return true;
      case IResource.FILE:
        if (!isExcluded
            && org.eclipse.jdt.internal.compiler.util.Util.isClassFileName(resource.getName())) {
          // perform full build if a managed class file has been changed
          IPath typePath =
              resource.getFullPath().removeFirstSegments(segmentCount).removeFileExtension();
          if (this.newState.isKnownType(typePath.toString())) {
            if (JavaBuilder.DEBUG)
              System.out.println(
                  "MUST DO FULL BUILD. Found change to class file " + typePath); // $NON-NLS-1$
            return false;
          }
          return true;
        }
    }
    return true;
  }
 protected void removeClassFile(IPath typePath, IContainer outputFolder) throws CoreException {
   if (typePath.lastSegment().indexOf('$') == -1) { // is not a nested type
     this.newState.removeQualifiedTypeName(typePath.toString());
     // add dependents even when the type thinks it does not exist to be on the safe side
     if (JavaBuilder.DEBUG) System.out.println("Found removed type " + typePath); // $NON-NLS-1$
     addDependentsOf(
         typePath,
         true); // when member types are removed, their enclosing type is structurally changed
   }
   IFile classFile =
       outputFolder.getFile(typePath.addFileExtension(SuffixConstants.EXTENSION_class));
   if (classFile.exists()) {
     if (JavaBuilder.DEBUG)
       System.out.println("Deleting class file of removed type " + typePath); // $NON-NLS-1$
     classFile.delete(IResource.FORCE, null);
   }
 }
  protected void deleteGeneratedFiles(IFile[] deletedGeneratedFiles) {
    // delete generated files and recompile any affected source files
    try {
      for (int j = deletedGeneratedFiles.length; --j >= 0; ) {
        IFile deletedFile = deletedGeneratedFiles[j];
        if (deletedFile.exists())
          continue; // only delete .class files for source files that were actually deleted

        SourceFile sourceFile = findSourceFile(deletedFile, false);
        String typeLocator = sourceFile.typeLocator();
        int mdSegmentCount = sourceFile.sourceLocation.sourceFolder.getFullPath().segmentCount();
        IPath typePath =
            sourceFile
                .resource
                .getFullPath()
                .removeFirstSegments(mdSegmentCount)
                .removeFileExtension();
        addDependentsOf(typePath, true); // add dependents of the source file since its now deleted
        this.previousSourceFiles =
            null; // existing source files did not see it as deleted since they were compiled before
        // it was
        char[][] definedTypeNames = this.newState.getDefinedTypeNamesFor(typeLocator);
        if (definedTypeNames == null) { // defined a single type matching typePath
          removeClassFile(typePath, sourceFile.sourceLocation.binaryFolder);
        } else {
          if (definedTypeNames.length > 0) { // skip it if it failed to successfully define a type
            IPath packagePath = typePath.removeLastSegments(1);
            for (int d = 0, l = definedTypeNames.length; d < l; d++)
              removeClassFile(
                  packagePath.append(new String(definedTypeNames[d])),
                  sourceFile.sourceLocation.binaryFolder);
          }
        }
        this.newState.removeLocator(typeLocator);
      }
    } catch (CoreException e) {
      // must continue with compile loop so just log the CoreException
      Util.log(
          e,
          "JavaBuilder logging CompilationParticipant's CoreException to help debugging"); //$NON-NLS-1$
    }
  }
  protected void addDependentsOf(
      IPath path,
      boolean isStructuralChange,
      StringSet qualifiedNames,
      StringSet simpleNames,
      StringSet rootNames) {
    path = path.setDevice(null);
    if (isStructuralChange) {
      String last = path.lastSegment();
      if (last.length() == TypeConstants.PACKAGE_INFO_NAME.length)
        if (CharOperation.equals(last.toCharArray(), TypeConstants.PACKAGE_INFO_NAME)) {
          path =
              path.removeLastSegments(
                  1); // the package-info file has changed so blame the package itself
          /* https://bugs.eclipse.org/bugs/show_bug.cgi?id=323785, in the case of default package,
            there is no need to blame the package itself as there can be no annotations or documentation
            comment tags in the package-info file that can influence the rest of the package. Just bail out
            so we don't touch null objects below.
          */
          if (path.isEmpty()) return;
        }
    }

    if (isStructuralChange && !this.hasStructuralChanges) {
      this.newState.tagAsStructurallyChanged();
      this.hasStructuralChanges = true;
    }
    // the qualifiedStrings are of the form 'p1/p2' & the simpleStrings are just 'X'
    rootNames.add(path.segment(0));
    String packageName = path.removeLastSegments(1).toString();
    boolean wasNew = qualifiedNames.add(packageName);
    String typeName = path.lastSegment();
    int memberIndex = typeName.indexOf('$');
    if (memberIndex > 0) typeName = typeName.substring(0, memberIndex);
    wasNew = simpleNames.add(typeName) | wasNew;
    if (wasNew && JavaBuilder.DEBUG)
      System.out.println(
          "  will look for dependents of " //$NON-NLS-1$
              + typeName
              + " in "
              + packageName); //$NON-NLS-1$
  }
 /**
  * @see
  *     org.eclipse.jdt.internal.core.builder.AbstractImageBuilder#writeClassFileContents(org.eclipse.jdt.internal.compiler.ClassFile,
  *     org.eclipse.core.resources.IFile, java.lang.String, boolean,
  *     org.eclipse.jdt.internal.core.builder.SourceFile)
  */
 protected void writeClassFileContents(
     ClassFile classfile,
     IFile file,
     String qualifiedFileName,
     boolean isTopLevelType,
     SourceFile compilationUnit)
     throws CoreException {
   // Before writing out the class file, compare it to the previous file
   // If structural changes occurred then add dependent source files
   byte[] bytes = classfile.getBytes();
   if (file.exists()) {
     if (writeClassFileCheck(file, qualifiedFileName, bytes)
         || compilationUnit.updateClassFile) { // see 46093
       if (JavaBuilder.DEBUG)
         System.out.println("Writing changed class file " + file.getName()); // $NON-NLS-1$
       if (!file.isDerived()) file.setDerived(true, null);
       file.setContents(new ByteArrayInputStream(bytes), true, false, null);
     } else if (JavaBuilder.DEBUG) {
       System.out.println("Skipped over unchanged class file " + file.getName()); // $NON-NLS-1$
     }
   } else {
     if (isTopLevelType) addDependentsOf(new Path(qualifiedFileName), true); // new type
     if (JavaBuilder.DEBUG)
       System.out.println("Writing new class file " + file.getName()); // $NON-NLS-1$
     try {
       file.create(new ByteArrayInputStream(bytes), IResource.FORCE | IResource.DERIVED, null);
     } catch (CoreException e) {
       if (e.getStatus().getCode() == IResourceStatus.CASE_VARIANT_EXISTS) {
         IStatus status = e.getStatus();
         if (status instanceof IResourceStatus) {
           IPath oldFilePath = ((IResourceStatus) status).getPath();
           char[] oldTypeName = oldFilePath.removeFileExtension().lastSegment().toCharArray();
           char[][] previousTypeNames =
               this.newState.getDefinedTypeNamesFor(compilationUnit.typeLocator());
           boolean fromSameFile = false;
           if (previousTypeNames == null) {
             fromSameFile = CharOperation.equals(compilationUnit.getMainTypeName(), oldTypeName);
           } else {
             for (int i = 0, l = previousTypeNames.length; i < l; i++) {
               if (CharOperation.equals(previousTypeNames[i], oldTypeName)) {
                 fromSameFile = true;
                 break;
               }
             }
           }
           if (fromSameFile) {
             // file is defined by the same compilationUnit, but won't be deleted until later so do
             // it now
             IFile collision = file.getParent().getFile(new Path(oldFilePath.lastSegment()));
             collision.delete(true, false, null);
             boolean success = false;
             try {
               file.create(
                   new ByteArrayInputStream(bytes), IResource.FORCE | IResource.DERIVED, null);
               success = true;
             } catch (CoreException ignored) {
               // ignore the second exception
             }
             if (success) return;
           }
         }
         // catch the case that a type has been renamed and collides on disk with an
         // as-yet-to-be-deleted type
         throw new AbortCompilation(true, new AbortIncrementalBuildException(qualifiedFileName));
       }
       throw e; // rethrow
     }
   }
 }
  protected boolean findSourceFiles(
      IResourceDelta sourceDelta, ClasspathMultiDirectory md, int segmentCount)
      throws CoreException {
    // When a package becomes a type or vice versa, expect 2 deltas,
    // one on the folder & one on the source file
    IResource resource = sourceDelta.getResource();
    // remember that if inclusion & exclusion patterns change then a full build is done
    boolean isExcluded =
        (md.exclusionPatterns != null || md.inclusionPatterns != null)
            && Util.isExcluded(resource, md.inclusionPatterns, md.exclusionPatterns);
    switch (resource.getType()) {
      case IResource.FOLDER:
        if (isExcluded && md.inclusionPatterns == null)
          return true; // no need to go further with this delta since its children cannot be
        // included

        switch (sourceDelta.getKind()) {
          case IResourceDelta.ADDED:
            if (!isExcluded) {
              IPath addedPackagePath = resource.getFullPath().removeFirstSegments(segmentCount);
              createFolder(
                  addedPackagePath, md.binaryFolder); // ensure package exists in the output folder
              // see if any known source file is from the same package... classpath already includes
              // new package
              if (this.sourceLocations.length > 1
                  && this.newState.isKnownPackage(addedPackagePath.toString())) {
                if (JavaBuilder.DEBUG)
                  System.out.println(
                      "Skipped dependents of added package " + addedPackagePath); // $NON-NLS-1$
              } else {
                if (JavaBuilder.DEBUG)
                  System.out.println("Found added package " + addedPackagePath); // $NON-NLS-1$
                addDependentsOf(addedPackagePath, true);
              }
            }
            // $FALL-THROUGH$ collect all the source files
          case IResourceDelta.CHANGED:
            IResourceDelta[] children = sourceDelta.getAffectedChildren();
            for (int i = 0, l = children.length; i < l; i++)
              if (!findSourceFiles(children[i], md, segmentCount)) return false;
            return true;
          case IResourceDelta.REMOVED:
            if (isExcluded) {
              // since this folder is excluded then there is nothing to delete (from this md), but
              // must walk any included subfolders
              children = sourceDelta.getAffectedChildren();
              for (int i = 0, l = children.length; i < l; i++)
                if (!findSourceFiles(children[i], md, segmentCount)) return false;
              return true;
            }
            IPath removedPackagePath = resource.getFullPath().removeFirstSegments(segmentCount);
            if (this.sourceLocations.length > 1) {
              for (int i = 0, l = this.sourceLocations.length; i < l; i++) {
                if (this.sourceLocations[i].sourceFolder.getFolder(removedPackagePath).exists()) {
                  // only a package fragment was removed, same as removing multiple source files
                  createFolder(
                      removedPackagePath,
                      md.binaryFolder); // ensure package exists in the output folder
                  IResourceDelta[] removedChildren = sourceDelta.getAffectedChildren();
                  for (int j = 0, m = removedChildren.length; j < m; j++)
                    if (!findSourceFiles(removedChildren[j], md, segmentCount)) return false;
                  return true;
                }
              }
            }
            if ((sourceDelta.getFlags() & IResourceDelta.MOVED_TO) != 0) {
              // same idea as moving a source file
              // see bug 163200
              IResource movedFolder =
                  this.javaBuilder.workspaceRoot.getFolder(sourceDelta.getMovedToPath());
              JavaBuilder.removeProblemsAndTasksFor(movedFolder);
            }
            IFolder removedPackageFolder = md.binaryFolder.getFolder(removedPackagePath);
            if (removedPackageFolder.exists()) removedPackageFolder.delete(IResource.FORCE, null);
            // add dependents even when the package thinks it does not exist to be on the safe side
            if (JavaBuilder.DEBUG)
              System.out.println("Found removed package " + removedPackagePath); // $NON-NLS-1$
            addDependentsOf(removedPackagePath, true);
            this.newState.removePackage(sourceDelta);
        }
        return true;
      case IResource.FILE:
        if (isExcluded) return true;

        String resourceName = resource.getName();
        // GROOVY start
        // determine if this is a Groovy project
        final boolean isInterestingProject =
            LanguageSupportFactory.isInterestingProject(this.javaBuilder.getProject());
        // GROOVY end

        // GROOVY start
        /* old {
        if (org.eclipse.jdt.internal.core.util.Util.isJavaLikeFileName(resourceName)) {
        } new */
        // GRECLIPSE-404 must call 'isJavaLikeFile' directly in order to make the Scala-Eclipse
        // plugin's weaving happy
        if ((!isInterestingProject
                && org.eclipse.jdt.internal.core.util.Util.isJavaLikeFileName(resourceName)
                && !LanguageSupportFactory.isInterestingSourceFile(resourceName))
            || (isInterestingProject
                && LanguageSupportFactory.isSourceFile(resourceName, isInterestingProject))) {
          // GROOVY end
          IPath typePath =
              resource.getFullPath().removeFirstSegments(segmentCount).removeFileExtension();
          String typeLocator = resource.getProjectRelativePath().toString();
          switch (sourceDelta.getKind()) {
            case IResourceDelta.ADDED:
              if (JavaBuilder.DEBUG)
                System.out.println("Compile this added source file " + typeLocator); // $NON-NLS-1$
              this.sourceFiles.add(new SourceFile((IFile) resource, md, true));
              String typeName = typePath.toString();
              if (!this.newState.isDuplicateLocator(
                  typeName, typeLocator)) { // adding dependents results in 2 duplicate errors
                if (JavaBuilder.DEBUG)
                  System.out.println("Found added source file " + typeName); // $NON-NLS-1$
                addDependentsOf(typePath, true);
              }
              return true;
            case IResourceDelta.REMOVED:
              char[][] definedTypeNames = this.newState.getDefinedTypeNamesFor(typeLocator);
              if (definedTypeNames == null) { // defined a single type matching typePath
                removeClassFile(typePath, md.binaryFolder);
                if ((sourceDelta.getFlags() & IResourceDelta.MOVED_TO) != 0) {
                  // remove problems and tasks for a compilation unit that is being moved (to
                  // another package or renamed)
                  // if the target file is a compilation unit, the new cu will be recompiled
                  // if the target file is a non-java resource, then markers are removed
                  // see bug 2857
                  IResource movedFile =
                      this.javaBuilder.workspaceRoot.getFile(sourceDelta.getMovedToPath());
                  JavaBuilder.removeProblemsAndTasksFor(movedFile);
                }
              } else {
                if (JavaBuilder.DEBUG)
                  System.out.println(
                      "Found removed source file " + typePath.toString()); // $NON-NLS-1$
                addDependentsOf(
                    typePath,
                    true); // add dependents of the source file since it may be involved in a name
                // collision
                if (definedTypeNames.length
                    > 0) { // skip it if it failed to successfully define a type
                  IPath packagePath = typePath.removeLastSegments(1);
                  for (int i = 0, l = definedTypeNames.length; i < l; i++)
                    removeClassFile(
                        packagePath.append(new String(definedTypeNames[i])), md.binaryFolder);
                }
              }
              this.newState.removeLocator(typeLocator);
              return true;
            case IResourceDelta.CHANGED:
              if ((sourceDelta.getFlags() & IResourceDelta.CONTENT) == 0
                  && (sourceDelta.getFlags() & IResourceDelta.ENCODING) == 0)
                return true; // skip it since it really isn't changed
              if (JavaBuilder.DEBUG)
                System.out.println(
                    "Compile this changed source file " + typeLocator); // $NON-NLS-1$
              this.sourceFiles.add(new SourceFile((IFile) resource, md, true));
          }
          return true;
        } else if (org.eclipse.jdt.internal.compiler.util.Util.isClassFileName(resourceName)) {
          // perform full build if a managed class file has been changed
          if (this.makeOutputFolderConsistent) {
            IPath typePath =
                resource.getFullPath().removeFirstSegments(segmentCount).removeFileExtension();
            if (this.newState.isKnownType(typePath.toString())) {
              if (JavaBuilder.DEBUG)
                System.out.println(
                    "MUST DO FULL BUILD. Found change to class file " + typePath); // $NON-NLS-1$
              return false;
            }
          }
          return true;
        } else if (md.hasIndependentOutputFolder) {
          if (this.javaBuilder.filterExtraResource(resource)) return true;

          // copy all other resource deltas to the output folder
          IPath resourcePath = resource.getFullPath().removeFirstSegments(segmentCount);
          IResource outputFile = md.binaryFolder.getFile(resourcePath);
          switch (sourceDelta.getKind()) {
            case IResourceDelta.ADDED:
              if (outputFile.exists()) {
                if (JavaBuilder.DEBUG)
                  System.out.println("Deleting existing file " + resourcePath); // $NON-NLS-1$
                outputFile.delete(IResource.FORCE, null);
              }
              if (JavaBuilder.DEBUG)
                System.out.println("Copying added file " + resourcePath); // $NON-NLS-1$
              createFolder(
                  resourcePath.removeLastSegments(1),
                  md.binaryFolder); // ensure package exists in the output folder
              copyResource(resource, outputFile);
              return true;
            case IResourceDelta.REMOVED:
              if (outputFile.exists()) {
                if (JavaBuilder.DEBUG)
                  System.out.println("Deleting removed file " + resourcePath); // $NON-NLS-1$
                outputFile.delete(IResource.FORCE, null);
              }
              return true;
            case IResourceDelta.CHANGED:
              if ((sourceDelta.getFlags() & IResourceDelta.CONTENT) == 0
                  && (sourceDelta.getFlags() & IResourceDelta.ENCODING) == 0)
                return true; // skip it since it really isn't changed
              if (outputFile.exists()) {
                if (JavaBuilder.DEBUG)
                  System.out.println("Deleting existing file " + resourcePath); // $NON-NLS-1$
                outputFile.delete(IResource.FORCE, null);
              }
              if (JavaBuilder.DEBUG)
                System.out.println("Copying changed file " + resourcePath); // $NON-NLS-1$
              createFolder(
                  resourcePath.removeLastSegments(1),
                  md.binaryFolder); // ensure package exists in the output folder
              copyResource(resource, outputFile);
          }
          return true;
        }
    }
    return true;
  }
 protected void findAffectedSourceFiles(
     IResourceDelta binaryDelta, int segmentCount, StringSet structurallyChangedTypes) {
   // When a package becomes a type or vice versa, expect 2 deltas,
   // one on the folder & one on the class file
   IResource resource = binaryDelta.getResource();
   switch (resource.getType()) {
     case IResource.FOLDER:
       switch (binaryDelta.getKind()) {
         case IResourceDelta.ADDED:
         case IResourceDelta.REMOVED:
           IPath packagePath = resource.getFullPath().removeFirstSegments(segmentCount);
           String packageName = packagePath.toString();
           if (binaryDelta.getKind() == IResourceDelta.ADDED) {
             // see if any known source file is from the same package... classpath already includes
             // new package
             if (!this.newState.isKnownPackage(packageName)) {
               if (JavaBuilder.DEBUG)
                 System.out.println("Found added package " + packageName); // $NON-NLS-1$
               addDependentsOf(packagePath, false);
               return;
             }
             if (JavaBuilder.DEBUG)
               System.out.println(
                   "Skipped dependents of added package " + packageName); // $NON-NLS-1$
           } else {
             // see if the package still exists on the classpath
             if (!this.nameEnvironment.isPackage(packageName)) {
               if (JavaBuilder.DEBUG)
                 System.out.println("Found removed package " + packageName); // $NON-NLS-1$
               addDependentsOf(packagePath, false);
               return;
             }
             if (JavaBuilder.DEBUG)
               System.out.println(
                   "Skipped dependents of removed package " + packageName); // $NON-NLS-1$
           }
           // $FALL-THROUGH$ traverse the sub-packages and .class files
         case IResourceDelta.CHANGED:
           IResourceDelta[] children = binaryDelta.getAffectedChildren();
           for (int i = 0, l = children.length; i < l; i++)
             findAffectedSourceFiles(children[i], segmentCount, structurallyChangedTypes);
       }
       return;
     case IResource.FILE:
       if (org.eclipse.jdt.internal.compiler.util.Util.isClassFileName(resource.getName())) {
         IPath typePath =
             resource.getFullPath().removeFirstSegments(segmentCount).removeFileExtension();
         switch (binaryDelta.getKind()) {
           case IResourceDelta.ADDED:
           case IResourceDelta.REMOVED:
             if (JavaBuilder.DEBUG)
               System.out.println("Found added/removed class file " + typePath); // $NON-NLS-1$
             addDependentsOf(typePath, false);
             return;
           case IResourceDelta.CHANGED:
             if ((binaryDelta.getFlags() & IResourceDelta.CONTENT) == 0)
               return; // skip it since it really isn't changed
             if (structurallyChangedTypes != null
                 && !structurallyChangedTypes.includes(typePath.toString()))
               return; // skip since it wasn't a structural change
             if (JavaBuilder.DEBUG)
               System.out.println("Found changed class file " + typePath); // $NON-NLS-1$
             addDependentsOf(typePath, false);
         }
         return;
       }
   }
 }