View Javadoc
1   package org.apache.maven.shared.jar.classes;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *   http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
20   */
21  
22  import org.apache.bcel.classfile.ClassFormatException;
23  import org.apache.bcel.classfile.ClassParser;
24  import org.apache.bcel.classfile.DescendingVisitor;
25  import org.apache.bcel.classfile.JavaClass;
26  import org.apache.bcel.classfile.LineNumberTable;
27  import org.apache.bcel.classfile.Method;
28  import org.apache.maven.shared.jar.JarAnalyzer;
29  import org.codehaus.plexus.logging.AbstractLogEnabled;
30  
31  import java.io.IOException;
32  import java.util.Iterator;
33  import java.util.List;
34  import java.util.jar.JarEntry;
35  
36  /**
37   * Analyze the classes in a JAR file. This class is thread safe and immutable as it retains no state.
38   * <p/>
39   * Note that you must first create an instance of {@link org.apache.maven.shared.jar.JarAnalyzer} - see its Javadoc for
40   * a typical use.
41   *
42   * @plexus.component role="org.apache.maven.shared.jar.classes.JarClassesAnalysis" role-hint="default"
43   * @see #analyze(org.apache.maven.shared.jar.JarAnalyzer)
44   */
45  public class JarClassesAnalysis
46      extends AbstractLogEnabled
47  {
48      private static final double JAVA_1_8_CLASS_VERSION = 52.0;
49  
50      private static final double JAVA_1_7_CLASS_VERSION = 51.0;
51  
52      private static final double JAVA_1_6_CLASS_VERSION = 50.0;
53  
54      private static final double JAVA_1_5_CLASS_VERSION = 49.0;
55  
56      private static final double JAVA_1_4_CLASS_VERSION = 48.0;
57  
58      private static final double JAVA_1_3_CLASS_VERSION = 47.0;
59  
60      private static final double JAVA_1_2_CLASS_VERSION = 46.0;
61  
62      private static final double JAVA_1_1_CLASS_VERSION = 45.3;
63  
64      /**
65       * Analyze a JAR and find any classes and their details. Note that if the provided JAR analyzer has previously
66       * analyzed the JAR, the cached results will be returned. You must obtain a new JAR analyzer to the re-read the
67       * contents of the file.
68       *
69       * @param jarAnalyzer the JAR to analyze. This must not yet have been closed.
70       * @return the details of the classes found
71       */
72      public JarClasses analyze( JarAnalyzer jarAnalyzer )
73      {
74          JarClasses classes = jarAnalyzer.getJarData().getJarClasses();
75          if ( classes == null )
76          {
77              String jarfilename = jarAnalyzer.getFile().getAbsolutePath();
78              classes = new JarClasses();
79  
80              List classList = jarAnalyzer.getClassEntries();
81  
82              classes.setDebugPresent( false );
83  
84              double maxVersion = 0.0;
85  
86              Iterator it = classList.iterator();
87              while ( it.hasNext() )
88              {
89                  JarEntry entry = (JarEntry) it.next();
90                  String classname = entry.getName();
91  
92                  try
93                  {
94                      ClassParser classParser = new ClassParser( jarfilename, classname );
95  
96                      JavaClass javaClass = classParser.parse();
97  
98                      String classSignature = javaClass.getClassName();
99  
100                     if ( !classes.isDebugPresent() )
101                     {
102                         if ( hasDebugSymbols( javaClass ) )
103                         {
104                             classes.setDebugPresent( true );
105                         }
106                     }
107 
108                     double classVersion = javaClass.getMajor();
109                     if ( javaClass.getMinor() > 0 )
110                     {
111                         classVersion = classVersion + javaClass.getMinor() / 10.0;
112                     }
113 
114                     if ( classVersion > maxVersion )
115                     {
116                         maxVersion = classVersion;
117                     }
118 
119                     Method[] methods = javaClass.getMethods();
120                     for ( int i = 0; i < methods.length; i++ )
121                     {
122                         classes.addMethod( classSignature + "." + methods[i].getName() + methods[i].getSignature() );
123                     }
124 
125                     String classPackageName = javaClass.getPackageName();
126 
127                     classes.addClassName( classSignature );
128                     classes.addPackage( classPackageName );
129 
130                     ImportVisitor importVisitor = new ImportVisitor( javaClass );
131                     DescendingVisitor descVisitor = new DescendingVisitor( javaClass, importVisitor );
132                     javaClass.accept( descVisitor );
133 
134                     classes.addImports( importVisitor.getImports() );
135                 }
136                 catch ( ClassFormatException e )
137                 {
138                     getLogger().warn( "Unable to process class " + classname + " in JarAnalyzer File " + jarfilename,
139                                       e );
140                 }
141                 catch ( IOException e )
142                 {
143                     getLogger().warn( "Unable to process JarAnalyzer File " + jarfilename, e );
144                 }
145             }
146 
147             if ( maxVersion == JAVA_1_8_CLASS_VERSION )
148             {
149                 classes.setJdkRevision( "1.8" );
150             }
151             else if ( maxVersion == JAVA_1_7_CLASS_VERSION )
152             {
153                 classes.setJdkRevision( "1.7" );
154             }
155             else if ( maxVersion == JAVA_1_6_CLASS_VERSION )
156             {
157                 classes.setJdkRevision( "1.6" );
158             }
159             else if ( maxVersion == JAVA_1_5_CLASS_VERSION )
160             {
161                 classes.setJdkRevision( "1.5" );
162             }
163             else if ( maxVersion == JAVA_1_4_CLASS_VERSION )
164             {
165                 classes.setJdkRevision( "1.4" );
166             }
167             else if ( maxVersion == JAVA_1_3_CLASS_VERSION )
168             {
169                 classes.setJdkRevision( "1.3" );
170             }
171             else if ( maxVersion == JAVA_1_2_CLASS_VERSION )
172             {
173                 classes.setJdkRevision( "1.2" );
174             }
175             else if ( maxVersion == JAVA_1_1_CLASS_VERSION )
176             {
177                 classes.setJdkRevision( "1.1" );
178             }
179 
180             jarAnalyzer.getJarData().setJarClasses( classes );
181         }
182         return classes;
183     }
184 
185     private boolean hasDebugSymbols( JavaClass javaClass )
186     {
187         boolean ret = false;
188         Method[] methods = javaClass.getMethods();
189         for ( int i = 0; i < methods.length; i++ )
190         {
191             LineNumberTable linenumbers = methods[i].getLineNumberTable();
192             if ( linenumbers != null && linenumbers.getLength() > 0 )
193             {
194                 ret = true;
195                 break;
196             }
197         }
198         return ret;
199     }
200 }