org.eclipse.jdt.internal.core.CopyResourceElementsOperation.java Source code

Java tutorial

Introduction

Here is the source code for org.eclipse.jdt.internal.core.CopyResourceElementsOperation.java

Source

/*******************************************************************************
 * Copyright (c) 2000, 2011 IBM Corporation and others.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     IBM Corporation - initial API and implementation
 *******************************************************************************/
package org.eclipse.jdt.internal.core; // GROOVY PATCHED

import java.util.*;

import org.codehaus.jdt.groovy.integration.LanguageSupportFactory;
import org.eclipse.core.resources.*;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.jobs.ISchedulingRule;
import org.eclipse.core.runtime.jobs.MultiRule;
import org.eclipse.jdt.core.*;
import org.eclipse.jdt.core.dom.AST;
import org.eclipse.jdt.core.dom.ASTNode;
import org.eclipse.jdt.core.dom.ASTParser;
import org.eclipse.jdt.core.dom.AbstractTypeDeclaration;
import org.eclipse.jdt.core.dom.CompilationUnit;
import org.eclipse.jdt.core.dom.Javadoc;
import org.eclipse.jdt.core.dom.MethodDeclaration;
import org.eclipse.jdt.core.dom.Name;
import org.eclipse.jdt.core.dom.PackageDeclaration;
import org.eclipse.jdt.core.dom.SimpleName;
import org.eclipse.jdt.core.dom.rewrite.ASTRewrite;
import org.eclipse.jdt.internal.compiler.util.SuffixConstants;
import org.eclipse.jdt.internal.core.util.Messages;
import org.eclipse.jdt.internal.core.util.Util;
import org.eclipse.text.edits.DeleteEdit;
import org.eclipse.text.edits.InsertEdit;
import org.eclipse.text.edits.MultiTextEdit;
import org.eclipse.text.edits.ReplaceEdit;
import org.eclipse.text.edits.TextEdit;

/**
 * This operation copies/moves/renames a collection of resources from their current
 * container to a new container, optionally renaming the
 * elements.
 * <p>Notes:<ul>
 *    <li>If there is already an resource with the same name in
 *    the new container, the operation either overwrites or aborts,
 *    depending on the collision policy setting. The default setting is
 *     abort.
 *
 *    <li>When a compilation unit is copied to a new package, the
 *    package declaration in the compilation unit is automatically updated.
 *
 *    <li>The collection of elements being copied must all share the
 *    same type of container.
 *
 *    <li>This operation can be used to copy and rename elements within
 *    the same container.
 *
 *    <li>This operation only copies compilation units and package fragments.
 *    It does not copy package fragment roots - a platform operation must be used for that.
 * </ul>
 *
 */
public class CopyResourceElementsOperation extends MultiOperation implements SuffixConstants {
    /**
     * The list of new resources created during this operation.
     */
    protected ArrayList createdElements;
    /**
     * Table specifying deltas for elements being
     * copied/moved/renamed. Keyed by elements' project(s), and
     * values are the corresponding deltas.
     */
    protected Map deltasPerProject = new HashMap(1);
    /**
     * The <code>ASTParser</code> used to manipulate the source code of
     * <code>ICompilationUnit</code>.
     */
    protected ASTParser parser;

    /**
     * When executed, this operation will copy the given resources to the
     * given containers.  The resources and destination containers must be in
     * the correct order. If there is > 1 destination, the number of destinations
     * must be the same as the number of resources being copied/moved.
     */
    public CopyResourceElementsOperation(IJavaElement[] resourcesToCopy, IJavaElement[] destContainers,
            boolean force) {
        super(resourcesToCopy, destContainers, force);
        initializeASTParser();
    }

    private void initializeASTParser() {
        this.parser = ASTParser.newParser(AST.JLS4);
    }

