001    /*
002     * Sonar, open source software quality management tool.
003     * Copyright (C) 2009 SonarSource SA
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     */
020    package org.sonar.java.bytecode;
021    
022    import java.util.ArrayList;
023    import java.util.HashSet;
024    import java.util.List;
025    import java.util.Set;
026    
027    import org.objectweb.asm.FieldVisitor;
028    import org.objectweb.asm.Label;
029    import org.objectweb.asm.MethodVisitor;
030    import org.objectweb.asm.Type;
031    import org.objectweb.asm.commons.EmptyVisitor;
032    import org.objectweb.asm.signature.SignatureReader;
033    import org.sonar.java.bytecode.asm.AsmSignatureVisitor;
034    import org.sonar.squid.api.SourceClass;
035    import org.sonar.squid.indexer.SquidIndex;
036    
037    public class AsmSquidBridge extends EmptyVisitor {
038    
039      private List<AsmVisitor> visitors = new ArrayList<AsmVisitor>();
040      private SourceClass currentClass;
041      private final SquidIndex indexer;
042    
043      public AsmSquidBridge(SquidIndex indexer, List<AsmVisitor> asmVisitors) {
044        this.indexer = indexer;
045        this.visitors = asmVisitors;
046      }
047    
048      @Override
049      public void visit(final int version, final int access, final String className, final String signature, final String superClass,
050          final String[] interfaces) {
051        currentClass = searchSquidClass(className);
052        for (AsmVisitor visitor : visitors) {
053          visitor.visitClass(new AccessFlags(access), currentClass, searchSquidClass(superClass), searchSquidClasses(interfaces),
054              searchSquidClasses(analyzeSignature(signature)));
055        }
056      }
057    
058      @Override
059      public FieldVisitor visitField(final int access, final String fieldName, final String fieldType, final String signature,
060          final Object value) {
061        for (AsmVisitor visitor : visitors) {
062          visitor.visitField(new AccessFlags(access), currentClass, fieldName, getSquidClassFromDescriptor(fieldType),
063              searchSquidClasses(analyzeSignature(signature)), analyzeFieldValue(value));
064        }
065        return this;
066      }
067    
068      @Override
069      public MethodVisitor visitMethod(final int access, final String methodName, final String methodDesc, final String signature,
070          final String[] exceptions) {
071        for (AsmVisitor visitor : visitors) {
072          visitor.visitMethod(new AccessFlags(access), currentClass, methodName, getReturnSquidClass(methodDesc),
073              getArgumentSquidClasses(methodDesc), searchSquidClasses(analyzeSignature(signature)), searchSquidClasses(exceptions));
074        }
075        return this;
076      }
077    
078      @Override
079      public void visitFieldInsn(final int opcode, final String ownerClass, final String name, final String fieldDescription) {
080        for (AsmVisitor visitor : visitors) {
081          visitor.visitOutsideFieldAccess(currentClass, searchSquidClass(ownerClass), getSquidClassFromDescriptor(fieldDescription));
082        }
083      }
084    
085      @Override
086      public void visitTypeInsn(final int opcode, final String type) {
087        for (AsmVisitor visitor : visitors) {
088          visitor.visitTypeInsn(currentClass, searchSquidClass(type));
089        }
090      }
091    
092      @Override
093      public void visitMethodInsn(final int opcode, final String ownerClass, final String methodName, final String methodDesc) {
094        for (AsmVisitor visitor : visitors) {
095          visitor.visitOutsideMethodAccess(currentClass, searchSquidClass(ownerClass), getArgumentSquidClasses(methodDesc));
096        }
097      }
098    
099      @Override
100      public void visitTryCatchBlock(final Label start, final Label end, final Label handler, final String exception) {
101        for (AsmVisitor visitor : visitors) {
102          visitor.visitTryCatchBlock(currentClass, searchSquidClass(exception));
103        }
104      }
105    
106      private Set<SourceClass> getArgumentSquidClasses(String desc) {
107        Set<SourceClass> resources = new HashSet<SourceClass>();
108        Type[] types = Type.getArgumentTypes(desc);
109        for (int i = 0; i < types.length; i++) {
110          resources.add(getSquidClassFromType(types[i]));
111        }
112        return resources;
113      }
114    
115      private SourceClass getReturnSquidClass(String desc) {
116        return getSquidClassFromType(Type.getReturnType(desc));
117      }
118    
119      private SourceClass analyzeFieldValue(Object value) {
120        if (value instanceof Type) {
121          return getSquidClassFromType((Type) value);
122        }
123        return null;
124      }
125    
126      private String[] analyzeSignature(String signature) {
127        AsmSignatureVisitor signatureVisitor = new AsmSignatureVisitor();
128        if (signature != null) {
129          new SignatureReader(signature).accept(signatureVisitor);
130        }
131        Set<String> internalNames = signatureVisitor.getInternalNames();
132        return internalNames.toArray(new String[internalNames.size()]);
133      }
134    
135      private SourceClass getSquidClassFromType(Type type) {
136        String className;
137        switch (type.getSort()) {
138          case Type.OBJECT:
139            className = type.getInternalName();
140            break;
141          case Type.ARRAY:
142            if (type.getElementType().getSort() == Type.OBJECT) {
143              className = type.getElementType().getInternalName();
144            } else {
145              return null;
146            }
147            break;
148          default:
149            return null;
150        }
151        return (SourceClass) indexer.search(className);
152      }
153    
154      private SourceClass getSquidClassFromDescriptor(String typeDescription) {
155        Type type = Type.getType(typeDescription);
156        return getSquidClassFromType(type);
157      }
158    
159      private SourceClass searchSquidClass(String internalName) {
160        if (internalName == null) {
161          return null;
162        }
163        internalName = attachAnoInnerClassToParentClass(internalName);
164        return (SourceClass) indexer.search(internalName);
165      }
166    
167      private String attachAnoInnerClassToParentClass(String internalName) {
168        if (indexer.search(internalName) == null && internalName.indexOf('$') != -1 && internalName.matches(".*\\d")) {
169          return internalName.substring(0, internalName.indexOf('$'));
170        }
171        return internalName;
172      }
173    
174      private Set<SourceClass> searchSquidClasses(String[] internalNames) {
175        Set<SourceClass> resources = new HashSet<SourceClass>();
176        if (internalNames == null) {
177          return resources;
178        }
179        for (int i = 0; i < internalNames.length; i++) {
180          resources.add(searchSquidClass(internalNames[i]));
181        }
182        return resources;
183      }
184    }