X-Git-Url: http://git.phpeclipse.com diff --git a/net.sourceforge.phpeclipse/src/net/sourceforge/phpdt/internal/core/CopyResourceElementsOperation.java b/net.sourceforge.phpeclipse/src/net/sourceforge/phpdt/internal/core/CopyResourceElementsOperation.java new file mode 100644 index 0000000..2e30065 --- /dev/null +++ b/net.sourceforge.phpeclipse/src/net/sourceforge/phpdt/internal/core/CopyResourceElementsOperation.java @@ -0,0 +1,648 @@ +/******************************************************************************* + * Copyright (c) 2000, 2003 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Common Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/cpl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package net.sourceforge.phpdt.internal.core; + +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; + +import net.sourceforge.phpdt.core.IBuffer; +import net.sourceforge.phpdt.core.ICompilationUnit; +import net.sourceforge.phpdt.core.IJavaElement; +import net.sourceforge.phpdt.core.IJavaElementDelta; +import net.sourceforge.phpdt.core.IJavaModelStatus; +import net.sourceforge.phpdt.core.IJavaModelStatusConstants; +import net.sourceforge.phpdt.core.IJavaProject; +import net.sourceforge.phpdt.core.IPackageFragment; +import net.sourceforge.phpdt.core.IPackageFragmentRoot; +import net.sourceforge.phpdt.core.IType; +import net.sourceforge.phpdt.core.JavaModelException; +import net.sourceforge.phpdt.core.jdom.DOMException; +import net.sourceforge.phpdt.core.jdom.DOMFactory; +import net.sourceforge.phpdt.core.jdom.IDOMCompilationUnit; +import net.sourceforge.phpdt.core.jdom.IDOMNode; +import net.sourceforge.phpdt.core.jdom.IDOMPackage; +import net.sourceforge.phpeclipse.PHPCore; + +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.Path; + +/** + * This operation copies/moves/renames a collection of resources from their current + * container to a new container, optionally renaming the + * elements. + *

Notes:

