001/*
002 * Sonar, open source software quality management tool.
003 * Copyright (C) 2008-2012 SonarSource
004 * mailto:contact AT sonarsource DOT com
005 *
006 * Sonar is free software; you can redistribute it and/or
007 * modify it under the terms of the GNU Lesser General Public
008 * License as published by the Free Software Foundation; either
009 * version 3 of the License, or (at your option) any later version.
010 *
011 * Sonar is distributed in the hope that it will be useful,
012 * but WITHOUT ANY WARRANTY; without even the implied warranty of
013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
014 * Lesser General Public License for more details.
015 *
016 * You should have received a copy of the GNU Lesser General Public
017 * License along with Sonar; if not, write to the Free Software
018 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02
019 */
020package org.sonar.java.bytecode.asm;
021
022import org.objectweb.asm.FieldVisitor;
023import org.objectweb.asm.MethodVisitor;
024import org.objectweb.asm.commons.EmptyVisitor;
025import org.sonar.java.bytecode.asm.AsmClassProvider.DETAIL_LEVEL;
026
027public class AsmClassVisitor extends EmptyVisitor {
028
029  private AsmClassProvider asmClassProvider;
030  private DETAIL_LEVEL level;
031  private AsmClass asmClass;
032
033  public AsmClassVisitor(AsmClassProvider asmClassProvider, AsmClass asmClass, DETAIL_LEVEL level) {
034    this.asmClassProvider = asmClassProvider;
035    this.level = level;
036    this.asmClass = asmClass;
037  }
038
039  @Override
040  public void visit(int version, int accessFlags, String internalName, String signature, String superClass, String[] interfaces) {
041    if (asmClass.getDetailLevel() == DETAIL_LEVEL.NOTHING) {
042      asmClass.setAccessFlags(accessFlags);
043      if (asmClass.isInterface()) {
044        if (interfaces.length == 1) {
045          asmClass.setSuperClass(asmClassProvider.getClass(interfaces[0], DETAIL_LEVEL.STRUCTURE));
046        }
047      } else {
048        if (superClass != null) {
049          asmClass.setSuperClass(asmClassProvider.getClass(superClass, DETAIL_LEVEL.STRUCTURE));
050        }
051        for (String interfaceName : interfaces) {
052          asmClass.addInterface(asmClassProvider.getClass(interfaceName, DETAIL_LEVEL.STRUCTURE));
053        }
054      }
055      if (signature != null) {
056        String[] internalNames = AsmSignature.extractInternalNames(signature);
057        AsmClass[] asmClasses = internalNamesToAsmClasses(internalNames, DETAIL_LEVEL.NOTHING);
058        asmClass.addUsesOfClasses(asmClasses);
059      }
060    }
061  }
062
063  @Override
064  public FieldVisitor visitField(int access, String fieldName, String description, String signature, Object value) {
065    AsmField field = asmClass.getFieldOrCreateIt(fieldName);
066    field.setAccessFlags(access);
067    String[] internalNames = AsmSignature.extractInternalNames(description, signature);
068    AsmClass[] asmClasses = internalNamesToAsmClasses(internalNames, DETAIL_LEVEL.NOTHING);
069    field.addUsesOfClasses(asmClasses);
070    return null;
071  }
072
073  @Override
074  public MethodVisitor visitMethod(int access, String methodName, String description, String signature, String[] exceptions) {
075    AsmMethod method = asmClass.getMethodOrCreateIt(methodName + description);
076    if (isInheritedMethodSignature(method.getParent(), method.getKey())) {
077      method.setInherited(true);
078    }
079    method.setSignature(signature);
080    method.setBodyLoaded(true);
081    method.setAccessFlags(access);
082    String[] internalNames = AsmSignature.extractInternalNames(description, signature);
083    AsmClass[] asmClasses = internalNamesToAsmClasses(internalNames, DETAIL_LEVEL.NOTHING);
084    method.addUsesOfClasses(asmClasses);
085    AsmClass[] asmExceptionClasses = internalNamesToAsmClasses(exceptions, DETAIL_LEVEL.NOTHING);
086    method.addUsesOfClasses(asmExceptionClasses);
087    if (level == DETAIL_LEVEL.STRUCTURE_AND_CALLS) {
088      return new AsmMethodVisitor(method, asmClassProvider);
089    }
090    return null;
091  }
092
093  @Override
094  public void visitEnd() {
095    asmClass.setDetailLevel(level);
096  }
097
098  private AsmClass[] internalNamesToAsmClasses(String[] internalNames, DETAIL_LEVEL level) {
099    if (internalNames == null) {
100      return new AsmClass[0];
101    }
102    AsmClass[] asmClasses = new AsmClass[internalNames.length];
103    for (int i = 0; i < internalNames.length; i++) {
104      asmClasses[i] = asmClassProvider.getClass(internalNames[i], level);
105    }
106    return asmClasses;
107  }
108
109  private boolean isInheritedMethodSignature(AsmClass parent, String key) {
110    if (parent.getSuperClass() != null
111        && (parent.getSuperClass().getMethod(key) != null || isInheritedMethodSignature(parent.getSuperClass(), key))) {
112      return true;
113    }
114    for (AsmClass interfaceClass : parent.getInterfaces()) {
115      if (interfaceClass.getMethod(key) != null || isInheritedMethodSignature(interfaceClass, key)) {
116        return true;
117      }
118    }
119    return false;
120  }
121}