コード例 #1
0
 protected static String getLineDelimiterPreference(IFile file) {
   IScopeContext[] scopeContext;
   if (file != null && file.getProject() != null) {
     // project preference
     scopeContext = new IScopeContext[] {new ProjectScope(file.getProject())};
     String lineDelimiter =
         Platform.getPreferencesService()
             .getString(Platform.PI_RUNTIME, Platform.PREF_LINE_SEPARATOR, null, scopeContext);
     if (lineDelimiter != null) return lineDelimiter;
   }
   // workspace preference
   scopeContext = new IScopeContext[] {InstanceScope.INSTANCE};
   return Platform.getPreferencesService()
       .getString(Platform.PI_RUNTIME, Platform.PREF_LINE_SEPARATOR, null, scopeContext);
 }
コード例 #2
0
 protected boolean writeClassFileCheck(IFile file, String fileName, byte[] newBytes)
     throws CoreException {
   try {
     byte[] oldBytes = Util.getResourceContentsAsByteArray(file);
     notEqual:
     if (newBytes.length == oldBytes.length) {
       for (int i = newBytes.length; --i >= 0; ) if (newBytes[i] != oldBytes[i]) break notEqual;
       return false; // bytes are identical so skip them
     }
     URI location = file.getLocationURI();
     if (location == null) return false; // unable to determine location of this class file
     String filePath = location.getSchemeSpecificPart();
     ClassFileReader reader = new ClassFileReader(oldBytes, filePath.toCharArray());
     // ignore local types since they're only visible inside a single method
     if (!(reader.isLocal() || reader.isAnonymous()) && reader.hasStructuralChanges(newBytes)) {
       if (JavaBuilder.DEBUG)
         System.out.println("Type has structural changes " + fileName); // $NON-NLS-1$
       addDependentsOf(new Path(fileName), true);
       this.newState.wasStructurallyChanged(fileName);
     }
   } catch (ClassFormatException e) {
     addDependentsOf(new Path(fileName), true);
     this.newState.wasStructurallyChanged(fileName);
   }
   return true;
 }
  /** Tests a builder that requests deltas for closed and missing projects. */
  public void testRequestMissingProject() {
    // add builder and do an initial build to get the instance
    try {
      addBuilder(project1, DeltaVerifierBuilder.BUILDER_NAME);
      project1.build(IncrementalProjectBuilder.FULL_BUILD, getMonitor());
    } catch (CoreException e) {
      fail("1.0", e);
    }
    final DeltaVerifierBuilder builder = DeltaVerifierBuilder.getInstance();
    assertTrue("1.1", builder != null);
    // always check deltas for all projects
    final IProject[] allProjects = new IProject[] {project1, project2, project3, project4};
    try {
      project2.close(getMonitor());
      project3.delete(IResource.ALWAYS_DELETE_PROJECT_CONTENT, getMonitor());
    } catch (CoreException e1) {
      fail("1.99", e1);
    }
    builder.checkDeltas(allProjects);

    // modify a file in project1 to force an autobuild
    try {
      file1.setContents(getRandomContents(), IResource.NONE, getMonitor());
    } catch (CoreException e2) {
      fail("2.99", e2);
    }
  }
コード例 #4
0
ファイル: CompilerFeeder.java プロジェクト: Galigator/db4o
 @Override
 public void visitArchive(IFile jar) {
   if (knownArchives.add(jar)) {
     System.out.println("jar: " + jar.name());
     ecj.addClasspathEntry(jar);
   }
 }
コード例 #5
0
 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);
   }
 }
コード例 #6
0
 public boolean checkIfFileInTarget(IFile fileToCheck) {
   String[] strings = selectedListBox.getItems();
   int size = selectedListBox.getItemCount();
   for (int i = 0; i < size; i++) {
     if (strings[i].compareTo(fileToCheck.getFullPath().toString()) == 0) return true;
   }
   return false;
 }
コード例 #7
0
 public ExtensionsErrorReporter(IFile file) {
   super(file);
   fModel = MonitorRegistry.findModel(file.getProject());
   try {
     if (fModel != null && fModel.getUnderlyingResource() != null)
       fBuildModel = ClasspathUtilCore.getBuild(fModel);
   } catch (CoreException e) {
   }
 }