+ * + */ +public class CopyResourceElementsOperation extends MultiOperation { + /** + * A collection of renamed compilation units. These cus do + * not need to be saved as they no longer exist. + */ + protected ArrayList fRenamedCompilationUnits = null; + /** + * Table specifying deltas for elements being + * copied/moved/renamed. Keyed by elements' project(s), and + * values are the corresponding deltas. + */ + protected Map fDeltasPerProject= new HashMap(1); + /** + * The DOMFactory used to manipulate the source code of + * ICompilationUnit. + */ + protected DOMFactory fFactory; + /** + * The list of new resources created during this operation. + */ + protected ArrayList fCreatedElements; + /** + * 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); + fFactory = new DOMFactory(); + } + /** + * When executed, this operation will copy the given resources to the + * given container. + */ + public CopyResourceElementsOperation(IJavaElement[] resourcesToCopy, IJavaElement destContainer, boolean force) { + this(resourcesToCopy, new IJavaElement[]{destContainer}, force); + } + /** + * Returns the children of source which are affected by this operation. + * If source is a K_SOURCE, these are the .java + * files, if it is a K_BINARY, they are the .class 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(child.getResource()); + } + } + // 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. + */ + private void createNeededPackageFragments(IContainer sourceFolder, IPackageFragmentRoot root, String newFragName, boolean moveFolder) throws JavaModelException { + IContainer parentFolder = (IContainer) root.getResource(); + JavaElementDelta projectDelta = null; + String[] names = net.sourceforge.phpdt.internal.core.Util.getTrimmedSimpleNames(newFragName); + StringBuffer sideEffectPackageName = new StringBuffer(); + char[][] exclusionsPatterns = ((PackageFragmentRoot)root).fullExclusionPatternChars(); + for (int i = 0; i < names.length; i++) { + String subFolderName = names[i]; + sideEffectPackageName.append(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 == names.length-1)) { + createFolder(parentFolder, subFolderName, fForce); + } + parentFolder = parentFolder.getFolder(new Path(subFolderName)); + sourceFolder = sourceFolder.getFolder(new Path(subFolderName)); + if (sourceFolder.isReadOnly()) { + parentFolder.setReadOnly(true); + } + IPackageFragment sideEffectPackage = root.getPackageFragment(sideEffectPackageName.toString()); + if (i < names.length - 1 // all but the last one are side effect packages + && !net.sourceforge.phpdt.internal.core.Util.isExcluded(parentFolder, exclusionsPatterns)) { + if (projectDelta == null) { + projectDelta = getDeltaFor(root.getJavaProject()); + } + projectDelta.added(sideEffectPackage); + } + fCreatedElements.add(sideEffectPackage); + } else { + parentFolder = (IContainer) subFolder; + } + sideEffectPackageName.append('.'); + } + } + /** + * Returns the JavaElementDelta for javaProject, + * creating it and putting it in fDeltasPerProject if + * it does not exist yet. + */ + private JavaElementDelta getDeltaFor(IJavaProject javaProject) { + JavaElementDelta delta = (JavaElementDelta) fDeltasPerProject.get(javaProject); + if (delta == null) { + delta = new JavaElementDelta(javaProject); + fDeltasPerProject.put(javaProject, delta); + } + return delta; + } + /** + * @see MultiOperation + */ + protected String getMainTaskName() { + return net.sourceforge.phpdt.internal.core.Util.bind("operation.copyResourceProgress"); //$NON-NLS-1$ + } + /** + * 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 + * 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 (net.sourceforge.phpdt.internal.core.Util.isExcluded(sourceElement) || net.sourceforge.phpdt.internal.core.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 newCUName + * to the destination package.
+ * 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, IPackageFragment dest) throws JavaModelException { + String newCUName = getNewNameFor(source); + String destName = (newCUName != null) ? newCUName : source.getElementName(); + String newContent = updatedContent(source, dest, newCUName); // null if unchanged + + // copy resource + IFile sourceResource = (IFile)(source.isWorkingCopy() ? source.getOriginalElement() : source).getResource(); + IContainer destFolder = (IContainer)dest.getResource(); // can be an IFolder or an IProject + IFile destFile = destFolder.getFile(new Path(destName)); + if (!destFile.equals(sourceResource)) { + try { + if (destFile.exists()) { + if (fForce) { + // we can remove it + deleteResource(destFile, IResource.KEEP_HISTORY); + } else { + // abort + throw new JavaModelException(new JavaModelStatus( + IJavaModelStatusConstants.NAME_COLLISION, + Util.bind("status.nameCollision", destFile.getFullPath().toString()))); //$NON-NLS-1$ + } + } + int flags = fForce ? IResource.FORCE : IResource.NONE; + if (this.isMove()) { + flags |= IResource.KEEP_HISTORY; + sourceResource.move(destFile.getFullPath(), flags, getSubProgressMonitor(1)); + } else { + if (newContent != null) flags |= IResource.KEEP_HISTORY; + sourceResource.copy(destFile.getFullPath(), flags, getSubProgressMonitor(1)); + } + this.setAttribute(HAS_MODIFIED_RESOURCE_ATTR, TRUE); + } catch (JavaModelException e) { + throw e; + } catch (CoreException e) { + throw new JavaModelException(e); + } + + // update new resource content + try { + if (newContent != null){ + String encoding = source.getJavaProject().getOption(PHPCore.CORE_ENCODING, true); + destFile.setContents( + new ByteArrayInputStream(encoding == null ? newContent.getBytes() : newContent.getBytes(encoding)), + fForce ? IResource.FORCE | IResource.KEEP_HISTORY : IResource.KEEP_HISTORY, + getSubProgressMonitor(1)); + } + } catch(IOException e) { + throw new JavaModelException(e, IJavaModelStatusConstants.IO_EXCEPTION); + } catch (CoreException e) { + throw new JavaModelException(e); + } + + // register the correct change deltas + ICompilationUnit destCU = dest.getCompilationUnit(destName); + prepareDeltas(source, destCU, isMove()); + if (newCUName != null) { + //the main type has been renamed + String oldName = source.getElementName(); + oldName = oldName.substring(0, oldName.length() - 5); + String newName = newCUName; + newName = newName.substring(0, newName.length() - 5); + prepareDeltas(source.getType(oldName), destCU.getType(newName), isMove()); + } + } else { + if (!fForce) { + throw new JavaModelException(new JavaModelStatus( + IJavaModelStatusConstants.NAME_COLLISION, + Util.bind("status.nameCollision", destFile.getFullPath().toString()))); //$NON-NLS-1$ + } + // 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 + try { + if (newContent != null){ + String encoding = source.getJavaProject().getOption(PHPCore.CORE_ENCODING, true); + destFile.setContents( + new ByteArrayInputStream(encoding == null ? newContent.getBytes() : newContent.getBytes(encoding)), + fForce ? IResource.FORCE | IResource.KEEP_HISTORY : IResource.KEEP_HISTORY, + getSubProgressMonitor(1)); + } + } catch(IOException e) { + throw new JavaModelException(e, IJavaModelStatusConstants.IO_EXCEPTION); + } catch (CoreException e) { + throw new JavaModelException(e); + } + } + } + /** + * Process all of the changed deltas generated by this operation. + */ + protected void processDeltas() { + for (Iterator deltas = this.fDeltasPerProject.values().iterator(); deltas.hasNext();){ + addDelta((IJavaElementDelta) deltas.next()); + } + } + /** + * @see MultiOperation + * This method delegates to processCompilationUnitResource or + * processPackageFragmentResource, depending on the type of + * element. + */ + protected void processElement(IJavaElement element) throws JavaModelException { + IJavaElement dest = getDestinationParent(element); + switch (element.getElementType()) { + case IJavaElement.COMPILATION_UNIT : + processCompilationUnitResource((ICompilationUnit) element, (IPackageFragment) dest); + fCreatedElements.add(((IPackageFragment) dest).getCompilationUnit(element.getElementName())); + break; + case IJavaElement.PACKAGE_FRAGMENT : + processPackageFragmentResource((IPackageFragment) element, (IPackageFragmentRoot) dest, getNewNameFor(element)); + break; + default : + throw new JavaModelException(new JavaModelStatus(IJavaModelStatusConstants.INVALID_ELEMENT_TYPES, element)); + } + } + /** + * @see MultiOperation + * Overridden to allow special processing of JavaElementDeltas + * and fResultElements. + */ + protected void processElements() throws JavaModelException { + fCreatedElements = new ArrayList(fElementsToProcess.length); + try { + super.processElements(); + } catch (JavaModelException jme) { + throw jme; + } finally { + fResultElements = new IJavaElement[fCreatedElements.size()]; + fCreatedElements.toArray(fResultElements); + processDeltas(); + } + } + /** + * Copies/moves a package fragment with the name newName + * to the destination package.
+ * + * @exception JavaModelException if the operation is unable to + * complete + */ + private void processPackageFragmentResource(IPackageFragment source, IPackageFragmentRoot root, String newName) throws JavaModelException { + try { + String newFragName = (newName == null) ? source.getElementName() : newName; + IPackageFragment 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.getResource().exists(); // if new pkg fragment exists, it is an override + IFolder srcFolder = (IFolder)source.getResource(); + 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; + } + } + } + } + createNeededPackageFragments((IContainer) source.getParent().getResource(), root, newFragName, shouldMoveFolder); + + // Process resources + if (shouldMoveFolder) { + // move underlying resource + srcFolder.move(destPath, fForce, true /* keep history */, getSubProgressMonitor(1)); + this.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 (fForce) { + deleteResource(destinationResource, IResource.KEEP_HISTORY); + } else { + throw new JavaModelException(new JavaModelStatus( + IJavaModelStatusConstants.NAME_COLLISION, + Util.bind("status.nameCollision", destinationResource.getFullPath().toString()))); //$NON-NLS-1$ + } + } + } + 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 (fForce) { + // 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, + Util.bind("status.nameCollision", destinationResource.getFullPath().toString()))); //$NON-NLS-1$ + } + } + } + copyResources(resources, destPath); + } + } + } + + // 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) + if (srcFolder.exists()) { + IResource[] remaingFiles = srcFolder.members(); + for (int i = 0, length = remaingFiles.length; i < length; i++) { + IResource file = remaingFiles[i]; + if (file instanceof IFile) { + this.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.getResource(); + } else { + rootResource = source.getParent().getResource(); + } + + // delete recursively empty folders + deleteEmptyPackageFragment(source, false, rootResource); + } + } + + // Update package statement in compilation unit if needed + if (!newFrag.getElementName().equals(source.getElementName())) { // if package has been renamed, update the compilation units + for (int i = 0; i < resources.length; i++) { + if (resources[i].getName().endsWith(".java")) { //$NON-NLS-1$ + // we only consider potential compilation units + ICompilationUnit cu = newFrag.getCompilationUnit(resources[i].getName()); + IDOMCompilationUnit domCU = fFactory.createCompilationUnit(cu.getSource(), cu.getElementName()); + if (domCU != null) { + updatePackageStatement(domCU, newFragName); + IBuffer buffer = cu.getBuffer(); + if (buffer == null) continue; + String bufferContents = buffer.getContents(); + if (bufferContents == null) continue; + String domCUContents = domCU.getContents(); + String cuContents = null; + if (domCUContents != null) { + cuContents = net.sourceforge.phpdt.internal.core.Util.normalizeCRs(domCU.getContents(), bufferContents); + } else { + // See PR http://dev.eclipse.org/bugs/show_bug.cgi?id=11285 + cuContents = bufferContents;//$NON-NLS-1$ + } + buffer.setContents(cuContents); + cu.save(null, false); + } + } + } + } + + //register the correct change deltas + prepareDeltas(source, newFrag, isMove() && isEmpty); + } catch (DOMException dom) { + throw new JavaModelException(dom, IJavaModelStatusConstants.DOM_EXCEPTION); + } catch (JavaModelException e) { + throw e; + } catch (CoreException ce) { + throw new JavaModelException(ce); + } + } + /** + * Updates the content of cu, modifying the type name and/or package + * declaration as necessary. + * + * @return the new source + */ + private String updatedContent(ICompilationUnit cu, IPackageFragment dest, String newName) throws JavaModelException { + String currPackageName = cu.getParent().getElementName(); + String destPackageName = dest.getElementName(); + if (currPackageName.equals(destPackageName) && newName == null) { + return null; //nothing to change + } else { + String typeName = cu.getElementName(); + typeName = typeName.substring(0, typeName.length() - 5); + IDOMCompilationUnit cuDOM = null; + IBuffer buffer = cu.getBuffer(); + if (buffer == null) return null; + char[] contents = buffer.getCharacters(); + if (contents == null) return null; + cuDOM = fFactory.createCompilationUnit(contents, typeName); + updateTypeName(cu, cuDOM, cu.getElementName(), newName); + updatePackageStatement(cuDOM, destPackageName); + return cuDOM.getContents(); + } + } + /** + * Makes sure that cu declares to be in the pkgName package. + */ + private void updatePackageStatement(IDOMCompilationUnit domCU, String pkgName) throws JavaModelException { + boolean defaultPackage = pkgName.equals(IPackageFragment.DEFAULT_PACKAGE_NAME); + boolean seenPackageNode = false; + Enumeration enum = domCU.getChildren(); + while (enum.hasMoreElements()) { + IDOMNode node = (IDOMNode) enum.nextElement(); + if (node.getNodeType() == IDOMNode.PACKAGE) { + if (! defaultPackage) { + node.setName(pkgName); + } else { + node.remove(); + } + seenPackageNode = true; + break; + } + } + if (!seenPackageNode && !defaultPackage) { + //the cu was in a default package...no package declaration + //create the new package declaration as the first child of the cu +// IDOMPackage pkg = fFactory.createPackage("package " + pkgName + ";" + net.sourceforge.phpdt.internal.compiler.util.Util.LINE_SEPARATOR); //$NON-NLS-1$ //$NON-NLS-2$ +// IDOMNode firstChild = domCU.getFirstChild(); +// if (firstChild != null) { +// firstChild.insertSibling(pkg); +// } // else the cu was empty: leave it empty + } + } + /** + * Renames the main type in cu. + */ + private void updateTypeName(ICompilationUnit cu, IDOMCompilationUnit domCU, String oldName, String newName) throws JavaModelException { + if (newName != null) { + if (fRenamedCompilationUnits == null) { + fRenamedCompilationUnits= new ArrayList(1); + } + fRenamedCompilationUnits.add(cu); + String oldTypeName= oldName.substring(0, oldName.length() - 5); + String newTypeName= newName.substring(0, newName.length() - 5); + // 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)) { + IDOMNode typeNode = ((JavaElement) currentType).findNode(domCU); + if (typeNode != null) { + typeNode.setName(newTypeName); + } + } + } + } + } + /** + * Possible failures: + * + */ + protected IJavaModelStatus verify() { + IJavaModelStatus status = super.verify(); + if (!status.isOK()) { + return status; + } + + if (fRenamingsList != null && fRenamingsList.length != fElementsToProcess.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 = element.getResource(); + if (resource instanceof IFolder) { + if (resource.isLinked()) { + error(JavaModelStatus.INVALID_RESOURCE, element); + } + } + + int elementType = element.getElementType(); + + if (elementType == IJavaElement.COMPILATION_UNIT) { + if (isMove() && ((ICompilationUnit) element).isWorkingCopy()) + 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 (fRenamings != null) { + verifyRenaming(element); + } +} +}