/******************************************************************************* * Copyright (c) 2000, 2008 QNX Software Systems 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: * QNX Software Systems - Initial API and implementation * Anton Leherbauer (Wind River Systems) *******************************************************************************/ package org.eclipse.cdt.internal.core.model; import java.util.ArrayList; import java.util.HashMap; import java.util.Map; import org.eclipse.cdt.core.model.CModelException; import org.eclipse.cdt.core.model.ICElement; import org.eclipse.cdt.core.model.ICModelStatus; import org.eclipse.cdt.core.model.ICModelStatusConstants; import org.eclipse.cdt.core.model.ICProject; import org.eclipse.core.resources.IContainer; import org.eclipse.core.resources.IFile; import org.eclipse.core.runtime.CoreException; 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. * <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>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 translation units. * </ul> * */ public class CopyResourceElementsOperation extends MultiOperation { /** * The list of new resources created during this operation. */ protected ArrayList<ICElement> fCreatedElements; /** * Table specifying deltas for elements being * copied/moved/renamed. Keyed by elements' project(s), and * values are the corresponding deltas. */ protected Map<ICProject, CElementDelta> fDeltasPerProject= new HashMap<ICProject, CElementDelta>(1); public CopyResourceElementsOperation(ICElement[] src, ICElement[] dst, boolean force) { super(src, dst, force); } /** * Returns the <code>CElementDelta</code> for <code>cProject</code>, * creating it and putting it in <code>fDeltasPerProject</code> if * it does not exist yet. */ private CElementDelta getDeltaFor(ICProject cProject) { CElementDelta delta = fDeltasPerProject.get(cProject); if (delta == null) { delta = new CElementDelta(cProject); fDeltasPerProject.put(cProject, delta); } return delta; } /** * @see MultiOperation */ @Override protected String getMainTaskName() { return CoreModelMessages.getString("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<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(ICElement sourceElement, ICElement destinationElement) { ICProject destProject = destinationElement.getCProject(); if (isMove()) { ICProject sourceProject = sourceElement.getCProject(); getDeltaFor(sourceProject).movedFrom(sourceElement, destinationElement); getDeltaFor(destProject).movedTo(destinationElement, sourceElement); } else { getDeltaFor(destProject).added(destinationElement); } } /** * Process all of the changed deltas generated by this operation. */ protected void processDeltas() { for (CElementDelta elementDelta : this.fDeltasPerProject.values()) { addDelta(elementDelta); } } /** * Copies/moves a compilation unit with the name <code>newName</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 CModelException if the operation is unable to * complete */ private void processResource(ICElement source, ICElement dest) throws CModelException { String newName = getNewNameFor(source); String destName = (newName != null) ? newName : source.getElementName(); // copy resource IFile sourceResource = (IFile)source.getResource(); // can be an IFolder or an IProject IContainer destFolder = (IContainer)dest.getResource(); IFile destFile = destFolder.getFile(new Path(destName)); if (!destFile.equals(sourceResource)) { try { if (destFile.exists()) { if (fForce) { // we can remove it deleteResource(destFile, false); } else { // abort throw new CModelException(new CModelStatus(ICModelStatusConstants.NAME_COLLISION)); } } if (this.isMove()) { sourceResource.move(destFile.getFullPath(), fForce, true, getSubProgressMonitor(1)); } else { sourceResource.copy(destFile.getFullPath(), fForce, getSubProgressMonitor(1)); } this.hasModifiedResource = true; } catch (CModelException e) { throw e; } catch (CoreException e) { throw new CModelException(e); } // update new resource content // register the correct change deltas ICElement cdest = CModelManager.getDefault().create(destFile, null); prepareDeltas(source, cdest); fCreatedElements.add(cdest); //if (newName != null) { //the main type has been renamed //String oldName = source.getElementName(); //oldName = oldName.substring(0, oldName.length() - 5); //String nName = newName; //nName = nName.substring(0, nName.length() - 5); //prepareDeltas(source.getType(oldName), cdest.getType(nName)); //} } else { if (!fForce) { throw new CModelException(new CModelStatus(ICModelStatusConstants.NAME_COLLISION)); } // 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 } } /** * @see MultiOperation * This method delegates to <code>processResource</code> or * <code>processPackageFragmentResource</code>, depending on the type of * <code>element</code>. */ @Override protected void processElement(ICElement element) throws CModelException { ICElement dest = getDestinationParent(element); if (element.getElementType() <= ICElement.C_UNIT) { processResource(element, dest); //fCreatedElements.add(dest.getCompilationUnit(element.getElementName())); } else { throw new CModelException(new CModelStatus(ICModelStatusConstants.INVALID_ELEMENT_TYPES, element)); } } /** * @see MultiOperation * Overridden to allow special processing of <code>CElementDelta</code>s * and <code>fResultElements</code>. */ @Override protected void processElements() throws CModelException { fCreatedElements = new ArrayList<ICElement>(fElementsToProcess.length); try { super.processElements(); } catch (CModelException cme) { throw cme; } finally { fResultElements = new ICElement[fCreatedElements.size()]; fCreatedElements.toArray(fResultElements); processDeltas(); } } /** * 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> */ @Override protected ICModelStatus verify() { ICModelStatus status = super.verify(); if (!status.isOK()) { return status; } if (fRenamingsList != null && fRenamingsList.length != fElementsToProcess.length) { return new CModelStatus(ICModelStatusConstants.INDEX_OUT_OF_BOUNDS); } return CModelStatus.VERIFIED_OK; } /** * @see MultiOperation */ @Override protected void verify(ICElement element) throws CModelException { if (element == null || !element.exists()) error(ICModelStatusConstants.ELEMENT_DOES_NOT_EXIST, element); else if (element.isReadOnly() && (isRename() || isMove())) error(ICModelStatusConstants.READ_ONLY, element); else { CElement dest = (CElement) getDestinationParent(element); verifyDestination(element, dest); if (fRenamings != null) { verifyRenaming(element); } } } }