コード例 #8
0
  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$
    }
  }
  /** Test for Bug #5102. Never reproduced but interesting little test, worth keeping around */
  public void testPR() throws Exception {
    // create a project with a RefreshLocalJavaFileBuilder and a SortBuilder on the classpath
    IProject project = getWorkspace().getRoot().getProject("P1");
    project.create(null);
    project.open(null);
    IProjectDescription desc = project.getDescription();
    ICommand one = desc.newCommand();
    one.setBuilderName(RefreshLocalJavaFileBuilder.BUILDER_NAME);
    ICommand two = desc.newCommand();
    two.setBuilderName(SortBuilder.BUILDER_NAME);
    desc.setBuildSpec(new ICommand[] {one, two});
    project.setDescription(desc, null);

    // do a full build
    project.build(IncrementalProjectBuilder.FULL_BUILD, null);

    // do an incremental build by creating a file
    IFile file = project.getFile("Foo");
    file.create(getRandomContents(), true, getMonitor());
  }
コード例 #10
0
  private ISourceContainer getArchiveSourceContainer(String location) throws JavaModelException {
    IWorkspaceRoot root = PDELaunchingPlugin.getWorkspace().getRoot();
    IFile[] containers = root.findFilesForLocationURI(URIUtil.toURI(location));
    for (int i = 0; i < containers.length; i++) {
      IJavaElement element = JavaCore.create(containers[i]);
      if (element instanceof IPackageFragmentRoot) {
        IPackageFragmentRoot archive = (IPackageFragmentRoot) element;
        IPath path = archive.getSourceAttachmentPath();
        if (path == null || path.segmentCount() == 0) continue;

        IPath rootPath = archive.getSourceAttachmentRootPath();
        boolean detectRootPath = rootPath != null && rootPath.segmentCount() > 0;

        IFile archiveFile = root.getFile(path);
        if (archiveFile.exists()) return new ArchiveSourceContainer(archiveFile, detectRootPath);

        File file = path.toFile();
        if (file.exists())
          return new ExternalArchiveSourceContainer(file.getAbsolutePath(), detectRootPath);
      }
    }
    return null;
  }
コード例 #11
0
ファイル: CompilerFeeder.java プロジェクト: Galigator/db4o
  @Override
  public void visitSourceFolder(IFile sourceFolder) {
    sourceFolder.accept(
        new FileVisitor() {

          @Override
          public void visit(IFile child) {
            if (child.isFile() && child.name().toLowerCase().endsWith(".java")) {
              if (knownSources.add(child)) {
                ecj.addSourceFile(child);
              }
            } else if (child.isDirectory()) {
              child.accept(this);
            }
          }
        });
  }
 private void saveContent(
     PackageFragment dest, String destName, TextEdit edits, String sourceEncoding, IFile destFile)
     throws JavaModelException {
   try {
     // TODO (frederic) remove when bug 67606 will be fixed (bug 67823)
     // fix bug 66898
     if (sourceEncoding != null) destFile.setCharset(sourceEncoding, this.progressMonitor);
     // end todo
   } catch (CoreException ce) {
     // use no encoding
   }
   // when the file was copied, its read-only flag was preserved -> temporary set it to false
   // note this doesn't interfere with repository providers as this is a new resource that cannot
   // be under
   // version control yet
   Util.setReadOnly(destFile, false);
   ICompilationUnit destCU = dest.getCompilationUnit(destName);
   applyTextEdit(destCU, edits);
   destCU.save(getSubProgressMonitor(1), this.force);
 }
