2 * Copyright 2003-2006 Bas Leijdekkers
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
.siyeh
.ig
.fixes
;
18 import com
.intellij
.openapi
.project
.Project
;
19 import com
.intellij
.psi
.*;
20 import com
.intellij
.psi
.search
.GlobalSearchScope
;
21 import com
.intellij
.psi
.tree
.IElementType
;
22 import com
.intellij
.psi
.util
.PsiFormatUtil
;
23 import com
.intellij
.psi
.util
.PsiUtil
;
24 import com
.siyeh
.ig
.psiutils
.ClassUtils
;
25 import org
.jetbrains
.annotations
.NonNls
;
26 import org
.jetbrains
.annotations
.NotNull
;
28 import java
.io
.ByteArrayOutputStream
;
29 import java
.io
.DataOutputStream
;
30 import java
.io
.IOException
;
31 import java
.lang
.reflect
.Modifier
;
32 import java
.security
.MessageDigest
;
33 import java
.security
.NoSuchAlgorithmException
;
36 public class SerialVersionUIDBuilder
extends JavaRecursiveElementVisitor
{
38 @NonNls private static final String ACCESS_METHOD_NAME_PREFIX
= "access$";
40 private static final String SERIALIZABLE_CLASS_NAME
= "java.io.Serializable";
42 private PsiClass clazz
;
43 private int index
= -1;
44 private Set
<MemberSignature
> nonPrivateConstructors
;
45 private Set
<MemberSignature
> nonPrivateMethods
;
46 private Set
<MemberSignature
> nonPrivateFields
;
47 private List
<MemberSignature
> staticInitializers
;
48 private boolean assertStatement
= false;
49 private boolean classObjectAccessExpression
= false;
50 private Map
<PsiElement
, String
> memberMap
=
51 new HashMap
<PsiElement
, String
>();
53 private static final Comparator
<PsiClass
> INTERFACE_COMPARATOR
=
54 new Comparator
<PsiClass
>(){
55 public int compare(PsiClass object1
, PsiClass object2
){
56 if(object1
== null && object2
== null)
67 final String name1
= object1
.getQualifiedName();
68 final String name2
= object2
.getQualifiedName();
69 if(name1
== null && name2
== null){
78 return name1
.compareTo(name2
);
81 @NonNls private static final String CLASS_ACCESS_METHOD_PREFIX
= "class$";
83 private SerialVersionUIDBuilder(PsiClass clazz
){
86 nonPrivateMethods
= new HashSet
<MemberSignature
>();
87 final PsiMethod
[] methods
= clazz
.getMethods();
88 for(final PsiMethod method
: methods
){
89 if(!method
.isConstructor() &&
90 !method
.hasModifierProperty(PsiModifier
.PRIVATE
)){
91 final MemberSignature methodSignature
=
92 new MemberSignature(method
);
93 nonPrivateMethods
.add(methodSignature
);
96 nonPrivateFields
= new HashSet
<MemberSignature
>();
97 final PsiField
[] fields
= clazz
.getFields();
98 for(final PsiField field
: fields
){
99 if(!field
.hasModifierProperty(PsiModifier
.PRIVATE
) ||
100 !(field
.hasModifierProperty(PsiModifier
.STATIC
) ||
101 field
.hasModifierProperty(PsiModifier
.TRANSIENT
))){
102 final MemberSignature fieldSignature
=
103 new MemberSignature(field
);
104 nonPrivateFields
.add(fieldSignature
);
108 staticInitializers
= new ArrayList
<MemberSignature
>();
109 final PsiClassInitializer
[] initializers
= clazz
.getInitializers();
110 if(initializers
.length
> 0){
111 for(final PsiClassInitializer initializer
: initializers
){
112 final PsiModifierList modifierList
=
113 initializer
.getModifierList();
114 if(modifierList
!= null &&
115 modifierList
.hasModifierProperty(PsiModifier
.STATIC
)){
116 final MemberSignature initializerSignature
=
117 MemberSignature
.getStaticInitializerMemberSignature();
118 staticInitializers
.add(initializerSignature
);
123 if(staticInitializers
.isEmpty()){
124 final PsiField
[] psiFields
= clazz
.getFields();
125 for(final PsiField field
: psiFields
){
126 if(hasStaticInitializer(field
)){
127 final MemberSignature initializerSignature
=
128 MemberSignature
.getStaticInitializerMemberSignature();
129 staticInitializers
.add(initializerSignature
);
135 nonPrivateConstructors
= new HashSet
<MemberSignature
>();
136 final PsiMethod
[] constructors
= clazz
.getConstructors();
137 if(constructors
.length
== 0 && !clazz
.isInterface()){
138 // generated empty constructor if no constructor is defined in the source
139 final MemberSignature constructorSignature
;
140 if(clazz
.hasModifierProperty(PsiModifier
.PUBLIC
)){
141 constructorSignature
= MemberSignature
.getPublicConstructor();
143 constructorSignature
= MemberSignature
.getPackagePrivateConstructor();
145 nonPrivateConstructors
.add(constructorSignature
);
147 for(final PsiMethod constructor
: constructors
){
148 if(!constructor
.hasModifierProperty(PsiModifier
.PRIVATE
)){
149 final MemberSignature constructorSignature
=
150 new MemberSignature(constructor
);
151 nonPrivateConstructors
.add(constructorSignature
);
156 public static long computeDefaultSUID(PsiClass psiClass
) {
157 final Project project
= psiClass
.getProject();
158 final GlobalSearchScope scope
= GlobalSearchScope
.allScope(project
);
159 final PsiClass serializable
= JavaPsiFacade
.getInstance(project
).findClass(SERIALIZABLE_CLASS_NAME
, scope
);
160 if (serializable
== null) {
161 // no jdk defined for project.
165 final boolean isSerializable
= psiClass
.isInheritor(serializable
, true);
166 if (!isSerializable
) {
170 final SerialVersionUIDBuilder serialVersionUIDBuilder
= new SerialVersionUIDBuilder(psiClass
);
172 final ByteArrayOutputStream byteArrayOutputStream
= new ByteArrayOutputStream();
173 final DataOutputStream dataOutputStream
= new DataOutputStream(byteArrayOutputStream
);
175 final String className
= PsiFormatUtil
.getExternalName(psiClass
);
176 dataOutputStream
.writeUTF(className
);
178 final PsiModifierList classModifierList
= psiClass
.getModifierList();
179 int classModifiers
= classModifierList
!= null ? MemberSignature
.calculateModifierBitmap(classModifierList
) : 0;
180 final MemberSignature
[] methodSignatures
= serialVersionUIDBuilder
.getNonPrivateMethodSignatures();
181 if (psiClass
.isInterface()) {
182 classModifiers
|= Modifier
.INTERFACE
;
183 if (methodSignatures
.length
== 0) {
184 // interfaces were not marked abstract when they did't have methods in java 1.0
185 // For serialization compatibility the abstract modifier is ignored.
186 classModifiers
&= ~Modifier
.ABSTRACT
;
189 dataOutputStream
.writeInt(classModifiers
);
191 final PsiClass
[] interfaces
= psiClass
.getInterfaces();
192 Arrays
.sort(interfaces
, INTERFACE_COMPARATOR
);
193 for (PsiClass aInterfaces
: interfaces
) {
194 final String name
= aInterfaces
.getQualifiedName();
195 dataOutputStream
.writeUTF(name
);
198 final MemberSignature
[] fields
= serialVersionUIDBuilder
.getNonPrivateFields();
200 for (final MemberSignature field
: fields
) {
201 dataOutputStream
.writeUTF(field
.getName());
202 dataOutputStream
.writeInt(field
.getModifiers());
203 dataOutputStream
.writeUTF(field
.getSignature());
206 final MemberSignature
[] staticInitializers
= serialVersionUIDBuilder
.getStaticInitializers();
207 for (final MemberSignature staticInitializer
: staticInitializers
) {
208 dataOutputStream
.writeUTF(staticInitializer
.getName());
209 dataOutputStream
.writeInt(staticInitializer
.getModifiers());
210 dataOutputStream
.writeUTF(staticInitializer
.getSignature());
213 final MemberSignature
[] constructors
= serialVersionUIDBuilder
.getNonPrivateConstructors();
214 Arrays
.sort(constructors
);
215 for (final MemberSignature constructor
: constructors
) {
216 dataOutputStream
.writeUTF(constructor
.getName());
217 dataOutputStream
.writeInt(constructor
.getModifiers());
218 dataOutputStream
.writeUTF(constructor
.getSignature());
221 Arrays
.sort(methodSignatures
);
222 for (final MemberSignature methodSignature
: methodSignatures
) {
223 dataOutputStream
.writeUTF(methodSignature
.getName());
224 dataOutputStream
.writeInt(methodSignature
.getModifiers());
225 dataOutputStream
.writeUTF(methodSignature
.getSignature());
228 dataOutputStream
.flush();
229 @NonNls final String algorithm
= "SHA";
230 final MessageDigest digest
= MessageDigest
.getInstance(algorithm
);
231 final byte[] digestBytes
= digest
.digest(byteArrayOutputStream
.toByteArray());
232 long serialVersionUID
= 0L;
233 for (int i
= Math
.min(digestBytes
.length
, 8) - 1; i
>= 0; i
--) {
234 serialVersionUID
= serialVersionUID
<< 8 | digestBytes
[i
] & 0xFF;
236 return serialVersionUID
;
238 catch(IOException exception
){
239 final InternalError internalError
= new InternalError(exception
.getMessage());
240 internalError
.initCause(exception
);
243 catch(NoSuchAlgorithmException exception
){
244 final SecurityException securityException
= new SecurityException(exception
.getMessage());
245 securityException
.initCause(exception
);
246 throw securityException
;
250 private void createClassObjectAccessSynthetics(PsiType type
){
251 if(!classObjectAccessExpression
){
252 final MemberSignature syntheticMethod
=
253 MemberSignature
.getClassAccessMethodMemberSignature();
254 nonPrivateMethods
.add(syntheticMethod
);
256 PsiType unwrappedType
= type
;
257 @NonNls final StringBuffer fieldNameBuffer
;
258 if(type
instanceof PsiArrayType
){
259 fieldNameBuffer
= new StringBuffer();
260 fieldNameBuffer
.append("array");
261 while(unwrappedType
instanceof PsiArrayType
){
262 final PsiArrayType arrayType
= (PsiArrayType
) unwrappedType
;
263 unwrappedType
= arrayType
.getComponentType();
264 fieldNameBuffer
.append('$');
267 fieldNameBuffer
= new StringBuffer(CLASS_ACCESS_METHOD_PREFIX
);
269 if(unwrappedType
instanceof PsiPrimitiveType
){
270 final PsiPrimitiveType primitiveType
= (PsiPrimitiveType
) unwrappedType
;
271 fieldNameBuffer
.append(MemberSignature
.createPrimitiveType(primitiveType
));
273 final String text
= unwrappedType
.getCanonicalText().replace('.',
275 fieldNameBuffer
.append(text
);
277 final String fieldName
= fieldNameBuffer
.toString();
278 final MemberSignature memberSignature
=
279 new MemberSignature(fieldName
, Modifier
.STATIC
,
280 "Ljava/lang/Class;");
281 if(!nonPrivateFields
.contains(memberSignature
)){
282 nonPrivateFields
.add(memberSignature
);
284 classObjectAccessExpression
= true;
287 private String
getAccessMethodIndex(PsiElement element
){
288 String cache
= memberMap
.get(element
);
290 cache
= String
.valueOf(index
);
292 memberMap
.put(element
, cache
);
297 public MemberSignature
[] getNonPrivateConstructors(){
299 return nonPrivateConstructors
.toArray(new MemberSignature
[nonPrivateConstructors
.size()]);
302 public MemberSignature
[] getNonPrivateFields(){
304 return nonPrivateFields
.toArray(new MemberSignature
[nonPrivateFields
.size()]);
307 public MemberSignature
[] getNonPrivateMethodSignatures(){
309 return nonPrivateMethods
.toArray(new MemberSignature
[nonPrivateMethods
.size()]);
312 public MemberSignature
[] getStaticInitializers(){
314 return staticInitializers
.toArray(new MemberSignature
[staticInitializers
.size()]);
317 private static boolean hasStaticInitializer(PsiField field
){
318 if(field
.hasModifierProperty(PsiModifier
.STATIC
)){
319 final PsiManager manager
= field
.getManager();
320 final Project project
= manager
.getProject();
321 final GlobalSearchScope scope
= GlobalSearchScope
.allScope(project
);
322 final PsiExpression initializer
= field
.getInitializer();
323 if(initializer
== null){
326 final PsiType fieldType
= field
.getType();
327 final PsiType stringType
= PsiType
.getJavaLangString(manager
,
329 if(field
.hasModifierProperty(PsiModifier
.FINAL
) &&
330 (fieldType
instanceof PsiPrimitiveType
||
331 fieldType
.equals(stringType
))) {
332 return !PsiUtil
.isConstantExpression(initializer
);
343 clazz
.acceptChildren(this);
347 @Override public void visitAssertStatement(PsiAssertStatement statement
){
348 super.visitAssertStatement(statement
);
352 final MemberSignature memberSignature
=
353 MemberSignature
.getAssertionsDisabledFieldMemberSignature();
354 nonPrivateFields
.add(memberSignature
);
355 final PsiManager manager
= clazz
.getManager();
356 final PsiElementFactory factory
= JavaPsiFacade
.getInstance(manager
.getProject()).getElementFactory();
357 final PsiClassType classType
= factory
.createType(clazz
);
358 createClassObjectAccessSynthetics(classType
);
359 if(staticInitializers
.isEmpty()){
360 final MemberSignature initializerSignature
=
361 MemberSignature
.getStaticInitializerMemberSignature();
362 staticInitializers
.add(initializerSignature
);
364 assertStatement
= true;
367 @Override public void visitClassObjectAccessExpression(
368 PsiClassObjectAccessExpression expression
){
369 final PsiTypeElement operand
= expression
.getOperand();
370 final PsiType type
= operand
.getType();
371 if(!(type
instanceof PsiPrimitiveType
)){
372 createClassObjectAccessSynthetics(type
);
374 super.visitClassObjectAccessExpression(expression
);
377 @Override public void visitMethodCallExpression(
378 @NotNull PsiMethodCallExpression methodCallExpression
){
379 // for navigating the psi tree in the order javac navigates its AST
380 final PsiExpressionList argumentList
=
381 methodCallExpression
.getArgumentList();
382 final PsiExpression
[] expressions
= argumentList
.getExpressions();
383 for(final PsiExpression expression
: expressions
){
384 expression
.accept(this);
386 final PsiReferenceExpression methodExpression
=
387 methodCallExpression
.getMethodExpression();
388 methodExpression
.accept(this);
391 @Override public void visitReferenceElement(PsiJavaCodeReferenceElement reference
){
392 super.visitReferenceElement(reference
);
393 final PsiElement parentClass
= ClassUtils
.getContainingClass(reference
);
394 if(reference
.getParent() instanceof PsiTypeElement
){
397 final PsiElement element
= reference
.resolve();
398 if(!(element
instanceof PsiClass
)){
401 final PsiClass elementParentClass
=
402 ClassUtils
.getContainingClass(element
);
403 if(elementParentClass
== null ||
404 !elementParentClass
.equals(clazz
) ||
405 element
.equals(parentClass
)){
408 final PsiClass innerClass
= (PsiClass
) element
;
409 if(!innerClass
.hasModifierProperty(PsiModifier
.PRIVATE
)){
412 final PsiMethod
[] constructors
= innerClass
.getConstructors();
413 if(constructors
.length
== 0){
414 getAccessMethodIndex(innerClass
);
418 @Override public void visitReferenceExpression(
419 @NotNull PsiReferenceExpression expression
){
420 super.visitReferenceExpression(expression
);
421 final PsiElement element
= expression
.resolve();
422 final PsiElement elementParentClass
=
423 ClassUtils
.getContainingClass(element
);
424 final PsiElement expressionParentClass
=
425 ClassUtils
.getContainingClass(expression
);
426 if(expressionParentClass
== null || expressionParentClass
427 .equals(elementParentClass
)){
430 PsiElement parentOfParentClass
=
431 ClassUtils
.getContainingClass(expressionParentClass
);
432 while(parentOfParentClass
!= null &&
433 !parentOfParentClass
.equals(clazz
)){
434 if(!(expressionParentClass
instanceof PsiAnonymousClass
)){
435 getAccessMethodIndex(expressionParentClass
);
437 getAccessMethodIndex(parentOfParentClass
);
438 parentOfParentClass
= ClassUtils
.getContainingClass(parentOfParentClass
);
440 if(element
instanceof PsiField
){
441 final PsiField field
= (PsiField
) element
;
442 if(field
.hasModifierProperty(PsiModifier
.PRIVATE
)){
443 boolean isStatic
= false;
444 final PsiType type
= field
.getType();
445 if(field
.hasModifierProperty(PsiModifier
.STATIC
)){
446 if(field
.hasModifierProperty(PsiModifier
.FINAL
) &&
447 type
instanceof PsiPrimitiveType
){
448 final PsiExpression initializer
= field
.getInitializer();
449 if(PsiUtil
.isConstantExpression(initializer
)){
455 final String returnTypeSignature
=
456 MemberSignature
.createTypeSignature(type
).replace('/',
458 final String className
= clazz
.getQualifiedName();
459 @NonNls final StringBuilder signatureBuffer
=
460 new StringBuilder("(");
462 signatureBuffer
.append('L').append(className
).append(';');
464 final String accessMethodIndex
= getAccessMethodIndex(field
);
465 if(!field
.getContainingClass().equals(clazz
)){
468 @NonNls String name
= null;
469 final PsiElement parent
= expression
.getParent();
470 if(parent
instanceof PsiAssignmentExpression
){
471 final PsiAssignmentExpression assignment
= (PsiAssignmentExpression
) parent
;
472 if(assignment
.getLExpression().equals(expression
)){
473 name
= ACCESS_METHOD_NAME_PREFIX
+ accessMethodIndex
+
475 signatureBuffer
.append(returnTypeSignature
);
477 } else if(parent
instanceof PsiPostfixExpression
){
478 final PsiPostfixExpression postfixExpression
=
479 (PsiPostfixExpression
) parent
;
480 final PsiJavaToken operationSign
= postfixExpression
.getOperationSign();
481 final IElementType tokenType
= operationSign
.getTokenType();
482 if(tokenType
.equals(JavaTokenType
.PLUSPLUS
)){
483 name
= ACCESS_METHOD_NAME_PREFIX
+ accessMethodIndex
+
485 } else if(tokenType
.equals(JavaTokenType
.MINUSMINUS
)){
486 name
= ACCESS_METHOD_NAME_PREFIX
+ accessMethodIndex
+
489 } else if(parent
instanceof PsiPrefixExpression
){
490 final PsiPrefixExpression prefixExpression
= (PsiPrefixExpression
) parent
;
491 final PsiJavaToken operationSign
= prefixExpression
.getOperationSign();
492 final IElementType tokenType
= operationSign
.getTokenType();
493 if(tokenType
.equals(JavaTokenType
.PLUSPLUS
)){
494 name
= ACCESS_METHOD_NAME_PREFIX
+ accessMethodIndex
+
496 } else if(tokenType
.equals(JavaTokenType
.MINUSMINUS
)){
497 name
= ACCESS_METHOD_NAME_PREFIX
+ accessMethodIndex
+
502 name
= ACCESS_METHOD_NAME_PREFIX
+ accessMethodIndex
+ "00";
504 signatureBuffer
.append(')').append(returnTypeSignature
);
505 final String signature
= signatureBuffer
.toString();
506 final MemberSignature methodSignature
=
507 new MemberSignature(name
, Modifier
.STATIC
, signature
);
508 nonPrivateMethods
.add(methodSignature
);
510 } else if(element
instanceof PsiMethod
){
511 final PsiMethod method
= (PsiMethod
) element
;
512 if(method
.hasModifierProperty(PsiModifier
.PRIVATE
) &&
513 method
.getContainingClass().equals(clazz
)){
514 final String signature
;
515 if(method
.hasModifierProperty(PsiModifier
.STATIC
)){
517 MemberSignature
.createMethodSignature(method
)
520 final String returnTypeSignature
=
521 MemberSignature
.createTypeSignature(method
.getReturnType())
523 @NonNls final StringBuilder signatureBuffer
=
525 signatureBuffer
.append("(L");
526 signatureBuffer
.append(clazz
.getQualifiedName())
528 final PsiParameter
[] parameters
= method
.getParameterList()
530 for(final PsiParameter parameter
: parameters
){
531 final PsiType type
= parameter
.getType();
532 final String typeSignature
= MemberSignature
.createTypeSignature(type
)
534 signatureBuffer
.append(typeSignature
);
536 signatureBuffer
.append(')');
537 signatureBuffer
.append(returnTypeSignature
);
538 signature
= signatureBuffer
.toString();
540 final String accessMethodIndex
= getAccessMethodIndex(method
);
541 final MemberSignature methodSignature
=
542 new MemberSignature(ACCESS_METHOD_NAME_PREFIX
+
543 accessMethodIndex
+ "00",
544 Modifier
.STATIC
, signature
);
545 nonPrivateMethods
.add(methodSignature
);