/** * Copyright (c) 2005-2013 by Appcelerator, Inc. All Rights Reserved. * Licensed under the terms of the Eclipse Public License (EPL). * Please see the license.txt included with this distribution for details. * Any modifications to this file must keep this entire header intact. */ package com.python.pydev.refactoring.changes; import java.io.ByteArrayInputStream; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; import org.eclipse.core.resources.IContainer; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IFolder; import org.eclipse.core.resources.IResource; import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.Path; import org.eclipse.ltk.core.refactoring.Change; import org.eclipse.ltk.core.refactoring.RefactoringStatus; import org.python.pydev.core.FullRepIterable; import org.python.pydev.shared_core.string.StringUtils; import org.python.pydev.shared_core.utils.ArrayUtils; /** * This action is able to do a rename / move for some python module. */ public final class PyRenameResourceChange extends PyChange { private final String fComment; private final String fNewName; private final IPath fResourcePath; private final long fStampToRestore; private final String fInitialName; private final IResource[] fCreatedFiles; private IContainer target; private PyRenameResourceChange(IPath resourcePath, String initialName, String newName, String comment, long stampToRestore, IResource[] createdFiles) { fResourcePath = resourcePath; fNewName = newName; fInitialName = initialName; fComment = comment; fStampToRestore = stampToRestore; fCreatedFiles = createdFiles; } /** * @param target: if passed, that's the destination. Otherwise it'll be computed based on the current location * (i.e.: won't change source folder). */ public PyRenameResourceChange(IResource resource, String initialName, String newName, String comment, IContainer target) { this(resource.getFullPath(), initialName, newName, comment, IResource.NULL_STAMP, new IResource[0]); this.target = target; } @Override public Object getModifiedElement() { return getResource(); } @Override public String getName() { return StringUtils.format("Change %s to %s", fInitialName, fNewName); } public String getNewName() { return fNewName; } private IResource getResource() { return ResourcesPlugin.getWorkspace().getRoot().findMember(fResourcePath); } @Override public RefactoringStatus isValid(IProgressMonitor pm) throws CoreException { IResource resource = getResource(); if (resource == null || !resource.exists()) { return RefactoringStatus.createFatalErrorStatus(StringUtils.format( "Resource %s does not exist", fResourcePath)); } else { return super.isValid(pm, DIRTY); } } @Override public Change perform(IProgressMonitor pm) throws CoreException { try { pm.beginTask(getName(), 1); IResource resource = getResource(); long currentStamp = resource.getModificationStamp(); IContainer destination = target != null ? target : getDestination(resource, fInitialName, fNewName, pm); IResource[] createdFiles = createDestination(destination); IPath newPath; boolean copyChildrenInsteadOfMove = false; if (resource.getType() == IResource.FILE) { //Renaming file newPath = destination.getFullPath().append(FullRepIterable.getLastPart(fNewName) + ".py"); } else { //Renaming folder newPath = destination.getFullPath().append(FullRepIterable.getLastPart(fNewName)); IPath fullPath = resource.getFullPath(); if (fullPath.isPrefixOf(newPath)) { copyChildrenInsteadOfMove = true; } } if (copyChildrenInsteadOfMove) { IContainer container = (IContainer) resource; IResource[] members = container.members(true); //Note: get the members before creating the target. IFolder folder = container.getFolder(new Path(newPath.lastSegment())); IFile initFile = container.getFile(new Path("__init__.py")); folder.create(IResource.NONE, true, null); createdFiles = ArrayUtils.concatArrays(createdFiles, new IResource[] { folder }); for (IResource member : members) { member.move(newPath.append(member.getFullPath().lastSegment()), IResource.SHALLOW, pm); } initFile.create(new ByteArrayInputStream(new byte[0]), IResource.NONE, null); } else { //simple move resource.move(newPath, IResource.SHALLOW, pm); } if (fStampToRestore != IResource.NULL_STAMP) { IResource newResource = ResourcesPlugin.getWorkspace().getRoot().findMember(newPath); newResource.revertModificationStamp(fStampToRestore); } for (IResource r : this.fCreatedFiles) { r.delete(true, null); } //The undo command return new PyRenameResourceChange(newPath, fNewName, fInitialName, fComment, currentStamp, createdFiles); } finally { pm.done(); } } /** * Creates the destination folder and returns the created files. */ private IResource[] createDestination(IContainer destination) throws CoreException { ArrayList<IResource> lst = new ArrayList<IResource>(); if (!destination.exists()) { //Create parent structure first IContainer parent = destination.getParent(); lst.addAll(Arrays.asList(createDestination(parent))); IFolder folder = parent.getFolder(new Path(destination.getFullPath().lastSegment())); IFile file = destination.getFile(new Path("__init__.py")); folder.create(IResource.NONE, true, null); file.create(new ByteArrayInputStream(new byte[0]), IResource.NONE, null); //Add in the order to delete later (so, first file then folder). lst.add(file); lst.add(folder); } return lst.toArray(new IResource[lst.size()]); } /** * Returns the final folder for the created module and the resources created in the process. * * Receives the resource (i.e.: in filesystem), the resolved name (i.e.: my.mod1) and the final name (i.e.: bar.foo). */ public static IContainer getDestination(IResource initialResource, String initialName, String finalName, IProgressMonitor pm) { List<String> initialParts = StringUtils.split(initialName, "."); List<String> finalParts = StringUtils.split(finalName, "."); int startFrom = 0; int finalPartSize = finalParts.size(); int initialPartSize = initialParts.size(); initialPartSize--; String initialNamePart = initialParts.remove(initialPartSize); //remove the last, as that's the name finalPartSize--; String finalNamePart = finalParts.remove(finalPartSize); //remove the last, as that's the name //Get variable startFrom to the first place where the parts differ. for (; startFrom < finalPartSize; startFrom++) { String part = finalParts.get(startFrom); if (startFrom < initialPartSize) { String initial = initialParts.get(startFrom); if (!initial.equals(part)) { break; } } else { break; } } List<String> createParts = finalParts.subList(startFrom, finalPartSize); //the last path is the file, not the folder, so, skip it. List<String> backtrackParts = initialParts.subList(startFrom, initialPartSize); Collections.reverse(backtrackParts); IResource resource = initialResource; IContainer container = resource.getParent(); //always start from our container. for (String string : backtrackParts) { container = container.getParent(); } if (createParts.size() > 0) { container = container.getFolder(new Path(StringUtils.join("/", createParts))); } return container; } }