/*******************************************************************************
* Copyright (c) 2007, 2008 IBM Corporation 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:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.jdt.core.refactoring.descriptors;
import java.util.ArrayList;
import java.util.Map;
import org.eclipse.core.runtime.Assert;
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.Flags;
import org.eclipse.jdt.core.IField;
import org.eclipse.jdt.core.IType;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.core.refactoring.IJavaRefactorings;
import org.eclipse.jdt.internal.core.refactoring.descriptors.JavaRefactoringDescriptorUtil;
/**
* Refactoring descriptor for the extract class 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 ExtractClassDescriptor extends JavaRefactoringDescriptor {
private static final String CREATE_GETTER_SETTER= "createGetterSetter"; //$NON-NLS-1$
private static final String PACKAGE_NAME= "packageName"; //$NON-NLS-1$
private static final String CLASS_NAME= "className"; //$NON-NLS-1$
private static final String FIELD_NAME= "fieldName"; //$NON-NLS-1$
private static final String CREATE_TOP_LEVEL= "createTopLevel"; //$NON-NLS-1$
private static final String NEW_FIELD_COUNT= "newFieldCount"; //$NON-NLS-1$
private static final String CREATE_FIELD_COUNT= "createFieldCount"; //$NON-NLS-1$
private static final String CREATE_FIELD= "createField"; //$NON-NLS-1$
private static final String NEW_FIELD_NAME= "newFieldName"; //$NON-NLS-1$
private static final String OLD_FIELD_NAME= "oldFieldName"; //$NON-NLS-1$
private static final String OLD_FIELD_COUNT= "oldFieldCount"; //$NON-NLS-1$
/**
* Instances of {@link ExtractClassDescriptor.Field} describe which fields will be moved to
* the extracted class and their new name there.
*/
public static class Field {
private final String fFieldName;
private String fNewFieldName;
private boolean fCreateField= true;
private Field(String fieldName) {
super();
Assert.isNotNull(fieldName);
this.fFieldName= fieldName;
this.fNewFieldName= fieldName;
}
/**
* The name of the field in the selected type
*
* @return the name of the field in the selected type
*/
public String getFieldName() {
return fFieldName;
}
/**
* The name of the field in the extracted class. The default is the same as in the selected type
*
* @return the name of the field in the extracted class
*/
public String getNewFieldName() {
return fNewFieldName;
}
/**
* Sets the name of the field in the extracted class. The default is the same as in the selected type
*
* @param newFieldName the new field name. Must not be <code>null</code>
*/
public void setNewFieldName(String newFieldName) {
Assert.isNotNull(newFieldName);
this.fNewFieldName= newFieldName;
}
public int hashCode() {
final int prime= 31;
int result= 1;
result= prime * result + ((fFieldName == null) ? 0 : fFieldName.hashCode());
return result;
}
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Field other= (Field) obj;
if (fFieldName == null) {
if (other.fFieldName != null)
return false;
} else if (!fFieldName.equals(other.fFieldName))
return false;
return true;
}
public String toString() {
return "Field:" + fFieldName + " new name:" + fNewFieldName + " create field:" + fCreateField; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
}
/**
* Returns whether the field will be moved to extracted class. The default is <code>true</code>
*
* @return <code>true</code> if the field will be moved
*/
public boolean isCreateField() {
return fCreateField;
}
/**
* Sets whether the field will be moved to extracted class. The default is <code>true</code>
*
* @param createField if <code>true</code> the field will be moved
*/
public void setCreateField(boolean createField) {
fCreateField= createField;
}
}
private Field[] fFields;
/**
* Creates a new refactoring descriptor.
*/
public ExtractClassDescriptor() {
super(IJavaRefactorings.EXTRACT_CLASS);
}
/**
* 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 ExtractClassDescriptor(String project, String description, String comment, Map arguments, int flags) throws IllegalArgumentException {
super(IJavaRefactorings.EXTRACT_CLASS, project, description, comment, arguments, flags);
if (JavaRefactoringDescriptorUtil.getString(arguments, OLD_FIELD_COUNT, true) != null) {
String[] oldFieldNames= JavaRefactoringDescriptorUtil.getStringArray(arguments, OLD_FIELD_COUNT, OLD_FIELD_NAME, 0);
boolean[] createField= JavaRefactoringDescriptorUtil.getBooleanArray(arguments, CREATE_FIELD_COUNT, CREATE_FIELD, 0);
fFields= new Field[oldFieldNames.length];
for (int i= 0; i < oldFieldNames.length; i++) {
fFields[i]= new Field(oldFieldNames[i]);
fFields[i].setCreateField(createField[i]);
if (createField[i])
fFields[i].setNewFieldName(JavaRefactoringDescriptorUtil.getString(arguments, JavaRefactoringDescriptorUtil.getAttributeName(NEW_FIELD_NAME, i)));
}
}
}
/**
* Creates {@link Field} objects for all instance fields of the type
*
* @param type the type declaring the field that will be moved to the extracted class
* @return an instance of {@link Field} for every field declared in type that is not static
* @throws JavaModelException if the type does not exist or if an exception occurs while accessing its corresponding resource.
*/
public static Field[] getFields(IType type) throws JavaModelException {
IField[] fields= type.getFields();
ArrayList result= new ArrayList();
for (int i= 0; i < fields.length; i++) {
IField field= fields[i];
if (!Flags.isStatic(field.getFlags()) && !field.isEnumConstant())
result.add(new Field(field.getElementName()));
}
return (Field[]) result.toArray(new Field[result.size()]);
}
/**
* Sets the fields. The order is important and should be the same as the order
* returned from {@link #getFields(IType)}. Changing the order can have side effects because of different initialization order.
* Only fields which return <code>true</code> for {@link Field#isCreateField()} are created in the extracted class. Can be
* <code>null</code> to indicate that all instance fields should be moved
*
* @param fields the fields to move to the extracted class. Can be <code>null</code> to indicate that all instance fields should
* be moved
* @throws IllegalArgumentException if one of the fields is <code>null</code>
*/
public void setFields(Field[] fields) throws IllegalArgumentException {
for (int i= 0; i < fields.length; i++) {
Field field= fields[i];
if (field == null)
throw new IllegalArgumentException("Field can not be null"); //$NON-NLS-1$
}
fFields= fields;
}
/**
* Returns the fields. The order of the fields is the same as they will appear in the extracted class if {@link Field#isCreateField()}
* returns <code>true</code>.
*
* @return the fields or <code>null</code>. If <code>null</code> all instance fields from the selected type will be moved
*/
public Field[] getFields() {
return fFields;
}
/**
* Returns the type from which the fields are moved
*
* @return the type
*/
public IType getType() {
return (IType) JavaRefactoringDescriptorUtil.getJavaElement(fArguments, ATTRIBUTE_INPUT, getProject());
}
/**
* Sets the type to extract class from
* @param type the type to extract class from
*/
public void setType(IType type) {
Assert.isNotNull(type);
String project= type.getJavaProject().getElementName();
setProject(project);
JavaRefactoringDescriptorUtil.setJavaElement(fArguments, ATTRIBUTE_INPUT, project, type);
}
/**
* Returns the package where the extracted class will be created in if {{@link #isCreateTopLevel()} returns <code>true</code>.
* Can return <code>null</code> to indicate that the package will be the same as the type
*
* @return the package for the toplevel extracted class or <code>null</code>. If <code>null</code> the package will be the same
* as the type
*/
public String getPackage() {
return JavaRefactoringDescriptorUtil.getString(fArguments, PACKAGE_NAME, true);
}
/**
* Sets the package in which the top level class will be created. Can be <code>null</code> to indicate that the
* package will be the same as the type
*
* @param packageName the package in which the top level class will be created. Can be <code>null</code> to indicate that the
* package will be the same as the type
*/
public void setPackage(String packageName) {
JavaRefactoringDescriptorUtil.setString(fArguments, PACKAGE_NAME, packageName);
}
/**
* Returns the class name for the extracted class or <code>null</code> if the refactoring should choose a name
*
* @return the class name for the extracted class or <code>null</code> if the refactoring should choose a name
*/
public String getClassName() {
return JavaRefactoringDescriptorUtil.getString(fArguments, CLASS_NAME, true);
}
/**
* Sets the class name for the extracted class or <code>null</code> if the refactoring should choose a name
*
* @param className the class name for the extracted class or <code>null</code> if the refactoring should choose a name
*/
public void setClassName(String className) {
JavaRefactoringDescriptorUtil.setString(fArguments, CLASS_NAME, className);
}
/**
* Returns the field name for the generated field or <code>null</code> if the refactoring should choose a name
*
* @return the field name for the generated field or <code>null</code> if the refactoring should choose a name
*/
public String getFieldName() {
return JavaRefactoringDescriptorUtil.getString(fArguments, FIELD_NAME, true);
}
/**
* Sets the field name for the generated field or <code>null</code> if the refactoring should choose a name
*
* @param fieldName the field name for the generated field or <code>null</code> if the refactoring should choose a name
*/
public void setFieldName(String fieldName) {
JavaRefactoringDescriptorUtil.setString(fArguments, FIELD_NAME, fieldName);
}
/**
* Returns whether the extracted class will be created as top level class or as nested class. If <code>true</code> the
* extracted class will be generated as top level class. The default is <code>true</code>
*
* @return if <code>true</code> the extracted class will be generated as top level class. The default is <code>true</code>
*/
public boolean isCreateTopLevel() {
return JavaRefactoringDescriptorUtil.getBoolean(fArguments, CREATE_TOP_LEVEL, true);
}
/**
* Sets whether the extracted class will be created as top level class or as nested class. If <code>true</code> the
* extracted class will be generated as top level class. Else the class will be created as nested class in the type.
* The default is <code>true</code>
*
* @param createTopLevel <code>true</code> to generated as top level class. The default is <code>true</code>
*/
public void setCreateTopLevel(boolean createTopLevel) {
JavaRefactoringDescriptorUtil.setBoolean(fArguments, CREATE_TOP_LEVEL, createTopLevel);
}
/**
* Sets whether getters and setters will be created for all fields.
*
* @param createGetterSetter <code>true</code> to create getters and setters. Default is <code>false</code>.
*/
public void setCreateGetterSetter(boolean createGetterSetter) {
JavaRefactoringDescriptorUtil.setBoolean(fArguments, CREATE_GETTER_SETTER, createGetterSetter);
}
/**
* Returns <code>true</code> if getters and setters are generated for fields. Default is <code>false</code>.
*
* @return <code>true</code> if getters and setters are generated for fields. Default is <code>false</code>
*/
public boolean isCreateGetterSetter() {
return JavaRefactoringDescriptorUtil.getBoolean(fArguments, CREATE_GETTER_SETTER, false);
}
/* (non-Javadoc)
* @see org.eclipse.jdt.core.refactoring.descriptors.JavaRefactoringDescriptor#populateArgumentMap()
*/
protected void populateArgumentMap() {
super.populateArgumentMap();
if (fFields != null) {
String[] oldFieldNames= new String[fFields.length];
String[] newFieldNames= new String[fFields.length];
boolean[] createField= new boolean[fFields.length];
for (int i= 0; i < fFields.length; i++) {
Field field= fFields[i];
Assert.isNotNull(field);
oldFieldNames[i]= field.getFieldName();
createField[i]= field.isCreateField();
if (field.isCreateField())
newFieldNames[i]= field.getNewFieldName();
}
JavaRefactoringDescriptorUtil.setStringArray(fArguments, OLD_FIELD_COUNT, OLD_FIELD_NAME, oldFieldNames, 0);
JavaRefactoringDescriptorUtil.setStringArray(fArguments, NEW_FIELD_COUNT, NEW_FIELD_NAME, newFieldNames, 0);
JavaRefactoringDescriptorUtil.setBooleanArray(fArguments, CREATE_FIELD_COUNT, CREATE_FIELD, createField, 0);
}
}
/* (non-Javadoc)
* @see org.eclipse.jdt.core.refactoring.descriptors.JavaRefactoringDescriptor#validateDescriptor()
*/
public RefactoringStatus validateDescriptor() {
RefactoringStatus status= super.validateDescriptor();
if (getType() == null)
status.addFatalError("The type may not be null"); //$NON-NLS-1$
return status;
}
}