Copyright (c) 2000, 2013 IBM Corporation and others.
This program and the accompanying materials
are made available under the terms of the Eclipse Public License 2.0
which accompanies this distribution, and is available at
https://www.eclipse.org/legal/epl-2.0/
SPDX-License-Identifier: EPL-2.0
Contributors:
IBM Corporation - initial API and implementation
/*******************************************************************************
* Copyright (c) 2000, 2013 IBM Corporation and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.jdt.internal.core;
import java.util.HashMap;
import java.util.Map;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.jdt.core.*;
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.
/**
* This class is used to perform operations on multiple <code>IJavaElement</code>.
* It is responible for running each operation in turn, collecting
* the errors and merging the corresponding <code>JavaElementDelta</code>s.
* <p>
* If several errors occured, they are collected in a multi-status
* <code>JavaModelStatus</code>. Otherwise, a simple <code>JavaModelStatus</code>
* is thrown.
*/
@SuppressWarnings({"rawtypes", "unchecked"})
public abstract class MultiOperation extends JavaModelOperation {
Table specifying insertion positions for elements being
copied/moved/renamed. Keyed by elements being processed, and
values are the corresponding insertion point.
See Also: - processElements()
/**
* Table specifying insertion positions for elements being
* copied/moved/renamed. Keyed by elements being processed, and
* values are the corresponding insertion point.
* @see #processElements()
*/
protected Map insertBeforeElements = new HashMap(1);
Table specifying the new parent for elements being
copied/moved/renamed.
Keyed by elements being processed, and
values are the corresponding destination parent.
/**
* 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 newParents;
This table presents the data in fRenamingList
in a more
convenient way.
/**
* This table presents the data in <code>fRenamingList</code> in a more
* convenient way.
*/
protected Map renamings;
The list of renamings supplied to the operation
/**
* The list of renamings supplied to the operation
*/
protected String[] renamingsList = null;
Creates a new MultiOperation
on elementsToProcess
.
/**
* Creates a new <code>MultiOperation</code> on <code>elementsToProcess</code>.
*/
protected MultiOperation(IJavaElement[] elementsToProcess, boolean force) {
super(elementsToProcess, force);
}
Creates a new MultiOperation
.
/**
* Creates a new <code>MultiOperation</code>.
*/
protected MultiOperation(IJavaElement[] elementsToProcess, IJavaElement[] parentElements, boolean force) {
super(elementsToProcess, parentElements, force);
this.newParents = new HashMap(elementsToProcess.length);
if (elementsToProcess.length == parentElements.length) {
for (int i = 0; i < elementsToProcess.length; i++) {
this.newParents.put(elementsToProcess[i], parentElements[i]);
}
} else { //same destination for all elements to be moved/copied/renamed
for (int i = 0; i < elementsToProcess.length; i++) {
this.newParents.put(elementsToProcess[i], parentElements[0]);
}
}
}
Convenience method to create a JavaModelException
embending a JavaModelStatus
.
/**
* Convenience method to create a <code>JavaModelException</code>
* embending a <code>JavaModelStatus</code>.
*/
protected void error(int code, IJavaElement element) throws JavaModelException {
throw new JavaModelException(new JavaModelStatus(code, element));
}
Executes the operation.
Throws: - 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.
/**
* Executes the operation.
*
* @exception JavaModelException if one or several errors occured during the operation.
* If multiple errors occured, the corresponding <code>JavaModelStatus</code> is a
* multi-status. Otherwise, it is a simple one.
*/
@Override
protected void executeOperation() throws JavaModelException {
processElements();
}
Returns the parent of the element being copied/moved/renamed.
/**
* Returns the parent of the element being copied/moved/renamed.
*/
protected IJavaElement getDestinationParent(IJavaElement child) {
return (IJavaElement)this.newParents.get(child);
}
Returns the name to be used by the progress monitor.
/**
* 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.
/**
* Returns the new name for <code>element</code>, or <code>null</code>
* if there are no renamings specified.
*/
protected String getNewNameFor(IJavaElement element) throws JavaModelException {
String newName = null;
if (this.renamings != null)
newName = (String) this.renamings.get(element);
if (newName == null && element instanceof IMethod && ((IMethod) element).isConstructor())
newName = getDestinationParent(element).getElementName();
return newName;
}
Sets up the renamings hashtable - keys are the elements and
values are the new name.
/**
* Sets up the renamings hashtable - keys are the elements and
* values are the new name.
*/
private void initializeRenamings() {
if (this.renamingsList != null && this.renamingsList.length == this.elementsToProcess.length) {
this.renamings = new HashMap(this.renamingsList.length);
for (int i = 0; i < this.renamingsList.length; i++) {
if (this.renamingsList[i] != null) {
this.renamings.put(this.elementsToProcess[i], this.renamingsList[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.
/**
* Returns <code>true</code> if this operation represents a move or rename, <code>false</code>
* if this operation represents a copy.<br>
* 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.
/**
* Returns <code>true</code> if this operation represents a rename, <code>false</code>
* if this operation represents a copy or move.
*/
protected boolean isRename() {
return false;
}
Subclasses must implement this method to process a given IJavaElement
.
/**
* Subclasses must implement this method to process a given <code>IJavaElement</code>.
*/
protected abstract void processElement(IJavaElement element) throws JavaModelException;
Processes all the IJavaElement
s in turn, collecting errors
and updating the progress monitor.
Throws: - JavaModelException – if one or several operation(s) was unable to
be completed.
/**
* Processes all the <code>IJavaElement</code>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 {
try {
beginTask(getMainTaskName(), this.elementsToProcess.length);
IJavaModelStatus[] errors = new IJavaModelStatus[3];
int errorsCounter = 0;
for (int i = 0; i < this.elementsToProcess.length; i++) {
try {
verify(this.elementsToProcess[i]);
processElement(this.elementsToProcess[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);
}
}
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));
}
} finally {
done();
}
}
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.
/**
* 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 <code>null</code>, which indicates that the element is to be
* inserted at the end of the container.
*/
public void setInsertBefore(IJavaElement modifiedElement, IJavaElement newSibling) {
this.insertBeforeElements.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.
/**
* 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 <code>null</code> entry in the list indicates that an element
* is not to be renamed.
*
* <p>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[] renamingsList) {
this.renamingsList = renamingsList;
initializeRenamings();
}
This method is called for each IJavaElement
before
processElement
. It should check that this element
can be processed.
/**
* This method is called for each <code>IJavaElement</code> before
* <code>processElement</code>. It should check that this <code>element</code>
* 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
.
/**
* Verifies that the <code>destination</code> specified for the <code>element</code> is valid for the types of the
* <code>element</code> and <code>destination</code>.
*/
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 {
CompilationUnit cu = (CompilationUnit)element;
if (isMove() && cu.isWorkingCopy() && !cu.isPrimary())
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.
/**
* Verify that the new name specified for <code>element</code> is
* valid for that type of Java element.
*/
protected void verifyRenaming(IJavaElement element) throws JavaModelException {
String newName = getNewNameFor(element);
boolean isValid = true;
IJavaProject project = element.getJavaProject();
String sourceLevel = project.getOption(JavaCore.COMPILER_SOURCE, true);
String complianceLevel = project.getOption(JavaCore.COMPILER_COMPLIANCE, true);
switch (element.getElementType()) {
case IJavaElement.PACKAGE_FRAGMENT :
if (((IPackageFragment) element).isDefaultPackage()) {
// don't allow renaming of default package (see PR #1G47GUM)
throw new JavaModelException(new JavaModelStatus(IJavaModelStatusConstants.NAME_COLLISION, element));
}
isValid = JavaConventions.validatePackageName(newName, sourceLevel, complianceLevel).getSeverity() != IStatus.ERROR;
break;
case IJavaElement.COMPILATION_UNIT :
isValid = JavaConventions.validateCompilationUnitName(newName,sourceLevel, complianceLevel).getSeverity() != IStatus.ERROR;
break;
case IJavaElement.INITIALIZER :
isValid = false; //cannot rename initializers
break;
default :
isValid = JavaConventions.validateIdentifier(newName, sourceLevel, complianceLevel).getSeverity() != IStatus.ERROR;
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
.
/**
* Verifies that the positioning sibling specified for the <code>element</code> is exists and
* its parent is the destination container of this <code>element</code>.
*/
protected void verifySibling(IJavaElement element, IJavaElement destination) throws JavaModelException {
IJavaElement insertBeforeElement = (IJavaElement) this.insertBeforeElements.get(element);
if (insertBeforeElement != null) {
if (!insertBeforeElement.exists() || !insertBeforeElement.getParent().equals(destination)) {
error(IJavaModelStatusConstants.INVALID_SIBLING, insertBeforeElement);
}
}
}
}