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_6_CLASS_VERSION = 50.0;
49  
50      private static final double JAVA_1_5_CLASS_VERSION = 49.0;
51  
52      private static final double JAVA_1_4_CLASS_VERSION = 47.0;
53  
54      private static final double JAVA_1_3_CLASS_VERSION = 46.0;
55  
56      private static final double JAVA_1_2_CLASS_VERSION = 45.65536;
57  
58      private static final double JAVA_1_1_CLASS_VERSION = 45.3;
59  
60      /**
61       * Analyze a JAR and find any classes and their details. Note that if the provided JAR analyzer has previously
62       * analyzed the JAR, the cached results will be returned. You must obtain a new JAR analyzer to the re-read the
63       * contents of the file.
64       *
65       * @param jarAnalyzer the JAR to analyze. This must not yet have been closed.
66       * @return the details of the classes found
67       */
68      public JarClasses analyze( JarAnalyzer jarAnalyzer )
69      {
70          JarClasses classes = jarAnalyzer.getJarData().getJarClasses();
71          if ( classes == null )
72          {
73              String jarfilename = jarAnalyzer.getFile().getAbsolutePath();
74              classes = new JarClasses();
75  
76              List classList = jarAnalyzer.getClassEntries();
77  
78              classes.setDebugPresent( false );
79  
80              double maxVersion = 0.0;
81  
82              Iterator it = classList.iterator();
83              while ( it.hasNext() )
84              {
85                  JarEntry entry = (JarEntry) it.next();
86                  String classname = entry.getName();
87  
88                  try
89                  {
90                      ClassParser classParser = new ClassParser( jarfilename, classname );
91  
92                      JavaClass javaClass = classParser.parse();
93  
94                      String classSignature = javaClass.getClassName();
95  
96                      if ( !classes.isDebugPresent() )
97                      {
98                          if ( hasDebugSymbols( javaClass ) )
99                          {
100                             classes.setDebugPresent( true );
101                         }
102                     }
103 
104                     double classVersion = javaClass.getMajor();
105                     if ( javaClass.getMinor() > 0 )
106                     {
107                         classVersion = classVersion + 1 / (double) javaClass.getMinor();
108                     }
109 
110                     if ( classVersion > maxVersion )
111                     {
112                         maxVersion = classVersion;
113                     }
114 
115                     Method[] methods = javaClass.getMethods();
116                     for ( int i = 0; i < methods.length; i++ )
117                     {
118                         classes.addMethod( classSignature + "." + methods[i].getName() + methods[i].getSignature() );
119                     }
120 
121                     String classPackageName = javaClass.getPackageName();
122 
123                     classes.addClassName( classSignature );
124                     classes.addPackage( classPackageName );
125 
126                     ImportVisitor importVisitor = new ImportVisitor( javaClass );
127                     DescendingVisitor descVisitor = new DescendingVisitor( javaClass, importVisitor );
128                     javaClass.accept( descVisitor );
129 
130                     classes.addImports( importVisitor.getImports() );
131                 }
132                 catch ( ClassFormatException e )
133                 {
134                     getLogger().warn( "Unable to process class " + classname + " in JarAnalyzer File " + jarfilename,
135                                       e );
136                 }
137                 catch ( IOException e )
138                 {
139                     getLogger().warn( "Unable to process JarAnalyzer File " + jarfilename, e );
140                 }
141             }
142 
143             // TODO: check these since they are > instead of >=
144             if ( maxVersion >= JAVA_1_6_CLASS_VERSION )
145             {
146                 classes.setJdkRevision( "1.6" );
147             }
148             else if ( maxVersion >= JAVA_1_5_CLASS_VERSION )
149             {
150                 classes.setJdkRevision( "1.5" );
151             }
152             else if ( maxVersion > JAVA_1_4_CLASS_VERSION )
153             {
154                 classes.setJdkRevision( "1.4" );
155             }
156             else if ( maxVersion > JAVA_1_3_CLASS_VERSION )
157             {
158                 classes.setJdkRevision( "1.3" );
159             }
160             else if ( maxVersion > JAVA_1_2_CLASS_VERSION )
161             {
162                 classes.setJdkRevision( "1.2" );
163             }
164             else if ( maxVersion > JAVA_1_1_CLASS_VERSION )
165             {
166                 classes.setJdkRevision( "1.1" );
167             }
168             else if ( maxVersion > 0 )
169             {
170                 classes.setJdkRevision( "1.0" );
171             }
172 
173             jarAnalyzer.getJarData().setJarClasses( classes );
174         }
175         return classes;
176     }
177 
178     private boolean hasDebugSymbols( JavaClass javaClass )
179     {
180         boolean ret = false;
181         Method[] methods = javaClass.getMethods();
182         for ( int i = 0; i < methods.length; i++ )
183         {
184             LineNumberTable linenumbers = methods[i].getLineNumberTable();
185             if ( linenumbers != null && linenumbers.getLength() > 0 )
186             {
187                 ret = true;
188                 break;
189             }
190         }
191         return ret;
192     }
193 }