blob: 9acda950ebbd8208e5186ee94b29f2c382762fbe [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2005, 2008 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.core.refactoring.descriptors;
import java.util.Map;
import org.eclipse.ltk.core.refactoring.RefactoringContribution;
import org.eclipse.ltk.core.refactoring.RefactoringCore;
import org.eclipse.ltk.core.refactoring.RefactoringStatus;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.IMethod;
import org.eclipse.jdt.core.IType;
import org.eclipse.jdt.core.JavaConventions;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.core.refactoring.IJavaRefactorings;
import org.eclipse.jdt.internal.core.refactoring.descriptors.JavaRefactoringDescriptorUtil;
/**
* Refactoring descriptor for the introduce parameter object refactoring.
* <p>
* An instance of this refactoring descriptor may be obtained by calling
* {@link RefactoringContribution#createDescriptor()} on a refactoring
* contribution requested by invoking
* {@link RefactoringCore#getRefactoringContribution(String)} with the
* appropriate refactoring id.
* </p>
*
* @since 1.2
*
* @noinstantiate This class is not intended to be instantiated by clients.
* @noextend This class is not intended to be subclassed by clients.
*/
public class IntroduceParameterObjectDescriptor extends JavaRefactoringDescriptor {
/**
* Instances of Parameter are used to describe the position of parameter and fields.
*/
public static class Parameter {
private boolean fCreateField= false;
private String fFieldName= null;
private final int fIndex;
/**
* Creates a new parameter object. It is not recommended to call this constructor directly.
* Use {@link IntroduceParameterObjectDescriptor#createParameters(IMethod)} instead.
* @param index the index of the parameter in the method
*/
public Parameter(int index) {
super();
fIndex= index;
}
/**
* The name of the field that will be created if {@link #isCreateField()} is <code>true</code>
*
* @return the field name
* @see #isCreateField()
* @see #setFieldName(String)
*/
public String getFieldName() {
return fFieldName;
}
/**
* The index of the parameter in the original method signature. The parameter object has the special index {@link IntroduceParameterObjectDescriptor#PARAMETER_OBJECT_IDX}.
* The position in the new method signature depends on the position in the array passed to {@link IntroduceParameterObjectDescriptor#setParameters(IntroduceParameterObjectDescriptor.Parameter[])}
*
* @return returns the index of the parameter in the original method signature or {@link IntroduceParameterObjectDescriptor#PARAMETER_OBJECT_IDX}
* for the parameter object
*
* @see IntroduceParameterObjectDescriptor#PARAMETER_OBJECT
* @see IntroduceParameterObjectDescriptor#PARAMETER_OBJECT_IDX
* @see IntroduceParameterObjectDescriptor#setParameters(IntroduceParameterObjectDescriptor.Parameter[])
*/
public int getIndex() {
return fIndex;
}
/**
* If <code>true</code> the parameter will be removed from the method's signature and will be added
* to the parameter object. The default is <code>false</code>
*
* @return <code>true</code> if the parameter will be created as field, <code>false</code> if
* it will remain in the method
*/
public boolean isCreateField() {
return fCreateField;
}
/**
* Sets whether the parameter will be removed from the method's signature or will be added to the parameter object. The
* default is <code>false</code>. Changing the creatField property of the parameter object will throw a {@link IllegalArgumentException}
*
* @param createField <code>true</code> if the parameter should be created as field, <code>false</code>
* if it will remain in the method
*/
public void setCreateField(boolean createField) {
if (fIndex == PARAMETER_OBJECT_IDX)
throw new IllegalArgumentException("You can not set create field for the parameter object"); //$NON-NLS-1$
fCreateField= createField;
}
/**
* Sets the name of the field that will be created in the parameter object if {@link #isCreateField()}
* is <code>true</code>. Changing the fieldName of the parameter object will throw a {@link IllegalArgumentException}
*
* @param fieldName the new name of the field. A <code>null</code> indicates that the field name should be automatically derived
* @see #isCreateField()
*/
public void setFieldName(String fieldName) {
if (fIndex == PARAMETER_OBJECT_IDX)
throw new IllegalArgumentException("You can not set the field name of the parameter object"); //$NON-NLS-1$
fFieldName= fieldName;
}
}
private static final String PARAMETER_COUNT= "PARAMETER_COUNT"; //$NON-NLS-1$
private static final String PARAMETER_IDX= "PARAMETER_IDX"; //$NON-NLS-1$
private static final String PARAMETER_CREATE_FIELD= "PARAMETER_CREATE_FIELD"; //$NON-NLS-1$
private static final String PARAMETER_FIELD_NAME= "PARAMETER_FIELD_NAME"; //$NON-NLS-1$
private static final String CLASS_NAME= "class_name"; //$NON-NLS-1$
private static final String DELEGATE= "delegate"; //$NON-NLS-1$
private static final String DEPRECATE_DELEGATE= "deprecate_delegate"; //$NON-NLS-1$
private static final String GETTERS= "getters"; //$NON-NLS-1$
private static final String PACKAGE_NAME= "package_name"; //$NON-NLS-1$
private static final String PARAMETER_NAME= "parameter_name"; //$NON-NLS-1$
private static final String SETTERS= "setters"; //$NON-NLS-1$
private static final String TOP_LEVEL= "top_level"; //$NON-NLS-1$
/**
* The parameter index of the special parameter object. The value is "-1".
*/
public static final int PARAMETER_OBJECT_IDX= -1;
/**
* Singleton instance that represents the parameter object
*/
public static final Parameter PARAMETER_OBJECT= new Parameter(PARAMETER_OBJECT_IDX);
/**
* Creates the parameters for this method. The first object is the parameter object.
* By default all parameters are marked for field creation
*
* @param method derive parameter from this method
* @return an array of parameter corresponding to the parameter declared in the method. The first object
* will be the parameter object. All parameter are marked for field creation
*/
public static Parameter[] createParameters(IMethod method) {
int length= method.getNumberOfParameters();
Parameter[] result= new Parameter[length + 1];
result[0]= PARAMETER_OBJECT;
for (int i= 0; i < length; i++) {
result[i + 1]= new Parameter(i);
result[i + 1].setCreateField(true);
}
return result;
}
private String fClassName;
private boolean fDelegate= false;
private boolean fDeprecateDelegate= true;
private boolean fGetters= false;
private IMethod fMethod;
private String fPackageName;
private String fParameterName;
private Parameter[] fParameters;
private boolean fSetters= false;
/**
* If <code>true</code> the class will be created as top level class. Default true
*/
private boolean fTopLevel= true;
/**
* Creates a new refactoring descriptor.
*/
public IntroduceParameterObjectDescriptor() {
super(IJavaRefactorings.INTRODUCE_PARAMETER_OBJECT);
}
/**
* Creates a new refactoring descriptor.
*
* @param project
* the non-empty name of the project associated with this
* refactoring, or <code>null</code> for a workspace
* refactoring
* @param description
* a non-empty human-readable description of the particular
* refactoring instance
* @param comment
* the human-readable comment of the particular refactoring
* instance, or <code>null</code> for no comment
* @param arguments
* a map of arguments that will be persisted and describes
* all settings for this refactoring
* @param flags
* the flags of the refactoring descriptor
* @throws IllegalArgumentException if the argument map contains invalid keys/values
*/
public IntroduceParameterObjectDescriptor(final String project, final String description, final String comment, final Map<String, String> arguments, final int flags) throws IllegalArgumentException {
super(IJavaRefactorings.INTRODUCE_PARAMETER_OBJECT, project, description, comment, arguments, flags);
initializeFromMap(arguments);
}
/**
* The name of the class that will be generated. If <code>null</code> the refactoring will automatically choose a class name.
*
* @return the name of the class that will be generated or <code>null</code> if the name will be automatically chosen
*/
public String getClassName() {
return fClassName;
}
/**
* The method the refactoring will operate on. Can be set using {@link #setMethod(IMethod)}.
*
* @return the method that the refactoring will operate on.
*/
public IMethod getMethod() {
return fMethod;
}
/**
* The parameter object class will be created in this package if the top level is <code>true</code>. Can be set using
* {@link #setPackageName(String)}. If the package name was <code>null</code> and the method has already been set this method returns
* the package where the method is declared in.
*
* @return the package name that has been set or the package where the method is declared. Can return <code>null</code>
* if neither the package nor the method has been set
*/
public String getPackageName() {
if (fPackageName == null && fMethod != null) {
IType type= fMethod.getDeclaringType();
if (type != null)
return type.getPackageFragment().getElementName();
}
return fPackageName;
}
/**
* Returns the name of the parameter. Can return <code>null</code> in which case the refactoring chooses a name. Default is <code>null</code>
*
* @return the name of the parameter. Can return <code>null</code> in which case the refactoring chooses a name. Default is <code>null</code>
*/
public String getParameterName() {
return fParameterName;
}
/**
* Returns the parameters. Can return <code>null</code> if all parameters should be converted to fields. Default is <code>null</code>.
*
* @return the parameters. Can return <code>null</code> if all parameters should be converted to fields. Default is <code>null</code>
*/
public Parameter[] getParameters() {
return fParameters;
}
/**
* Returns <code>true</code> if delegates will be kept. Default is <code>false</code>.
*
* @return <code>true</code> if delegates will be kept. Default is <code>false</code>
*/
public boolean isDelegate() {
return fDelegate;
}
/**
* Returns <code>true</code> if delegates will be marked as deprecated. Default is <code>false</code>.
*
* @return <code>true</code> if delegates will be marked as deprecated. Default is <code>false</code>
*/
public boolean isDeprecateDelegate() {
return fDeprecateDelegate;
}
/**
* Returns <code>true</code> if getters are generated for fields. Default is <code>false</code>.
*
* @return <code>true</code> if getters are generated for fields. Default is <code>false</code>
*/
public boolean isGetters() {
return fGetters;
}
/**
* Returns <code>true</code> if setters are generated for fields. Default is <code>false</code>.
*
* @return <code>true</code> if setters are generated for fields. Default is <code>false</code>
*/
public boolean isSetters() {
return fSetters;
}
/**
* Returns <code>true</code> if the new type is created as top level type.
* <code>false</code> is returned when the type is created as enclosing type
* of the type declaring the method declaration to be changed. Default is <code>true</code>.
*
* @return <code>true</code> if the new type is created as top level type.
* <code>false</code> is returned when the type is created as enclosing
* type of the type declaring the method declaration to be changed. Default is <code>true</code>
*/
public boolean isTopLevel() {
return fTopLevel;
}
@Override
protected void populateArgumentMap() {
super.populateArgumentMap();
JavaRefactoringDescriptorUtil.setJavaElement(fArguments, ATTRIBUTE_INPUT, getProject(), fMethod);
Parameter[] parameters= fParameters;
if (parameters == null) {
parameters= createParameters(fMethod);
}
JavaRefactoringDescriptorUtil.setInt(fArguments, PARAMETER_COUNT, parameters.length);
for (int i= 0; i < parameters.length; i++) {
Parameter param= parameters[i];
JavaRefactoringDescriptorUtil.setInt(fArguments, JavaRefactoringDescriptorUtil.getAttributeName(PARAMETER_IDX, i), param.getIndex());
JavaRefactoringDescriptorUtil.setBoolean(fArguments, JavaRefactoringDescriptorUtil.getAttributeName(PARAMETER_CREATE_FIELD, i), param.isCreateField());
if (param.isCreateField())
JavaRefactoringDescriptorUtil.setString(fArguments, JavaRefactoringDescriptorUtil.getAttributeName(PARAMETER_FIELD_NAME, i), param.getFieldName());
}
JavaRefactoringDescriptorUtil.setString(fArguments, CLASS_NAME, fClassName);
JavaRefactoringDescriptorUtil.setString(fArguments, PACKAGE_NAME, fPackageName);
JavaRefactoringDescriptorUtil.setString(fArguments, PARAMETER_NAME, fParameterName);
JavaRefactoringDescriptorUtil.setBoolean(fArguments, DELEGATE, fDelegate);
JavaRefactoringDescriptorUtil.setBoolean(fArguments, DEPRECATE_DELEGATE, fDeprecateDelegate);
JavaRefactoringDescriptorUtil.setBoolean(fArguments, GETTERS, fGetters);
JavaRefactoringDescriptorUtil.setBoolean(fArguments, SETTERS, fSetters);
JavaRefactoringDescriptorUtil.setBoolean(fArguments, TOP_LEVEL, fTopLevel);
}
private void initializeFromMap(Map<String, String> map) throws IllegalArgumentException {
IMethod method= (IMethod) JavaRefactoringDescriptorUtil.getJavaElement(map, ATTRIBUTE_INPUT, getProject());
setMethod(method);
initializeParameter(map);
setClassName(JavaRefactoringDescriptorUtil.getString(map, CLASS_NAME, true));
setPackageName(JavaRefactoringDescriptorUtil.getString(map, PACKAGE_NAME, true));
setParameterName(JavaRefactoringDescriptorUtil.getString(map, PARAMETER_NAME, true));
setDelegate(JavaRefactoringDescriptorUtil.getBoolean(map, DELEGATE, fDelegate));
setDeprecateDelegate(JavaRefactoringDescriptorUtil.getBoolean(map, DEPRECATE_DELEGATE, fDeprecateDelegate));
setGetters(JavaRefactoringDescriptorUtil.getBoolean(map, GETTERS, fGetters));
setSetters(JavaRefactoringDescriptorUtil.getBoolean(map, SETTERS, fSetters));
setTopLevel(JavaRefactoringDescriptorUtil.getBoolean(map, TOP_LEVEL, fTopLevel));
}
private void initializeParameter(Map<String, String> map) throws IllegalArgumentException {
int[] idx= JavaRefactoringDescriptorUtil.getIntArray(map, PARAMETER_COUNT, PARAMETER_IDX);
boolean[] createField= JavaRefactoringDescriptorUtil.getBooleanArray(map, PARAMETER_COUNT, PARAMETER_CREATE_FIELD, 0);
Parameter[] result=new Parameter[idx.length];
for (int i= 0; i < idx.length; i++) {
int index= idx[i];
if (index == PARAMETER_OBJECT_IDX) {
result[i]= new Parameter(PARAMETER_OBJECT_IDX);
} else {
Parameter parameter= new Parameter(index);
result[i]= parameter;
parameter.setCreateField(createField[i]);
if (createField[i])
parameter.setFieldName(JavaRefactoringDescriptorUtil.getString(map, JavaRefactoringDescriptorUtil.getAttributeName(PARAMETER_FIELD_NAME, i)));
}
}
setParameters(result);
}
/**
* Sets the name of the class for the generated parameter object. The name can be <code>null</code> to
* indicate that the refactoring should chose one.
*
* @param className the name of the generated class or <code>null</code>. Default is <code>null</code>
*/
public void setClassName(String className) {
fClassName= className;
}
/**
* Sets delegate keeping. If <code>true</code> delegates will be kept.
*
* @param delegate <code>true</code> to keep delegates. Default is <code>false</code>
*/
public void setDelegate(boolean delegate) {
fDelegate= delegate;
}
/**
* Sets deprecate delegate. If <code>true</code> generated delegates will be marked as deprecated.
*
* @param deprecateDelegate <code>true</code> to deprecate kept delegates. Default is <code>false</code>
*/
public void setDeprecateDelegate(boolean deprecateDelegate) {
fDeprecateDelegate= deprecateDelegate;
}
/**
* Sets whether getters will be created for all fields.
*
* @param getters <code>true</code> to create getters. Default is <code>false</code>.
*/
public void setGetters(boolean getters) {
fGetters= getters;
}
/**
* Sets the method. The method may not be <code>null</code>, has to exist, and has to be
* in a Java project.
*
* @param method the method. May not be <code>null</code>
*/
public void setMethod(IMethod method) {
if (method == null)
throw new IllegalArgumentException("The method must not be null"); //$NON-NLS-1$
if (!method.exists())
throw new IllegalArgumentException("The method must exist"); //$NON-NLS-1$
if (method.getJavaProject() == null)
throw new IllegalArgumentException("The method has to be in a Java project"); //$NON-NLS-1$
fMethod= method;
}
/**
* Sets the package where the parameter object will be created in if it is created as top level class.
* The package can be <code>null</code> to indicate that the package of the method should be used.
*
* @param packageName the package for the top level class or <code>null</code>. Default is <code>null</code>.
*/
public void setPackageName(String packageName) {
fPackageName= packageName;
}
/**
* Sets the name of the parameter object as it will appear in the method signature.
* The name can be <code>null</code> to indicate that the refactoring will choose a name.
*
* @param parameterName the name of the parameter or <code>null</code>. Default is <code>null</code>.
*/
public void setParameterName(String parameterName) {
fParameterName= parameterName;
}
/**
* Sets the parameters. The parameters can be <code>null</code> to indicate that all parameter
* should be used as fields. If not <code>null</code>, the number of parameters passed has to be
* the number of parameter of the method + 1. One element has to be the {@link #PARAMETER_OBJECT}.
* Each parameter may only appear once.
*
* @param parameters the parameters or <code>null</code>. Default is <code>null</code>
*/
public void setParameters(Parameter[] parameters) {
fParameters= parameters;
}
/**
* Sets whether setters will be created for all fields.
*
* @param setters <code>true</code> to create setters. Default is <code>false</code>
*/
public void setSetters(boolean setters) {
fSetters= setters;
}
/**
* Sets whether the parameter object class will be created as top level class.
* if <code>true</code> the class will be created as top level class in the package
* returned by {@link #getPackageName()}. If <code>false</code> the class will be
* created as as nested class in the class containing the method
*
* @param topLevel <code>true</code> to create the parameter object as top level. Default is <code>true</code>
*/
public void setTopLevel(boolean topLevel) {
fTopLevel= topLevel;
}
@Override
public RefactoringStatus validateDescriptor() {
RefactoringStatus result= super.validateDescriptor();
if (!result.isOK())
return result;
if (fMethod == null) {
result.addFatalError("The method must not be null"); //$NON-NLS-1$
return result;
}
IJavaProject javaProject= fMethod.getJavaProject();
if (javaProject == null) {
result.addFatalError("Can not derive Java project from method"); //$NON-NLS-1$
return result;
}
String sourceLevel= javaProject.getOption(JavaCore.COMPILER_SOURCE, true);
String complianceLevel= javaProject.getOption(JavaCore.COMPILER_COMPLIANCE, true);
if (fParameters != null) {
if (fParameters.length - 1 != fMethod.getNumberOfParameters()) {
result.addFatalError("The number of parameters does not match the number of parameters of the method"); //$NON-NLS-1$
}
boolean hasParameterObject= false;
for (int i= 0; i < fParameters.length; i++) {
Parameter parameter= fParameters[i];
if (parameter.isCreateField()) {
String fieldName= parameter.getFieldName();
if (fieldName == null)
result.addError("The parameter " + parameter.getIndex() + " is marked for field creation but does not have a field name"); //$NON-NLS-1$ //$NON-NLS-2$
else {
result.merge(RefactoringStatus.create(JavaConventions.validateFieldName(fieldName, sourceLevel, complianceLevel)));
}
}
if (parameter == PARAMETER_OBJECT) {
if (hasParameterObject)
result.addError("Can not have more than one parameter object"); //$NON-NLS-1$
else
hasParameterObject= true;
}
}
}
if (fClassName != null) {
result.merge(RefactoringStatus.create(JavaConventions.validateIdentifier(fClassName, sourceLevel, complianceLevel)));
}
if (fParameterName != null) {
result.merge(RefactoringStatus.create(JavaConventions.validateIdentifier(fParameterName, sourceLevel, complianceLevel)));
}
if (fPackageName != null && !"".equals(fPackageName)) { //$NON-NLS-1$
result.merge(RefactoringStatus.create(JavaConventions.validatePackageName(fPackageName, sourceLevel, complianceLevel)));
}
return result;
}
}