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