update copyright
[fedora-idea.git] / java / java-impl / src / com / intellij / refactoring / extractclass / ExtractClassProcessor.java
blob9252fafdd9bc50fe8df0d7c96f46d6d7f3d4907b
1 /*
2 * Copyright 2000-2009 JetBrains s.r.o.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
16 package com.intellij.refactoring.extractclass;
18 import com.intellij.ide.util.PackageUtil;
19 import com.intellij.openapi.application.ApplicationManager;
20 import com.intellij.openapi.diagnostic.Logger;
21 import com.intellij.openapi.module.Module;
22 import com.intellij.openapi.module.ModuleUtil;
23 import com.intellij.openapi.project.Project;
24 import com.intellij.openapi.util.Condition;
25 import com.intellij.openapi.util.Ref;
26 import com.intellij.openapi.util.text.StringUtil;
27 import com.intellij.psi.*;
28 import com.intellij.psi.codeStyle.CodeStyleManager;
29 import com.intellij.psi.codeStyle.CodeStyleSettings;
30 import com.intellij.psi.codeStyle.CodeStyleSettingsManager;
31 import com.intellij.psi.codeStyle.JavaCodeStyleManager;
32 import com.intellij.psi.javadoc.PsiDocTagValue;
33 import com.intellij.psi.search.GlobalSearchScope;
34 import com.intellij.psi.search.searches.ReferencesSearch;
35 import com.intellij.psi.tree.IElementType;
36 import com.intellij.psi.util.InheritanceUtil;
37 import com.intellij.psi.util.PropertyUtil;
38 import com.intellij.psi.util.PsiTreeUtil;
39 import com.intellij.psi.util.PsiUtil;
40 import com.intellij.refactoring.RefactorJBundle;
41 import com.intellij.refactoring.extractclass.usageInfo.*;
42 import com.intellij.refactoring.psi.MethodInheritanceUtils;
43 import com.intellij.refactoring.psi.TypeParametersVisitor;
44 import com.intellij.refactoring.util.FixableUsageInfo;
45 import com.intellij.refactoring.util.FixableUsagesRefactoringProcessor;
46 import com.intellij.refactoring.util.RefactoringUtil;
47 import com.intellij.usageView.UsageInfo;
48 import com.intellij.usageView.UsageViewDescriptor;
49 import com.intellij.util.IncorrectOperationException;
50 import com.intellij.util.containers.MultiMap;
51 import org.jetbrains.annotations.NonNls;
52 import org.jetbrains.annotations.NotNull;
54 import java.util.*;
56 public class ExtractClassProcessor extends FixableUsagesRefactoringProcessor {
57 private static final Logger logger = Logger.getInstance("com.siyeh.rpp.extractclass.ExtractClassProcessor");
59 private final PsiClass sourceClass;
60 private final List<PsiField> fields;
61 private final List<PsiMethod> methods;
62 private final List<PsiClass> innerClasses;
63 private final Set<PsiClass> innerClassesToMakePublic = new HashSet<PsiClass>();
64 private final List<PsiTypeParameter> typeParams = new ArrayList<PsiTypeParameter>();
65 private final String newPackageName;
66 private final boolean myGenerateAccessors;
67 private final String newClassName;
68 private final String delegateFieldName;
69 private final boolean requiresBackpointer;
70 private boolean delegationRequired = false;
72 public ExtractClassProcessor(PsiClass sourceClass,
73 List<PsiField> fields,
74 List<PsiMethod> methods,
75 List<PsiClass> innerClasses,
76 String newPackageName,
77 String newClassName) {
78 this(sourceClass, fields, methods, innerClasses, newPackageName, newClassName, false);
81 public ExtractClassProcessor(PsiClass sourceClass,
82 List<PsiField> fields,
83 List<PsiMethod> methods,
84 List<PsiClass> classes,
85 String packageName,
86 String newClassName,
87 boolean generateAccessors) {
88 super(sourceClass.getProject());
89 this.sourceClass = sourceClass;
90 this.newPackageName = packageName;
91 myGenerateAccessors = generateAccessors;
92 this.fields = new ArrayList<PsiField>(fields);
93 this.methods = new ArrayList<PsiMethod>(methods);
94 this.innerClasses = new ArrayList<PsiClass>(classes);
95 this.newClassName = newClassName;
96 delegateFieldName = calculateDelegateFieldName();
97 requiresBackpointer = new BackpointerUsageVisitor(fields, innerClasses, methods, sourceClass).backpointerRequired();
98 if (requiresBackpointer) {
99 typeParams.addAll(Arrays.asList(sourceClass.getTypeParameters()));
101 else {
102 final Set<PsiTypeParameter> typeParamSet = new HashSet<PsiTypeParameter>();
103 final TypeParametersVisitor visitor = new TypeParametersVisitor(typeParamSet);
104 for (PsiField field : fields) {
105 field.accept(visitor);
107 for (PsiMethod method : methods) {
108 method.accept(visitor);
110 typeParams.addAll(typeParamSet);
114 @Override
115 protected boolean preprocessUsages(final Ref<UsageInfo[]> refUsages) {
116 final MultiMap<PsiElement, String> conflicts = new MultiMap<PsiElement, String>();
117 final Project project = sourceClass.getProject();
118 final GlobalSearchScope scope = GlobalSearchScope.allScope(project);
119 final PsiClass existingClass =
120 JavaPsiFacade.getInstance(project).findClass(StringUtil.getQualifiedName(newPackageName, newClassName), scope);
121 if (existingClass != null) {
122 conflicts.putValue(existingClass, RefactorJBundle.message("cannot.perform.the.refactoring") +
123 RefactorJBundle.message("there.already.exists.a.class.with.the.chosen.name"));
126 if (!myGenerateAccessors) {
127 calculateInitializersConflicts(conflicts);
128 final NecessaryAccessorsVisitor visitor = new NecessaryAccessorsVisitor();
129 for (PsiField field : fields) {
130 field.accept(visitor);
132 for (PsiMethod method : methods) {
133 method.accept(visitor);
135 for (PsiClass innerClass : innerClasses) {
136 innerClass.accept(visitor);
139 final Set<PsiField> fieldsNeedingGetter = visitor.getFieldsNeedingGetter();
140 for (PsiField field : fieldsNeedingGetter) {
141 conflicts.putValue(field, "Field \'" + field.getName() + "\' needs getter");
143 final Set<PsiField> fieldsNeedingSetter = visitor.getFieldsNeedingSetter();
144 for (PsiField field : fieldsNeedingSetter) {
145 conflicts.putValue(field, "Field \'" + field.getName() + "\' needs getter");
148 return showConflicts(conflicts);
151 @Override
152 protected boolean showConflicts(final MultiMap<PsiElement, String> conflicts) {
153 if (!conflicts.isEmpty() && ApplicationManager.getApplication().isUnitTestMode()) {
154 throw new RuntimeException(StringUtil.join(conflicts.values(), "\n"));
156 return super.showConflicts(conflicts);
159 private void calculateInitializersConflicts(MultiMap<PsiElement, String> conflicts) {
160 final PsiClassInitializer[] initializers = sourceClass.getInitializers();
161 for (PsiClassInitializer initializer : initializers) {
162 if (initializerDependsOnMoved(initializer)) {
163 conflicts.putValue(initializer, "Class initializer requires moved members");
166 for (PsiMethod constructor : sourceClass.getConstructors()) {
167 if (initializerDependsOnMoved(constructor.getBody())) {
168 conflicts.putValue(constructor, "Constructor requires moved members");
173 private boolean initializerDependsOnMoved(PsiElement initializer) {
174 final boolean [] dependsOnMoved = new boolean[]{false};
175 initializer.accept(new JavaRecursiveElementWalkingVisitor(){
176 public void visitReferenceExpression(final PsiReferenceExpression expression) {
177 super.visitReferenceExpression(expression);
178 final PsiElement resolved = expression.resolve();
179 if (resolved != null) {
180 dependsOnMoved[0] |= isInMovedElement(resolved);
184 return dependsOnMoved[0];
187 private String calculateDelegateFieldName() {
188 final Project project = sourceClass.getProject();
189 final CodeStyleSettingsManager settingsManager = CodeStyleSettingsManager.getInstance(project);
190 final CodeStyleSettings settings = settingsManager.getCurrentSettings();
192 final String baseName = settings.FIELD_NAME_PREFIX.length() == 0 ? StringUtil.decapitalize(newClassName) : newClassName;
193 String name = settings.FIELD_NAME_PREFIX + baseName + settings.FIELD_NAME_SUFFIX;
194 if (!existsFieldWithName(name) && !JavaPsiFacade.getInstance(project).getNameHelper().isKeyword(name)) {
195 return name;
197 int counter = 1;
198 while (true) {
199 name = settings.FIELD_NAME_PREFIX + baseName + counter + settings.FIELD_NAME_SUFFIX;
200 if (!existsFieldWithName(name) && !JavaPsiFacade.getInstance(project).getNameHelper().isKeyword(name)) {
201 return name;
203 counter++;
208 private boolean existsFieldWithName(String name) {
209 final PsiField[] allFields = sourceClass.getAllFields();
210 for (PsiField field : allFields) {
211 if (name.equals(field.getName()) && !fields.contains(field)) {
212 return true;
215 return false;
218 protected String getCommandName() {
219 return RefactorJBundle.message("extracted.class.command.name", newClassName);
222 protected UsageViewDescriptor createUsageViewDescriptor(UsageInfo[] usageInfos) {
223 return new ExtractClassUsageViewDescriptor(sourceClass);
226 protected void performRefactoring(UsageInfo[] usageInfos) {
227 if (!buildClass()) return;
228 if (delegationRequired) {
229 buildDelegate();
231 super.performRefactoring(usageInfos);
235 private void buildDelegate() {
236 final PsiManager manager = sourceClass.getManager();
237 final PsiElementFactory factory = JavaPsiFacade.getInstance(manager.getProject()).getElementFactory();
238 final CodeStyleManager codeStyleManager = manager.getCodeStyleManager();
239 @NonNls final StringBuilder fieldBuffer = new StringBuilder();
240 final String delegateVisibility = calculateDelegateVisibility();
241 fieldBuffer.append(delegateVisibility).append(' ');
242 fieldBuffer.append("final ");
243 final String fullyQualifiedName = StringUtil.getQualifiedName(newPackageName, newClassName);
244 fieldBuffer.append(fullyQualifiedName);
245 if (!typeParams.isEmpty()) {
246 fieldBuffer.append('<');
247 for (PsiTypeParameter typeParameter : typeParams) {
248 fieldBuffer.append(typeParameter.getName());
250 fieldBuffer.append('>');
252 fieldBuffer.append(' ');
253 fieldBuffer.append(delegateFieldName);
254 fieldBuffer.append('=');
255 fieldBuffer.append("new ").append(fullyQualifiedName);
256 if (!typeParams.isEmpty()) {
257 fieldBuffer.append('<');
258 for (PsiTypeParameter typeParameter : typeParams) {
259 fieldBuffer.append(typeParameter.getName());
261 fieldBuffer.append('>');
263 fieldBuffer.append('(');
264 boolean isFirst = true;
265 if (requiresBackpointer) {
266 fieldBuffer.append("this");
267 isFirst = false;
269 for (PsiField field : fields) {
270 if (field.hasModifierProperty(PsiModifier.STATIC)) {
271 continue;
273 if (!field.hasInitializer()) {
274 continue;
276 final PsiExpression initializer = field.getInitializer();
277 if (PsiUtil.isConstantExpression(initializer)) {
278 continue;
280 if (!isFirst) {
281 fieldBuffer.append(", ");
283 isFirst = false;
284 assert initializer != null;
285 fieldBuffer.append(initializer.getText());
288 fieldBuffer.append(");");
289 try {
290 final String fieldString = fieldBuffer.toString();
291 final PsiField field = factory.createFieldFromText(fieldString, sourceClass);
292 final PsiElement newField = sourceClass.add(field);
293 codeStyleManager.reformat(JavaCodeStyleManager.getInstance(myProject).shortenClassReferences(newField));
295 catch (IncorrectOperationException e) {
296 logger.error(e);
300 @NonNls
301 private String calculateDelegateVisibility() {
302 for (PsiField field : fields) {
303 if (field.hasModifierProperty(PsiModifier.PUBLIC) && !field.hasModifierProperty(PsiModifier.STATIC)) {
304 return "public";
307 for (PsiField field : fields) {
308 if (field.hasModifierProperty(PsiModifier.PROTECTED) && !field.hasModifierProperty(PsiModifier.STATIC)) {
309 return "protected";
312 for (PsiField field : fields) {
313 if (field.hasModifierProperty(PsiModifier.PACKAGE_LOCAL) && !field.hasModifierProperty(PsiModifier.STATIC)) {
314 return "";
317 return "private";
320 public void findUsages(@NotNull List<FixableUsageInfo> usages) {
321 for (PsiField field : fields) {
322 findUsagesForField(field, usages);
323 usages.add(new RemoveField(field));
325 for (PsiClass innerClass : innerClasses) {
326 findUsagesForInnerClass(innerClass, usages);
327 usages.add(new RemoveInnerClass(innerClass));
329 for (PsiMethod method : methods) {
330 if (method.hasModifierProperty(PsiModifier.STATIC)) {
331 findUsagesForStaticMethod(method, usages);
333 else {
334 findUsagesForMethod(method, usages);
339 private void findUsagesForInnerClass(PsiClass innerClass, List<FixableUsageInfo> usages) {
340 final PsiManager psiManager = innerClass.getManager();
341 final Project project = psiManager.getProject();
342 final GlobalSearchScope scope = GlobalSearchScope.allScope(project);
343 final Iterable<PsiReference> calls = ReferencesSearch.search(innerClass, scope);
344 final String innerName = innerClass.getQualifiedName();
345 assert innerName != null;
346 final String sourceClassQualifiedName = sourceClass.getQualifiedName();
347 assert sourceClassQualifiedName != null;
348 final String newInnerClassName = StringUtil.getQualifiedName(newPackageName, newClassName) + innerName.substring(sourceClassQualifiedName.length());
349 boolean hasExternalReference = false;
350 for (PsiReference reference : calls) {
351 final PsiElement referenceElement = reference.getElement();
352 if (referenceElement instanceof PsiJavaCodeReferenceElement) {
353 if (!isInMovedElement(referenceElement)) {
355 usages.add(new ReplaceClassReference((PsiJavaCodeReferenceElement)referenceElement, newInnerClassName));
356 hasExternalReference = true;
360 if (hasExternalReference) {
361 innerClassesToMakePublic.add(innerClass);
365 private void findUsagesForMethod(PsiMethod method, List<FixableUsageInfo> usages) {
366 final PsiManager psiManager = method.getManager();
367 final Project project = psiManager.getProject();
368 final GlobalSearchScope scope = GlobalSearchScope.allScope(project);
369 final Iterable<PsiReference> calls = ReferencesSearch.search(method, scope);
370 for (PsiReference reference : calls) {
371 final PsiElement referenceElement = reference.getElement();
372 final PsiElement parent = referenceElement.getParent();
373 if (parent instanceof PsiMethodCallExpression) {
374 final PsiMethodCallExpression call = (PsiMethodCallExpression)parent;
375 if (isInMovedElement(call)) {
376 continue;
378 final PsiReferenceExpression methodExpression = call.getMethodExpression();
379 final PsiExpression qualifier = methodExpression.getQualifierExpression();
380 if (qualifier == null || qualifier instanceof PsiThisExpression) {
381 usages.add(new ReplaceThisCallWithDelegateCall(call, delegateFieldName));
383 delegationRequired = true;
387 if (!delegationRequired && MethodInheritanceUtils.hasSiblingMethods(method)) {
388 delegationRequired = true;
391 if (delegationRequired) {
392 usages.add(new MakeMethodDelegate(method, delegateFieldName));
394 else {
395 usages.add(new RemoveMethod(method));
399 private void findUsagesForStaticMethod(PsiMethod method, List<FixableUsageInfo> usages) {
400 final PsiManager psiManager = method.getManager();
401 final Project project = psiManager.getProject();
402 final GlobalSearchScope scope = GlobalSearchScope.allScope(project);
403 final Iterable<PsiReference> calls = ReferencesSearch.search(method, scope);
404 for (PsiReference reference : calls) {
405 final PsiElement referenceElement = reference.getElement();
407 final PsiElement parent = referenceElement.getParent();
408 if (parent instanceof PsiMethodCallExpression) {
409 final PsiMethodCallExpression call = (PsiMethodCallExpression)parent;
410 if (!isInMovedElement(call)) {
411 final String fullyQualifiedName = StringUtil.getQualifiedName(newPackageName, newClassName);
412 usages.add(new RetargetStaticMethodCall(call, fullyQualifiedName));
416 usages.add(new RemoveMethod(method));
419 private boolean isInMovedElement(PsiElement exp) {
420 for (PsiField field : fields) {
421 if (PsiTreeUtil.isAncestor(field, exp, false)) {
422 return true;
425 for (PsiMethod method : methods) {
426 if (PsiTreeUtil.isAncestor(method, exp, false)) {
427 return true;
430 return false;
433 private void findUsagesForField(PsiField field, List<FixableUsageInfo> usages) {
434 final PsiManager psiManager = field.getManager();
435 final Project project = psiManager.getProject();
436 final GlobalSearchScope scope = GlobalSearchScope.allScope(project);
438 final String qualifiedName = StringUtil.getQualifiedName(newPackageName, newClassName);
439 @NonNls final String getter = PropertyUtil.suggestGetterName(myProject, field);
440 @NonNls final String setter = PropertyUtil.suggestSetterName(myProject, field);
441 final boolean isStatic = field.hasModifierProperty(PsiModifier.STATIC);
443 for (PsiReference reference : ReferencesSearch.search(field, scope)) {
444 final PsiElement element = reference.getElement();
445 if (isInMovedElement(element)) {
446 continue;
449 if (element instanceof PsiReferenceExpression) {
450 final PsiReferenceExpression exp = (PsiReferenceExpression)element;
451 if (RefactoringUtil.isPlusPlusOrMinusMinus(exp)) {
452 usages.add(isStatic
453 ? new ReplaceStaticVariableIncrementDecrement(exp, qualifiedName)
454 : new ReplaceInstanceVariableIncrementDecrement(exp, delegateFieldName, setter, getter));
456 else if (RefactoringUtil.isAssignmentLHS(exp)) {
457 usages.add(isStatic
458 ? new ReplaceStaticVariableAssignment(exp, qualifiedName)
459 : new ReplaceInstanceVariableAssignment(PsiTreeUtil.getParentOfType(exp, PsiAssignmentExpression.class),
460 delegateFieldName, setter, getter));
463 else {
464 usages.add(isStatic
465 ? new ReplaceStaticVariableAccess(exp, qualifiedName)
466 : new ReplaceInstanceVariableAccess(exp, delegateFieldName, getter));
469 if (!isStatic) {
470 delegationRequired = true;
472 } else if (element instanceof PsiDocTagValue) {
473 usages.add(new BindJavadocReference(element, qualifiedName, field.getName()));
478 private boolean hasGetter(final PsiField field) {
479 return hasGetterOrSetter(sourceClass.findMethodsBySignature(PropertyUtil.generateGetterPrototype(field), false));
482 private boolean hasSetter(final PsiField field) {
483 return hasGetterOrSetter(sourceClass.findMethodsBySignature(PropertyUtil.generateSetterPrototype(field), false));
486 private boolean hasGetterOrSetter(final PsiMethod[] getters) {
487 for (PsiMethod getter : getters) {
488 if (!isInMovedElement(getter)) return true;
490 return false;
494 private boolean buildClass() {
495 final PsiManager manager = sourceClass.getManager();
496 final Project project = sourceClass.getProject();
497 final ExtractedClassBuilder extractedClassBuilder = new ExtractedClassBuilder();
498 extractedClassBuilder.setProject(myProject);
499 extractedClassBuilder.setClassName(newClassName);
500 extractedClassBuilder.setPackageName(newPackageName);
501 extractedClassBuilder.setOriginalClassName(sourceClass.getQualifiedName());
502 extractedClassBuilder.setRequiresBackPointer(requiresBackpointer);
503 for (PsiField field : fields) {
504 extractedClassBuilder.addField(field);
506 for (PsiMethod method : methods) {
507 extractedClassBuilder.addMethod(method);
509 for (PsiClass innerClass : innerClasses) {
510 extractedClassBuilder.addInnerClass(innerClass, innerClassesToMakePublic.contains(innerClass));
512 extractedClassBuilder.setTypeArguments(typeParams);
513 final List<PsiClass> interfaces = calculateInterfacesSupported();
514 extractedClassBuilder.setInterfaces(interfaces);
516 if (myGenerateAccessors) {
517 final NecessaryAccessorsVisitor visitor = new NecessaryAccessorsVisitor() {
518 @Override
519 protected boolean isProhibitedReference(PsiField field) {
520 if (fields.contains(field)) {
521 return true;
523 if (innerClasses.contains(field.getContainingClass())) {
524 return true;
526 return false;
529 sourceClass.accept(visitor);
530 extractedClassBuilder.setFieldsNeedingGetters(visitor.getFieldsNeedingGetter());
531 extractedClassBuilder.setFieldsNeedingSetters(visitor.getFieldsNeedingSetter());
534 final String classString = extractedClassBuilder.buildBeanClass();
536 try {
537 final PsiFile containingFile = sourceClass.getContainingFile();
539 final PsiDirectory containingDirectory = containingFile.getContainingDirectory();
540 final Module module = ModuleUtil.findModuleForPsiElement(containingFile);
541 assert module != null;
542 final PsiDirectory directory = PackageUtil.findOrCreateDirectoryForPackage(module, newPackageName, containingDirectory, false);
543 if (directory != null) {
544 final PsiFile newFile = PsiFileFactory.getInstance(project).createFileFromText(newClassName + ".java", classString);
545 final PsiElement addedFile = directory.add(newFile);
546 final CodeStyleManager codeStyleManager = manager.getCodeStyleManager();
547 final PsiElement shortenedFile = JavaCodeStyleManager.getInstance(project).shortenClassReferences(addedFile);
548 codeStyleManager.reformat(shortenedFile);
549 } else {
550 return false;
553 catch (IncorrectOperationException e) {
554 return false;
556 return true;
559 private List<PsiClass> calculateInterfacesSupported() {
560 final List<PsiClass> out = new ArrayList<PsiClass>();
561 final PsiClass[] supers = sourceClass.getSupers();
562 for (PsiClass superClass : supers) {
563 if (!superClass.isInterface()) {
564 continue;
566 final PsiMethod[] superclassMethods = superClass.getMethods();
567 if (superclassMethods.length == 0) {
568 continue;
570 boolean allMethodsCovered = true;
572 for (PsiMethod method : superclassMethods) {
573 boolean isCovered = false;
574 for (PsiMethod movedMethod : methods) {
575 if (isSuperMethod(method, movedMethod)) {
576 isCovered = true;
577 break;
580 if (!isCovered) {
581 allMethodsCovered = false;
582 break;
585 if (allMethodsCovered) {
586 out.add(superClass);
589 final Project project = sourceClass.getProject();
590 final PsiManager manager = sourceClass.getManager();
591 final GlobalSearchScope scope = GlobalSearchScope.allScope(project);
592 if (usesDefaultSerialization(sourceClass)) {
593 final PsiClass serializable = JavaPsiFacade.getInstance(manager.getProject()).findClass("java.io.Serializable", scope);
594 out.add(serializable);
596 if (usesDefaultClone(sourceClass)) {
597 final PsiClass cloneable = JavaPsiFacade.getInstance(manager.getProject()).findClass("java.lang.Cloneable", scope);
598 out.add(cloneable);
600 return out;
603 private static boolean isSuperMethod(PsiMethod method, PsiMethod movedMethod) {
604 final PsiMethod[] superMethods = movedMethod.findSuperMethods();
605 for (PsiMethod testMethod : superMethods) {
606 if (testMethod.equals(method)) {
607 return true;
610 return false;
613 private static boolean usesDefaultClone(PsiClass aClass) {
614 final Project project = aClass.getProject();
615 final PsiManager manager = aClass.getManager();
616 final GlobalSearchScope scope = GlobalSearchScope.allScope(project);
617 final PsiClass cloneable = JavaPsiFacade.getInstance(manager.getProject()).findClass("java.lang.Cloneable", scope);
618 if (!InheritanceUtil.isCorrectDescendant(aClass, cloneable, true)) {
619 return false;
621 final PsiMethod[] methods = aClass.findMethodsByName("clone", false);
622 for (PsiMethod method : methods) {
623 final PsiParameterList parameterList = method.getParameterList();
624 final PsiParameter[] parameters = parameterList.getParameters();
625 if (parameters.length == 0) {
626 return false;
629 return true;
632 private static boolean usesDefaultSerialization(PsiClass aClass) {
633 final Project project = aClass.getProject();
634 final PsiManager manager = aClass.getManager();
635 final GlobalSearchScope scope = GlobalSearchScope.allScope(project);
636 final PsiClass serializable = JavaPsiFacade.getInstance(manager.getProject()).findClass("java.io.Serializable", scope);
637 if (!InheritanceUtil.isCorrectDescendant(aClass, serializable, true)) {
638 return false;
640 final PsiMethod[] methods = aClass.findMethodsByName("writeObject", false);
641 for (PsiMethod method : methods) {
642 final PsiParameterList parameterList = method.getParameterList();
643 final PsiParameter[] parameters = parameterList.getParameters();
644 if (parameters.length == 1) {
645 final PsiType type = parameters[0].getType();
646 final String text = type.getCanonicalText();
647 if ("java.io.DataOutputStream".equals(text)) {
648 return false;
652 return true;
655 private class NecessaryAccessorsVisitor extends JavaRecursiveElementWalkingVisitor {
656 private final Set<PsiField> fieldsNeedingGetter = new HashSet<PsiField>();
657 private final Set<PsiField> fieldsNeedingSetter = new HashSet<PsiField>();
659 public void visitReferenceExpression(PsiReferenceExpression expression) {
660 super.visitReferenceExpression(expression);
661 if (isProhibitedReference(expression)) {
662 final PsiField field = getReferencedField(expression);
663 if (!hasGetter(field)) {
664 fieldsNeedingGetter.add(field);
669 public void visitAssignmentExpression(PsiAssignmentExpression expression) {
670 super.visitAssignmentExpression(expression);
672 final PsiExpression lhs = expression.getLExpression();
673 if (isProhibitedReference(lhs)) {
674 final PsiField field = getReferencedField(lhs);
675 if (!hasGetter(field)) {
676 fieldsNeedingSetter.add(field);
681 public void visitPostfixExpression(PsiPostfixExpression expression) {
682 super.visitPostfixExpression(expression);
683 checkSetterNeeded(expression.getOperand(), expression.getOperationSign());
686 public void visitPrefixExpression(PsiPrefixExpression expression) {
687 super.visitPrefixExpression(expression);
688 checkSetterNeeded(expression.getOperand(), expression.getOperationSign());
691 private void checkSetterNeeded(final PsiExpression operand, final PsiJavaToken sign) {
692 final IElementType tokenType = sign.getTokenType();
693 if (!tokenType.equals(JavaTokenType.PLUSPLUS) && !tokenType.equals(JavaTokenType.MINUSMINUS)) {
694 return;
696 if (isProhibitedReference(operand)) {
697 final PsiField field = getReferencedField(operand);
698 if (!hasSetter(field)) {
699 fieldsNeedingSetter.add(field);
704 public Set<PsiField> getFieldsNeedingGetter() {
705 return fieldsNeedingGetter;
708 public Set<PsiField> getFieldsNeedingSetter() {
709 return fieldsNeedingSetter;
713 protected boolean isProhibitedReference(PsiExpression expression) {
714 return BackpointerUtil.isBackpointerReference(expression, new Condition<PsiField>() {
715 public boolean value(final PsiField field) {
716 return NecessaryAccessorsVisitor.this.isProhibitedReference(field);
721 protected boolean isProhibitedReference(PsiField field) {
722 if (fields.contains(field)) {
723 return false;
725 if (innerClasses.contains(field.getContainingClass())) {
726 return false;
728 return true;
731 private PsiField getReferencedField(PsiExpression expression) {
732 if (expression instanceof PsiParenthesizedExpression) {
733 final PsiExpression contents = ((PsiParenthesizedExpression)expression).getExpression();
734 return getReferencedField(contents);
736 final PsiReferenceExpression reference = (PsiReferenceExpression)expression;
737 return (PsiField)reference.resolve();