/******************************************************************************* * 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.JavaCore; 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.internal.core.util.Util; 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.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, force); } parentFolder = parentFolder.getFolder(new Path(subFolderName)); sourceFolder = sourceFolder.getFolder(new Path(subFolderName)); if (sourceFolder.getResourceAttributes().isReadOnly()) { parentFolder.getResourceAttributes().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.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.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.Util .isExcluded(sourceElement) || net.sourceforge.phpdt.internal.core.util.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 (force) { // 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 = force ? 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( JavaCore.CORE_ENCODING, true); destFile.setContents(new ByteArrayInputStream( encoding == null ? newContent.getBytes() : newContent.getBytes(encoding)), force ? 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 (!force) { 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( JavaCore.CORE_ENCODING, true); destFile.setContents(new ByteArrayInputStream( encoding == null ? newContent.getBytes() : newContent.getBytes(encoding)), force ? 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 { resultElements = new IJavaElement[fCreatedElements.size()]; fCreatedElements.toArray(resultElements); 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, force, 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 (force) { 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 (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, 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.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 e = domCU.getChildren(); while (e.hasMoreElements()) { IDOMNode node = (IDOMNode) e.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.ProjectPrefUtil.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); } } }