/*******************************************************************************
* 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);
}
}
}