blob: e0a92cc56aa050711249933a428d3e55c11d92ce [file] [log] [blame]
package org.apache.maven.shared.jar.classes;
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import org.apache.bcel.classfile.ClassFormatException;
import org.apache.bcel.classfile.ClassParser;
import org.apache.bcel.classfile.DescendingVisitor;
import org.apache.bcel.classfile.JavaClass;
import org.apache.bcel.classfile.LineNumberTable;
import org.apache.bcel.classfile.Method;
import org.apache.maven.shared.jar.JarAnalyzer;
import org.codehaus.plexus.logging.AbstractLogEnabled;
import java.io.IOException;
import java.util.List;
import java.util.jar.JarEntry;
/**
* Analyze the classes in a JAR file. This class is thread safe and immutable as it retains no state.
* <p/>
* Note that you must first create an instance of {@link org.apache.maven.shared.jar.JarAnalyzer} - see its Javadoc for
* a typical use.
*
* @plexus.component role="org.apache.maven.shared.jar.classes.JarClassesAnalysis" role-hint="default"
* @see #analyze(org.apache.maven.shared.jar.JarAnalyzer)
*/
public class JarClassesAnalysis
extends AbstractLogEnabled
{
private static final double JAVA_1_8_CLASS_VERSION = 52.0;
private static final double JAVA_1_7_CLASS_VERSION = 51.0;
private static final double JAVA_1_6_CLASS_VERSION = 50.0;
private static final double JAVA_1_5_CLASS_VERSION = 49.0;
private static final double JAVA_1_4_CLASS_VERSION = 48.0;
private static final double JAVA_1_3_CLASS_VERSION = 47.0;
private static final double JAVA_1_2_CLASS_VERSION = 46.0;
private static final double JAVA_1_1_CLASS_VERSION = 45.3;
/**
* Analyze a JAR and find any classes and their details. Note that if the provided JAR analyzer has previously
* analyzed the JAR, the cached results will be returned. You must obtain a new JAR analyzer to the re-read the
* contents of the file.
*
* @param jarAnalyzer the JAR to analyze. This must not yet have been closed.
* @return the details of the classes found
*/
public JarClasses analyze( JarAnalyzer jarAnalyzer )
{
JarClasses classes = jarAnalyzer.getJarData().getJarClasses();
if ( classes == null )
{
String jarfilename = jarAnalyzer.getFile().getAbsolutePath();
classes = new JarClasses();
List<JarEntry> classList = jarAnalyzer.getClassEntries();
classes.setDebugPresent( false );
double maxVersion = 0.0;
for ( JarEntry entry : classList )
{
String classname = entry.getName();
try
{
ClassParser classParser = new ClassParser( jarfilename, classname );
JavaClass javaClass = classParser.parse();
String classSignature = javaClass.getClassName();
if ( !classes.isDebugPresent() )
{
if ( hasDebugSymbols( javaClass ) )
{
classes.setDebugPresent( true );
}
}
double classVersion = javaClass.getMajor();
if ( javaClass.getMinor() > 0 )
{
classVersion = classVersion + javaClass.getMinor() / 10.0;
}
if ( classVersion > maxVersion )
{
maxVersion = classVersion;
}
Method[] methods = javaClass.getMethods();
for ( int i = 0; i < methods.length; i++ )
{
classes.addMethod( classSignature + "." + methods[i].getName() + methods[i].getSignature() );
}
String classPackageName = javaClass.getPackageName();
classes.addClassName( classSignature );
classes.addPackage( classPackageName );
ImportVisitor importVisitor = new ImportVisitor( javaClass );
DescendingVisitor descVisitor = new DescendingVisitor( javaClass, importVisitor );
javaClass.accept( descVisitor );
classes.addImports( importVisitor.getImports() );
}
catch ( ClassFormatException e )
{
getLogger().warn( "Unable to process class " + classname + " in JarAnalyzer File " + jarfilename,
e );
}
catch ( IOException e )
{
getLogger().warn( "Unable to process JarAnalyzer File " + jarfilename, e );
}
}
if ( maxVersion == JAVA_1_8_CLASS_VERSION )
{
classes.setJdkRevision( "1.8" );
}
else if ( maxVersion == JAVA_1_7_CLASS_VERSION )
{
classes.setJdkRevision( "1.7" );
}
else if ( maxVersion == JAVA_1_6_CLASS_VERSION )
{
classes.setJdkRevision( "1.6" );
}
else if ( maxVersion == JAVA_1_5_CLASS_VERSION )
{
classes.setJdkRevision( "1.5" );
}
else if ( maxVersion == JAVA_1_4_CLASS_VERSION )
{
classes.setJdkRevision( "1.4" );
}
else if ( maxVersion == JAVA_1_3_CLASS_VERSION )
{
classes.setJdkRevision( "1.3" );
}
else if ( maxVersion == JAVA_1_2_CLASS_VERSION )
{
classes.setJdkRevision( "1.2" );
}
else if ( maxVersion == JAVA_1_1_CLASS_VERSION )
{
classes.setJdkRevision( "1.1" );
}
jarAnalyzer.getJarData().setJarClasses( classes );
}
return classes;
}
private boolean hasDebugSymbols( JavaClass javaClass )
{
boolean ret = false;
Method[] methods = javaClass.getMethods();
for ( int i = 0; i < methods.length; i++ )
{
LineNumberTable linenumbers = methods[i].getLineNumberTable();
if ( linenumbers != null && linenumbers.getLength() > 0 )
{
ret = true;
break;
}
}
return ret;
}
}