/******************************************************************************* * Copyright (c) 2002, 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.HashMap; import java.util.Iterator; import java.util.Map; import org.eclipse.cdt.core.model.CModelException; import org.eclipse.cdt.core.model.IBuffer; import org.eclipse.cdt.core.model.ICElement; import org.eclipse.cdt.core.model.ICModelStatusConstants; import org.eclipse.cdt.core.model.IRegion; import org.eclipse.cdt.core.model.ISourceRange; import org.eclipse.cdt.core.model.ISourceReference; import org.eclipse.cdt.core.model.ITranslationUnit; import org.eclipse.cdt.internal.core.CharOperation; /** * DeleteElementsOperation */ 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<ITranslationUnit, IRegion> fChildrenToRemove; /** * 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(ICElement[] elementsToDelete, boolean force) { super(elementsToDelete, force); } /** * @see MultiOperation */ @Override protected String getMainTaskName() { return CoreModelMessages.getString("operation.deleteElementProgress"); //$NON-NLS-1$ } /** * 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 CModelException { fChildrenToRemove = new HashMap<ITranslationUnit, IRegion>(1); int uniqueTUs = 0; for (ICElement e : fElementsToProcess) { ITranslationUnit tu = getTranslationUnitFor(e); if (tu == null) { throw new CModelException(new CModelStatus(ICModelStatusConstants.READ_ONLY, e)); } IRegion region = fChildrenToRemove.get(tu); if (region == null) { region = new Region(); fChildrenToRemove.put(tu, region); uniqueTUs++; } region.add(e); } fElementsToProcess = new ICElement[uniqueTUs]; Iterator<ITranslationUnit> iter = fChildrenToRemove.keySet().iterator(); int i = 0; while (iter.hasNext()) { fElementsToProcess[i++] = iter.next(); } } /** * Deletes this element from its compilation unit. * @see MultiOperation */ @Override protected void processElement(ICElement element) throws CModelException { ITranslationUnit tu = (ITranslationUnit) element; IBuffer buffer = tu.getBuffer(); if (buffer == null) return; CElementDelta delta = new CElementDelta(tu); ICElement[] cuElements = fChildrenToRemove.get(tu).getElements(); for (ICElement e : cuElements) { if (e.exists()) { char[] contents = buffer.getCharacters(); if (contents == null) continue; String tuName = tu.getElementName(); replaceElementInBuffer(buffer, e, tuName); delta.removed(e); } } if (delta.getAffectedChildren().length > 0) { tu.save(getSubProgressMonitor(1), fForce); if (!tu.isWorkingCopy()) { // if unit is working copy, then save will have already fired the delta addDelta(delta); // this.setAttribute(HAS_MODIFIED_RESOURCE_ATTR, TRUE); } } } /** * @deprecated marked deprecated, future to use ASTRewrite */ @Deprecated private void replaceElementInBuffer(IBuffer buffer, ICElement elementToRemove, String cuName) throws CModelException { if (elementToRemove instanceof ISourceReference) { ISourceRange range = ((ISourceReference)elementToRemove).getSourceRange(); int startPosition = range.getStartPos(); int length = range.getLength(); // Copy the extra spaces and newLines like it is part of // the element. Note: the CopyElementAction is doing the same. boolean newLineFound = false; for (int offset = range.getStartPos() + range.getLength();;++offset) { try { char c = buffer.getChar(offset); // TODO:Bug in the Parser, it does not give the semicolon if (c == ';') { length++; } else if (c == '\r' || c == '\n') { newLineFound = true; length++; } else if (!newLineFound && c == ' ') { // Do not include the spaces after the newline length++ ; } else { break; } } catch (Exception e) { break; } } buffer.replace(startPosition, length, CharOperation.NO_CHAR); } } /** * @see MultiOperation * This method first group the elements by <code>ICompilationUnit</code>, * and then processes the <code>ICompilationUnit</code>. */ @Override protected void processElements() throws CModelException { groupElements(); super.processElements(); } /** * @see MultiOperation */ @Override protected void verify(ICElement element) throws CModelException { ICElement[] children = fChildrenToRemove.get(element).getElements(); for (ICElement child : children) { if (child.getResource() != null) error(ICModelStatusConstants.INVALID_ELEMENT_TYPES, child); if (child.isReadOnly()) error(ICModelStatusConstants.READ_ONLY, child); } } }