    /**
     * Returns the children of <code>source</code> which are affected by this operation.
     * If <code>source</code> is a <code>K_SOURCE</code>, these are the <code>.java</code>
     * files, if it is a <code>K_BINARY</code>, they are the <code>.class</code> files.
     */
    private IResource[] collectResourcesOfInterest(IPackageFragment source) throws JavaModelException {
        IJavaElement[] children = source.getChildren();
        int childOfInterest = IJavaElement.COMPILATION_UNIT;
        if (source.getKind() == IPackageFragmentRoot.K_BINARY) {
            childOfInterest = IJavaElement.CLASS_FILE;
        }
        ArrayList correctKindChildren = new ArrayList(children.length);
        for (int i = 0; i < children.length; i++) {
            IJavaElement child = children[i];
            if (child.getElementType() == childOfInterest) {
                correctKindChildren.add(((JavaElement) child).resource());
            }
        }
        // Gather non-java resources
        Object[] nonJavaResources = source.getNonJavaResources();
        int actualNonJavaResourceCount = 0;
        for (int i = 0, max = nonJavaResources.length; i < max; i++) {
            if (nonJavaResources[i] instanceof IResource)
                actualNonJavaResourceCount++;
        }
        IResource[] actualNonJavaResources = new IResource[actualNonJavaResourceCount];
        for (int i = 0, max = nonJavaResources.length, index = 0; i < max; i++) {
            if (nonJavaResources[i] instanceof IResource)
                actualNonJavaResources[index++] = (IResource) nonJavaResources[i];
        }

        if (actualNonJavaResourceCount != 0) {
            int correctKindChildrenSize = correctKindChildren.size();
            IResource[] result = new IResource[correctKindChildrenSize + actualNonJavaResourceCount];
            correctKindChildren.toArray(result);
            System.arraycopy(actualNonJavaResources, 0, result, correctKindChildrenSize,
                    actualNonJavaResourceCount);
            return result;
        } else {
            IResource[] result = new IResource[correctKindChildren.size()];
            correctKindChildren.toArray(result);
            return result;
        }
    }

    /**
     * Creates any destination package fragment(s) which do not exists yet.
     * Return true if a read-only package fragment has been found among package fragments, false otherwise
     */
    private boolean createNeededPackageFragments(IContainer sourceFolder, PackageFragmentRoot root,
            String[] newFragName, boolean moveFolder) throws JavaModelException {
        boolean containsReadOnlyPackageFragment = false;
        IContainer parentFolder = (IContainer) root.resource();
        JavaElementDelta projectDelta = null;
        String[] sideEffectPackageName = null;
        char[][] inclusionPatterns = root.fullInclusionPatternChars();
        char[][] exclusionPatterns = root.fullExclusionPatternChars();
        for (int i = 0; i < newFragName.length; i++) {
            String subFolderName = newFragName[i];
            sideEffectPackageName = Util.arrayConcat(sideEffectPackageName, subFolderName);
            IResource subFolder = parentFolder.findMember(subFolderName);
            if (subFolder == null) {
                // create deepest folder only if not a move (folder will be moved in processPackageFragmentResource)
                if (!(moveFolder && i == newFragName.length - 1)) {
                    createFolder(parentFolder, subFolderName, this.force);
                }
                parentFolder = parentFolder.getFolder(new Path(subFolderName));
                sourceFolder = sourceFolder.getFolder(new Path(subFolderName));
                if (Util.isReadOnly(sourceFolder)) {
                    containsReadOnlyPackageFragment = true;
                }
                IPackageFragment sideEffectPackage = root.getPackageFragment(sideEffectPackageName);
                if (i < newFragName.length - 1 // all but the last one are side effect packages
                        && !Util.isExcluded(parentFolder, inclusionPatterns, exclusionPatterns)) {
                    if (projectDelta == null) {
                        projectDelta = getDeltaFor(root.getJavaProject());
                    }
                    projectDelta.added(sideEffectPackage);
                }
                this.createdElements.add(sideEffectPackage);
            } else {
                parentFolder = (IContainer) subFolder;
            }
        }
        return containsReadOnlyPackageFragment;
    }

    /**
     * Returns the <code>JavaElementDelta</code> for <code>javaProject</code>,
     * creating it and putting it in <code>fDeltasPerProject</code> if
     * it does not exist yet.
     */
    private JavaElementDelta getDeltaFor(IJavaProject javaProject) {
        JavaElementDelta delta = (JavaElementDelta) this.deltasPerProject.get(javaProject);
        if (delta == null) {
            delta = new JavaElementDelta(javaProject);
            this.deltasPerProject.put(javaProject, delta);
        }
        return delta;
    }

