IDEADEV-31824 (Incorrect "manual array copy" warning)
[fedora-idea.git] / plugins / InspectionGadgets / src / com / siyeh / ig / fixes / SerialVersionUIDBuilder.java
blobb6ef1dd9cc1cf6a6dbf6d3edc3bed708e578d532
1 /*
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;
34 import java.util.*;
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)
58 return 0;
59 }if(object1 == null)
61 return 1;
63 if(object2 == null)
65 return -1;
67 final String name1 = object1.getQualifiedName();
68 final String name2 = object2.getQualifiedName();
69 if(name1 == null && name2 == null){
70 return 0;
72 if(name1 == null){
73 return 1;
75 if(name2 == null){
76 return -1;
78 return name1.compareTo(name2);
81 @NonNls private static final String CLASS_ACCESS_METHOD_PREFIX = "class$";
83 private SerialVersionUIDBuilder(PsiClass clazz){
84 super();
85 this.clazz = 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);
119 break;
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);
130 break;
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();
142 } else{
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.
162 return -1L;
165 final boolean isSerializable = psiClass.isInheritor(serializable, true);
166 if (!isSerializable) {
167 return 0L;
170 final SerialVersionUIDBuilder serialVersionUIDBuilder = new SerialVersionUIDBuilder(psiClass);
171 try {
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();
199 Arrays.sort(fields);
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);
241 throw internalError;
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('$');
266 } else{
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));
272 } else{
273 final String text = unwrappedType.getCanonicalText().replace('.',
274 '$');
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);
289 if(cache == null){
290 cache = String.valueOf(index);
291 index++;
292 memberMap.put(element, cache);
294 return cache;
297 public MemberSignature[] getNonPrivateConstructors(){
298 init();
299 return nonPrivateConstructors.toArray(new MemberSignature[nonPrivateConstructors.size()]);
302 public MemberSignature[] getNonPrivateFields(){
303 init();
304 return nonPrivateFields.toArray(new MemberSignature[nonPrivateFields.size()]);
307 public MemberSignature[] getNonPrivateMethodSignatures(){
308 init();
309 return nonPrivateMethods.toArray(new MemberSignature[nonPrivateMethods.size()]);
312 public MemberSignature[] getStaticInitializers(){
313 init();
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){
324 return false;
326 final PsiType fieldType = field.getType();
327 final PsiType stringType = PsiType.getJavaLangString(manager,
328 scope);
329 if(field.hasModifierProperty(PsiModifier.FINAL) &&
330 (fieldType instanceof PsiPrimitiveType ||
331 fieldType.equals(stringType))) {
332 return !PsiUtil.isConstantExpression(initializer);
333 } else{
334 return true;
337 return false;
340 private void init(){
341 if(index < 0){
342 index = 0;
343 clazz.acceptChildren(this);
347 @Override public void visitAssertStatement(PsiAssertStatement statement){
348 super.visitAssertStatement(statement);
349 if(assertStatement){
350 return;
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){
395 return;
397 final PsiElement element = reference.resolve();
398 if(!(element instanceof PsiClass)){
399 return;
401 final PsiClass elementParentClass =
402 ClassUtils.getContainingClass(element);
403 if(elementParentClass == null ||
404 !elementParentClass.equals(clazz) ||
405 element.equals(parentClass)){
406 return;
408 final PsiClass innerClass = (PsiClass) element;
409 if(!innerClass.hasModifierProperty(PsiModifier.PRIVATE)){
410 return;
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)){
428 return;
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)){
450 return;
453 isStatic = true;
455 final String returnTypeSignature =
456 MemberSignature.createTypeSignature(type).replace('/',
457 '.');
458 final String className = clazz.getQualifiedName();
459 @NonNls final StringBuilder signatureBuffer =
460 new StringBuilder("(");
461 if(!isStatic){
462 signatureBuffer.append('L').append(className).append(';');
464 final String accessMethodIndex = getAccessMethodIndex(field);
465 if(!field.getContainingClass().equals(clazz)){
466 return;
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 +
474 "02";
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 +
484 "08";
485 } else if(tokenType.equals(JavaTokenType.MINUSMINUS)){
486 name = ACCESS_METHOD_NAME_PREFIX + accessMethodIndex +
487 "10";
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 +
495 "04";
496 } else if(tokenType.equals(JavaTokenType.MINUSMINUS)){
497 name = ACCESS_METHOD_NAME_PREFIX + accessMethodIndex +
498 "06";
501 if(name == null){
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)){
516 signature =
517 MemberSignature.createMethodSignature(method)
518 .replace('/', '.');
519 } else{
520 final String returnTypeSignature =
521 MemberSignature.createTypeSignature(method.getReturnType())
522 .replace('/', '.');
523 @NonNls final StringBuilder signatureBuffer =
524 new StringBuilder();
525 signatureBuffer.append("(L");
526 signatureBuffer.append(clazz.getQualifiedName())
527 .append(';');
528 final PsiParameter[] parameters = method.getParameterList()
529 .getParameters();
530 for(final PsiParameter parameter : parameters){
531 final PsiType type = parameter.getType();
532 final String typeSignature = MemberSignature.createTypeSignature(type)
533 .replace('/', '.');
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);