/******************************************************************************* * Copyright (c) 2000, 2008 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; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import org.eclipse.core.resources.IResource; import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.runtime.Assert; import org.eclipse.core.runtime.jobs.ISchedulingRule; import org.eclipse.jdt.core.ICompilationUnit; import org.eclipse.jdt.core.IJavaElement; import org.eclipse.jdt.core.IJavaModelStatusConstants; import org.eclipse.jdt.core.IRegion; import org.eclipse.jdt.core.JavaModelException; 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.CompilationUnit; import org.eclipse.jdt.core.dom.rewrite.ASTRewrite; import org.eclipse.jdt.internal.core.util.Messages; import org.eclipse.text.edits.TextEdit; /** * This operation deletes a collection of elements (and all of their children). If an element does * not exist, it is ignored. * * <p> * NOTE: This operation only deletes elements contained within leaf resources - that is, elements * within compilation units. To delete a compilation unit or a package, etc (which have an actual * resource), a DeleteResourcesOperation should be used. */ public class DeleteElementsOperation extends MultiOperation { /** * The elements this operation processes grouped by compilation unit * * @see #processElements() Keys are compilation units, values are <code>IRegion</code>s of * elements to be processed in each compilation unit. */ protected Map childrenToRemove; /** * The <code>ASTParser</code> used to manipulate the source code of * <code>ICompilationUnit</code>. */ protected ASTParser parser; /** * When executed, this operation will delete the given elements. The elements to delete cannot * be <code>null</code> or empty, and must be contained within a compilation unit. */ public DeleteElementsOperation(IJavaElement[] elementsToDelete, boolean force) { super(elementsToDelete, force); initASTParser(); } private void deleteElement(IJavaElement elementToRemove, ICompilationUnit cu) throws JavaModelException { // ensure cu is consistent (noop if already consistent) cu.makeConsistent(this.progressMonitor); this.parser.setSource(cu); CompilationUnit astCU= (CompilationUnit)this.parser.createAST(this.progressMonitor); ASTNode node= ((JavaElement)elementToRemove).findNode(astCU); if (node == null) Assert.isTrue(false, "Failed to locate " + elementToRemove.getElementName() + " in " + cu.getElementName()); //$NON-NLS-1$//$NON-NLS-2$ AST ast= astCU.getAST(); ASTRewrite rewriter= ASTRewrite.create(ast); rewriter.remove(node, null); TextEdit edits= rewriter.rewriteAST(); applyTextEdit(cu, edits); } private void initASTParser() { this.parser= ASTParser.newParser(AST.JLS3); } /** * @see MultiOperation */ protected String getMainTaskName() { return Messages.operation_deleteElementProgress; } protected ISchedulingRule getSchedulingRule() { if (this.elementsToProcess != null && this.elementsToProcess.length == 1) { IResource resource= this.elementsToProcess[0].getResource(); if (resource != null) return ResourcesPlugin.getWorkspace().getRuleFactory().modifyRule(resource); } return super.getSchedulingRule(); } /** * Groups the elements to be processed by their compilation unit. If parent/child combinations * are present, children are discarded (only the parents are processed). Removes any duplicates * specified in elements to be processed. */ protected void groupElements() throws JavaModelException { this.childrenToRemove= new HashMap(1); int uniqueCUs= 0; for (int i= 0, length= this.elementsToProcess.length; i < length; i++) { IJavaElement e= this.elementsToProcess[i]; ICompilationUnit cu= getCompilationUnitFor(e); if (cu == null) { throw new JavaModelException(new JavaModelStatus(IJavaModelStatusConstants.READ_ONLY, e)); } else { IRegion region= (IRegion)this.childrenToRemove.get(cu); if (region == null) { region= new Region(); this.childrenToRemove.put(cu, region); uniqueCUs+= 1; } region.add(e); } } this.elementsToProcess= new IJavaElement[uniqueCUs]; Iterator iter= this.childrenToRemove.keySet().iterator(); int i= 0; while (iter.hasNext()) { this.elementsToProcess[i++]= (IJavaElement)iter.next(); } } /** * Deletes this element from its compilation unit. * * @see MultiOperation */ protected void processElement(IJavaElement element) throws JavaModelException { ICompilationUnit cu= (ICompilationUnit)element; // keep track of the import statements - if all are removed, delete // the import container (and report it in the delta) int numberOfImports= cu.getImports().length; JavaElementDelta delta= new JavaElementDelta(cu); IJavaElement[] cuElements= ((IRegion)this.childrenToRemove.get(cu)).getElements(); for (int i= 0, length= cuElements.length; i < length; i++) { IJavaElement e= cuElements[i]; if (e.exists()) { deleteElement(e, cu); delta.removed(e); if (e.getElementType() == IJavaElement.IMPORT_DECLARATION) { numberOfImports--; if (numberOfImports == 0) { delta.removed(cu.getImportContainer()); } } } } if (delta.getAffectedChildren().length > 0) { cu.save(getSubProgressMonitor(1), this.force); if (!cu.isWorkingCopy()) { // if unit is working copy, then save will have already fired the delta addDelta(delta); setAttribute(HAS_MODIFIED_RESOURCE_ATTR, TRUE); } } } /** * @see MultiOperation This method first group the elements by <code>ICompilationUnit</code>, * and then processes the <code>ICompilationUnit</code>. */ protected void processElements() throws JavaModelException { groupElements(); super.processElements(); } /** * @see MultiOperation */ protected void verify(IJavaElement element) throws JavaModelException { IJavaElement[] children= ((IRegion)this.childrenToRemove.get(element)).getElements(); for (int i= 0; i < children.length; i++) { IJavaElement child= children[i]; if (child.getCorrespondingResource() != null) error(IJavaModelStatusConstants.INVALID_ELEMENT_TYPES, child); if (child.isReadOnly()) error(IJavaModelStatusConstants.READ_ONLY, child); } } }