/*******************************************************************************
* Copyright (c) 2000, 2003 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Common Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/cpl-v10.html
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package net.sourceforge.phpdt.internal.core;
import java.util.HashMap;
import java.util.Map;
import net.sourceforge.phpdt.core.ICompilationUnit;
import net.sourceforge.phpdt.core.IJavaElement;
import net.sourceforge.phpdt.core.IJavaModelStatus;
import net.sourceforge.phpdt.core.IJavaModelStatusConstants;
import net.sourceforge.phpdt.core.IPackageFragment;
import net.sourceforge.phpdt.core.JavaModelException;
/**
* This class is used to perform operations on multiple
* IJavaElement
. It is responible for running each operation in
* turn, collecting the errors and merging the corresponding
* JavaElementDelta
s.
*
* If several errors occured, they are collected in a multi-status
* JavaModelStatus
. Otherwise, a simple
* JavaModelStatus
is thrown.
*/
public abstract class MultiOperation extends JavaModelOperation {
/**
* The list of renamings supplied to the operation
*/
protected String[] fRenamingsList = null;
/**
* Table specifying the new parent for elements being copied/moved/renamed.
* Keyed by elements being processed, and values are the corresponding
* destination parent.
*/
protected Map fParentElements;
/**
* Table specifying insertion positions for elements being
* copied/moved/renamed. Keyed by elements being processed, and values are
* the corresponding insertion point.
*
* @see processElements(IProgressMonitor)
*/
protected Map fInsertBeforeElements = new HashMap(1);
/**
* This table presents the data in fRenamingList
in a more
* convenient way.
*/
protected Map fRenamings;
/**
* Creates a new MultiOperation
.
*/
protected MultiOperation(IJavaElement[] elementsToProcess,
IJavaElement[] parentElements, boolean force) {
super(elementsToProcess, parentElements, force);
fParentElements = new HashMap(elementsToProcess.length);
if (elementsToProcess.length == parentElements.length) {
for (int i = 0; i < elementsToProcess.length; i++) {
fParentElements.put(elementsToProcess[i], parentElements[i]);
}
} else { // same destination for all elements to be
// moved/copied/renamed
for (int i = 0; i < elementsToProcess.length; i++) {
fParentElements.put(elementsToProcess[i], parentElements[0]);
}
}
}
/**
* Creates a new MultiOperation
on
* elementsToProcess
.
*/
protected MultiOperation(IJavaElement[] elementsToProcess, boolean force) {
super(elementsToProcess, force);
}
/**
* Convenience method to create a JavaModelException
* embending a JavaModelStatus
.
*/
protected void error(int code, IJavaElement element)
throws JavaModelException {
throw new JavaModelException(new JavaModelStatus(code, element));
}
/**
* Executes the operation.
*
* @exception JavaModelException
* if one or several errors occured during the operation. If
* multiple errors occured, the corresponding
* JavaModelStatus
is a multi-status.
* Otherwise, it is a simple one.
*/
protected void executeOperation() throws JavaModelException {
processElements();
}
/**
* Returns the parent of the element being copied/moved/renamed.
*/
protected IJavaElement getDestinationParent(IJavaElement child) {
return (IJavaElement) fParentElements.get(child);
}
/**
* Returns the name to be used by the progress monitor.
*/
protected abstract String getMainTaskName();
/**
* Returns the new name for element
, or null
* if there are no renamings specified.
*/
protected String getNewNameFor(IJavaElement element) {
if (fRenamings != null)
return (String) fRenamings.get(element);
else
return null;
}
/**
* Sets up the renamings hashtable - keys are the elements and values are
* the new name.
*/
private void initializeRenamings() {
if (fRenamingsList != null
&& fRenamingsList.length == fElementsToProcess.length) {
fRenamings = new HashMap(fRenamingsList.length);
for (int i = 0; i < fRenamingsList.length; i++) {
if (fRenamingsList[i] != null) {
fRenamings.put(fElementsToProcess[i], fRenamingsList[i]);
}
}
}
}
/**
* Returns true
if this operation represents a move or
* rename, false
if this operation represents a copy.
* Note: a rename is just a move within the same parent with a name change.
*/
protected boolean isMove() {
return false;
}
/**
* Returns true
if this operation represents a rename,
* false
if this operation represents a copy or move.
*/
protected boolean isRename() {
return false;
}
/**
* Subclasses must implement this method to process a given
* IJavaElement
.
*/
protected abstract void processElement(IJavaElement element)
throws JavaModelException;
/**
* Processes all the IJavaElement
s in turn, collecting
* errors and updating the progress monitor.
*
* @exception JavaModelException
* if one or several operation(s) was unable to be completed.
*/
protected void processElements() throws JavaModelException {
beginTask(getMainTaskName(), fElementsToProcess.length);
IJavaModelStatus[] errors = new IJavaModelStatus[3];
int errorsCounter = 0;
for (int i = 0; i < fElementsToProcess.length; i++) {
try {
verify(fElementsToProcess[i]);
processElement(fElementsToProcess[i]);
} catch (JavaModelException jme) {
if (errorsCounter == errors.length) {
// resize
System.arraycopy(errors, 0,
(errors = new IJavaModelStatus[errorsCounter * 2]),
0, errorsCounter);
}
errors[errorsCounter++] = jme.getJavaModelStatus();
} finally {
worked(1);
}
}
done();
if (errorsCounter == 1) {
throw new JavaModelException(errors[0]);
} else if (errorsCounter > 1) {
if (errorsCounter != errors.length) {
// resize
System.arraycopy(errors, 0,
(errors = new IJavaModelStatus[errorsCounter]), 0,
errorsCounter);
}
throw new JavaModelException(JavaModelStatus.newMultiStatus(errors));
}
}
/**
* Sets the insertion position in the new container for the modified
* element. The element being modified will be inserted before the specified
* new sibling. The given sibling must be a child of the destination
* container specified for the modified element. The default is
* null
, which indicates that the element is to be inserted
* at the end of the container.
*/
public void setInsertBefore(IJavaElement modifiedElement,
IJavaElement newSibling) {
fInsertBeforeElements.put(modifiedElement, newSibling);
}
/**
* Sets the new names to use for each element being copied. The renamings
* correspond to the elements being processed, and the number of renamings
* must match the number of elements being processed. A null
* entry in the list indicates that an element is not to be renamed.
*
*
* Note that some renamings may not be used. If both a parent and a child
* have been selected for copy/move, only the parent is changed. Therefore,
* if a new name is specified for the child, the child's name will not be
* changed.
*/
public void setRenamings(String[] renamings) {
fRenamingsList = renamings;
initializeRenamings();
}
/**
* This method is called for each IJavaElement
before
* processElement
. It should check that this
* element
can be processed.
*/
protected abstract void verify(IJavaElement element)
throws JavaModelException;
/**
* Verifies that the destination
specified for the
* element
is valid for the types of the element
* and destination
.
*/
protected void verifyDestination(IJavaElement element,
IJavaElement destination) throws JavaModelException {
if (destination == null || !destination.exists())
error(IJavaModelStatusConstants.ELEMENT_DOES_NOT_EXIST, destination);
int destType = destination.getElementType();
switch (element.getElementType()) {
case IJavaElement.PACKAGE_DECLARATION:
case IJavaElement.IMPORT_DECLARATION:
if (destType != IJavaElement.COMPILATION_UNIT)
error(IJavaModelStatusConstants.INVALID_DESTINATION, element);
break;
case IJavaElement.TYPE:
if (destType != IJavaElement.COMPILATION_UNIT
&& destType != IJavaElement.TYPE)
error(IJavaModelStatusConstants.INVALID_DESTINATION, element);
break;
case IJavaElement.METHOD:
case IJavaElement.FIELD:
// case IJavaElement.INITIALIZER :
// if (destType != IJavaElement.TYPE || destination instanceof
// BinaryType)
// error(IJavaModelStatusConstants.INVALID_DESTINATION, element);
// break;
case IJavaElement.COMPILATION_UNIT:
if (destType != IJavaElement.PACKAGE_FRAGMENT)
error(IJavaModelStatusConstants.INVALID_DESTINATION, element);
else if (isMove() && ((ICompilationUnit) element).isWorkingCopy())
error(IJavaModelStatusConstants.INVALID_ELEMENT_TYPES, element);
break;
case IJavaElement.PACKAGE_FRAGMENT:
IPackageFragment fragment = (IPackageFragment) element;
IJavaElement parent = fragment.getParent();
if (parent.isReadOnly())
error(IJavaModelStatusConstants.READ_ONLY, element);
else if (destType != IJavaElement.PACKAGE_FRAGMENT_ROOT)
error(IJavaModelStatusConstants.INVALID_DESTINATION, element);
break;
default:
error(IJavaModelStatusConstants.INVALID_ELEMENT_TYPES, element);
}
}
/**
* Verify that the new name specified for element
is valid
* for that type of Java element.
*/
protected void verifyRenaming(IJavaElement element)
throws JavaModelException {
String newName = getNewNameFor(element);
boolean isValid = true;
switch (element.getElementType()) {
case IJavaElement.PACKAGE_FRAGMENT:
if (element.getElementName().equals(
IPackageFragment.DEFAULT_PACKAGE_NAME)) {
// don't allow renaming of default package (see PR #1G47GUM)
throw new JavaModelException(new JavaModelStatus(
IJavaModelStatusConstants.NAME_COLLISION, element));
}
// isValid =
// JavaConventions.validatePackageName(newName).getSeverity() !=
// IStatus.ERROR;
isValid = true;
break;
case IJavaElement.COMPILATION_UNIT:
// isValid =
// JavaConventions.validateCompilationUnitName(newName).getSeverity()
// != IStatus.ERROR;
isValid = true;
break;
case IJavaElement.INITIALIZER:
isValid = false; // cannot rename initializers
break;
default:
// isValid =
// JavaConventions.validateIdentifier(newName).getSeverity() !=
// IStatus.ERROR;
isValid = true;
break;
}
if (!isValid) {
throw new JavaModelException(new JavaModelStatus(
IJavaModelStatusConstants.INVALID_NAME, element, newName));
}
}
/**
* Verifies that the positioning sibling specified for the
* element
is exists and its parent is the destination
* container of this element
.
*/
protected void verifySibling(IJavaElement element, IJavaElement destination)
throws JavaModelException {
IJavaElement insertBeforeElement = (IJavaElement) fInsertBeforeElements
.get(element);
if (insertBeforeElement != null) {
if (!insertBeforeElement.exists()
|| !insertBeforeElement.getParent().equals(destination)) {
error(IJavaModelStatusConstants.INVALID_SIBLING,
insertBeforeElement);
}
}
}
}