]> git.uio.no Git - ifi-stolz-refaktor.git/blame - case-study/jdt-before/core refactoring/org/eclipse/jdt/internal/corext/refactoring/structure/ExtractClassRefactoring.java
Case Study: adding data and statistics
[ifi-stolz-refaktor.git] / case-study / jdt-before / core refactoring / org / eclipse / jdt / internal / corext / refactoring / structure / ExtractClassRefactoring.java
CommitLineData
1b2798f6
EK
1/*******************************************************************************
2 * Copyright (c) 2007, 2011 IBM Corporation and others.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the Eclipse Public License v1.0
5 * which accompanies this distribution, and is available at
6 * http://www.eclipse.org/legal/epl-v10.html
7 *
8 * Contributors:
9 * IBM Corporation - initial API and implementation
10 *******************************************************************************/
11package org.eclipse.jdt.internal.corext.refactoring.structure;
12
13import java.util.ArrayList;
14import java.util.Arrays;
15import java.util.HashSet;
16import java.util.Iterator;
17import java.util.LinkedHashMap;
18import java.util.List;
19import java.util.Map;
20import java.util.Set;
21
22import org.eclipse.core.runtime.Assert;
23import org.eclipse.core.runtime.CoreException;
24import org.eclipse.core.runtime.IProgressMonitor;
25import org.eclipse.core.runtime.OperationCanceledException;
26import org.eclipse.core.runtime.SubProgressMonitor;
27
28import org.eclipse.text.edits.TextEditGroup;
29
30import org.eclipse.ltk.core.refactoring.Change;
31import org.eclipse.ltk.core.refactoring.Refactoring;
32import org.eclipse.ltk.core.refactoring.RefactoringDescriptor;
33import org.eclipse.ltk.core.refactoring.RefactoringStatus;
34import org.eclipse.ltk.core.refactoring.resource.ResourceChange;
35
36import org.eclipse.jdt.core.Flags;
37import org.eclipse.jdt.core.ICompilationUnit;
38import org.eclipse.jdt.core.IField;
39import org.eclipse.jdt.core.IJavaElement;
40import org.eclipse.jdt.core.IJavaProject;
41import org.eclipse.jdt.core.IPackageFragment;
42import org.eclipse.jdt.core.IPackageFragmentRoot;
43import org.eclipse.jdt.core.IType;
44import org.eclipse.jdt.core.ITypeRoot;
45import org.eclipse.jdt.core.JavaModelException;
46import org.eclipse.jdt.core.NamingConventions;
47import org.eclipse.jdt.core.Signature;
48import org.eclipse.jdt.core.dom.AST;
49import org.eclipse.jdt.core.dom.ASTNode;
50import org.eclipse.jdt.core.dom.ASTVisitor;
51import org.eclipse.jdt.core.dom.ArrayCreation;
52import org.eclipse.jdt.core.dom.ArrayInitializer;
53import org.eclipse.jdt.core.dom.Assignment;
54import org.eclipse.jdt.core.dom.ClassInstanceCreation;
55import org.eclipse.jdt.core.dom.CompilationUnit;
56import org.eclipse.jdt.core.dom.Expression;
57import org.eclipse.jdt.core.dom.FieldAccess;
58import org.eclipse.jdt.core.dom.FieldDeclaration;
59import org.eclipse.jdt.core.dom.IExtendedModifier;
60import org.eclipse.jdt.core.dom.IPackageBinding;
61import org.eclipse.jdt.core.dom.ITypeBinding;
62import org.eclipse.jdt.core.dom.IVariableBinding;
63import org.eclipse.jdt.core.dom.Javadoc;
64import org.eclipse.jdt.core.dom.Modifier;
65import org.eclipse.jdt.core.dom.Modifier.ModifierKeyword;
66import org.eclipse.jdt.core.dom.NodeFinder;
67import org.eclipse.jdt.core.dom.NullLiteral;
68import org.eclipse.jdt.core.dom.QualifiedName;
69import org.eclipse.jdt.core.dom.SimpleName;
70import org.eclipse.jdt.core.dom.SuperFieldAccess;
71import org.eclipse.jdt.core.dom.Type;
72import org.eclipse.jdt.core.dom.TypeDeclaration;
73import org.eclipse.jdt.core.dom.VariableDeclaration;
74import org.eclipse.jdt.core.dom.VariableDeclarationFragment;
75import org.eclipse.jdt.core.dom.rewrite.ASTRewrite;
76import org.eclipse.jdt.core.dom.rewrite.ListRewrite;
77import org.eclipse.jdt.core.refactoring.descriptors.ExtractClassDescriptor;
78import org.eclipse.jdt.core.refactoring.descriptors.ExtractClassDescriptor.Field;
79import org.eclipse.jdt.core.refactoring.descriptors.JavaRefactoringDescriptor;
80import org.eclipse.jdt.core.search.IJavaSearchConstants;
81import org.eclipse.jdt.core.search.SearchMatch;
82import org.eclipse.jdt.core.search.SearchPattern;
83
84import org.eclipse.jdt.internal.corext.codemanipulation.GetterSetterUtil;
85import org.eclipse.jdt.internal.corext.codemanipulation.StubUtility;
86import org.eclipse.jdt.internal.corext.dom.ASTNodes;
87import org.eclipse.jdt.internal.corext.dom.Bindings;
88import org.eclipse.jdt.internal.corext.dom.TypeBindingVisitor;
89import org.eclipse.jdt.internal.corext.refactoring.Checks;
90import org.eclipse.jdt.internal.corext.refactoring.JDTRefactoringDescriptorComment;
91import org.eclipse.jdt.internal.corext.refactoring.ParameterInfo;
92import org.eclipse.jdt.internal.corext.refactoring.RefactoringCoreMessages;
93import org.eclipse.jdt.internal.corext.refactoring.RefactoringScopeFactory;
94import org.eclipse.jdt.internal.corext.refactoring.RefactoringSearchEngine;
95import org.eclipse.jdt.internal.corext.refactoring.SearchResultGroup;
96import org.eclipse.jdt.internal.corext.refactoring.base.JavaStatusContext;
97import org.eclipse.jdt.internal.corext.refactoring.changes.DynamicValidationRefactoringChange;
98import org.eclipse.jdt.internal.corext.refactoring.structure.ParameterObjectFactory.CreationListener;
99import org.eclipse.jdt.internal.corext.refactoring.util.TextChangeManager;
100import org.eclipse.jdt.internal.corext.util.JavaModelUtil;
101import org.eclipse.jdt.internal.corext.util.Messages;
102
103import org.eclipse.jdt.internal.ui.JavaPlugin;
104import org.eclipse.jdt.internal.ui.text.correction.ASTResolving;
105import org.eclipse.jdt.internal.ui.viewsupport.BasicElementLabels;
106
107public class ExtractClassRefactoring extends Refactoring {
108
109 public static class ExtractClassDescriptorVerification {
110 private ExtractClassDescriptor fDescriptor;
111
112 public ExtractClassDescriptorVerification(ExtractClassDescriptor descriptor) {
113 fDescriptor= descriptor;
114 }
115
116 public RefactoringStatus validateClassName() {
117 RefactoringStatus status= new RefactoringStatus();
118 status.merge(Checks.checkTypeName(fDescriptor.getClassName(), fDescriptor.getType()));
119 status.merge(checkClass());
120 return status;
121 }
122
123 private RefactoringStatus checkClass() {
124 RefactoringStatus status= new RefactoringStatus();
125 IType type= fDescriptor.getType();
126 if (!fDescriptor.isCreateTopLevel()) {
127 if (type.getType(fDescriptor.getClassName()).exists()) {
128 status.addError(Messages.format(RefactoringCoreMessages.ExtractClassRefactoring_errror_nested_name_clash, new Object[] { BasicElementLabels.getJavaElementName(fDescriptor.getClassName()), BasicElementLabels.getJavaElementName(type.getElementName()) }));
129 }
130 } else {
131 status.merge(checkPackageClass());
132 }
133 return status;
134 }
135
136 private RefactoringStatus checkPackageClass() {
137 RefactoringStatus status= new RefactoringStatus();
138 IType type= fDescriptor.getType();
139 IPackageFragmentRoot ancestor= (IPackageFragmentRoot) type.getAncestor(IJavaElement.PACKAGE_FRAGMENT_ROOT);
140 IPackageFragment packageFragment= ancestor.getPackageFragment(fDescriptor.getPackage());
141 if (packageFragment.getCompilationUnit(fDescriptor.getClassName() + JavaModelUtil.DEFAULT_CU_SUFFIX).exists())
142 status.addError(Messages.format(RefactoringCoreMessages.ExtractClassRefactoring_error_toplevel_name_clash, new Object[] { BasicElementLabels.getJavaElementName(fDescriptor.getClassName()), BasicElementLabels.getJavaElementName(fDescriptor.getPackage()) }));
143 return status;
144 }
145
146 public RefactoringStatus validateTopLevel() {
147 return checkClass();
148 }
149
150 public RefactoringStatus validateParameterName() {
151 RefactoringStatus status= new RefactoringStatus();
152 String parameterName= fDescriptor.getFieldName();
153 IType type= fDescriptor.getType();
154 status.merge(Checks.checkFieldName(parameterName, type));
155 validateFieldNames(status, parameterName, type);
156 return status;
157 }
158
159 private void validateFieldNames(RefactoringStatus status, String parameterName, IType type) {
160 if (type.getField(parameterName).exists()) {
161 Field[] fields= fDescriptor.getFields();
162 for (int i= 0; i < fields.length; i++) {
163 Field field= fields[i];
164 if (parameterName.equals(field.getFieldName())){
165 if (!field.isCreateField())
166 status.addError(Messages.format(RefactoringCoreMessages.ExtractClassRefactoring_error_field_already_exists, BasicElementLabels.getJavaElementName(parameterName)));
167 }
168 }
169 }
170 }
171
172 public RefactoringStatus validateFields() {
173 RefactoringStatus status= new RefactoringStatus();
174 Field[] fields= fDescriptor.getFields();
175 Set<String> names= new HashSet<String>();
176 for (int i= 0; i < fields.length; i++) {
177 Field field= fields[i];
178 if (field.isCreateField()) {
179 if (names.contains(field.getNewFieldName())) {
180 status.addError(Messages.format(RefactoringCoreMessages.ExtractClassRefactoring_error_duplicate_field_name, BasicElementLabels.getJavaElementName(field.getNewFieldName())));
181 }
182 names.add(field.getNewFieldName());
183 status.merge(Checks.checkFieldName(field.getNewFieldName(), fDescriptor.getType()));
184 }
185 }
186 if (names.size() == 0) {
187 status.addError(RefactoringCoreMessages.ExtractClassRefactoring_error_msg_one_field);
188 }
189 validateFieldNames(status, fDescriptor.getFieldName(), fDescriptor.getType());
190 return status;
191 }
192
193 public RefactoringStatus validateAll() {
194 RefactoringStatus status= new RefactoringStatus();
195 status.merge(validateClassName()); //also validates toplevel
196 status.merge(validateFields());
197 status.merge(validateParameterName());
198 return status;
199 }
200 }
201
202
203 private final class FieldReferenceFinder extends ASTVisitor {
204 public boolean fFieldRefFound= false;
205
206 private FieldReferenceFinder() {
207 }
208
209 @Override
210 public boolean visit(FieldAccess node) {
211 IVariableBinding fieldBinding= node.resolveFieldBinding();
212 return checkVariableBinding(fieldBinding);
213 }
214
215 @Override
216 public boolean visit(SimpleName node) {
217 IVariableBinding variableBinding= ASTNodes.getVariableBinding(node);
218 return checkVariableBinding(variableBinding);
219 }
220
221 private boolean checkVariableBinding(IVariableBinding fieldBinding) {
222 if (fieldBinding != null) {
223 if (fieldBinding.isField()) {
224 ITypeBinding declaringClass= fieldBinding.getDeclaringClass();
225 if ((declaringClass != null) && declaringClass.getQualifiedName().equals(fDescriptor.getType().getFullyQualifiedName())) {
226 FieldInfo fi= fVariables.get(fieldBinding.getName());
227 if (fi != null && isCreateField(fi) && Bindings.equals(fieldBinding, fi.pi.getOldBinding())) {
228 fFieldRefFound= true;
229 return false;
230 }
231 }
232 }
233 }
234 return true;
235 }
236 }
237
238 private final class FieldInfo {
239 ParameterInfo pi;
240 VariableDeclarationFragment declaration;
241 IField ifield;
242 String name;
243 Expression initializer;
244 private Boolean hasFieldReferences= null;
245
246 public boolean hasFieldReference() {
247 if (hasFieldReferences == null) {
248 if (initializer != null) {
249 FieldReferenceFinder frf= new FieldReferenceFinder();
250 initializer.accept(frf);
251 hasFieldReferences= Boolean.valueOf(frf.fFieldRefFound);
252 } else {
253 hasFieldReferences= Boolean.FALSE;
254 }
255 }
256 return hasFieldReferences.booleanValue();
257 }
258
259 private FieldInfo(ParameterInfo parameterInfo, IField ifield) {
260 super();
261 this.pi= parameterInfo;
262 this.ifield= ifield;
263 this.name= ifield.getElementName();
264 }
265 }
266
267 private ExtractClassDescriptor fDescriptor;
268 private Map<String, FieldInfo> fVariables;
269 private CompilationUnitRewrite fBaseCURewrite;
270 private TextChangeManager fChangeManager;
271 private ParameterObjectFactory fParameterObjectFactory;
272 private ExtractClassDescriptorVerification fVerification;
273
274 public ExtractClassRefactoring(ExtractClassDescriptor descriptor) {
275 fDescriptor= descriptor;
276 IType type= fDescriptor.getType();
277 if (fDescriptor.getPackage() == null) {
278 fDescriptor.setPackage(type.getPackageFragment().getElementName());
279 }
280 if (fDescriptor.getClassName() == null) {
281 fDescriptor.setClassName(type.getElementName() + "Data"); //$NON-NLS-1$
282 }
283 if (fDescriptor.getFieldName() == null) {
284 fDescriptor.setFieldName(StubUtility.getVariableNameSuggestions(NamingConventions.VK_INSTANCE_FIELD, type.getJavaProject(), "data", 0, null, true)[0]); //$NON-NLS-1$
285 }
286 if (fDescriptor.getFields() == null) {
287 try {
288 fDescriptor.setFields(ExtractClassDescriptor.getFields(type));
289 } catch (JavaModelException e) {
290 JavaPlugin.log(e);
291 }
292 }
293 fVerification= new ExtractClassDescriptorVerification(descriptor);
294 }
295
296
297 @Override
298 public RefactoringStatus checkInitialConditions(IProgressMonitor pm) throws CoreException, OperationCanceledException {
299 RefactoringStatus result= new RefactoringStatus();
300 pm.beginTask(RefactoringCoreMessages.ExtractClassRefactoring_progress_msg_check_initial_condition, 5);
301 try {
302 result.merge(fDescriptor.validateDescriptor());
303 if (!result.isOK())
304 return result;
305 IType type= fDescriptor.getType();
306 result.merge(Checks.checkAvailability(type));
307 if (!result.isOK())
308 return result;
309 pm.worked(1);
310 Field[] fields= ExtractClassDescriptor.getFields(fDescriptor.getType());
311 pm.worked(1);
312 if (pm.isCanceled())
313 throw new OperationCanceledException();
314 fVariables= new LinkedHashMap<String, FieldInfo>();
315 if (fields.length == 0) {
316 result.addFatalError(RefactoringCoreMessages.ExtractClassRefactoring_error_no_usable_fields, JavaStatusContext.create(type));
317 return result;
318 }
319 for (int i= 0; i < fields.length; i++) {
320 Field field= fields[i];
321 String fieldName= field.getFieldName();
322 IField declField= type.getField(fieldName);
323 ParameterInfo info= new ParameterInfo(Signature.toString(declField.getTypeSignature()), fieldName, i);
324 fVariables.put(fieldName, new FieldInfo(info, declField));
325 if (pm.isCanceled())
326 throw new OperationCanceledException();
327 }
328 pm.worked(3);
329 } finally {
330 pm.done();
331 }
332 return result;
333 }
334
335 @Override
336 public RefactoringStatus checkFinalConditions(IProgressMonitor pm) throws CoreException, OperationCanceledException {
337 RefactoringStatus result= new RefactoringStatus();
338 result.merge(fVerification.validateAll());
339 try {
340 pm.beginTask(RefactoringCoreMessages.ExtractClassRefactoring_progress_final_conditions, 95);
341 for (Iterator<FieldInfo> iter= fVariables.values().iterator(); iter.hasNext();) {
342 FieldInfo fi= iter.next();
343 boolean createField= isCreateField(fi);
344 if (createField) {
345 IField field= fi.ifield;
346 int flags= field.getFlags();
347 if (Flags.isStatic(flags)) {
348 result.addFatalError(Messages.format(RefactoringCoreMessages.ExtractClassRefactoring_error_field_is_static, BasicElementLabels.getJavaElementName(field.getElementName())), JavaStatusContext.create(field));
349 return result;
350 }
351 if (Flags.isTransient(flags)) {
352 result.addWarning(Messages.format(RefactoringCoreMessages.ExtractClassRefactoring_warning_field_is_transient, BasicElementLabels.getJavaElementName(field.getElementName())), JavaStatusContext.create(field));
353 }
354 if (Flags.isVolatile(flags)) {
355 result.addWarning(Messages.format(RefactoringCoreMessages.ExtractClassRefactoring_warning_field_is_volatile, BasicElementLabels.getJavaElementName(field.getElementName())), JavaStatusContext.create(field));
356 }
357 }
358 }
359 pm.worked(5);
360 fChangeManager= new TextChangeManager();
361 fParameterObjectFactory= initializeFactory();
362 IType type= fDescriptor.getType();
363 pm.worked(5);
364
365 FieldDeclaration field= performFieldRewrite(type, fParameterObjectFactory, result);
366 int flags= RefactoringDescriptor.STRUCTURAL_CHANGE | JavaRefactoringDescriptor.JAR_MIGRATION | JavaRefactoringDescriptor.JAR_REFACTORING | JavaRefactoringDescriptor.JAR_SOURCE_ATTACHMENT;
367 if (!Modifier.isPrivate(field.getModifiers()))
368 flags|= RefactoringDescriptor.MULTI_CHANGE;
369 fDescriptor.setFlags(flags);
370
371 result.merge(updateReferences(type, fParameterObjectFactory, new SubProgressMonitor(pm, 65)));
372
373 } finally {
374 pm.done();
375 }
376 return result;
377 }
378
379 @Override
380 public Change createChange(IProgressMonitor pm) throws CoreException, OperationCanceledException {
381 pm.beginTask(RefactoringCoreMessages.ExtractClassRefactoring_progress_create_change, 10);
382 try {
383 ICompilationUnit typeCU= fDescriptor.getType().getCompilationUnit();
384 IPackageFragmentRoot packageRoot= (IPackageFragmentRoot) typeCU.getAncestor(IJavaElement.PACKAGE_FRAGMENT_ROOT);
385 ArrayList<Change> changes= new ArrayList<Change>();
386
387 changes.addAll(createParameterObject(fParameterObjectFactory, packageRoot));
388 fChangeManager.manage(typeCU, fBaseCURewrite.createChange(true, pm));
389 changes.addAll(Arrays.asList(fChangeManager.getAllChanges()));
390 String project= fDescriptor.getType().getJavaProject().getElementName();
391 fDescriptor.setProject(project);
392 fDescriptor.setDescription(getName());
393 fDescriptor.setComment(createComment());
394 DynamicValidationRefactoringChange change= new DynamicValidationRefactoringChange(fDescriptor, RefactoringCoreMessages.ExtractClassRefactoring_change_name, changes
395 .toArray(new Change[changes.size()]));
396 return change;
397 } finally {
398 pm.done();
399 }
400 }
401
402 private String createComment() {
403 Object[] keys= new Object[] { BasicElementLabels.getJavaElementName(fDescriptor.getClassName()), BasicElementLabels.getJavaElementName(fDescriptor.getType().getElementName()) };
404 String header= Messages.format(RefactoringCoreMessages.ExtractClassRefactoring_change_comment_header, keys);
405 JDTRefactoringDescriptorComment comment= new JDTRefactoringDescriptorComment(fDescriptor.getType().getJavaProject().getElementName(), this, header);
406 comment.addSetting(Messages.format(RefactoringCoreMessages.ExtractClassRefactoring_comment_extracted_class, BasicElementLabels.getJavaElementName(fDescriptor.getClassName())));
407
408 if (fDescriptor.isCreateTopLevel())
409 comment.addSetting(Messages.format(RefactoringCoreMessages.ExtractClassRefactoring_comment_package, BasicElementLabels.getJavaElementName(fDescriptor.getPackage())));
410
411 Field[] fields= fDescriptor.getFields();
412 ArrayList<String> strings= new ArrayList<String>();
413 for (int i= 0; i < fields.length; i++) {
414 Field field= fields[i];
415 if (field.isCreateField()) {
416 strings.add(Messages.format(RefactoringCoreMessages.ExtractClassRefactoring_comment_field_renamed, new Object[] { BasicElementLabels.getJavaElementName(field.getFieldName()), BasicElementLabels.getJavaElementName(field.getNewFieldName()) }));
417 }
418 }
419 String fieldString= JDTRefactoringDescriptorComment.createCompositeSetting(RefactoringCoreMessages.ExtractClassRefactoring_comment_move_field, strings.toArray(new String[strings
420 .size()]));
421 comment.addSetting(fieldString);
422
423 if (fDescriptor.isCreateGetterSetter())
424 comment.addSetting(RefactoringCoreMessages.ExtractClassRefactoring_comment_getters);
425
426 comment.addSetting(Messages.format(RefactoringCoreMessages.ExtractClassRefactoring_comment_fieldname, BasicElementLabels.getJavaElementName(fDescriptor.getFieldName())));
427 return comment.asString();
428 }
429
430 private class FieldUpdate extends CreationListener {
431 @Override
432 public void fieldCreated(CompilationUnitRewrite cuRewrite, FieldDeclaration field, ParameterInfo pi) {
433 FieldInfo fieldInfo= getFieldInfo(pi.getOldName());
434 FieldDeclaration parent= (FieldDeclaration) fieldInfo.declaration.getParent();
435 List<IExtendedModifier> modifiers= parent.modifiers();
436 ListRewrite listRewrite= cuRewrite.getASTRewrite().getListRewrite(field, FieldDeclaration.MODIFIERS2_PROPERTY);
437 for (Iterator<IExtendedModifier> iterator= modifiers.iterator(); iterator.hasNext();) {
438 IExtendedModifier mod= iterator.next();
439 //Temporarily disabled until initialization of final fields is handled correctly
440// if (mod.isModifier()) {
441// Modifier modifier= (Modifier) mod;
442// if (modifier.isFinal())
443// listRewrite.insertLast(moveNode(cuRewrite, modifier), null);
444// }
445 if (mod.isAnnotation()) {
446 listRewrite.insertFirst(moveNode(cuRewrite, (ASTNode) mod), null);
447 }
448 }
449 if (fieldInfo.initializer != null && fieldInfo.hasFieldReference()) {
450 List<VariableDeclarationFragment> fragments= field.fragments();
451 for (Iterator<VariableDeclarationFragment> iterator= fragments.iterator(); iterator.hasNext();) {
452 VariableDeclarationFragment vdf= iterator.next();
453 vdf.setInitializer((Expression) moveNode(cuRewrite, fieldInfo.initializer));
454 }
455 }
456 if (parent.getJavadoc() != null) {
457 field.setJavadoc((Javadoc) moveNode(cuRewrite, parent.getJavadoc()));
458 }
459 }
460
461 @Override
462 public boolean isCreateSetter(ParameterInfo pi) {
463 return true; //ignore that the original variable was final
464 }
465
466 @Override
467 public boolean isUseInConstructor(ParameterInfo pi) {
468 FieldInfo fi= getFieldInfo(pi.getOldName());
469 return fi.initializer != null && !fi.hasFieldReference();
470 }
471 }
472
473 private List<ResourceChange> createParameterObject(ParameterObjectFactory pof, IPackageFragmentRoot packageRoot) throws CoreException {
474 FieldUpdate fieldUpdate= new FieldUpdate();
475 if (fDescriptor.isCreateTopLevel())
476 return pof.createTopLevelParameterObject(packageRoot, fieldUpdate);
477 else {
478 CompilationUnit root= fBaseCURewrite.getRoot();
479 TypeDeclaration typeDecl= (TypeDeclaration) NodeFinder.perform(root, fDescriptor.getType().getSourceRange());
480 ASTRewrite rewrite= fBaseCURewrite.getASTRewrite();
481 ListRewrite listRewrite= rewrite.getListRewrite(typeDecl, TypeDeclaration.BODY_DECLARATIONS_PROPERTY);
482 TypeDeclaration paramClass= pof.createClassDeclaration(typeDecl.getName().getFullyQualifiedName(), fBaseCURewrite, fieldUpdate);
483 paramClass.modifiers().add(rewrite.getAST().newModifier(ModifierKeyword.PUBLIC_KEYWORD));
484 paramClass.modifiers().add(rewrite.getAST().newModifier(ModifierKeyword.STATIC_KEYWORD));
485 listRewrite.insertFirst(paramClass, fBaseCURewrite.createGroupDescription(RefactoringCoreMessages.ExtractClassRefactoring_group_insert_parameter));
486 return new ArrayList<ResourceChange>(); //Change will be generated later for fBaseCURewrite
487 }
488
489 }
490
491 private ParameterObjectFactory initializeFactory() {
492 ParameterObjectFactory pof= new ParameterObjectFactory();
493 pof.setClassName(fDescriptor.getClassName());
494 pof.setPackage(fDescriptor.getPackage());
495 pof.setEnclosingType(fDescriptor.getType().getFullyQualifiedName());
496 pof.setCreateGetter(fDescriptor.isCreateGetterSetter());
497 pof.setCreateSetter(fDescriptor.isCreateGetterSetter());
498 List<ParameterInfo> variables= new ArrayList<ParameterInfo>();
499 for (Iterator<FieldInfo> iterator= fVariables.values().iterator(); iterator.hasNext();) {
500 FieldInfo info= iterator.next();
501 boolean createField= isCreateField(info);
502 info.pi.setCreateField(createField);
503 if (createField) {
504 Field field= getField(info.name);
505 info.pi.setNewName(field.getNewFieldName());
506 }
507 variables.add(info.pi);
508 }
509 pof.setVariables(variables);
510 return pof;
511 }
512
513 private Field getField(String name) {
514 Field[] fields= fDescriptor.getFields();
515 for (int i= 0; i < fields.length; i++) {
516 Field field= fields[i];
517 if (field.getFieldName().equals(name))
518 return field;
519 }
520 return null;
521 }
522
523
524 private RefactoringStatus updateReferences(IType type, ParameterObjectFactory pof, IProgressMonitor pm) throws CoreException {
525 RefactoringStatus status= new RefactoringStatus();
526 pm.beginTask(RefactoringCoreMessages.ExtractClassRefactoring_progress_updating_references, 100);
527 try {
528 pm.worked(10);
529 if (pm.isCanceled())
530 throw new OperationCanceledException();
531 List<IField> validIFields= new ArrayList<IField>();
532 for (Iterator<FieldInfo> iterator= fVariables.values().iterator(); iterator.hasNext();) {
533 FieldInfo info= iterator.next();
534 if (isCreateField(info))
535 validIFields.add(info.ifield);
536 }
537 if (validIFields.size() == 0) {
538 status.addWarning(RefactoringCoreMessages.ExtractClassRefactoring_warning_no_fields_moved, JavaStatusContext.create(type));
539 return status;
540 }
541 SearchPattern pattern= RefactoringSearchEngine.createOrPattern(validIFields.toArray(new IField[validIFields.size()]), IJavaSearchConstants.ALL_OCCURRENCES);
542 SearchResultGroup[] results= RefactoringSearchEngine.search(pattern, RefactoringScopeFactory.create(type), pm, status);
543 SubProgressMonitor spm= new SubProgressMonitor(pm, 90);
544 spm.beginTask(RefactoringCoreMessages.ExtractClassRefactoring_progress_updating_references, results.length * 10);
545 try {
546 for (int i= 0; i < results.length; i++) {
547 SearchResultGroup group= results[i];
548 ICompilationUnit unit= group.getCompilationUnit();
549
550 CompilationUnitRewrite cuRewrite;
551 if (unit.equals(fBaseCURewrite.getCu()))
552 cuRewrite= fBaseCURewrite;
553 else
554 cuRewrite= new CompilationUnitRewrite(unit);
555 spm.worked(1);
556
557 status.merge(replaceReferences(pof, group, cuRewrite));
558 if (cuRewrite != fBaseCURewrite) //Change for fBaseCURewrite will be generated later
559 fChangeManager.manage(unit, cuRewrite.createChange(true, new SubProgressMonitor(spm, 9)));
560 if (spm.isCanceled())
561 throw new OperationCanceledException();
562 }
563 } finally {
564 spm.done();
565 }
566 } finally {
567 pm.done();
568 }
569 return status;
570 }
571
572 private RefactoringStatus replaceReferences(ParameterObjectFactory pof, SearchResultGroup group, CompilationUnitRewrite cuRewrite) {
573 TextEditGroup writeGroup= cuRewrite.createGroupDescription(RefactoringCoreMessages.ExtractClassRefactoring_group_replace_write);
574 TextEditGroup readGroup= cuRewrite.createGroupDescription(RefactoringCoreMessages.ExtractClassRefactoring_group_replace_read);
575 ITypeRoot typeRoot= cuRewrite.getCu();
576 IJavaProject javaProject= typeRoot.getJavaProject();
577 AST ast= cuRewrite.getAST();
578
579 RefactoringStatus status= new RefactoringStatus();
580 String parameterName= fDescriptor.getFieldName();
581
582 SearchMatch[] searchResults= group.getSearchResults();
583 for (int j= 0; j < searchResults.length; j++) {
584 SearchMatch searchMatch= searchResults[j];
585 ASTNode node= NodeFinder.perform(cuRewrite.getRoot(), searchMatch.getOffset(), searchMatch.getLength());
586 ASTNode parent= node.getParent();
587 boolean isDeclaration= parent instanceof VariableDeclaration && ((VariableDeclaration)parent).getInitializer() != node;
588 if (!isDeclaration && node instanceof SimpleName) {
589 ASTRewrite rewrite= cuRewrite.getASTRewrite();
590 if (parent.getNodeType() == ASTNode.SWITCH_CASE)
591 status.addError(RefactoringCoreMessages.ExtractClassRefactoring_error_switch, JavaStatusContext.create(typeRoot, node));
592
593 SimpleName name= (SimpleName) node;
594 ParameterInfo pi= getFieldInfo(name.getIdentifier()).pi;
595 boolean writeAccess= ASTResolving.isWriteAccess(name);
596 if (writeAccess && fDescriptor.isCreateGetterSetter()) {
597 boolean useSuper= parent.getNodeType() == ASTNode.SUPER_FIELD_ACCESS;
598 Expression qualifier= getQualifier(parent);
599 ASTNode replaceNode= getReplacementNode(parent, useSuper, qualifier);
600 Expression assignedValue= getAssignedValue(pof, parameterName, javaProject, status, rewrite, pi, useSuper, name.resolveTypeBinding(), qualifier, replaceNode, typeRoot);
601 if (assignedValue == null) {
602 status.addError(RefactoringCoreMessages.ExtractClassRefactoring_error_unable_to_convert_node, JavaStatusContext.create(typeRoot, replaceNode));
603 } else {
604 NullLiteral marker= qualifier == null ? null : ast.newNullLiteral();
605 Expression access= pof.createFieldWriteAccess(pi, parameterName, ast, javaProject, assignedValue, useSuper, marker);
606 replaceMarker(rewrite, qualifier, access, marker);
607 rewrite.replace(replaceNode, access, writeGroup);
608 }
609 } else {
610 Expression fieldReadAccess= pof.createFieldReadAccess(pi, parameterName, ast, javaProject, false, null); //qualifier is already there
611 rewrite.replace(name, fieldReadAccess, readGroup);
612 }
613 }
614 }
615 return status;
616 }
617
618 private Expression getAssignedValue(ParameterObjectFactory pof, String parameterName, IJavaProject javaProject, RefactoringStatus status, ASTRewrite rewrite, ParameterInfo pi, boolean useSuper, ITypeBinding typeBinding, Expression qualifier, ASTNode replaceNode, ITypeRoot typeRoot) {
619 AST ast= rewrite.getAST();
620 boolean is50OrHigher= JavaModelUtil.is50OrHigher(javaProject);
621 Expression assignedValue= handleSimpleNameAssignment(replaceNode, pof, parameterName, ast, javaProject, useSuper);
622 if (assignedValue == null) {
623 NullLiteral marker= qualifier == null ? null : ast.newNullLiteral();
624 Expression fieldReadAccess= pof.createFieldReadAccess(pi, parameterName, ast, javaProject, useSuper, marker);
625 assignedValue= GetterSetterUtil.getAssignedValue(replaceNode, rewrite, fieldReadAccess, typeBinding, is50OrHigher);
626 boolean markerReplaced= replaceMarker(rewrite, qualifier, assignedValue, marker);
627 if (markerReplaced) {
628 switch (qualifier.getNodeType()) {
629 case ASTNode.METHOD_INVOCATION:
630 case ASTNode.CLASS_INSTANCE_CREATION:
631 case ASTNode.SUPER_METHOD_INVOCATION:
632 case ASTNode.PARENTHESIZED_EXPRESSION:
633 status.addWarning(RefactoringCoreMessages.ExtractClassRefactoring_warning_semantic_change, JavaStatusContext.create(typeRoot, replaceNode));
634 break;
635 }
636 }
637 }
638 return assignedValue;
639 }
640
641 private ASTNode getReplacementNode(ASTNode parent, boolean useSuper, Expression qualifier) {
642 if (qualifier != null || useSuper) {
643 return parent.getParent();
644 } else {
645 return parent;
646 }
647 }
648
649 private Expression getQualifier(ASTNode parent) {
650 switch (parent.getNodeType()) {
651 case ASTNode.FIELD_ACCESS:
652 return ((FieldAccess) parent).getExpression();
653 case ASTNode.QUALIFIED_NAME:
654 return ((QualifiedName)parent).getQualifier();
655 case ASTNode.SUPER_FIELD_ACCESS:
656 return ((SuperFieldAccess)parent).getQualifier();
657 default:
658 return null;
659 }
660 }
661
662 /*
663 * Replaces the NullLiteral dummy with the copied qualifier
664 */
665 private boolean replaceMarker(final ASTRewrite rewrite, final Expression qualifier, Expression assignedValue, final NullLiteral marker) {
666 class MarkerReplacer extends ASTVisitor {
667
668 private boolean fReplaced= false;
669
670 @Override
671 public boolean visit(NullLiteral node) {
672 if (node == marker) {
673 rewrite.replace(node, rewrite.createCopyTarget(qualifier), null);
674 fReplaced= true;
675 return false;
676 }
677 return true;
678 }
679 }
680 if (assignedValue != null && qualifier != null) {
681 MarkerReplacer visitor= new MarkerReplacer();
682 assignedValue.accept(visitor);
683 return visitor.fReplaced;
684 }
685 return false;
686 }
687
688 private Expression handleSimpleNameAssignment(ASTNode replaceNode, ParameterObjectFactory pof, String parameterName, AST ast, IJavaProject javaProject, boolean useSuper) {
689 if (replaceNode instanceof Assignment) {
690 Assignment assignment= (Assignment) replaceNode;
691 Expression rightHandSide= assignment.getRightHandSide();
692 if (rightHandSide.getNodeType() == ASTNode.SIMPLE_NAME) {
693 SimpleName sn= (SimpleName) rightHandSide;
694 IVariableBinding binding= ASTNodes.getVariableBinding(sn);
695 if (binding != null && binding.isField()) {
696 if (fDescriptor.getType().getFullyQualifiedName().equals(binding.getDeclaringClass().getQualifiedName())) {
697 FieldInfo fieldInfo= getFieldInfo(binding.getName());
698 if (fieldInfo != null && binding == fieldInfo.pi.getOldBinding()) {
699 return pof.createFieldReadAccess(fieldInfo.pi, parameterName, ast, javaProject, useSuper, null);
700 }
701 }
702 }
703 }
704 }
705 return null;
706 }
707
708 private FieldInfo getFieldInfo(String identifier) {
709 return fVariables.get(identifier);
710 }
711
712 private FieldDeclaration performFieldRewrite(IType type, ParameterObjectFactory pof, RefactoringStatus status) throws CoreException {
713 fBaseCURewrite= new CompilationUnitRewrite(type.getCompilationUnit());
714 SimpleName name= (SimpleName) NodeFinder.perform(fBaseCURewrite.getRoot(), type.getNameRange());
715 TypeDeclaration typeNode= (TypeDeclaration) ASTNodes.getParent(name, ASTNode.TYPE_DECLARATION);
716 ASTRewrite rewrite= fBaseCURewrite.getASTRewrite();
717 int modifier= Modifier.PRIVATE;
718 TextEditGroup removeFieldGroup= fBaseCURewrite.createGroupDescription(RefactoringCoreMessages.ExtractClassRefactoring_group_remove_field);
719 FieldDeclaration lastField= null;
720 initializeDeclaration(typeNode);
721 for (Iterator<FieldInfo> iter= fVariables.values().iterator(); iter.hasNext();) {
722 FieldInfo pi= iter.next();
723 if (isCreateField(pi)) {
724 VariableDeclarationFragment vdf= pi.declaration;
725 FieldDeclaration parent= (FieldDeclaration) vdf.getParent();
726 if (lastField == null)
727 lastField= parent;
728 else if (lastField.getStartPosition() < parent.getStartPosition())
729 lastField= parent;
730
731 ListRewrite listRewrite= rewrite.getListRewrite(parent, FieldDeclaration.FRAGMENTS_PROPERTY);
732 removeNode(vdf, removeFieldGroup, fBaseCURewrite);
733 if (listRewrite.getRewrittenList().size() == 0) {
734 removeNode(parent, removeFieldGroup, fBaseCURewrite);
735 }
736
737 if (fDescriptor.isCreateTopLevel()) {
738 IVariableBinding binding= vdf.resolveBinding();
739 ITypeRoot typeRoot= fBaseCURewrite.getCu();
740 if (binding == null || binding.getType() == null){
741 status.addFatalError(Messages.format(RefactoringCoreMessages.ExtractClassRefactoring_fatal_error_cannot_resolve_binding, BasicElementLabels.getJavaElementName(pi.name)), JavaStatusContext.create(typeRoot, vdf));
742 } else {
743 ITypeBinding typeBinding= binding.getType();
744 if (Modifier.isPrivate(typeBinding.getDeclaredModifiers())){
745 status.addError(Messages.format(RefactoringCoreMessages.ExtractClassRefactoring_error_referencing_private_class, BasicElementLabels.getJavaElementName(typeBinding.getName())), JavaStatusContext.create(typeRoot, vdf));
746 } else if (Modifier.isProtected(typeBinding.getDeclaredModifiers())){
747 ITypeBinding declaringClass= typeBinding.getDeclaringClass();
748 if (declaringClass != null) {
749 IPackageBinding package1= declaringClass.getPackage();
750 if (package1 != null && !fDescriptor.getPackage().equals(package1.getName())){
751 status.addError(Messages.format(RefactoringCoreMessages.ExtractClassRefactoring_error_referencing_protected_class, new String[] {BasicElementLabels.getJavaElementName(typeBinding.getName()), BasicElementLabels.getJavaElementName(fDescriptor.getPackage())}), JavaStatusContext.create(typeRoot, vdf));
752 }
753 }
754 }
755 }
756 }
757 Expression initializer= vdf.getInitializer();
758 if (initializer != null)
759 pi.initializer= initializer;
760 int modifiers= parent.getModifiers();
761 if (!MemberVisibilityAdjustor.hasLowerVisibility(modifiers, modifier)){
762 modifier= modifiers;
763 }
764 }
765 }
766 FieldDeclaration fieldDeclaration= createParameterObjectField(pof, typeNode, modifier);
767 ListRewrite bodyDeclList= rewrite.getListRewrite(typeNode, TypeDeclaration.BODY_DECLARATIONS_PROPERTY);
768 if (lastField != null)
769 bodyDeclList.insertAfter(fieldDeclaration, lastField, null);
770 else
771 bodyDeclList.insertFirst(fieldDeclaration, null);
772 return fieldDeclaration;
773 }
774
775 private void initializeDeclaration(TypeDeclaration node) {
776 FieldDeclaration[] fields= node.getFields();
777 for (int i= 0; i < fields.length; i++) {
778 FieldDeclaration fieldDeclaration= fields[i];
779 List<VariableDeclarationFragment> fragments= fieldDeclaration.fragments();
780 for (Iterator<VariableDeclarationFragment> iterator= fragments.iterator(); iterator.hasNext();) {
781 VariableDeclarationFragment vdf= iterator.next();
782 FieldInfo fieldInfo= getFieldInfo(vdf.getName().getIdentifier());
783 if (fieldInfo != null) {
784 Assert.isNotNull(vdf);
785 fieldInfo.declaration= vdf;
786 fieldInfo.pi.setOldBinding(vdf.resolveBinding());
787 }
788 }
789 }
790 }
791
792 private void removeNode(ASTNode parent, TextEditGroup removeFieldGroup, CompilationUnitRewrite baseCURewrite) {
793 baseCURewrite.getASTRewrite().remove(parent, removeFieldGroup);
794 baseCURewrite.getImportRemover().registerRemovedNode(parent);
795 }
796
797 private FieldDeclaration createParameterObjectField(ParameterObjectFactory pof, TypeDeclaration typeNode, int modifier) {
798 AST ast= fBaseCURewrite.getAST();
799 ClassInstanceCreation creation= ast.newClassInstanceCreation();
800 creation.setType(pof.createType(fDescriptor.isCreateTopLevel(), fBaseCURewrite, typeNode.getStartPosition()));
801 ListRewrite listRewrite= fBaseCURewrite.getASTRewrite().getListRewrite(creation, ClassInstanceCreation.ARGUMENTS_PROPERTY);
802 for (Iterator<FieldInfo> iter= fVariables.values().iterator(); iter.hasNext();) {
803 FieldInfo fi= iter.next();
804 if (isCreateField(fi)) {
805 Expression expression= fi.initializer;
806 if (expression != null && !fi.hasFieldReference()) {
807 importNodeTypes(expression, fBaseCURewrite);
808 ASTNode createMoveTarget= fBaseCURewrite.getASTRewrite().createMoveTarget(expression);
809 if (expression instanceof ArrayInitializer) {
810 ArrayInitializer ai= (ArrayInitializer) expression;
811 ITypeBinding componentType= ai.resolveTypeBinding().getComponentType();
812 ArrayCreation arrayCreation= ast.newArrayCreation();
813 Type addImport= fBaseCURewrite.getImportRewrite().addImport(componentType, ast);
814 fBaseCURewrite.getImportRemover().registerAddedImports(addImport);
815 arrayCreation.setType(ast.newArrayType(addImport));
816 arrayCreation.setInitializer((ArrayInitializer) createMoveTarget);
817 listRewrite.insertLast(arrayCreation, null);
818 } else {
819 listRewrite.insertLast(createMoveTarget, null);
820 }
821 }
822 }
823 }
824
825 VariableDeclarationFragment fragment= ast.newVariableDeclarationFragment();
826 fragment.setName(ast.newSimpleName(fDescriptor.getFieldName()));
827 fragment.setInitializer(creation);
828
829 ModifierKeyword acc= null;
830 if (Modifier.isPublic(modifier)) {
831 acc= ModifierKeyword.PUBLIC_KEYWORD;
832 } else if (Modifier.isProtected(modifier)) {
833 acc= ModifierKeyword.PROTECTED_KEYWORD;
834 } else if (Modifier.isPrivate(modifier)) {
835 acc= ModifierKeyword.PRIVATE_KEYWORD;
836 }
837
838 FieldDeclaration fieldDeclaration= ast.newFieldDeclaration(fragment);
839 fieldDeclaration.setType(pof.createType(fDescriptor.isCreateTopLevel(), fBaseCURewrite, typeNode.getStartPosition()));
840 if (acc != null)
841 fieldDeclaration.modifiers().add(ast.newModifier(acc));
842 return fieldDeclaration;
843 }
844
845 private void importNodeTypes(ASTNode node, final CompilationUnitRewrite cuRewrite) {
846 ASTResolving.visitAllBindings(node, new TypeBindingVisitor() {
847 public boolean visit(ITypeBinding nodeBinding) {
848 ParameterObjectFactory.importBinding(nodeBinding, cuRewrite);
849 return false;
850 }
851 });
852 }
853
854 private boolean isCreateField(FieldInfo fi) {
855 return getField(fi.name).isCreateField();
856 }
857
858
859 @Override
860 public String getName() {
861 return RefactoringCoreMessages.ExtractClassRefactoring_refactoring_name;
862 }
863
864 /* (non-Javadoc)
865 * @see org.eclipse.ltk.core.refactoring.Refactoring#getAdapter(java.lang.Class)
866 */
867 @Override
868 public Object getAdapter(Class adapter) {
869 if (adapter == ExtractClassDescriptorVerification.class) {
870 return fVerification;
871 }
872 return super.getAdapter(adapter);
873 }
874}