コード例 #13
0
 /**
  * @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
     }
   }
 }
コード例 #14
0
  /**
   * Creates a locale specific properties file within the fragment project based on the content of
   * the host plug-in's properties file.
   *
   * @param fragmentProject
   * @param locale
   * @throws CoreException
   * @throws IOException
   */
  private void createLocaleSpecificPropertiesFile(
      final IProject fragmentProject, IPluginModelBase plugin, final Locale locale)
      throws CoreException, IOException {
    final IFolder localeResourceFolder =
        fragmentProject.getFolder(RESOURCE_FOLDER_PARENT).getFolder(locale.toString());

    // Case 1: External plug-in
    if (plugin instanceof ExternalPluginModelBase) {
      final String installLocation = plugin.getInstallLocation();
      // Case 1a: External plug-in is a jar file
      if (new File(installLocation).isFile()) {
        ZipFile zf = new ZipFile(installLocation);
        for (Enumeration e = zf.entries(); e.hasMoreElements(); ) {
          worked();

          ZipEntry zfe = (ZipEntry) e.nextElement();
          String name = zfe.getName();

          String[] segments = name.split(SLASH);
          IPath path = Path.fromPortableString(join(SLASH, segments, 0, segments.length - 1));
          String resourceName = segments[segments.length - 1];
          String localizedResourceName = localeSpecificName(resourceName, locale);
          if (propertiesFilter.include(name)) {

            createParents(fragmentProject, path);
            IFile file = fragmentProject.getFile(path.append(localizedResourceName));
            InputStream is = zf.getInputStream(zfe);
            file.create(is, false, getProgressMonitor());
          } else if (resourceFilter.include(name)) {
            IPath target = localeResourceFolder.getFullPath().append(path).append(resourceName);
            createParents(fragmentProject, target.removeLastSegments(1).removeFirstSegments(1));
            IFile file = fragmentProject.getFile(target.removeFirstSegments(1));
            file.create(zf.getInputStream(zfe), false, getProgressMonitor());
          }
        }
      }
      // Case 1b: External plug-in has a folder structure
      else {
        Visitor visitor =
            new Visitor() {
              public void visit(File file) throws CoreException, FileNotFoundException {
                worked();

                String relativePath =
                    file.getAbsolutePath()
                        .substring(installLocation.length())
                        .replaceAll(File.separator, SLASH);
                String[] segments = relativePath.split(SLASH);
                IPath path = Path.fromPortableString(join(SLASH, segments, 0, segments.length - 1));
                String resourceName = segments[segments.length - 1];
                String localizedResourceName = localeSpecificName(resourceName, locale);

                if (propertiesFilter.include(
                    relativePath + (file.isDirectory() ? SLASH : EMPTY_STRING))) {
                  createParents(fragmentProject, path);
                  IFile iFile = fragmentProject.getFile(path.append(localizedResourceName));
                  iFile.create(new FileInputStream(file), false, getProgressMonitor());
                } else if (resourceFilter.include(
                    relativePath + (file.isDirectory() ? SLASH : EMPTY_STRING))) {
                  IPath target = localeResourceFolder.getFullPath().append(relativePath);
                  createParents(
                      fragmentProject, target.removeLastSegments(1).removeFirstSegments(1));
                  IFile iFile = fragmentProject.getFile(target.removeFirstSegments(1));
                  iFile.create(new FileInputStream(file), false, getProgressMonitor());
                }

                if (file.isDirectory()) {
                  File[] children = file.listFiles();
                  for (int i = 0; i < children.length; i++) {
                    visit(children[i]);
                  }
                }
              }
            };

        visitor.visit(new File(installLocation));
      }
    }
    // Case 2: Workspace plug-in
    else {
      final IProject project = plugin.getUnderlyingResource().getProject();

      project.accept(
          new IResourceVisitor() {
            public boolean visit(IResource resource) throws CoreException {
              worked();

              IPath parent = resource.getFullPath().removeLastSegments(1).removeFirstSegments(1);
              if (propertiesFilter.include(resource)) {
                String segment = localeSpecificName(resource.getFullPath().lastSegment(), locale);
                IPath fragmentResource =
                    fragmentProject.getFullPath().append(parent).append(segment);

                createParents(fragmentProject, parent);
                resource.copy(fragmentResource, true, getProgressMonitor());
              } else if (resourceFilter.include(resource)) {
                IPath target =
                    localeResourceFolder
                        .getFullPath()
                        .append(parent)
                        .append(resource.getFullPath().lastSegment());
                createParents(fragmentProject, target.removeLastSegments(1).removeFirstSegments(1));
                resource.copy(target, true, getProgressMonitor());
              }
              return true;
            }
          });
    }
  }
  /**
   * Copies/moves a compilation unit with the name <code>newCUName</code> to the destination
   * package.<br>
   * The package statement in the compilation unit is updated if necessary. The main type of the
   * compilation unit is renamed if necessary.
   *
   * @exception JavaModelException if the operation is unable to complete
   */
  private void processCompilationUnitResource(ICompilationUnit source, PackageFragment dest)
      throws JavaModelException {
    String newCUName = getNewNameFor(source);
    String destName = (newCUName != null) ? newCUName : source.getElementName();
    TextEdit edit = updateContent(source, dest, newCUName); // null if unchanged

    // TODO (frederic) remove when bug 67606 will be fixed (bug 67823)
    // store encoding (fix bug 66898)
    IFile sourceResource = (IFile) source.getResource();
    String sourceEncoding = null;
    try {
      sourceEncoding = sourceResource.getCharset(false);
    } catch (CoreException ce) {
      // no problem, use default encoding
    }
    // end todo
    // copy resource
    IContainer destFolder = (IContainer) dest.getResource(); // can be an IFolder or an IProject
    IFile destFile = destFolder.getFile(new Path(destName));
    org.eclipse.jdt.internal.core.CompilationUnit destCU =
        new org.eclipse.jdt.internal.core.CompilationUnit(
            dest, destName, DefaultWorkingCopyOwner.PRIMARY);
    if (!destFile.equals(sourceResource)) {
      try {
        if (!destCU.isWorkingCopy()) {
          if (destFile.exists()) {
            if (this.force) {
              // we can remove it
              deleteResource(destFile, IResource.KEEP_HISTORY);
              destCU.close(); // ensure the in-memory buffer for the dest CU is closed
            } else {
              // abort
              throw new JavaModelException(
                  new JavaModelStatus(
                      IJavaModelStatusConstants.NAME_COLLISION,
                      Messages.bind(
                          Messages.status_nameCollision, destFile.getFullPath().toString())));
            }
          }
          int flags = this.force ? IResource.FORCE : IResource.NONE;
          if (isMove()) {
            flags |= IResource.KEEP_HISTORY;
            sourceResource.move(destFile.getFullPath(), flags, getSubProgressMonitor(1));
          } else {
            if (edit != null) flags |= IResource.KEEP_HISTORY;
            sourceResource.copy(destFile.getFullPath(), flags, getSubProgressMonitor(1));
          }
          setAttribute(HAS_MODIFIED_RESOURCE_ATTR, TRUE);
        } else {
          destCU.getBuffer().setContents(source.getBuffer().getContents());
        }
      } catch (JavaModelException e) {
        throw e;
      } catch (CoreException e) {
        throw new JavaModelException(e);
      }

      // update new resource content
      if (edit != null) {
        boolean wasReadOnly = destFile.isReadOnly();
        try {
          saveContent(dest, destName, edit, sourceEncoding, destFile);
        } catch (CoreException e) {
          if (e instanceof JavaModelException) throw (JavaModelException) e;
          throw new JavaModelException(e);
        } finally {
          Util.setReadOnly(destFile, wasReadOnly);
        }
      }

      // register the correct change deltas
      prepareDeltas(source, destCU, isMove());
      if (newCUName != null) {
        // the main type has been renamed
        String oldName = Util.getNameWithoutJavaLikeExtension(source.getElementName());
        String newName = Util.getNameWithoutJavaLikeExtension(newCUName);
        prepareDeltas(source.getType(oldName), destCU.getType(newName), isMove());
      }
    } else {
      if (!this.force) {
        throw new JavaModelException(
            new JavaModelStatus(
                IJavaModelStatusConstants.NAME_COLLISION,
                Messages.bind(Messages.status_nameCollision, destFile.getFullPath().toString())));
      }
      // update new resource content
      // in case we do a saveas on the same resource we have to simply update the contents
      // see http://dev.eclipse.org/bugs/show_bug.cgi?id=9351
      if (edit != null) {
        saveContent(dest, destName, edit, sourceEncoding, destFile);
      }
    }
  }