    /**
     * @see MultiOperation
     */
    protected String getMainTaskName() {
        return Messages.operation_copyResourceProgress;
    }

    protected ISchedulingRule getSchedulingRule() {
        if (this.elementsToProcess == null)
            return null;
        int length = this.elementsToProcess.length;
        if (length == 1)
            return getSchedulingRule(this.elementsToProcess[0]);
        ISchedulingRule[] rules = new ISchedulingRule[length];
        int index = 0;
        for (int i = 0; i < length; i++) {
            ISchedulingRule rule = getSchedulingRule(this.elementsToProcess[i]);
            if (rule != null) {
                rules[index++] = rule;
            }
        }
        if (index != length)
            System.arraycopy(rules, 0, rules = new ISchedulingRule[index], 0, index);
        return new MultiRule(rules);
    }

    private ISchedulingRule getSchedulingRule(IJavaElement element) {
        if (element == null)
            return null;
        IResource sourceResource = getResource(element);
        IResource destContainer = getResource(getDestinationParent(element));
        if (!(destContainer instanceof IContainer)) {
            return null;
        }
        String newName;
        try {
            newName = getNewNameFor(element);
        } catch (JavaModelException e) {
            return null;
        }
        if (newName == null)
            newName = element.getElementName();
        IResource destResource;
        String sourceEncoding = null;
        if (sourceResource.getType() == IResource.FILE) {
            destResource = ((IContainer) destContainer).getFile(new Path(newName));
            try {
                sourceEncoding = ((IFile) sourceResource).getCharset(false);
            } catch (CoreException ce) {
                // use default encoding
            }
        } else {
            destResource = ((IContainer) destContainer).getFolder(new Path(newName));
        }
        IResourceRuleFactory factory = ResourcesPlugin.getWorkspace().getRuleFactory();
        ISchedulingRule rule;
        if (isMove()) {
            rule = factory.moveRule(sourceResource, destResource);
        } else {
            rule = factory.copyRule(sourceResource, destResource);
        }
        if (sourceEncoding != null) {
            rule = new MultiRule(new ISchedulingRule[] { rule, factory.charsetRule(destResource) });
        }
        return rule;
    }

    private IResource getResource(IJavaElement element) {
        if (element == null)
            return null;
        if (element.getElementType() == IJavaElement.PACKAGE_FRAGMENT) {
            String pkgName = element.getElementName();
            int firstDot = pkgName.indexOf('.');
            if (firstDot != -1) {
                element = ((IPackageFragmentRoot) element.getParent())
                        .getPackageFragment(pkgName.substring(0, firstDot));
            }
        }
        return element.getResource();
    }

    /**
     * Sets the deltas to register the changes resulting from this operation
     * for this source element and its destination.
     * If the operation is a cross project operation<ul>
     * <li>On a copy, the delta should be rooted in the dest project
     * <li>On a move, two deltas are generated<ul>
     *          <li>one rooted in the source project
     *         <li>one rooted in the destination project</ul></ul>
     * If the operation is rooted in a single project, the delta is rooted in that project
     *
     */
    protected void prepareDeltas(IJavaElement sourceElement, IJavaElement destinationElement, boolean isMove) {
        if (Util.isExcluded(sourceElement) || Util.isExcluded(destinationElement))
            return;
        IJavaProject destProject = destinationElement.getJavaProject();
        if (isMove) {
            IJavaProject sourceProject = sourceElement.getJavaProject();
            getDeltaFor(sourceProject).movedFrom(sourceElement, destinationElement);
            getDeltaFor(destProject).movedTo(destinationElement, sourceElement);
        } else {
            getDeltaFor(destProject).added(destinationElement);
        }
    }

