View Javadoc

1   package com.atlassian.plugin.osgi.util;
2   
3   import aQute.lib.osgi.ClassDataCollector;
4   import aQute.lib.osgi.Clazz;
5   import aQute.lib.osgi.Resource;
6   import com.google.common.collect.ImmutableSet;
7   import org.apache.commons.io.IOUtils;
8   import org.apache.commons.lang.StringUtils;
9   
10  import java.io.IOException;
11  import java.io.InputStream;
12  import java.io.OutputStream;
13  import java.util.Set;
14  
15  /**
16   * Contains all the utility methods and classes for scanning class file binary.
17   *
18   * @since 2.10
19   */
20  public class ClassBinaryScanner
21  {
22      /**
23       * Scans class binary to extract 1) referred classes 2) imported packages 3) the class's superclass.
24       *
25       * @param clazz the input class binary
26       * @return result, never null
27       * @throws java.io.IOException if the scan dies along the way
28       *
29       * @since 2.10
30       */
31      public static ScanResult scanClassBinary(Clazz clazz) throws IOException
32      {
33          final ImmutableSet.Builder<String> allReferredClasses = new ImmutableSet.Builder<String>();
34          final String[] superClassName = new String[] { null };
35          try
36          {
37              // TODO: Perhaps, we should start scanning for annotations as well. This ClassDataCollector in bndlib 1.4.3 is quite powerful.
38              clazz.parseClassFileWithCollector(new ClassDataCollector() {
39  
40                  @Override
41                  public void extendsClass(String name)
42                  {
43                      superClassName[0] = name;
44                      allReferredClasses.add(name);
45                  }
46  
47                  @Override
48                  public void implementsInterfaces(String[] names)
49                  {
50                      for(String name:names)
51                      {
52                          allReferredClasses.add(name);
53                      }
54                  }
55  
56                  @Override
57                  public void addReference(String name)
58                  {
59                      // the class name is in the form "abc.def.ghi" instead of "abc/def/ghi" which is different from other methods for unknown reason.
60                      allReferredClasses.add(StringUtils.replace(name, ".", "/"));
61                  }
62              });
63          }
64          catch (Exception e)
65          {
66              throw new IOException("Error parsing class file", e);
67          }
68  
69          return new ScanResult(allReferredClasses.build(), ImmutableSet.copyOf(clazz.getReferred()), superClassName[0]);
70      }
71  
72      /**
73       * Contains the result of class binary scanning.
74       *
75       * @since 2.10
76       */
77      public static class ScanResult
78      {
79          // this classes are referred to as strings since we don't want to load them all at this stage.
80          private Set<String> referredClasses;
81          private Set<String> referredPackages;
82          private String superClass;
83  
84          public ScanResult(Set<String> referredClasses,  Set<String> referredPackages, String superClass)
85          {
86              this.referredClasses = referredClasses;
87              this.referredPackages = referredPackages;
88              this.superClass = superClass;
89          }
90  
91          public Set<String> getReferredClasses()
92          {
93              return referredClasses;
94          }
95  
96          public Set<String> getReferredPackages()
97          {
98              return referredPackages;
99          }
100 
101         public String getSuperClass()
102         {
103             return superClass;
104         }
105     }
106 
107     /**
108      * InputStream-based resource for class scanning purpose (in the format required by bndlib).
109      *
110      * @since 2.10
111      */
112     public static class InputStreamResource implements Resource
113     {
114         private String extra;
115         private InputStream inputStream;
116 
117         public InputStreamResource(InputStream inputStream)
118         {
119             this.inputStream = inputStream;
120         }
121 
122         public InputStream openInputStream() throws Exception
123         {
124             return inputStream;
125         }
126 
127         public void write(OutputStream out) throws Exception
128         {
129             throw new UnsupportedOperationException("Not for write");
130         }
131 
132         public long lastModified()
133         {
134             return -1;
135         }
136 
137         public void setExtra(String extra)
138         {
139             this.extra = extra;
140         }
141 
142         public String getExtra()
143         {
144             return extra;
145         }
146 
147         public void close()
148         {
149             IOUtils.closeQuietly(inputStream);
150         }
151     }
152 }