/******************************************************************************* * Copyright (c) 2000, 2011 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.JLS4); } /** * @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); } } }