    /**
     * 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));
        // GROOVY start
        /* old {
        org.eclipse.jdt.internal.core.CompilationUnit destCU = new org.eclipse.jdt.internal.core.CompilationUnit(dest, destName, DefaultWorkingCopyOwner.PRIMARY);
        } new */
        org.eclipse.jdt.internal.core.CompilationUnit destCU = LanguageSupportFactory.newCompilationUnit(dest,
                destName, DefaultWorkingCopyOwner.PRIMARY);
        // GROOVY end

        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);
            }
        }
    }

    /**
     * Process all of the changed deltas generated by this operation.
     */
    protected void processDeltas() {
        for (Iterator deltas = this.deltasPerProject.values().iterator(); deltas.hasNext();) {
            addDelta((IJavaElementDelta) deltas.next());
        }
    }

    /**
     * @see MultiOperation
     * This method delegates to <code>processCompilationUnitResource</code> or
     * <code>processPackageFragmentResource</code>, depending on the type of
     * <code>element</code>.
     */
    protected void processElement(IJavaElement element) throws JavaModelException {
        IJavaElement dest = getDestinationParent(element);
        switch (element.getElementType()) {
        case IJavaElement.COMPILATION_UNIT:
            processCompilationUnitResource((ICompilationUnit) element, (PackageFragment) dest);
            this.createdElements.add(((IPackageFragment) dest).getCompilationUnit(element.getElementName()));
            break;
        case IJavaElement.PACKAGE_FRAGMENT:
            processPackageFragmentResource((PackageFragment) element, (PackageFragmentRoot) dest,
                    getNewNameFor(element));
            break;
        default:
            throw new JavaModelException(
                    new JavaModelStatus(IJavaModelStatusConstants.INVALID_ELEMENT_TYPES, element));
        }
    }

    /**
     * @see MultiOperation
     * Overridden to allow special processing of <code>JavaElementDelta</code>s
     * and <code>fResultElements</code>.
     */
    protected void processElements() throws JavaModelException {
        this.createdElements = new ArrayList(this.elementsToProcess.length);
        try {
            super.processElements();
        } catch (JavaModelException jme) {
            throw jme;
        } finally {
            this.resultElements = new IJavaElement[this.createdElements.size()];
            this.createdElements.toArray(this.resultElements);
            processDeltas();
        }
    }

    /**
     * Copies/moves a package fragment with the name <code>newName</code>
     * to the destination package.<br>
     *
     * @exception JavaModelException if the operation is unable to
     * complete
     */
    private void processPackageFragmentResource(PackageFragment source, PackageFragmentRoot root, String newName)
            throws JavaModelException {
        try {
            String[] newFragName = (newName == null) ? source.names : Util.getTrimmedSimpleNames(newName);
            PackageFragment newFrag = root.getPackageFragment(newFragName);
            IResource[] resources = collectResourcesOfInterest(source);

            // if isMove() can we move the folder itself ? (see http://bugs.eclipse.org/bugs/show_bug.cgi?id=22458)
            boolean shouldMoveFolder = isMove() && !newFrag.resource().exists(); // if new pkg fragment exists, it is an override
            IFolder srcFolder = (IFolder) source.resource();
            IPath destPath = newFrag.getPath();
            if (shouldMoveFolder) {
                // check if destination is not included in source
                if (srcFolder.getFullPath().isPrefixOf(destPath)) {
                    shouldMoveFolder = false;
                } else {
                    // check if there are no sub-packages
                    IResource[] members = srcFolder.members();
                    for (int i = 0; i < members.length; i++) {
                        if (members[i] instanceof IFolder) {
                            shouldMoveFolder = false;
                            break;
                        }
                    }
                }
            }
            boolean containsReadOnlySubPackageFragments = createNeededPackageFragments(
                    (IContainer) source.parent.resource(), root, newFragName, shouldMoveFolder);
            boolean sourceIsReadOnly = Util.isReadOnly(srcFolder);

            // Process resources
            if (shouldMoveFolder) {
                // move underlying resource
                // TODO Revisit once bug 43044 is fixed
                if (sourceIsReadOnly) {
                    Util.setReadOnly(srcFolder, false);
                }
                srcFolder.move(destPath, this.force, true /* keep history */, getSubProgressMonitor(1));
                if (sourceIsReadOnly) {
                    Util.setReadOnly(srcFolder, true);
                }
                setAttribute(HAS_MODIFIED_RESOURCE_ATTR, TRUE);
            } else {
                // process the leaf resources
                if (resources.length > 0) {
                    if (isRename()) {
                        if (!destPath.equals(source.getPath())) {
                            moveResources(resources, destPath);
                        }
                    } else if (isMove()) {
                        // we need to delete this resource if this operation wants to override existing resources
                        for (int i = 0, max = resources.length; i < max; i++) {
                            IResource destinationResource = ResourcesPlugin.getWorkspace().getRoot()
                                    .findMember(destPath.append(resources[i].getName()));
                            if (destinationResource != null) {
                                if (this.force) {
                                    deleteResource(destinationResource, IResource.KEEP_HISTORY);
                                } else {
                                    throw new JavaModelException(
                                            new JavaModelStatus(IJavaModelStatusConstants.NAME_COLLISION,
                                                    Messages.bind(Messages.status_nameCollision,
                                                            destinationResource.getFullPath().toString())));
                                }
                            }
                        }
                        moveResources(resources, destPath);
                    } else {
                        // we need to delete this resource if this operation wants to override existing resources
                        for (int i = 0, max = resources.length; i < max; i++) {
                            IResource destinationResource = ResourcesPlugin.getWorkspace().getRoot()
                                    .findMember(destPath.append(resources[i].getName()));
                            if (destinationResource != null) {
                                if (this.force) {
                                    // we need to delete this resource if this operation wants to override existing resources
                                    deleteResource(destinationResource, IResource.KEEP_HISTORY);
                                } else {
                                    throw new JavaModelException(
                                            new JavaModelStatus(IJavaModelStatusConstants.NAME_COLLISION,
                                                    Messages.bind(Messages.status_nameCollision,
                                                            destinationResource.getFullPath().toString())));
                                }
                            }
                        }
                        copyResources(resources, destPath);
                    }
                }
            }

            // Update package statement in compilation unit if needed
            if (!Util.equalArraysOrNull(newFragName, source.names)) { // if package has been renamed, update the compilation units
                char[][] inclusionPatterns = root.fullInclusionPatternChars();
                char[][] exclusionPatterns = root.fullExclusionPatternChars();
                for (int i = 0; i < resources.length; i++) {
                    String resourceName = resources[i].getName();
                    if (Util.isJavaLikeFileName(resourceName)) {
                        // we only consider potential compilation units
                        ICompilationUnit cu = newFrag.getCompilationUnit(resourceName);
                        if (Util.isExcluded(cu.getPath(), inclusionPatterns, exclusionPatterns,
                                false/*not a folder*/))
                            continue;
                        this.parser.setSource(cu);
                        CompilationUnit astCU = (CompilationUnit) this.parser.createAST(this.progressMonitor);
                        AST ast = astCU.getAST();
                        ASTRewrite rewrite = ASTRewrite.create(ast);
                        updatePackageStatement(astCU, newFragName, rewrite, cu);
                        TextEdit edits = rewrite.rewriteAST();
                        applyTextEdit(cu, edits);
                        cu.save(null, false);
                    }
                }
            }

            // Discard empty old package (if still empty after the rename)
            boolean isEmpty = true;
            if (isMove()) {
                // delete remaining files in this package (.class file in the case where Proj=src=bin)
                // in case of a copy
                updateReadOnlyPackageFragmentsForMove((IContainer) source.parent.resource(), root, newFragName,
                        sourceIsReadOnly);
                if (srcFolder.exists()) {
                    IResource[] remaining = srcFolder.members();
                    for (int i = 0, length = remaining.length; i < length; i++) {
                        IResource file = remaining[i];
                        if (file instanceof IFile) {
                            if (Util.isReadOnly(file)) {
                                Util.setReadOnly(file, false);
                            }
                            deleteResource(file, IResource.FORCE | IResource.KEEP_HISTORY);
                        } else {
                            isEmpty = false;
                        }
                    }
                }
                if (isEmpty) {
                    IResource rootResource;
                    // check if source is included in destination
                    if (destPath.isPrefixOf(srcFolder.getFullPath())) {
                        rootResource = newFrag.resource();
                    } else {
                        rootResource = source.parent.resource();
                    }

                    // delete recursively empty folders
                    deleteEmptyPackageFragment(source, false, rootResource);
                }
            } else if (containsReadOnlySubPackageFragments) {
                // in case of a copy
                updateReadOnlyPackageFragmentsForCopy((IContainer) source.parent.resource(), root, newFragName);
            }
            // workaround for bug https://bugs.eclipse.org/bugs/show_bug.cgi?id=24505
            if (isEmpty && isMove() && !(Util.isExcluded(source) || Util.isExcluded(newFrag))) {
                IJavaProject sourceProject = source.getJavaProject();
                getDeltaFor(sourceProject).movedFrom(source, newFrag);
                IJavaProject destProject = newFrag.getJavaProject();
                getDeltaFor(destProject).movedTo(newFrag, source);
            }
        } catch (JavaModelException e) {
            throw e;
        } catch (CoreException ce) {
            throw new JavaModelException(ce);
        }
    }

    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);
    }

    /**
     * Updates the content of <code>cu</code>, modifying the type name and/or package
     * declaration as necessary.
     *
     * @return an AST rewrite or null if no rewrite needed
     */
    private TextEdit updateContent(ICompilationUnit cu, PackageFragment dest, String newName)
            throws JavaModelException {
        String[] currPackageName = ((PackageFragment) cu.getParent()).names;
        String[] destPackageName = dest.names;
        if (Util.equalArraysOrNull(currPackageName, destPackageName) && newName == null) {
            return null; //nothing to change
        } else {
            // ensure cu is consistent (noop if already consistent)
            cu.makeConsistent(this.progressMonitor);

            // GROOVY start
            // don't use the ASTParser if not a Java compilation unit
            if (LanguageSupportFactory.isInterestingSourceFile(cu.getElementName())) {
                return updateNonJavaContent(cu, destPackageName, currPackageName, newName);
            }
            // GROOVY end

            this.parser.setSource(cu);
            CompilationUnit astCU = (CompilationUnit) this.parser.createAST(this.progressMonitor);
            AST ast = astCU.getAST();
            ASTRewrite rewrite = ASTRewrite.create(ast);
            updateTypeName(cu, astCU, cu.getElementName(), newName, rewrite);
            updatePackageStatement(astCU, destPackageName, rewrite, cu);
            return rewrite.rewriteAST();
        }
    }

    // GROOVY start
    // create the content for non-Java files 
    private TextEdit updateNonJavaContent(ICompilationUnit cu, String[] destPackageName, String[] currPackageName,
            String newName) throws JavaModelException {
        // package statement
        IPackageDeclaration[] packageDecls = cu.getPackageDeclarations();
        boolean doPackage = !Util.equalArraysOrNull(currPackageName, destPackageName);
        boolean doName = newName != null;
        MultiTextEdit multiEdit = new MultiTextEdit();
        if (doPackage) {
            if (packageDecls.length == 1) {
                ISourceRange packageRange = packageDecls[0].getSourceRange();
                if (destPackageName == null || destPackageName.length == 0) {
                    // move to default package
                    multiEdit.addChild(new DeleteEdit(packageRange.getOffset(), packageRange.getLength()));
                } else {
                    multiEdit.addChild(new ReplaceEdit(packageRange.getOffset(), packageRange.getLength(),
                            "package " + Util.concatWith(destPackageName, '.'))); //$NON-NLS-1$
                }
            } else {
                // move from default package
                // we don't keep track of comments, so we don't know where they start.  Just add the package declaration at location 0
                multiEdit.addChild(new InsertEdit(0, "package " + Util.concatWith(destPackageName, '.') + "\n")); //$NON-NLS-1$//$NON-NLS-2$
            }
        }

        if (doName) {
            int dotIndex = cu.getElementName().indexOf('.');
            dotIndex = dotIndex == -1 ? cu.getElementName().length() : dotIndex;
            String oldTypeName = cu.getElementName().substring(0, dotIndex);
            dotIndex = newName.indexOf('.');
            dotIndex = dotIndex == -1 ? newName.length() : dotIndex;
            String newTypeName = newName.substring(0, dotIndex);
            IType type = cu.getType(oldTypeName);
            if (type.exists()) {
                ISourceRange nameRange = type.getNameRange();
                // main type can be implicitly defined, so check offsets
                if (nameRange.getOffset() > 0 && nameRange.getLength() > 0
                        && oldTypeName.length() == nameRange.getLength()) {
                    multiEdit.addChild(new ReplaceEdit(nameRange.getOffset(), nameRange.getLength(), newTypeName));
                }
                IJavaElement[] children = type.getChildren();
                for (int i = 0; i < children.length; i++) {
                    if (children[i].getElementType() == IJavaElement.METHOD) {
                        IMethod method = (IMethod) children[i];
                        if (method.isConstructor()) {
                            nameRange = method.getNameRange();
                            // main type can be implicitly defined, so check offsets
                            if (nameRange.getOffset() > 0 && nameRange.getLength() > 0) {
                                multiEdit.addChild(
                                        new ReplaceEdit(nameRange.getOffset(), nameRange.getLength(), newTypeName));
                            }
                        }
                    }
                }
            }
        }
        return multiEdit;
    }
    // GROOVY end

    private void updatePackageStatement(CompilationUnit astCU, String[] pkgName, ASTRewrite rewriter,
            ICompilationUnit cu) throws JavaModelException {
        boolean defaultPackage = pkgName.length == 0;
        AST ast = astCU.getAST();
        if (defaultPackage) {
            // remove existing package statement
            PackageDeclaration pkg = astCU.getPackage();
            if (pkg != null) {
                int pkgStart;
                Javadoc javadoc = pkg.getJavadoc();
                if (javadoc != null) {
                    pkgStart = javadoc.getStartPosition() + javadoc.getLength() + 1;
                } else {
                    pkgStart = pkg.getStartPosition();
                }
                int extendedStart = astCU.getExtendedStartPosition(pkg);
                if (pkgStart != extendedStart) {
                    // keep the comments associated with package declaration
                    // see https://bugs.eclipse.org/bugs/show_bug.cgi?id=247757
                    String commentSource = cu.getSource().substring(extendedStart, pkgStart);
                    ASTNode comment = rewriter.createStringPlaceholder(commentSource, ASTNode.PACKAGE_DECLARATION);
                    rewriter.set(astCU, CompilationUnit.PACKAGE_PROPERTY, comment, null);
                } else {
                    rewriter.set(astCU, CompilationUnit.PACKAGE_PROPERTY, null, null);
                }
            }
        } else {
            org.eclipse.jdt.core.dom.PackageDeclaration pkg = astCU.getPackage();
            if (pkg != null) {
                // rename package statement
                Name name = ast.newName(pkgName);
                rewriter.set(pkg, PackageDeclaration.NAME_PROPERTY, name, null);
            } else {
                // create new package statement
                pkg = ast.newPackageDeclaration();
                pkg.setName(ast.newName(pkgName));
                rewriter.set(astCU, CompilationUnit.PACKAGE_PROPERTY, pkg, null);
            }
        }
    }

    private void updateReadOnlyPackageFragmentsForCopy(IContainer sourceFolder, PackageFragmentRoot root,
            String[] newFragName) {
        IContainer parentFolder = (IContainer) root.resource();
        for (int i = 0, length = newFragName.length; i < length; i++) {
            String subFolderName = newFragName[i];
            parentFolder = parentFolder.getFolder(new Path(subFolderName));
            sourceFolder = sourceFolder.getFolder(new Path(subFolderName));
            if (sourceFolder.exists() && Util.isReadOnly(sourceFolder)) {
                Util.setReadOnly(parentFolder, true);
            }
        }
    }

    private void updateReadOnlyPackageFragmentsForMove(IContainer sourceFolder, PackageFragmentRoot root,
            String[] newFragName, boolean sourceFolderIsReadOnly) {
        IContainer parentFolder = (IContainer) root.resource();
        for (int i = 0, length = newFragName.length; i < length; i++) {
            String subFolderName = newFragName[i];
            parentFolder = parentFolder.getFolder(new Path(subFolderName));
            sourceFolder = sourceFolder.getFolder(new Path(subFolderName));
            if ((sourceFolder.exists() && Util.isReadOnly(sourceFolder))
                    || (i == length - 1 && sourceFolderIsReadOnly)) {
                Util.setReadOnly(parentFolder, true);
                // the source folder will be deleted anyway (move operation)
                Util.setReadOnly(sourceFolder, false);
            }
        }
    }

    /**
    * Renames the main type in <code>cu</code>.
    */
    private void updateTypeName(ICompilationUnit cu, CompilationUnit astCU, String oldName, String newName,
            ASTRewrite rewriter) throws JavaModelException {
        if (newName != null) {
            String oldTypeName = Util.getNameWithoutJavaLikeExtension(oldName);
            String newTypeName = Util.getNameWithoutJavaLikeExtension(newName);
            AST ast = astCU.getAST();
            // update main type name
            IType[] types = cu.getTypes();
            for (int i = 0, max = types.length; i < max; i++) {
                IType currentType = types[i];
                if (currentType.getElementName().equals(oldTypeName)) {
                    AbstractTypeDeclaration typeNode = (AbstractTypeDeclaration) ((JavaElement) currentType)
                            .findNode(astCU);
                    if (typeNode != null) {
                        // rename type
                        rewriter.replace(typeNode.getName(), ast.newSimpleName(newTypeName), null);
                        // rename constructors
                        Iterator bodyDeclarations = typeNode.bodyDeclarations().iterator();
                        while (bodyDeclarations.hasNext()) {
                            Object bodyDeclaration = bodyDeclarations.next();
                            if (bodyDeclaration instanceof MethodDeclaration) {
                                MethodDeclaration methodDeclaration = (MethodDeclaration) bodyDeclaration;
                                if (methodDeclaration.isConstructor()) {
                                    SimpleName methodName = methodDeclaration.getName();
                                    if (methodName.getIdentifier().equals(oldTypeName)) {
                                        rewriter.replace(methodName, ast.newSimpleName(newTypeName), null);
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }

    /**
     * Possible failures:
     * <ul>
     *  <li>NO_ELEMENTS_TO_PROCESS - no elements supplied to the operation
     *   <li>INDEX_OUT_OF_BOUNDS - the number of renamings supplied to the operation
     *      does not match the number of elements that were supplied.
     * </ul>
     */
    protected IJavaModelStatus verify() {
        IJavaModelStatus status = super.verify();
        if (!status.isOK()) {
            return status;
        }

        if (this.renamingsList != null && this.renamingsList.length != this.elementsToProcess.length) {
            return new JavaModelStatus(IJavaModelStatusConstants.INDEX_OUT_OF_BOUNDS);
        }
        return JavaModelStatus.VERIFIED_OK;
    }

    /**
     * @see MultiOperation
     */
    protected void verify(IJavaElement element) throws JavaModelException {
        if (element == null || !element.exists())
            error(IJavaModelStatusConstants.ELEMENT_DOES_NOT_EXIST, element);

        if (element.isReadOnly() && (isRename() || isMove()))
            error(IJavaModelStatusConstants.READ_ONLY, element);

        IResource resource = ((JavaElement) element).resource();
        if (resource instanceof IFolder) {
            if (resource.isLinked()) {
                error(IJavaModelStatusConstants.INVALID_RESOURCE, element);
            }
        }

        int elementType = element.getElementType();

        if (elementType == IJavaElement.COMPILATION_UNIT) {
            org.eclipse.jdt.internal.core.CompilationUnit compilationUnit = (org.eclipse.jdt.internal.core.CompilationUnit) element;
            if (isMove() && compilationUnit.isWorkingCopy() && !compilationUnit.isPrimary())
                error(IJavaModelStatusConstants.INVALID_ELEMENT_TYPES, element);
        } else if (elementType != IJavaElement.PACKAGE_FRAGMENT) {
            error(IJavaModelStatusConstants.INVALID_ELEMENT_TYPES, element);
        }

        JavaElement dest = (JavaElement) getDestinationParent(element);
        verifyDestination(element, dest);
        if (this.renamings != null) {
            verifyRenaming(element);
        }
    }
}