コード例 #16
0
ファイル: MoveDelegate.java プロジェクト: spiritman1990/pdt
  /**
   * Creates the text changes for all the affected files. Updates all the include statements in the
   * current file and all the includes in the "including " files. In case of folders, creates the
   * changes recursively
   *
   * @param pm - progress monitor
   * @param rootChange - the root change that the new changes are added to
   * @param sourceResources
   * @return the root change after the additions
   * @throws CoreException
   */
  private Change createTextChanges(
      IProgressMonitor pm,
      CompositeChange rootChange,
      Set<IFile> phpFiles,
      IResource[] sourceResources)
      throws CoreException {
    List<ProgramFileChange> changes = new ArrayList<ProgramFileChange>();
    try {
      pm.beginTask(PhpRefactoringCoreMessages.getString("MoveDelegate.1"), 100); // $NON-NLS-1$

      // creat text changes:
      // for each file that will be moved, update its includes
      // and update all the files that include it,

      IResource[] uniqueSourceResources = removeDuplicateResources(sourceResources);

      for (Iterator<IFile> it = phpFiles.iterator(); it.hasNext(); ) {
        IFile currentMovedResource = it.next();

        Map<IFile, Program> participantFiles = collectReferencingFiles(currentMovedResource, pm);

        for (Entry<IFile, Program> entry : participantFiles.entrySet()) {
          final IFile file = entry.getKey();
          if (phpFiles.contains(file)) {
            continue;
          }
          final Program program = entry.getValue();

          final ChangeIncludePath rename =
              new ChangeIncludePath(
                  currentMovedResource, file, fMainDestinationPath, false, uniqueSourceResources);
          // aggregate the changes identifiers
          program.accept(rename);

          if (pm.isCanceled()) throw new OperationCanceledException();

          pm.worked(1);

          if (rename.hasChanges()) {
            ProgramFileChange change = new ProgramFileChange(file.getName(), file, program);
            change.setEdit(new MultiTextEdit());
            change.setTextType("php"); // $NON-NLS-1$

            changes.add(change);
            rename.updateChange(change);
          }
        }

        ISourceModule sourceModule = DLTKCore.createSourceModuleFrom(currentMovedResource);

        if (sourceModule instanceof ISourceModule) {

          Program program = null;
          try {
            program = ASTUtils.createProgramFromSource(sourceModule);
          } catch (Exception e) {
          }

          if (program != null) {
            final ChangeIncludePath rename =
                new ChangeIncludePath(
                    currentMovedResource,
                    currentMovedResource,
                    fMainDestinationPath,
                    true,
                    uniqueSourceResources);

            // aggregate the changes identifiers
            program.accept(rename);

            if (pm.isCanceled()) throw new OperationCanceledException();

            pm.worked(1);

            if (rename.hasChanges()) {
              ProgramFileChange change =
                  new ProgramFileChange(
                      currentMovedResource.getName(), currentMovedResource, program);
              change.setEdit(new MultiTextEdit());
              change.setTextType("php"); // $NON-NLS-1$

              changes.add(change);
              rename.updateChange(change);
            }
          }
        }
      }
      pm.worked(70);

    } finally {
      pm.done();
    } // getChildren()

    Map<IFile, List<TextEdit>> changeMap = new HashMap<IFile, List<TextEdit>>();
    Map<IFile, ProgramFileChange> fileMap = new HashMap<IFile, ProgramFileChange>();
    for (ProgramFileChange programFileChange : changes) {
      List<TextEdit> list = changeMap.get(programFileChange.getFile());
      if (list == null) {
        list = new ArrayList<TextEdit>();
        changeMap.put(programFileChange.getFile(), list);
        fileMap.put(programFileChange.getFile(), programFileChange);
      } else {

      }
      list.addAll(Arrays.asList(programFileChange.getEdit().getChildren()));
    }
    for (IFile file : changeMap.keySet()) {
      ProgramFileChange change =
          new ProgramFileChange(file.getName(), file, fileMap.get(file).getProgram());
      change.setEdit(new MultiTextEdit());
      change.setTextType("php"); // $NON-NLS-1$

      List<TextEdit> list = changeMap.get(file);
      Collections.sort(
          list,
          new Comparator<TextEdit>() {
            public int compare(TextEdit o1, TextEdit o2) {
              return o2.getOffset() - o1.getOffset();
            }
          });

      for (TextEdit textEdit : list) {
        if (textEdit instanceof ReplaceEdit) {
          ReplaceEdit replaceEdit = (ReplaceEdit) textEdit;
          change.addEdit(
              new ReplaceEdit(
                  replaceEdit.getOffset(), replaceEdit.getLength(), replaceEdit.getText()));
        }
      }
      rootChange.add(change);
    }
    return rootChange;
  }