View Javadoc
1   package com.atlassian.plugin.osgi.util;
2   
3   import aQute.bnd.osgi.AbstractResource;
4   import aQute.bnd.osgi.ClassDataCollector;
5   import aQute.bnd.osgi.Clazz;
6   import aQute.bnd.osgi.Descriptors;
7   import com.google.common.base.Function;
8   import com.google.common.collect.Collections2;
9   import com.google.common.collect.ImmutableSet;
10  import com.google.common.collect.Sets;
11  import org.apache.commons.io.IOUtils;
12  
13  import javax.annotation.Nullable;
14  import java.io.IOException;
15  import java.io.InputStream;
16  import java.util.Set;
17  
18  /**
19   * Contains all the utility methods and classes for scanning class file binary.
20   *
21   * @since 2.10
22   */
23  public class ClassBinaryScanner {
24      /**
25       * Scans class binary to extract 1) referred classes 2) imported packages 3) the class's superclass.
26       *
27       * @param clazz the input class binary
28       * @return result, never null
29       * @throws java.io.IOException if the scan dies along the way
30       * @since 2.10
31       */
32      public static ScanResult scanClassBinary(Clazz clazz) throws IOException {
33          final ImmutableSet.Builder<String> allReferredClasses = new ImmutableSet.Builder<String>();
34          final String[] superClassName = new String[]{null};
35          try {
36              // TODO: Perhaps, we should start scanning for annotations as well. This ClassDataCollector in bndlib 1.4.3 is quite powerful.
37              clazz.parseClassFileWithCollector(new ClassDataCollector() {
38  
39                  @Override
40                  public void extendsClass(Descriptors.TypeRef ref) {
41                      superClassName[0] = ref.getBinary();
42                      allReferredClasses.add(ref.getBinary());
43                  }
44  
45                  @Override
46                  public void implementsInterfaces(Descriptors.TypeRef[] refs) {
47                      for (Descriptors.TypeRef ref : refs) {
48                          allReferredClasses.add(ref.getBinary());
49                      }
50                  }
51  
52                  @Override
53                  public void addReference(Descriptors.TypeRef ref) {
54                      allReferredClasses.add(ref.getBinary());
55                  }
56              });
57          } catch (Exception e) {
58              throw new IOException("Error parsing class file", e);
59          }
60  
61          Set<String> referredPackages = Sets.newHashSet(Collections2.transform(clazz.getReferred(),
62                  new Function<Descriptors.PackageRef, String>() {
63                      @Override
64                      public String apply(@Nullable Descriptors.PackageRef packageRef) {
65                          if (packageRef != null) {
66                              return packageRef.getFQN();
67                          } else {
68                              return null;
69                          }
70                      }
71                  }));
72          return new ScanResult(allReferredClasses.build(), referredPackages, superClassName[0]);
73      }
74  
75      /**
76       * Contains the result of class binary scanning.
77       *
78       * @since 2.10
79       */
80      public static class ScanResult {
81          // this classes are referred to as strings since we don't want to load them all at this stage.
82          private Set<String> referredClasses;
83          private Set<String> referredPackages;
84          private String superClass;
85  
86          public ScanResult(Set<String> referredClasses, Set<String> referredPackages, String superClass) {
87              this.referredClasses = referredClasses;
88              this.referredPackages = referredPackages;
89              this.superClass = superClass;
90          }
91  
92          public Set<String> getReferredClasses() {
93              return referredClasses;
94          }
95  
96          public Set<String> getReferredPackages() {
97              return referredPackages;
98          }
99  
100         public String getSuperClass() {
101             return superClass;
102         }
103     }
104 
105     /**
106      * InputStream-based resource for class scanning purpose (in the format required by bndlib).
107      *
108      * @since 2.10
109      */
110     public static class InputStreamResource extends AbstractResource {
111         private InputStream inputStream;
112 
113         public InputStreamResource(InputStream inputStream) {
114             super(-1);
115             this.inputStream = inputStream;
116         }
117 
118         @Override
119         protected byte[] getBytes() throws Exception {
120             return IOUtils.toByteArray(inputStream);
121         }
122 
123         public void close() {
124             IOUtils.closeQuietly(inputStream);
125         }
126 
127     }
128 }