0001: /*******************************************************************************
0002: * Copyright (c) 2007 IBM Corporation and others.
0003: * All rights reserved. This program and the accompanying materials
0004: * are made available under the terms of the Eclipse Public License v1.0
0005: * which accompanies this distribution, and is available at
0006: * http://www.eclipse.org/legal/epl-v10.html
0007: *
0008: * Contributors:
0009: * IBM Corporation - initial API and implementation
0010: *******************************************************************************/package org.eclipse.jdt.internal.corext.refactoring.structure;
0011:
0012: import java.util.ArrayList;
0013: import java.util.Arrays;
0014: import java.util.HashSet;
0015: import java.util.Iterator;
0016: import java.util.LinkedHashMap;
0017: import java.util.List;
0018: import java.util.Map;
0019: import java.util.Set;
0020:
0021: import org.eclipse.text.edits.TextEditGroup;
0022:
0023: import org.eclipse.core.runtime.Assert;
0024: import org.eclipse.core.runtime.CoreException;
0025: import org.eclipse.core.runtime.IProgressMonitor;
0026: import org.eclipse.core.runtime.OperationCanceledException;
0027: import org.eclipse.core.runtime.SubProgressMonitor;
0028:
0029: import org.eclipse.ltk.core.refactoring.Change;
0030: import org.eclipse.ltk.core.refactoring.Refactoring;
0031: import org.eclipse.ltk.core.refactoring.RefactoringDescriptor;
0032: import org.eclipse.ltk.core.refactoring.RefactoringStatus;
0033:
0034: import org.eclipse.jdt.core.Flags;
0035: import org.eclipse.jdt.core.ICompilationUnit;
0036: import org.eclipse.jdt.core.IField;
0037: import org.eclipse.jdt.core.IJavaElement;
0038: import org.eclipse.jdt.core.IJavaProject;
0039: import org.eclipse.jdt.core.IPackageFragment;
0040: import org.eclipse.jdt.core.IPackageFragmentRoot;
0041: import org.eclipse.jdt.core.IType;
0042: import org.eclipse.jdt.core.ITypeRoot;
0043: import org.eclipse.jdt.core.JavaModelException;
0044: import org.eclipse.jdt.core.Signature;
0045: import org.eclipse.jdt.core.dom.AST;
0046: import org.eclipse.jdt.core.dom.ASTNode;
0047: import org.eclipse.jdt.core.dom.ASTVisitor;
0048: import org.eclipse.jdt.core.dom.ArrayCreation;
0049: import org.eclipse.jdt.core.dom.ArrayInitializer;
0050: import org.eclipse.jdt.core.dom.Assignment;
0051: import org.eclipse.jdt.core.dom.ClassInstanceCreation;
0052: import org.eclipse.jdt.core.dom.CompilationUnit;
0053: import org.eclipse.jdt.core.dom.Expression;
0054: import org.eclipse.jdt.core.dom.FieldAccess;
0055: import org.eclipse.jdt.core.dom.FieldDeclaration;
0056: import org.eclipse.jdt.core.dom.IExtendedModifier;
0057: import org.eclipse.jdt.core.dom.IPackageBinding;
0058: import org.eclipse.jdt.core.dom.ITypeBinding;
0059: import org.eclipse.jdt.core.dom.IVariableBinding;
0060: import org.eclipse.jdt.core.dom.Javadoc;
0061: import org.eclipse.jdt.core.dom.Modifier;
0062: import org.eclipse.jdt.core.dom.NullLiteral;
0063: import org.eclipse.jdt.core.dom.QualifiedName;
0064: import org.eclipse.jdt.core.dom.SimpleName;
0065: import org.eclipse.jdt.core.dom.SuperFieldAccess;
0066: import org.eclipse.jdt.core.dom.Type;
0067: import org.eclipse.jdt.core.dom.TypeDeclaration;
0068: import org.eclipse.jdt.core.dom.VariableDeclaration;
0069: import org.eclipse.jdt.core.dom.VariableDeclarationFragment;
0070: import org.eclipse.jdt.core.dom.Modifier.ModifierKeyword;
0071: import org.eclipse.jdt.core.dom.rewrite.ASTRewrite;
0072: import org.eclipse.jdt.core.dom.rewrite.ListRewrite;
0073: import org.eclipse.jdt.core.refactoring.descriptors.ExtractClassDescriptor;
0074: import org.eclipse.jdt.core.refactoring.descriptors.JavaRefactoringDescriptor;
0075: import org.eclipse.jdt.core.refactoring.descriptors.ExtractClassDescriptor.Field;
0076: import org.eclipse.jdt.core.search.IJavaSearchConstants;
0077: import org.eclipse.jdt.core.search.SearchMatch;
0078: import org.eclipse.jdt.core.search.SearchPattern;
0079:
0080: import org.eclipse.jdt.internal.corext.codemanipulation.GetterSetterUtil;
0081: import org.eclipse.jdt.internal.corext.codemanipulation.StubUtility;
0082: import org.eclipse.jdt.internal.corext.dom.ASTNodes;
0083: import org.eclipse.jdt.internal.corext.dom.Bindings;
0084: import org.eclipse.jdt.internal.corext.dom.NodeFinder;
0085: import org.eclipse.jdt.internal.corext.dom.TypeBindingVisitor;
0086: import org.eclipse.jdt.internal.corext.refactoring.Checks;
0087: import org.eclipse.jdt.internal.corext.refactoring.JDTRefactoringDescriptorComment;
0088: import org.eclipse.jdt.internal.corext.refactoring.ParameterInfo;
0089: import org.eclipse.jdt.internal.corext.refactoring.RefactoringCoreMessages;
0090: import org.eclipse.jdt.internal.corext.refactoring.RefactoringScopeFactory;
0091: import org.eclipse.jdt.internal.corext.refactoring.RefactoringSearchEngine;
0092: import org.eclipse.jdt.internal.corext.refactoring.SearchResultGroup;
0093: import org.eclipse.jdt.internal.corext.refactoring.base.JavaStatusContext;
0094: import org.eclipse.jdt.internal.corext.refactoring.changes.DynamicValidationRefactoringChange;
0095: import org.eclipse.jdt.internal.corext.refactoring.structure.ParameterObjectFactory.CreationListener;
0096: import org.eclipse.jdt.internal.corext.refactoring.util.TextChangeManager;
0097: import org.eclipse.jdt.internal.corext.util.JavaModelUtil;
0098: import org.eclipse.jdt.internal.corext.util.Messages;
0099:
0100: import org.eclipse.jdt.internal.ui.JavaPlugin;
0101: import org.eclipse.jdt.internal.ui.text.correction.ASTResolving;
0102:
0103: public class ExtractClassRefactoring extends Refactoring {
0104:
0105: public static class ExtractClassDescriptorVerification {
0106: private ExtractClassDescriptor fDescriptor;
0107:
0108: public ExtractClassDescriptorVerification(
0109: ExtractClassDescriptor descriptor) {
0110: fDescriptor = descriptor;
0111: }
0112:
0113: public RefactoringStatus validateClassName() {
0114: RefactoringStatus status = new RefactoringStatus();
0115: status.merge(Checks.checkTypeName(fDescriptor
0116: .getClassName(), fDescriptor.getType()));
0117: status.merge(checkClass());
0118: return status;
0119: }
0120:
0121: private RefactoringStatus checkClass() {
0122: RefactoringStatus status = new RefactoringStatus();
0123: IType type = fDescriptor.getType();
0124: if (!fDescriptor.isCreateTopLevel()) {
0125: if (type.getType(fDescriptor.getClassName()).exists()) {
0126: status
0127: .addError(Messages
0128: .format(
0129: RefactoringCoreMessages.ExtractClassRefactoring_errror_nested_name_clash,
0130: new Object[] {
0131: fDescriptor
0132: .getClassName(),
0133: type
0134: .getElementName() }));
0135: }
0136: } else {
0137: status.merge(checkPackageClass());
0138: }
0139: return status;
0140: }
0141:
0142: private RefactoringStatus checkPackageClass() {
0143: RefactoringStatus status = new RefactoringStatus();
0144: IType type = fDescriptor.getType();
0145: IPackageFragmentRoot ancestor = (IPackageFragmentRoot) type
0146: .getAncestor(IJavaElement.PACKAGE_FRAGMENT_ROOT);
0147: IPackageFragment packageFragment = ancestor
0148: .getPackageFragment(fDescriptor.getPackage());
0149: if (packageFragment.getCompilationUnit(
0150: fDescriptor.getClassName()
0151: + JavaModelUtil.DEFAULT_CU_SUFFIX).exists())
0152: status
0153: .addError(Messages
0154: .format(
0155: RefactoringCoreMessages.ExtractClassRefactoring_error_toplevel_name_clash,
0156: new Object[] {
0157: fDescriptor
0158: .getClassName(),
0159: fDescriptor
0160: .getPackage() }));
0161: return status;
0162: }
0163:
0164: public RefactoringStatus validateTopLevel() {
0165: return checkClass();
0166: }
0167:
0168: public RefactoringStatus validateParameterName() {
0169: RefactoringStatus status = new RefactoringStatus();
0170: String parameterName = fDescriptor.getFieldName();
0171: IType type = fDescriptor.getType();
0172: status.merge(Checks.checkFieldName(parameterName, type));
0173: validateFieldNames(status, parameterName, type);
0174: return status;
0175: }
0176:
0177: private void validateFieldNames(RefactoringStatus status,
0178: String parameterName, IType type) {
0179: if (type.getField(parameterName).exists()) {
0180: Field[] fields = fDescriptor.getFields();
0181: for (int i = 0; i < fields.length; i++) {
0182: Field field = fields[i];
0183: if (parameterName.equals(field.getFieldName())) {
0184: if (!field.isCreateField())
0185: status
0186: .addError(Messages
0187: .format(
0188: RefactoringCoreMessages.ExtractClassRefactoring_error_field_already_exists,
0189: parameterName));
0190: }
0191: }
0192: }
0193: }
0194:
0195: public RefactoringStatus validateFields() {
0196: RefactoringStatus status = new RefactoringStatus();
0197: Field[] fields = fDescriptor.getFields();
0198: Set names = new HashSet();
0199: for (int i = 0; i < fields.length; i++) {
0200: Field field = fields[i];
0201: if (field.isCreateField()) {
0202: if (names.contains(field.getNewFieldName())) {
0203: status
0204: .addError(Messages
0205: .format(
0206: RefactoringCoreMessages.ExtractClassRefactoring_error_duplicate_field_name,
0207: field.getNewFieldName()));
0208: }
0209: names.add(field.getNewFieldName());
0210: status.merge(Checks.checkFieldName(field
0211: .getNewFieldName(), fDescriptor.getType()));
0212: }
0213: }
0214: if (names.size() == 0) {
0215: status
0216: .addError(RefactoringCoreMessages.ExtractClassRefactoring_error_msg_one_field);
0217: }
0218: validateFieldNames(status, fDescriptor.getFieldName(),
0219: fDescriptor.getType());
0220: return status;
0221: }
0222:
0223: public RefactoringStatus validateAll() {
0224: RefactoringStatus status = new RefactoringStatus();
0225: status.merge(validateClassName()); //also validates toplevel
0226: status.merge(validateFields());
0227: status.merge(validateParameterName());
0228: return status;
0229: }
0230: }
0231:
0232: private final class FieldReferenceFinder extends ASTVisitor {
0233: public boolean fFieldRefFound = false;
0234:
0235: private FieldReferenceFinder() {
0236: }
0237:
0238: public boolean visit(FieldAccess node) {
0239: IVariableBinding fieldBinding = node.resolveFieldBinding();
0240: return checkVariableBinding(fieldBinding);
0241: }
0242:
0243: public boolean visit(SimpleName node) {
0244: IVariableBinding variableBinding = ASTNodes
0245: .getVariableBinding(node);
0246: return checkVariableBinding(variableBinding);
0247: }
0248:
0249: private boolean checkVariableBinding(
0250: IVariableBinding fieldBinding) {
0251: if (fieldBinding != null) {
0252: if (fieldBinding.isField()) {
0253: ITypeBinding declaringClass = fieldBinding
0254: .getDeclaringClass();
0255: if ((declaringClass != null)
0256: && declaringClass
0257: .getQualifiedName()
0258: .equals(
0259: fDescriptor
0260: .getType()
0261: .getFullyQualifiedName())) {
0262: FieldInfo fi = (FieldInfo) fVariables
0263: .get(fieldBinding.getName());
0264: if (fi != null
0265: && isCreateField(fi)
0266: && Bindings.equals(fieldBinding, fi.pi
0267: .getOldBinding())) {
0268: fFieldRefFound = true;
0269: return false;
0270: }
0271: }
0272: }
0273: }
0274: return true;
0275: }
0276: }
0277:
0278: private final class FieldInfo {
0279: ParameterInfo pi;
0280: VariableDeclarationFragment declaration;
0281: IField ifield;
0282: String name;
0283: Expression initializer;
0284: private Boolean hasFieldReferences = null;
0285:
0286: public boolean hasFieldReference() {
0287: if (hasFieldReferences == null) {
0288: if (initializer != null) {
0289: FieldReferenceFinder frf = new FieldReferenceFinder();
0290: initializer.accept(frf);
0291: hasFieldReferences = Boolean
0292: .valueOf(frf.fFieldRefFound);
0293: } else {
0294: hasFieldReferences = Boolean.FALSE;
0295: }
0296: }
0297: return hasFieldReferences.booleanValue();
0298: }
0299:
0300: private FieldInfo(ParameterInfo parameterInfo, IField ifield) {
0301: super ();
0302: this .pi = parameterInfo;
0303: this .ifield = ifield;
0304: this .name = ifield.getElementName();
0305: }
0306: }
0307:
0308: private ExtractClassDescriptor fDescriptor;
0309: private Map fVariables;
0310: private CompilationUnitRewrite fBaseCURewrite;
0311: private TextChangeManager fChangeManager;
0312: private ParameterObjectFactory fParameterObjectFactory;
0313: private ExtractClassDescriptorVerification fVerification;
0314:
0315: public ExtractClassRefactoring(ExtractClassDescriptor descriptor) {
0316: fDescriptor = descriptor;
0317: IType type = fDescriptor.getType();
0318: if (fDescriptor.getPackage() == null) {
0319: fDescriptor.setPackage(type.getPackageFragment()
0320: .getElementName());
0321: }
0322: if (fDescriptor.getClassName() == null) {
0323: fDescriptor.setClassName(type.getElementName() + "Data"); //$NON-NLS-1$
0324: }
0325: if (fDescriptor.getFieldName() == null) {
0326: fDescriptor.setFieldName(StubUtility
0327: .getVariableNameSuggestions(
0328: StubUtility.INSTANCE_FIELD, type
0329: .getJavaProject(),
0330: "data", 0, null, true)[0]); //$NON-NLS-1$
0331: }
0332: if (fDescriptor.getFields() == null) {
0333: try {
0334: fDescriptor.setFields(ExtractClassDescriptor
0335: .getFields(type));
0336: } catch (JavaModelException e) {
0337: JavaPlugin.log(e);
0338: }
0339: }
0340: fVerification = new ExtractClassDescriptorVerification(
0341: descriptor);
0342: }
0343:
0344: public RefactoringStatus checkInitialConditions(IProgressMonitor pm)
0345: throws CoreException, OperationCanceledException {
0346: RefactoringStatus result = new RefactoringStatus();
0347: pm
0348: .beginTask(
0349: RefactoringCoreMessages.ExtractClassRefactoring_progress_msg_check_initial_condition,
0350: 5);
0351: try {
0352: result.merge(fDescriptor.validateDescriptor());
0353: if (!result.isOK())
0354: return result;
0355: IType type = fDescriptor.getType();
0356: result.merge(Checks.checkAvailability(type));
0357: if (!result.isOK())
0358: return result;
0359: pm.worked(1);
0360: Field[] fields = ExtractClassDescriptor
0361: .getFields(fDescriptor.getType());
0362: pm.worked(1);
0363: if (pm.isCanceled())
0364: throw new OperationCanceledException();
0365: fVariables = new LinkedHashMap();
0366: if (fields.length == 0) {
0367: result
0368: .addFatalError(
0369: RefactoringCoreMessages.ExtractClassRefactoring_error_no_usable_fields,
0370: JavaStatusContext.create(type));
0371: return result;
0372: }
0373: for (int i = 0; i < fields.length; i++) {
0374: Field field = fields[i];
0375: String fieldName = field.getFieldName();
0376: IField declField = type.getField(fieldName);
0377: ParameterInfo info = new ParameterInfo(Signature
0378: .toString(declField.getTypeSignature()),
0379: fieldName, i);
0380: fVariables.put(fieldName,
0381: new FieldInfo(info, declField));
0382: if (pm.isCanceled())
0383: throw new OperationCanceledException();
0384: }
0385: pm.worked(3);
0386: } finally {
0387: pm.done();
0388: }
0389: return result;
0390: }
0391:
0392: public RefactoringStatus checkFinalConditions(IProgressMonitor pm)
0393: throws CoreException, OperationCanceledException {
0394: RefactoringStatus result = new RefactoringStatus();
0395: result.merge(fVerification.validateAll());
0396: try {
0397: pm
0398: .beginTask(
0399: RefactoringCoreMessages.ExtractClassRefactoring_progress_final_conditions,
0400: 95);
0401: for (Iterator iter = fVariables.values().iterator(); iter
0402: .hasNext();) {
0403: FieldInfo fi = (FieldInfo) iter.next();
0404: boolean createField = isCreateField(fi);
0405: if (createField) {
0406: IField field = fi.ifield;
0407: int flags = field.getFlags();
0408: if (Flags.isStatic(flags)) {
0409: result
0410: .addFatalError(
0411: Messages
0412: .format(
0413: RefactoringCoreMessages.ExtractClassRefactoring_error_field_is_static,
0414: field
0415: .getElementName()),
0416: JavaStatusContext.create(field));
0417: return result;
0418: }
0419: if (Flags.isTransient(flags)) {
0420: result
0421: .addWarning(
0422: Messages
0423: .format(
0424: RefactoringCoreMessages.ExtractClassRefactoring_warning_field_is_transient,
0425: field
0426: .getElementName()),
0427: JavaStatusContext.create(field));
0428: }
0429: if (Flags.isVolatile(flags)) {
0430: result
0431: .addWarning(
0432: Messages
0433: .format(
0434: RefactoringCoreMessages.ExtractClassRefactoring_warning_field_is_volatile,
0435: field
0436: .getElementName()),
0437: JavaStatusContext.create(field));
0438: }
0439: }
0440: }
0441: pm.worked(5);
0442: fChangeManager = new TextChangeManager();
0443: fParameterObjectFactory = initializeFactory();
0444: IType type = fDescriptor.getType();
0445: pm.worked(5);
0446:
0447: FieldDeclaration field = performFieldRewrite(type,
0448: fParameterObjectFactory, result);
0449: int flags = RefactoringDescriptor.STRUCTURAL_CHANGE
0450: | JavaRefactoringDescriptor.JAR_MIGRATION
0451: | JavaRefactoringDescriptor.JAR_REFACTORING
0452: | JavaRefactoringDescriptor.JAR_SOURCE_ATTACHMENT;
0453: if (!Modifier.isPrivate(field.getModifiers()))
0454: flags |= RefactoringDescriptor.MULTI_CHANGE;
0455:
0456: result.merge(updateReferences(type,
0457: fParameterObjectFactory, new SubProgressMonitor(pm,
0458: 65)));
0459:
0460: } finally {
0461: pm.done();
0462: }
0463: return result;
0464: }
0465:
0466: public Change createChange(IProgressMonitor pm)
0467: throws CoreException, OperationCanceledException {
0468: pm
0469: .beginTask(
0470: RefactoringCoreMessages.ExtractClassRefactoring_progress_create_change,
0471: 10);
0472: try {
0473: ICompilationUnit typeCU = fDescriptor.getType()
0474: .getCompilationUnit();
0475: IPackageFragmentRoot packageRoot = (IPackageFragmentRoot) typeCU
0476: .getAncestor(IJavaElement.PACKAGE_FRAGMENT_ROOT);
0477: ArrayList changes = new ArrayList();
0478:
0479: changes.addAll(createParameterObject(
0480: fParameterObjectFactory, packageRoot));
0481: fChangeManager.manage(typeCU, fBaseCURewrite.createChange(
0482: true, pm));
0483: changes.addAll(Arrays
0484: .asList(fChangeManager.getAllChanges()));
0485: String project = fDescriptor.getType().getJavaProject()
0486: .getElementName();
0487: fDescriptor.setProject(project);
0488: fDescriptor.setDescription(getName());
0489: fDescriptor.setComment(createComment());
0490: DynamicValidationRefactoringChange change = new DynamicValidationRefactoringChange(
0491: fDescriptor,
0492: RefactoringCoreMessages.ExtractClassRefactoring_change_name,
0493: (Change[]) changes.toArray(new Change[changes
0494: .size()]));
0495: return change;
0496: } finally {
0497: pm.done();
0498: }
0499: }
0500:
0501: private String createComment() {
0502: String header = Messages
0503: .format(
0504: RefactoringCoreMessages.ExtractClassRefactoring_change_comment_header,
0505: new Object[] { fDescriptor.getClassName(),
0506: fDescriptor.getType().getElementName() });
0507: JDTRefactoringDescriptorComment comment = new JDTRefactoringDescriptorComment(
0508: fDescriptor.getType().getJavaProject().getElementName(),
0509: this , header);
0510: comment
0511: .addSetting(Messages
0512: .format(
0513: RefactoringCoreMessages.ExtractClassRefactoring_comment_extracted_class,
0514: fDescriptor.getClassName()));
0515:
0516: if (fDescriptor.isCreateTopLevel())
0517: comment
0518: .addSetting(Messages
0519: .format(
0520: RefactoringCoreMessages.ExtractClassRefactoring_comment_package,
0521: fDescriptor.getPackage()));
0522:
0523: Field[] fields = fDescriptor.getFields();
0524: ArrayList strings = new ArrayList();
0525: for (int i = 0; i < fields.length; i++) {
0526: Field field = fields[i];
0527: if (field.isCreateField()) {
0528: strings
0529: .add(Messages
0530: .format(
0531: RefactoringCoreMessages.ExtractClassRefactoring_comment_field_renamed,
0532: new Object[] {
0533: field.getFieldName(),
0534: field.getNewFieldName() }));
0535: }
0536: }
0537: String fieldString = JDTRefactoringDescriptorComment
0538: .createCompositeSetting(
0539: RefactoringCoreMessages.ExtractClassRefactoring_comment_move_field,
0540: (String[]) strings.toArray(new String[strings
0541: .size()]));
0542: comment.addSetting(fieldString);
0543:
0544: if (fDescriptor.isCreateGetterSetter())
0545: comment
0546: .addSetting(RefactoringCoreMessages.ExtractClassRefactoring_comment_getters);
0547:
0548: comment
0549: .addSetting(Messages
0550: .format(
0551: RefactoringCoreMessages.ExtractClassRefactoring_comment_fieldname,
0552: fDescriptor.getFieldName()));
0553: return comment.asString();
0554: }
0555:
0556: private class FieldUpdate extends CreationListener {
0557: public void fieldCreated(CompilationUnitRewrite cuRewrite,
0558: FieldDeclaration field, ParameterInfo pi) {
0559: FieldInfo fieldInfo = getFieldInfo(pi.getOldName());
0560: FieldDeclaration parent = (FieldDeclaration) fieldInfo.declaration
0561: .getParent();
0562: List modifiers = parent.modifiers();
0563: ListRewrite listRewrite = cuRewrite.getASTRewrite()
0564: .getListRewrite(field,
0565: FieldDeclaration.MODIFIERS2_PROPERTY);
0566: for (Iterator iterator = modifiers.iterator(); iterator
0567: .hasNext();) {
0568: IExtendedModifier mod = (IExtendedModifier) iterator
0569: .next();
0570: //Temporarily disabled until initialization of final fields is handled correctly
0571: // if (mod.isModifier()) {
0572: // Modifier modifier= (Modifier) mod;
0573: // if (modifier.isFinal())
0574: // listRewrite.insertLast(moveNode(cuRewrite, modifier), null);
0575: // }
0576: if (mod.isAnnotation()) {
0577: listRewrite.insertFirst(moveNode(cuRewrite,
0578: (ASTNode) mod), null);
0579: }
0580: }
0581: if (fieldInfo.initializer != null
0582: && fieldInfo.hasFieldReference()) {
0583: List fragments = field.fragments();
0584: for (Iterator iterator = fragments.iterator(); iterator
0585: .hasNext();) {
0586: VariableDeclarationFragment vdf = (VariableDeclarationFragment) iterator
0587: .next();
0588: vdf.setInitializer((Expression) moveNode(cuRewrite,
0589: fieldInfo.initializer));
0590: }
0591: }
0592: if (parent.getJavadoc() != null) {
0593: field.setJavadoc((Javadoc) moveNode(cuRewrite, parent
0594: .getJavadoc()));
0595: }
0596: }
0597:
0598: public boolean isCreateSetter(ParameterInfo pi) {
0599: return true; //ignore that the original variable was final
0600: }
0601:
0602: public boolean isUseInConstructor(ParameterInfo pi) {
0603: FieldInfo fi = getFieldInfo(pi.getOldName());
0604: return fi.initializer != null && !fi.hasFieldReference();
0605: }
0606: }
0607:
0608: private List createParameterObject(ParameterObjectFactory pof,
0609: IPackageFragmentRoot packageRoot) throws CoreException {
0610: FieldUpdate fieldUpdate = new FieldUpdate();
0611: if (fDescriptor.isCreateTopLevel())
0612: return pof.createTopLevelParameterObject(packageRoot,
0613: fieldUpdate);
0614: else {
0615: CompilationUnit root = fBaseCURewrite.getRoot();
0616: TypeDeclaration typeDecl = (TypeDeclaration) NodeFinder
0617: .perform(root, fDescriptor.getType()
0618: .getSourceRange());
0619: ASTRewrite rewrite = fBaseCURewrite.getASTRewrite();
0620: ListRewrite listRewrite = rewrite.getListRewrite(typeDecl,
0621: TypeDeclaration.BODY_DECLARATIONS_PROPERTY);
0622: TypeDeclaration paramClass = pof.createClassDeclaration(
0623: typeDecl.getName().getFullyQualifiedName(),
0624: fBaseCURewrite, fieldUpdate);
0625: paramClass.modifiers().add(
0626: rewrite.getAST().newModifier(
0627: ModifierKeyword.PUBLIC_KEYWORD));
0628: paramClass.modifiers().add(
0629: rewrite.getAST().newModifier(
0630: ModifierKeyword.STATIC_KEYWORD));
0631: listRewrite
0632: .insertFirst(
0633: paramClass,
0634: fBaseCURewrite
0635: .createGroupDescription(RefactoringCoreMessages.ExtractClassRefactoring_group_insert_parameter));
0636: return new ArrayList(); //Change will be generated later for fBaseCURewrite
0637: }
0638:
0639: }
0640:
0641: private ParameterObjectFactory initializeFactory() {
0642: ParameterObjectFactory pof = new ParameterObjectFactory();
0643: pof.setClassName(fDescriptor.getClassName());
0644: pof.setPackage(fDescriptor.getPackage());
0645: pof.setEnclosingType(fDescriptor.getType()
0646: .getFullyQualifiedName());
0647: pof.setCreateGetter(fDescriptor.isCreateGetterSetter());
0648: pof.setCreateSetter(fDescriptor.isCreateGetterSetter());
0649: List variables = new ArrayList();
0650: for (Iterator iterator = fVariables.values().iterator(); iterator
0651: .hasNext();) {
0652: FieldInfo info = (FieldInfo) iterator.next();
0653: boolean createField = isCreateField(info);
0654: info.pi.setCreateField(createField);
0655: if (createField) {
0656: Field field = getField(info.name);
0657: info.pi.setNewName(field.getNewFieldName());
0658: }
0659: variables.add(info.pi);
0660: }
0661: pof.setVariables(variables);
0662: return pof;
0663: }
0664:
0665: private Field getField(String name) {
0666: Field[] fields = fDescriptor.getFields();
0667: for (int i = 0; i < fields.length; i++) {
0668: Field field = fields[i];
0669: if (field.getFieldName().equals(name))
0670: return field;
0671: }
0672: return null;
0673: }
0674:
0675: private RefactoringStatus updateReferences(IType type,
0676: ParameterObjectFactory pof, IProgressMonitor pm)
0677: throws CoreException {
0678: RefactoringStatus status = new RefactoringStatus();
0679: pm
0680: .beginTask(
0681: RefactoringCoreMessages.ExtractClassRefactoring_progress_updating_references,
0682: 100);
0683: try {
0684: pm.worked(10);
0685: if (pm.isCanceled())
0686: throw new OperationCanceledException();
0687: List validIFields = new ArrayList();
0688: for (Iterator iterator = fVariables.values().iterator(); iterator
0689: .hasNext();) {
0690: FieldInfo info = (FieldInfo) iterator.next();
0691: if (isCreateField(info))
0692: validIFields.add(info.ifield);
0693: }
0694: if (validIFields.size() == 0) {
0695: status
0696: .addWarning(
0697: RefactoringCoreMessages.ExtractClassRefactoring_warning_no_fields_moved,
0698: JavaStatusContext.create(type));
0699: return status;
0700: }
0701: SearchPattern pattern = RefactoringSearchEngine
0702: .createOrPattern((IField[]) validIFields
0703: .toArray(new IField[validIFields.size()]),
0704: IJavaSearchConstants.ALL_OCCURRENCES);
0705: SearchResultGroup[] results = RefactoringSearchEngine
0706: .search(pattern, RefactoringScopeFactory
0707: .create(type), pm, status);
0708: SubProgressMonitor spm = new SubProgressMonitor(pm, 90);
0709: spm
0710: .beginTask(
0711: RefactoringCoreMessages.ExtractClassRefactoring_progress_updating_references,
0712: results.length * 10);
0713: try {
0714: for (int i = 0; i < results.length; i++) {
0715: SearchResultGroup group = results[i];
0716: ICompilationUnit unit = group.getCompilationUnit();
0717:
0718: CompilationUnitRewrite cuRewrite;
0719: if (unit.equals(fBaseCURewrite.getCu()))
0720: cuRewrite = fBaseCURewrite;
0721: else
0722: cuRewrite = new CompilationUnitRewrite(unit);
0723: spm.worked(1);
0724:
0725: status.merge(replaceReferences(pof, group,
0726: cuRewrite));
0727: if (cuRewrite != fBaseCURewrite) //Change for fBaseCURewrite will be generated later
0728: fChangeManager
0729: .manage(unit, cuRewrite.createChange(
0730: true, new SubProgressMonitor(
0731: spm, 9)));
0732: if (spm.isCanceled())
0733: throw new OperationCanceledException();
0734: }
0735: } finally {
0736: spm.done();
0737: }
0738: } finally {
0739: pm.done();
0740: }
0741: return status;
0742: }
0743:
0744: private RefactoringStatus replaceReferences(
0745: ParameterObjectFactory pof, SearchResultGroup group,
0746: CompilationUnitRewrite cuRewrite) {
0747: TextEditGroup writeGroup = cuRewrite
0748: .createGroupDescription(RefactoringCoreMessages.ExtractClassRefactoring_group_replace_write);
0749: TextEditGroup readGroup = cuRewrite
0750: .createGroupDescription(RefactoringCoreMessages.ExtractClassRefactoring_group_replace_read);
0751: ITypeRoot typeRoot = cuRewrite.getCu();
0752: IJavaProject javaProject = typeRoot.getJavaProject();
0753: AST ast = cuRewrite.getAST();
0754:
0755: RefactoringStatus status = new RefactoringStatus();
0756: String parameterName = fDescriptor.getFieldName();
0757:
0758: SearchMatch[] searchResults = group.getSearchResults();
0759: for (int j = 0; j < searchResults.length; j++) {
0760: SearchMatch searchMatch = searchResults[j];
0761: ASTNode node = NodeFinder.perform(cuRewrite.getRoot(),
0762: searchMatch.getOffset(), searchMatch.getLength());
0763: ASTNode parent = node.getParent();
0764: boolean isDeclaration = parent instanceof VariableDeclaration
0765: && ((VariableDeclaration) parent).getInitializer() != node;
0766: if (!isDeclaration && node instanceof SimpleName) {
0767: ASTRewrite rewrite = cuRewrite.getASTRewrite();
0768: if (parent.getNodeType() == ASTNode.SWITCH_CASE)
0769: status
0770: .addError(
0771: RefactoringCoreMessages.ExtractClassRefactoring_error_switch,
0772: JavaStatusContext.create(typeRoot,
0773: node));
0774:
0775: SimpleName name = (SimpleName) node;
0776: ParameterInfo pi = getFieldInfo(name.getIdentifier()).pi;
0777: boolean writeAccess = ASTResolving.isWriteAccess(name);
0778: if (writeAccess && fDescriptor.isCreateGetterSetter()) {
0779: boolean useSuper = parent.getNodeType() == ASTNode.SUPER_FIELD_ACCESS;
0780: Expression qualifier = getQualifier(parent);
0781: ASTNode replaceNode = getReplacementNode(parent,
0782: useSuper, qualifier);
0783: Expression assignedValue = getAssignedValue(pof,
0784: parameterName, javaProject, status,
0785: rewrite, pi, useSuper, name
0786: .resolveTypeBinding(), qualifier,
0787: replaceNode, typeRoot);
0788: if (assignedValue == null) {
0789: status
0790: .addError(
0791: RefactoringCoreMessages.ExtractClassRefactoring_error_unable_to_convert_node,
0792: JavaStatusContext.create(
0793: typeRoot, replaceNode));
0794: } else {
0795: NullLiteral marker = qualifier == null ? null
0796: : ast.newNullLiteral();
0797: Expression access = pof.createFieldWriteAccess(
0798: pi, parameterName, ast, javaProject,
0799: assignedValue, useSuper, marker);
0800: replaceMarker(rewrite, qualifier, access,
0801: marker);
0802: rewrite
0803: .replace(replaceNode, access,
0804: writeGroup);
0805: }
0806: } else {
0807: Expression fieldReadAccess = pof
0808: .createFieldReadAccess(pi, parameterName,
0809: ast, javaProject, false, null); //qualifier is already there
0810: rewrite.replace(name, fieldReadAccess, readGroup);
0811: }
0812: }
0813: }
0814: return status;
0815: }
0816:
0817: private Expression getAssignedValue(ParameterObjectFactory pof,
0818: String parameterName, IJavaProject javaProject,
0819: RefactoringStatus status, ASTRewrite rewrite,
0820: ParameterInfo pi, boolean useSuper,
0821: ITypeBinding typeBinding, Expression qualifier,
0822: ASTNode replaceNode, ITypeRoot typeRoot) {
0823: AST ast = rewrite.getAST();
0824: boolean is50OrHigher = JavaModelUtil.is50OrHigher(javaProject);
0825: Expression assignedValue = handleSimpleNameAssignment(
0826: replaceNode, pof, parameterName, ast, javaProject,
0827: useSuper);
0828: if (assignedValue == null) {
0829: NullLiteral marker = qualifier == null ? null : ast
0830: .newNullLiteral();
0831: Expression fieldReadAccess = pof.createFieldReadAccess(pi,
0832: parameterName, ast, javaProject, useSuper, marker);
0833: assignedValue = GetterSetterUtil.getAssignedValue(
0834: replaceNode, rewrite, fieldReadAccess, typeBinding,
0835: is50OrHigher);
0836: boolean markerReplaced = replaceMarker(rewrite, qualifier,
0837: assignedValue, marker);
0838: if (markerReplaced) {
0839: switch (qualifier.getNodeType()) {
0840: case ASTNode.METHOD_INVOCATION:
0841: case ASTNode.CLASS_INSTANCE_CREATION:
0842: case ASTNode.SUPER_METHOD_INVOCATION:
0843: case ASTNode.PARENTHESIZED_EXPRESSION:
0844: status
0845: .addWarning(
0846: RefactoringCoreMessages.ExtractClassRefactoring_warning_semantic_change,
0847: JavaStatusContext.create(typeRoot,
0848: replaceNode));
0849: break;
0850: }
0851: }
0852: }
0853: return assignedValue;
0854: }
0855:
0856: private ASTNode getReplacementNode(ASTNode parent,
0857: boolean useSuper, Expression qualifier) {
0858: if (qualifier != null || useSuper) {
0859: return parent.getParent();
0860: } else {
0861: return parent;
0862: }
0863: }
0864:
0865: private Expression getQualifier(ASTNode parent) {
0866: switch (parent.getNodeType()) {
0867: case ASTNode.FIELD_ACCESS:
0868: return ((FieldAccess) parent).getExpression();
0869: case ASTNode.QUALIFIED_NAME:
0870: return ((QualifiedName) parent).getQualifier();
0871: case ASTNode.SUPER_FIELD_ACCESS:
0872: return ((SuperFieldAccess) parent).getQualifier();
0873: default:
0874: return null;
0875: }
0876: }
0877:
0878: /*
0879: * Replaces the NullLiteral dummy with the copied qualifier
0880: */
0881: private boolean replaceMarker(final ASTRewrite rewrite,
0882: final Expression qualifier, Expression assignedValue,
0883: final NullLiteral marker) {
0884: class MarkerReplacer extends ASTVisitor {
0885:
0886: private boolean fReplaced = false;
0887:
0888: public boolean visit(NullLiteral node) {
0889: if (node == marker) {
0890: rewrite.replace(node, rewrite
0891: .createCopyTarget(qualifier), null);
0892: fReplaced = true;
0893: return false;
0894: }
0895: return true;
0896: }
0897: }
0898: if (assignedValue != null && qualifier != null) {
0899: MarkerReplacer visitor = new MarkerReplacer();
0900: assignedValue.accept(visitor);
0901: return visitor.fReplaced;
0902: }
0903: return false;
0904: }
0905:
0906: private Expression handleSimpleNameAssignment(ASTNode replaceNode,
0907: ParameterObjectFactory pof, String parameterName, AST ast,
0908: IJavaProject javaProject, boolean useSuper) {
0909: if (replaceNode instanceof Assignment) {
0910: Assignment assignment = (Assignment) replaceNode;
0911: Expression rightHandSide = assignment.getRightHandSide();
0912: if (rightHandSide.getNodeType() == ASTNode.SIMPLE_NAME) {
0913: SimpleName sn = (SimpleName) rightHandSide;
0914: IVariableBinding binding = ASTNodes
0915: .getVariableBinding(sn);
0916: if (binding != null && binding.isField()) {
0917: if (fDescriptor.getType().getFullyQualifiedName()
0918: .equals(
0919: binding.getDeclaringClass()
0920: .getQualifiedName())) {
0921: FieldInfo fieldInfo = getFieldInfo(binding
0922: .getName());
0923: if (fieldInfo != null
0924: && binding == fieldInfo.pi
0925: .getOldBinding()) {
0926: return pof.createFieldReadAccess(
0927: fieldInfo.pi, parameterName, ast,
0928: javaProject, useSuper, null);
0929: }
0930: }
0931: }
0932: }
0933: }
0934: return null;
0935: }
0936:
0937: private FieldInfo getFieldInfo(String identifier) {
0938: return (FieldInfo) fVariables.get(identifier);
0939: }
0940:
0941: private FieldDeclaration performFieldRewrite(IType type,
0942: ParameterObjectFactory pof, RefactoringStatus status)
0943: throws CoreException {
0944: fBaseCURewrite = new CompilationUnitRewrite(type
0945: .getCompilationUnit());
0946: SimpleName name = (SimpleName) NodeFinder.perform(
0947: fBaseCURewrite.getRoot(), type.getNameRange());
0948: TypeDeclaration typeNode = (TypeDeclaration) ASTNodes
0949: .getParent(name, ASTNode.TYPE_DECLARATION);
0950: ASTRewrite rewrite = fBaseCURewrite.getASTRewrite();
0951: int modifier = Modifier.PRIVATE;
0952: TextEditGroup removeFieldGroup = fBaseCURewrite
0953: .createGroupDescription(RefactoringCoreMessages.ExtractClassRefactoring_group_remove_field);
0954: FieldDeclaration lastField = null;
0955: initializeDeclaration(typeNode);
0956: for (Iterator iter = fVariables.values().iterator(); iter
0957: .hasNext();) {
0958: FieldInfo pi = (FieldInfo) iter.next();
0959: if (isCreateField(pi)) {
0960: VariableDeclarationFragment vdf = pi.declaration;
0961: FieldDeclaration parent = (FieldDeclaration) vdf
0962: .getParent();
0963: if (lastField == null)
0964: lastField = parent;
0965: else if (lastField.getStartPosition() < parent
0966: .getStartPosition())
0967: lastField = parent;
0968:
0969: ListRewrite listRewrite = rewrite.getListRewrite(
0970: parent, FieldDeclaration.FRAGMENTS_PROPERTY);
0971: removeNode(vdf, removeFieldGroup, fBaseCURewrite);
0972: if (listRewrite.getRewrittenList().size() == 0) {
0973: removeNode(parent, removeFieldGroup, fBaseCURewrite);
0974: }
0975:
0976: if (fDescriptor.isCreateTopLevel()) {
0977: IVariableBinding binding = vdf.resolveBinding();
0978: ITypeRoot typeRoot = fBaseCURewrite.getCu();
0979: if (binding == null || binding.getType() == null) {
0980: status
0981: .addFatalError(
0982: Messages
0983: .format(
0984: RefactoringCoreMessages.ExtractClassRefactoring_fatal_error_cannot_resolve_binding,
0985: pi.name),
0986: JavaStatusContext.create(
0987: typeRoot, vdf));
0988: } else {
0989: ITypeBinding typeBinding = binding.getType();
0990: if (Modifier.isPrivate(typeBinding
0991: .getDeclaredModifiers())) {
0992: status
0993: .addError(
0994: Messages
0995: .format(
0996: RefactoringCoreMessages.ExtractClassRefactoring_error_referencing_private_class,
0997: typeBinding
0998: .getName()),
0999: JavaStatusContext.create(
1000: typeRoot, vdf));
1001: } else if (Modifier.isProtected(typeBinding
1002: .getDeclaredModifiers())) {
1003: ITypeBinding declaringClass = typeBinding
1004: .getDeclaringClass();
1005: if (declaringClass != null) {
1006: IPackageBinding package1 = declaringClass
1007: .getPackage();
1008: if (package1 != null
1009: && !fDescriptor
1010: .getPackage()
1011: .equals(
1012: package1
1013: .getName())) {
1014: status
1015: .addError(
1016: Messages
1017: .format(
1018: RefactoringCoreMessages.ExtractClassRefactoring_error_referencing_protected_class,
1019: new String[] {
1020: typeBinding
1021: .getName(),
1022: fDescriptor
1023: .getPackage() }),
1024: JavaStatusContext
1025: .create(
1026: typeRoot,
1027: vdf));
1028: }
1029: }
1030: }
1031: }
1032: }
1033: Expression initializer = vdf.getInitializer();
1034: if (initializer != null)
1035: pi.initializer = initializer;
1036: int modifiers = parent.getModifiers();
1037: if (!MemberVisibilityAdjustor.hasLowerVisibility(
1038: modifiers, modifier)) {
1039: modifier = modifiers;
1040: }
1041: }
1042: }
1043: FieldDeclaration fieldDeclaration = createParameterObjectField(
1044: pof, typeNode, modifier);
1045: ListRewrite bodyDeclList = rewrite.getListRewrite(typeNode,
1046: TypeDeclaration.BODY_DECLARATIONS_PROPERTY);
1047: if (lastField != null)
1048: bodyDeclList.insertAfter(fieldDeclaration, lastField, null);
1049: else
1050: bodyDeclList.insertFirst(fieldDeclaration, null);
1051: return fieldDeclaration;
1052: }
1053:
1054: private void initializeDeclaration(TypeDeclaration node) {
1055: FieldDeclaration[] fields = node.getFields();
1056: for (int i = 0; i < fields.length; i++) {
1057: FieldDeclaration fieldDeclaration = fields[i];
1058: List fragments = fieldDeclaration.fragments();
1059: for (Iterator iterator = fragments.iterator(); iterator
1060: .hasNext();) {
1061: VariableDeclarationFragment vdf = (VariableDeclarationFragment) iterator
1062: .next();
1063: FieldInfo fieldInfo = getFieldInfo(vdf.getName()
1064: .getIdentifier());
1065: if (fieldInfo != null) {
1066: Assert.isNotNull(vdf);
1067: fieldInfo.declaration = vdf;
1068: fieldInfo.pi.setOldBinding(vdf.resolveBinding());
1069: }
1070: }
1071: }
1072: }
1073:
1074: private void removeNode(ASTNode parent,
1075: TextEditGroup removeFieldGroup,
1076: CompilationUnitRewrite baseCURewrite) {
1077: baseCURewrite.getASTRewrite().remove(parent, removeFieldGroup);
1078: baseCURewrite.getImportRemover().registerRemovedNode(parent);
1079: }
1080:
1081: private FieldDeclaration createParameterObjectField(
1082: ParameterObjectFactory pof, TypeDeclaration typeNode,
1083: int modifier) {
1084: AST ast = fBaseCURewrite.getAST();
1085: ClassInstanceCreation creation = ast.newClassInstanceCreation();
1086: creation.setType(pof.createType(fDescriptor.isCreateTopLevel(),
1087: fBaseCURewrite, typeNode.getStartPosition()));
1088: ListRewrite listRewrite = fBaseCURewrite.getASTRewrite()
1089: .getListRewrite(creation,
1090: ClassInstanceCreation.ARGUMENTS_PROPERTY);
1091: for (Iterator iter = fVariables.values().iterator(); iter
1092: .hasNext();) {
1093: FieldInfo fi = (FieldInfo) iter.next();
1094: if (isCreateField(fi)) {
1095: Expression expression = fi.initializer;
1096: if (expression != null && !fi.hasFieldReference()) {
1097: importNodeTypes(expression, fBaseCURewrite);
1098: ASTNode createMoveTarget = fBaseCURewrite
1099: .getASTRewrite().createMoveTarget(
1100: expression);
1101: if (expression instanceof ArrayInitializer) {
1102: ArrayInitializer ai = (ArrayInitializer) expression;
1103: ITypeBinding componentType = ai
1104: .resolveTypeBinding()
1105: .getComponentType();
1106: ArrayCreation arrayCreation = ast
1107: .newArrayCreation();
1108: Type addImport = fBaseCURewrite
1109: .getImportRewrite().addImport(
1110: componentType, ast);
1111: fBaseCURewrite.getImportRemover()
1112: .registerAddedImports(addImport);
1113: arrayCreation.setType(ast
1114: .newArrayType(addImport));
1115: arrayCreation
1116: .setInitializer((ArrayInitializer) createMoveTarget);
1117: listRewrite.insertLast(arrayCreation, null);
1118: } else {
1119: listRewrite.insertLast(createMoveTarget, null);
1120: }
1121: }
1122: }
1123: }
1124:
1125: VariableDeclarationFragment fragment = ast
1126: .newVariableDeclarationFragment();
1127: fragment.setName(ast.newSimpleName(fDescriptor.getFieldName()));
1128: fragment.setInitializer(creation);
1129:
1130: ModifierKeyword acc = null;
1131: if (Modifier.isPublic(modifier)) {
1132: acc = ModifierKeyword.PUBLIC_KEYWORD;
1133: } else if (Modifier.isProtected(modifier)) {
1134: acc = ModifierKeyword.PROTECTED_KEYWORD;
1135: } else if (Modifier.isPrivate(modifier)) {
1136: acc = ModifierKeyword.PRIVATE_KEYWORD;
1137: }
1138:
1139: FieldDeclaration fieldDeclaration = ast
1140: .newFieldDeclaration(fragment);
1141: fieldDeclaration.setType(pof.createType(fDescriptor
1142: .isCreateTopLevel(), fBaseCURewrite, typeNode
1143: .getStartPosition()));
1144: if (acc != null)
1145: fieldDeclaration.modifiers().add(ast.newModifier(acc));
1146: return fieldDeclaration;
1147: }
1148:
1149: private void importNodeTypes(ASTNode node,
1150: final CompilationUnitRewrite cuRewrite) {
1151: ASTResolving.visitAllBindings(node, new TypeBindingVisitor() {
1152: public boolean visit(ITypeBinding nodeBinding) {
1153: ParameterObjectFactory.importBinding(nodeBinding,
1154: cuRewrite);
1155: return false;
1156: }
1157: });
1158: }
1159:
1160: private boolean isCreateField(FieldInfo fi) {
1161: return getField(fi.name).isCreateField();
1162: }
1163:
1164: public String getName() {
1165: return RefactoringCoreMessages.ExtractClassRefactoring_refactoring_name;
1166: }
1167:
1168: /* (non-Javadoc)
1169: * @see org.eclipse.ltk.core.refactoring.Refactoring#getAdapter(java.lang.Class)
1170: */
1171: public Object getAdapter(Class adapter) {
1172: if (adapter == ExtractClassDescriptorVerification.class) {
1173: return fVerification;
1174: }
1175: return super.getAdapter(adapter);
1176: }
1177: }
|