NPE fix plus a bunch of dead code removed
[idea/community.git] / plugins / generate-tostring / src / org / jetbrains / generate / tostring / psi / PsiAdapter.java
1 /*
2  * Copyright 2001-2007 the original author or authors.
3  *
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
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  */
16 package org.jetbrains.generate.tostring.psi;
17
18 import com.intellij.openapi.command.CommandProcessor;
19 import com.intellij.openapi.project.Project;
20 import com.intellij.psi.*;
21 import com.intellij.psi.codeStyle.CodeStyleManager;
22 import com.intellij.psi.codeStyle.JavaCodeStyleManager;
23 import com.intellij.psi.javadoc.PsiDocComment;
24 import com.intellij.psi.search.GlobalSearchScope;
25 import com.intellij.util.ArrayUtil;
26 import com.intellij.util.IncorrectOperationException;
27 import org.jetbrains.annotations.Nullable;
28 import org.jetbrains.generate.tostring.util.StringUtil;
29
30 /**
31  * Basic PSI Adapter with common function that works in all supported versions of IDEA.
32  */
33 public abstract class PsiAdapter {
34
35     /**
36      * Constructor - use {@link PsiAdapterFactory}.
37      */
38     protected PsiAdapter() {
39     }
40
41   /**
42      * Get's the fields for the class.
43      *
44      * @param clazz class.
45      * @return the fields for the class. If the class doesn't have any fields the array's size is 0.
46      */
47     public PsiField[] getFields(PsiClass clazz) {
48         return clazz.getFields();
49     }
50
51     /**
52      * Finds the class for the given element.
53      * <p/>
54      * Will look in the element's parent hieracy.
55      *
56      * @param element element to find it's class
57      * @return the class, null if not found.
58      */
59     @Nullable
60     public PsiClass findClass(PsiElement element) {
61         if (element instanceof PsiClass) {
62             return (PsiClass) element;
63         }
64
65         if (element.getParent() != null) {
66             return findClass(element.getParent());
67         }
68
69         return null;
70     }
71
72     /**
73      * Returns true if a field is constant.
74      * <p/>
75      * This is identifed as the name of the field is only in uppercase and it has
76      * a <code>static</code> modifier.
77      *
78      * @param field field to check if it's a constant
79      * @return true if constant.
80      */
81     public boolean isConstantField(PsiField field) {
82         PsiModifierList list = field.getModifierList();
83         if (list == null) {
84             return false;
85         }
86
87         // modifier must be static
88         if (!list.hasModifierProperty(PsiModifier.STATIC)) {
89             return false;
90         }
91
92         // name must NOT have any lowercase character
93         return !StringUtil.hasLowerCaseChar(field.getName());
94     }
95
96     /**
97      * Find's an existing method with the given name.
98      * If there isn't a method with the name, null is returned.
99      *
100      * @param clazz the class
101      * @param name  name of method to find
102      * @return the found method, null if none exist
103      */
104     @Nullable
105     public PsiMethod findMethodByName(PsiClass clazz, String name) {
106         PsiMethod[] methods = clazz.getMethods();
107
108         // use reverse to find from botton as the duplicate conflict resolution policy requires this
109         for (int i = methods.length - 1; i >= 0; i--) {
110             PsiMethod method = methods[i];
111             if (name.equals(method.getName()))
112                 return method;
113         }
114         return null;
115     }
116
117     /**
118      * Returns true if the given field a primtive array type (e.g., int[], long[], float[]).
119      *
120      * @param type type.
121      * @return true if field is a primitve array type.
122      */
123     public boolean isPrimitiveArrayType(PsiType type) {
124         return type instanceof PsiArrayType && isPrimitiveType(((PsiArrayType) type).getComponentType());
125     }
126
127     /**
128      * Is the type an Object array type (etc. String[], Object[])?
129      *
130      * @param type type.
131      * @return true if it's an Object array type.
132      */
133     public boolean isObjectArrayType(PsiType type) {
134         return type instanceof PsiArrayType && !isPrimitiveType(((PsiArrayType) type).getComponentType());
135     }
136
137     /**
138      * Is the type a String array type (etc. String[])?
139      *
140      * @param type type.
141      * @return true if it's a String array type.
142      */
143     public boolean isStringArrayType(PsiType type) {
144         if (isPrimitiveType(type))
145             return false;
146
147         return type.getCanonicalText().indexOf("String[]") > 0;
148     }
149
150     /**
151      * Is the given field a {@link java.util.Collection} type?
152      *
153      * @param factory element factory.
154      * @param type    type.
155      * @return true if it's a Collection type.
156      */
157     public boolean isCollectionType(PsiElementFactory factory, PsiType type) {
158         return isTypeOf(factory, type, "java.util.Collection");
159     }
160
161     /**
162      * Is the given field a {@link java.util.Map} type?
163      *
164      * @param factory element factory.
165      * @param type    type.
166      * @return true if it's a Map type.
167      */
168     public boolean isMapType(PsiElementFactory factory, PsiType type) {
169         return isTypeOf(factory, type, "java.util.Map");
170     }
171
172     /**
173      * Is the given field a {@link java.util.Set} type?
174      *
175      * @param factory element factory.
176      * @param type    type.
177      * @return true if it's a Map type.
178      */
179     public boolean isSetType(PsiElementFactory factory, PsiType type) {
180         return isTypeOf(factory, type, "java.util.Set");
181     }
182
183     /**
184      * Is the given field a {@link java.util.List} type?
185      *
186      * @param factory element factory.
187      * @param type    type.
188      * @return true if it's a Map type.
189      */
190     public boolean isListType(PsiElementFactory factory, PsiType type) {
191         return isTypeOf(factory, type, "java.util.List");
192     }
193
194     /**
195      * Is the given field a {@link java.lang.String} type?
196      *
197      * @param factory element factory.
198      * @param type    type.
199      * @return true if it's a String type.
200      */
201     public boolean isStringType(PsiElementFactory factory, PsiType type) {
202         return isTypeOf(factory, type, "java.lang.String");
203     }
204
205     /**
206      * Is the given field assignable from {@link java.lang.Object}?
207      *
208      * @param factory element factory.
209      * @param type    type.
210      * @return true if it's an Object type.
211      */
212     public boolean isObjectType(PsiElementFactory factory, PsiType type) {
213         return isTypeOf(factory, type, "java.lang.Object");
214     }
215
216     /**
217      * Is the given field a {@link java.util.Date} type?
218      *
219      * @param factory element factory.
220      * @param type    type.
221      * @return true if it's a Date type.
222      */
223     public boolean isDateType(PsiElementFactory factory, PsiType type) {
224         return isTypeOf(factory, type, "java.util.Date");
225     }
226
227     /**
228      * Is the given field a {@link java.util.Calendar} type?
229      *
230      * @param factory element factory.
231      * @param type    type.
232      * @return true if it's a Calendar type.
233      */
234     public boolean isCalendarType(PsiElementFactory factory, PsiType type) {
235         return isTypeOf(factory, type, "java.util.Calendar");
236     }
237
238     /**
239      * Is the given field a {@link java.lang.Boolean} type or a primitive boolean type?
240      *
241      * @param factory element factory.
242      * @param type    type.
243      * @return true if it's a Boolean or boolean type.
244      */
245     public boolean isBooleanType(PsiElementFactory factory, PsiType type) {
246         if (isPrimitiveType(type)) {
247             // test for simple type of boolean
248             String s = type.getCanonicalText();
249             return "boolean".equals(s);
250         } else {
251             // test for Object type of Boolean
252             return isTypeOf(factory, type, "java.lang.Boolean");
253         }
254     }
255
256     /**
257      * Is the given field a numeric type (assignable from java.lang.Numeric or a primitive type of byte, short, int, long, float, double type)?
258      *
259      * @param factory element factory.
260      * @param type    type.
261      * @return true if it's a numeric type.
262      */
263     public boolean isNumericType(PsiElementFactory factory, PsiType type) {
264         if (isPrimitiveType(type)) {
265             // test for simple type of numeric
266             String s = type.getCanonicalText();
267             return "byte".equals(s) || "double".equals(s) || "float".equals(s) || "int".equals(s) || "long".equals(s) || "short".equals(s);
268         } else {
269             // test for Object type of numeric
270             return isTypeOf(factory, type, "java.lang.Number");
271         }
272     }
273
274   /**
275      * Is there a <code>transient</code> modifier?
276      * <p/>eg: <code>private transient String myField;</code>.
277      *
278      * @param modifiers the modifiers
279      */
280     public boolean isModifierTransient(PsiModifierList modifiers) {
281         return modifiers.hasModifierProperty(PsiModifier.TRANSIENT);
282     }
283
284     /**
285      * Is there a <code>volatile</code> modifier?
286      * <p/>eg: <code>private volatile Image screen;</code>.
287      *
288      * @param modifiers the modifiers
289      */
290     public boolean isModifierVolatile(PsiModifierList modifiers) {
291         return modifiers.hasModifierProperty(PsiModifier.VOLATILE);
292     }
293
294     /**
295      * Is there a <code>public</code> modifier?
296      * <p/>eg: <code>public String myField;</code>.
297      *
298      * @param modifiers the modifiers
299      */
300     public boolean isModifierPublic(PsiModifierList modifiers) {
301         return modifiers.hasModifierProperty(PsiModifier.PUBLIC);
302     }
303
304     /**
305      * Is there a <code>protected</code> modifier?
306      * <p/>eg: <code>public String myField;</code>.
307      *
308      * @param modifiers the modifiers
309      */
310     public boolean isModifierProtected(PsiModifierList modifiers) {
311         return modifiers.hasModifierProperty(PsiModifier.PROTECTED);
312     }
313
314     /**
315      * Is there a <code>package-local</code> modifier?
316      * <p/>eg: <code>String myField;</code>.
317      *
318      * @param modifiers the modifiers
319      */
320     public boolean isModifierPackageLocal(PsiModifierList modifiers) {
321         return modifiers.hasModifierProperty(PsiModifier.PACKAGE_LOCAL);
322     }
323
324     /**
325      * Is there a <code>private</code> modifier?
326      * <p/>eg: <code>private static String myField;</code>.
327      *
328      * @param modifiers the modifiers
329      */
330     public boolean isModifierPrivate(PsiModifierList modifiers) {
331         return modifiers.hasModifierProperty(PsiModifier.PRIVATE);
332     }
333
334     /**
335      * Is there a <code>abstract</code> modifier?
336      * <p/>eg: <code>public abstract String getConfiguration()</code>.
337      *
338      * @param modifiers the modifiers
339      */
340     public boolean isModifierAbstract(PsiModifierList modifiers) {
341         return modifiers.hasModifierProperty(PsiModifier.ABSTRACT);
342     }
343
344     /**
345      * Is there a <code>final</code> modifier?
346      * <p/>eg: <code>final static boolean DEBUG = false;</code>.
347      *
348      * @param modifiers the modifiers
349      */
350     public boolean isModifierFinal(PsiModifierList modifiers) {
351         return modifiers.hasModifierProperty(PsiModifier.FINAL);
352     }
353
354     /**
355      * Is there a <code>static</code> modifier?
356      * <p/>eg: <code>private static String getMyField()</code>.
357      *
358      * @param modifiers the modifiers
359      */
360     public boolean isModifierStatic(PsiModifierList modifiers) {
361         return modifiers.hasModifierProperty(PsiModifier.STATIC);
362     }
363
364     /**
365      * Is there a <code>synchronized</code> modifier?
366      * <p/>eg: <code>public synchronized void putInCache()</code>.
367      *
368      * @param modifiers the modifiers
369      */
370     public boolean isModifierSynchronized(PsiModifierList modifiers) {
371         return modifiers.hasModifierProperty(PsiModifier.SYNCHRONIZED);
372     }
373
374     /**
375      * Get's the CodeStyleManager for the project.
376      *
377      * @param project project.
378      * @return the CodeStyleManager.
379      */
380     public CodeStyleManager getCodeStyleManager(Project project) {
381         return CodeStyleManager.getInstance(project);
382     }
383
384   /**
385      * Does the javafile have the import statement?
386      *
387      * @param javaFile        javafile.
388      * @param importStatement import statement to test existing for.
389      * @return true if the javafile has the import statement.
390      */
391     public boolean hasImportStatement(PsiJavaFile javaFile, String importStatement) {
392         PsiImportList importList = javaFile.getImportList();
393         if (importList == null) {
394             return false;
395         }
396
397         if (importStatement.endsWith(".*")) {
398             return (importList.findOnDemandImportStatement(fixImportStatement(importStatement)) != null);
399         } else {
400             return (importList.findSingleClassImportStatement(importStatement) != null);
401         }
402     }
403
404     /**
405      * Add's an importstatement to the javafile and optimizes the imports afterwards.
406      *
407      * @param javaFile                javafile.
408      * @param importStatementOnDemand name of importstatement, must be with a wildcard (etc. java.util.*).
409      * @param factory                 PSI element factory.
410      * @throws com.intellij.util.IncorrectOperationException
411      *          is thrown if there is an error creating the importstatement.
412      */
413     public void addImportStatement(PsiJavaFile javaFile, String importStatementOnDemand, PsiElementFactory factory) throws IncorrectOperationException {
414         PsiImportStatement is = factory.createImportStatementOnDemand(fixImportStatement(importStatementOnDemand));
415
416         // add the import to the file, and optimize the imports
417         PsiImportList importList = javaFile.getImportList();
418         if (importList != null) {
419             importList.add(is);
420         }
421
422         JavaCodeStyleManager.getInstance(javaFile.getProject()).optimizeImports(javaFile);
423     }
424
425     /**
426      * Fixes the import statement to be returned as packagename only (without .* or any Classname).
427      * <p/>
428      * <br/>Example: java.util will be returned as java.util
429      * <br/>Example: java.util.* will be returned as java.util
430      * <br/>Example: java.text.SimpleDateFormat will be returned as java.text
431      *
432      * @param importStatementOnDemand import statement
433      * @return import statement only with packagename
434      */
435     private String fixImportStatement(String importStatementOnDemand) {
436         if (importStatementOnDemand.endsWith(".*")) {
437             return importStatementOnDemand.substring(0, importStatementOnDemand.length() - 2);
438         } else {
439             boolean hasClassname = StringUtil.hasUpperCaseChar(importStatementOnDemand);
440
441             if (hasClassname) {
442                 // extract packagename part
443                 int pos = importStatementOnDemand.lastIndexOf(".");
444                 return importStatementOnDemand.substring(0, pos);
445             } else {
446                 // it is a pure packagename
447                 return importStatementOnDemand;
448             }
449         }
450     }
451
452     /**
453      * Does this class have a super class?
454      * <p/>
455      * If the class just extends java.lang.Object then false is returned.
456      * Extending java.lang.Object is <b>not</b> concidered the class to have a super class.
457      *
458      * @param project the IDEA project
459      * @param clazz   the class to test
460      * @return true if this class extends another class.
461      */
462     public boolean hasSuperClass(Project project, PsiClass clazz) {
463         PsiClass superClass = getSuperClass(project, clazz);
464         if (superClass == null) {
465             return false;
466         }
467
468         return (!"Object".equals(superClass.getName()));
469     }
470
471   /**
472      * Get's the fields fully qualified classname (etc java.lang.String, java.util.ArrayList)
473      *
474      * @param type the type.
475      * @return the fully qualified classname, null if the field is a primitive.
476      * @see #getTypeClassName(com.intellij.psi.PsiType) for the non qualified version.
477      */
478     @Nullable
479     public String getTypeQualifiedClassName(PsiType type) {
480         if (isPrimitiveType(type)) {
481             return null;
482         }
483
484         // avoid [] if the type is an array
485         String name = type.getCanonicalText();
486         if (name.endsWith("[]")) {
487             return name.substring(0, name.length() - 2);
488         }
489
490         return name;
491     }
492
493     /**
494      * Get's the fields classname (etc. String, ArrayList)
495      *
496      * @param type the type.
497      * @return the classname, null if the field is a primitive.
498      * @see #getTypeQualifiedClassName(com.intellij.psi.PsiType) for the qualified version.
499      */
500     @Nullable
501     public String getTypeClassName(PsiType type) {
502         String name = getTypeQualifiedClassName(type);
503
504         // return null if it was a primitive type
505         if (name == null) {
506             return null;
507         }
508
509         int i = name.lastIndexOf('.');
510         return name.substring(i + 1, name.length());
511     }
512
513   /**
514      * Finds the public static void main(String[] args) method.
515      *
516      * @param clazz the class.
517      * @return the method if it exists, null if not.
518      */
519     @Nullable
520     public PsiMethod findPublicStaticVoidMainMethod(PsiClass clazz) {
521         PsiMethod[] methods = clazz.findMethodsByName("main", false);
522
523         // is it public static void main(String[] args)
524         for (PsiMethod method : methods) {
525             // must be public
526             if (!method.hasModifierProperty("public")) {
527                 continue;
528             }
529
530             // must be static
531             if (!method.hasModifierProperty("static")) {
532                 continue;
533             }
534
535             // must have void as return type
536             PsiType returnType = method.getReturnType();
537             if (returnType == null || returnType.equalsToText("void")) {
538                 continue;
539             }
540
541             // must have one parameter
542             PsiParameter[] parameters = method.getParameterList().getParameters();
543             if (parameters.length != 1) {
544                 continue;
545             }
546
547             // parameter must be string array
548             if (!isStringArrayType(parameters[0].getType())) {
549                 continue;
550             }
551
552             // public static void main(String[] args) method found
553             return method;
554         }
555
556         // main not found
557         return null;
558     }
559
560     /**
561      * Add or replaces the javadoc comment to the given method.
562      *
563      * @param factory          element factory.
564      * @param codeStyleManager CodeStyleManager.
565      * @param method           the method the javadoc should be added/set to.
566      * @param javadoc          the javadoc comment.
567      * @param replace          true if any existing javadoc should be replaced. false will not replace any existing javadoc and thus leave the javadoc untouched.
568      * @return the added/replace javadoc comment, null if the was an existing javadoc and it should <b>not</b> be replaced.
569      * @throws IncorrectOperationException is thrown if error adding/replacing the javadoc comment.
570      */
571     @Nullable
572     public PsiComment addOrReplaceJavadoc(PsiElementFactory factory, CodeStyleManager codeStyleManager, PsiMethod method, String javadoc, boolean replace) throws IncorrectOperationException {
573         PsiComment comment = factory.createCommentFromText(javadoc, null);
574
575         // does a method already exists?
576         PsiDocComment doc = method.getDocComment();
577         if (doc != null) {
578             if (replace) {
579                 // javadoc already exists, so replace
580                 doc.replace(comment);
581                 codeStyleManager.reformat(method); // to reformat javadoc
582                 return comment;
583             } else {
584                 // do not replace existing javadoc
585                 return null;
586             }
587         } else {
588             // add new javadoc
589             method.addBefore(comment, method.getFirstChild());
590             codeStyleManager.reformat(method); // to reformat javadoc
591             return comment;
592         }
593     }
594
595     /**
596      * Get's the methods for the class.
597      *
598      * @param clazz class.
599      * @return the methods for the class. If the class doesn't have any methods the array's size is 0.
600      */
601     public PsiMethod[] getMethods(PsiClass clazz) {
602         return clazz.getMethods();
603     }
604
605     /**
606      * Find's an existing field with the given name.
607      * If there isn't a field with the name, null is returned.
608      *
609      * @param clazz the class
610      * @param name  name of field to find
611      * @return the found field, null if none exist
612      */
613     @Nullable
614     public PsiField findFieldByName(PsiClass clazz, String name) {
615         PsiField[] fields = clazz.getFields();
616
617         // use reverse to find from botton as the duplicate conflict resolution policy requires this
618         for (int i = fields.length - 1; i >= 0; i--) {
619             PsiField field = fields[i];
620             if (name.equals(field.getName()))
621                 return field;
622         }
623
624         return null;
625     }
626
627     /**
628      * Is the given type a "void" type.
629      *
630      * @param type the type.
631      * @return true if a void type, false if not.
632      */
633     public boolean isTypeOfVoid(PsiType type) {
634         return (type != null && type.equalsToText("void"));
635     }
636
637     /**
638      * Is the method a getter method?
639      * <p/>
640      * The name of the method must start with <code>get</code> or <code>is</code>.
641      * And if the method is a <code>isXXX</code> then the method must return a java.lang.Boolean or boolean.
642      *
643      * @param factory element factory.
644      * @param method  the method
645      * @return true if a getter method, false if not.
646      */
647     public boolean isGetterMethod(PsiElementFactory factory, PsiMethod method) {
648         // must not be a void method
649         if (isTypeOfVoid(method.getReturnType())) {
650             return false;
651         }
652
653         if (method.getName().matches("^(is|has)\\p{Upper}.*")) {
654             return isBooleanType(factory, method.getReturnType());
655         } else if (method.getName().matches("^(get)\\p{Upper}.*")) {
656             return true;
657         }
658
659         return false;
660     }
661
662     /**
663      * Get's the field name of the getter method.
664      * <p/>
665      * The method must be a getter method for a field.
666      * Returns null if this method is not a getter.
667      * <p/>
668      * The fieldname is the part of the name that is after the <code>get</code> or <code>is</code> part
669      * of the name.
670      * <p/>
671      * Example: methodName=getName will return fieldname=name
672      *
673      * @param factory element factory.
674      * @param method  the method
675      * @return the fieldname if this is a getter method.
676      * @see #isGetterMethod(com.intellij.psi.PsiElementFactory,com.intellij.psi.PsiMethod) for the getter check
677      */
678     @Nullable
679     public String getGetterFieldName(PsiElementFactory factory, PsiMethod method) {
680         // must be a getter
681         if (!isGetterMethod(factory, method)) {
682             return null;
683         }
684
685         // return part after get
686         String getName = StringUtil.after(method.getName(), "get");
687         if (getName != null) {
688             getName = StringUtil.firstLetterToLowerCase(getName);
689             return getName;
690         }
691
692         // return part after is
693         String isName = StringUtil.after(method.getName(), "is");
694         if (isName != null) {
695             isName = StringUtil.firstLetterToLowerCase(isName);
696             return isName;
697         }
698
699         return null;
700     }
701
702   /**
703      * Returns true if the field is enum (JDK1.5).
704      *
705      * @param field   field to check if it's a enum
706      * @return true if enum.
707      */
708     public boolean isEnumField(PsiField field) {
709         PsiType type = field.getType();
710
711         // must not be an primitive type
712         if (isPrimitiveType(type)) {
713             return false;
714         }
715
716         GlobalSearchScope scope = type.getResolveScope();
717         if (scope == null) {
718             return false;
719         }
720
721         // find the class
722         String name = type.getCanonicalText();
723         PsiClass clazz = JavaPsiFacade.getInstance(field.getProject()).findClass(name, scope);
724         if (clazz == null) {
725             return false;
726         }
727
728         return isEnumClass(clazz);
729     }
730
731     /**
732      * Returns true if the class is enum (JDK1.5).
733      *
734      * @param clazz class to check if it's a enum
735      * @return true if enum.
736      */
737     public boolean isEnumClass(PsiClass clazz) {
738         return clazz.isEnum();
739     }
740
741     /**
742      * Returns true if the class is deprecated.
743      *
744      * @param clazz class to check if it's deprecated
745      * @return true if deprecated.
746      */
747     public boolean isDeprecatedClass(PsiClass clazz) {
748         return clazz.isDeprecated();
749     }
750
751     /**
752      * Returns true if the method is deprecated.
753      *
754      * @param method method to check if it's deprecated
755      * @return true if deprecated.
756      */
757     public boolean isDeprecatedMethod(PsiMethod method) {
758         return method.isDeprecated();
759     }
760
761     /**
762      * Is the class an exception - extends Throwable (will check super).
763      *
764      * @param clazz class to check.
765      * @return true if class is an exception.
766      */
767     public boolean isExceptionClass(PsiClass clazz) {
768         PsiClass[] supers = clazz.getSupers();
769         for (PsiClass sup : supers) {
770             if ("java.lang.Throwable".equals(sup.getQualifiedName())) {
771                 return true;
772             } else if (isExceptionClass(sup)) {
773                 return true;
774             }
775         }
776
777         return false;
778     }
779
780     /**
781      * Is the class an abstract class
782      *
783      * @param clazz class to check.
784      * @return true if class is abstract.
785      */
786     public boolean isAbstractClass(PsiClass clazz) {
787         PsiModifierList list = clazz.getModifierList();
788         if (list == null) {
789             return false;
790         }
791         return isModifierAbstract(clazz.getModifierList());
792     }
793
794   /**
795      * Finds the public boolean equals(Object o) method.
796      *
797      * @param clazz the class.
798      * @return the method if it exists, null if not.
799      */
800     @Nullable
801     public PsiMethod findEqualsMethod(PsiClass clazz) {
802         PsiMethod[] methods = clazz.findMethodsByName("equals", false);
803
804         // is it public boolean equals(Object o)
805         for (PsiMethod method : methods) {
806             // must be public
807             if (!method.hasModifierProperty("public")) {
808                 continue;
809             }
810
811             // must not be static
812           if (method.hasModifierProperty("static")) {
813             continue;
814           }
815
816             // must have boolean as return type
817             PsiType returnType = method.getReturnType();
818             if (returnType == null || !returnType.equalsToText("boolean")) {
819                 continue;
820             }
821
822             // must have one parameter
823             PsiParameter[] parameters = method.getParameterList().getParameters();
824             if (parameters.length != 1) {
825                 continue;
826             }
827
828             // parameter must be Object
829             if (!(parameters[0].getType().getCanonicalText().equals("java.lang.Object"))) {
830                 continue;
831             }
832
833             // equals method found
834             return method;
835         }
836
837         // equals not found
838         return null;
839     }
840
841     /**
842      * Finds the public int hashCode() method.
843      *
844      * @param clazz the class.
845      * @return the method if it exists, null if not.
846      */
847     @Nullable
848     public PsiMethod findHashCodeMethod(PsiClass clazz) {
849         PsiMethod[] methods = clazz.findMethodsByName("hashCode", false);
850
851         // is it public int hashCode()
852         for (PsiMethod method : methods) {
853             // must be public
854             if (!method.hasModifierProperty("public")) {
855                 continue;
856             }
857
858             // must not be static
859           if (method.hasModifierProperty("static")) {
860             continue;
861           }
862
863             // must have int as return type
864             PsiType returnType = method.getReturnType();
865             if (returnType == null || !returnType.equalsToText("int")) {
866                 continue;
867             }
868
869             // must not have a parameter
870             PsiParameter[] parameters = method.getParameterList().getParameters();
871             if (parameters.length != 0) {
872                 continue;
873             }
874
875             // hashCode method found
876             return method;
877         }
878
879         // hashCode not found
880         return null;
881     }
882
883     /**
884      * Adds/replaces the given annotation text to the method.
885      *
886      * @param factory    element factory.
887      * @param method     the method the javadoc should be added/set to.
888      * @param annotation the annotation as text.
889      * @return the added annotation object
890      * @throws IncorrectOperationException is thrown if error adding/replacing the javadoc comment.
891      */
892     public PsiAnnotation addAnnotationToMethod(PsiElementFactory factory, PsiMethod method, String annotation) throws IncorrectOperationException {
893         PsiAnnotation ann = method.getModifierList().findAnnotation(annotation);
894         if (ann == null) {
895             // add new annotation
896             ann = factory.createAnnotationFromText(annotation, method.getModifierList());
897             PsiModifierList modifierList = method.getModifierList();
898             modifierList.addBefore(ann, modifierList.getFirstChild());
899         } else {
900             PsiModifierList modifierList = method.getModifierList();
901             modifierList.replace(ann); // already exist so replace
902         }
903
904         return ann;
905     }
906
907     /**
908      * Check if the given type against a FQ classname (assignable).
909      *
910      * @param factory         IDEA factory
911      * @param type            the type
912      * @param typeFQClassName the FQ classname to test against.
913      * @return true if the given type is assigneable of FQ classname.
914      */
915     protected boolean isTypeOf(PsiElementFactory factory, PsiType type, String typeFQClassName) {
916         // fix for IDEA where fields can have 'void' type and generate NPE.
917         if (isTypeOfVoid(type)) {
918             return false;
919         }
920
921         if (isPrimitiveType(type)) {
922             return false;
923         }
924
925         GlobalSearchScope scope = type.getResolveScope();
926         if (scope == null) {
927             return false;
928         }
929         PsiType typeTarget = factory.createTypeByFQClassName(typeFQClassName, scope);
930         return typeTarget.isAssignableFrom(type);
931     }
932
933     /**
934      * Get's the superclass.
935      *
936      * @param project IDEA project
937      * @param clazz   the class
938      * @return the super, null if not found.
939      */
940     @Nullable
941     public PsiClass getSuperClass(Project project, PsiClass clazz) {
942         PsiReferenceList list = clazz.getExtendsList();
943
944         // check if no superclass at all
945         if (list == null || list.getReferencedTypes().length != 1) {
946             return null;
947         }
948
949         // have superclass get it [0] is the index of the superclass (a class can not extend more than one class)
950         GlobalSearchScope scope = list.getReferencedTypes()[0].getResolveScope();
951         String classname = list.getReferencedTypes()[0].getCanonicalText();
952
953         return JavaPsiFacade.getInstance(project).findClass(classname, scope);
954     }
955
956     /**
957      * Get's the names the given class implements (not FQ names).
958      *
959      * @param clazz the class
960      * @return the names.
961      */
962     public String[] getImplementsClassnames(PsiClass clazz) {
963         PsiClass[] interfaces = clazz.getInterfaces();
964
965         if (interfaces == null || interfaces.length == 0) {
966           return ArrayUtil.EMPTY_STRING_ARRAY;
967         }
968
969         String[] names = new String[interfaces.length];
970         for (int i = 0; i < interfaces.length; i++) {
971             PsiClass anInterface = interfaces[i];
972             names[i] = anInterface.getName();
973         }
974
975         return names;
976     }
977
978     /**
979      * Is the given type a primitive?
980      *
981      * @param type the type.
982      * @return true if primitive, false if not.
983      */
984     public boolean isPrimitiveType(PsiType type) {
985         return type instanceof PsiPrimitiveType;
986     }
987
988     /**
989      * Executes the given runable in IDEA command.
990      *
991      * @param project IDEA project
992      * @param runable the runable task to exexute.
993      */
994     public void executeCommand(Project project, Runnable runable) {
995         CommandProcessor.getInstance().executeCommand(project, runable, "GenerateToString", null);
996     }
997
998     /**
999      * Add's the interface name to the class implementation list.
1000      *
1001      * @param project       IDEA project
1002      * @param clazz         the class
1003      * @param interfaceName the interface name the class should implement
1004      * @throws IncorrectOperationException is thrown by IDEA.
1005      */
1006     public void addImplements(Project project, PsiClass clazz, String interfaceName) throws IncorrectOperationException {
1007         JavaPsiFacade facade = JavaPsiFacade.getInstance(project);
1008
1009         // get the interface class
1010         PsiClass interfaceClass = facade.findClass(interfaceName, GlobalSearchScope.allScope(project));
1011
1012         // if the interface exists add it as a reference in the implements list
1013         if (interfaceClass != null) {
1014             PsiJavaCodeReferenceElement ref = facade.getElementFactory().createClassReferenceElement(interfaceClass);
1015             PsiReferenceList list = clazz.getImplementsList();
1016             if (list != null) {
1017                 list.add(ref);
1018             }
1019         }
1020     }
1021
1022   /**
1023      * Get's the full filename to this plugin .jar file
1024      *
1025      * @return the full filename to this plugin .jar file
1026      */
1027     public abstract String getPluginFilename();
1028
1029 }