Search code examples
javaeclipserefactoringpde

Eclipse move participant with many files


I work on a set of plugins developped with Xtext and Graphiti. It is related to a DSL and a Graph Editor for research needs. We have in some user projects many files with links and references between them. I need to implements a Move Participant, to updates automatically all files when we move something from a package to another, if that file is references in other files.

For example:

ProjectA
    |- src
    |   |-org.comp.A
    |   |   |-networkA
    |   |   |-networkB
    |   |-org.comp.B
    |   |   |-fileC
    |   |   |-fileD

And org.comp.A.networkA references org.comp.B.fileC and org.comp.B.fileD. If I move fileC and fileD to another package or another project, I need to update correctly networkA. There is at least 2 different replacement to perform in the content of networkA.

I already wrote a class, simplified here:

public class NetworkMoveParticipant extends MoveParticipant {

    private IFile originalNetworkFile;
    private IFolder destination;
    private IPath newNetworkPath;

    @Override
    protected boolean initialize(Object element) {
        if (element instanceof IFile) {
            originalNetworkFile = (IFile) element;
            destination = (IFolder) getArguments().getDestination();
            newNetworkPath = destination.getFile(
                     originalNetworkFile.getName()
            ).getFullPath();
            return true;
        }
        return false;
    }

    @Override
    public String getName() {
        return "The move participant";
    }

    @Override
    public RefactoringStatus checkConditions(IProgressMonitor pm,
            CheckConditionsContext context) throws OperationCanceledException {
        return new RefactoringStatus();
    }

    @Override
    public Change createChange(IProgressMonitor pm) throws CoreException,
            OperationCanceledException {
        return getOtherNetworksContentChanges();
    }

    private Change getOtherNetworksContentChanges() {
        final CompositeChange changes = new CompositeChange();

        for(IFile file : getAffectedFiles()) {
            final TextFileChange textFileChange =
                    new TextFileChange(file.getName(), file);
            textFileChange.setEdit(new ReplaceEdit(0,
                             content.length(), getNewFileContent(file)));
            changes.add(textFileChange);
        }
        return changes;
    }

    private Iterable<IFile> getAffectedFiles() {
        // Returns the list of projects' IFile which reference the moved file
    }

    private String getNewFileContent(IFile file) {
        // Perform the replacement in the content of the project's file
    }
}

This works perfectly when I move 1 file: all networks whose reference this file are updated as expected. The window show a preview of the changes before applying the replacements.

But if I try to move 2 or more files and if a network references them, even if the preview displayed is ok, the replacement are not correcty applied. In some case, there is an exception (because the file size is less than specified in the 2nd ReplaceEdit instance), in other case, the second replacement is performed but the last chars of the first version of the file remains at the end, and the first replacement is not visible.

This is due to

new ReplaceEdit(0, content.length(), "the new file content")

I also tried with better (and real) indexes when I initialize the replace edit instances, but no more luck. The problem here is that Eclipse, when it use the MoveParticipant class on more than 1 file, doesn't take care of the results of each replacement to adapt the others (update indexes, etc.)

Is there something in the Eclipse API to ensure many replacements on the same file when 2 files or more are moved will be correctly performed?


Solution

  • I finally found the solution to my issue. My move participant should implements ISharableParticipant interface. With this trick, the RefactoringProcess automatically reuse the participant instance, and register all the files using the addElement() method (the first object is still passed with initialize(). Then the concrete participant have to take care of the whole list of file to create Change instances correctly.