blob: 01de394817f2eb7c5710e1410f6e66861c42b1c0 [file] [log] [blame]
package org.apache.maven.shared.jar;
/*
* 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 java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.jar.Manifest;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.zip.ZipException;
/**
* Open a JAR file to be analyzed. Note that once created, the {@link #closeQuietly()} method should be called to
* release the associated file handle.
* <p/>
* Typical usage:
* <pre>
* JarAnalyzer jar = new JarAnalyzer( jarFile );
* <p/>
* try
* {
* // do some analysis, such as:
* jarClasses = jarClassAnalyzer.analyze( jar );
* }
* finally
* {
* jar.closeQuietly();
* }
* <p/>
* // use jar.getJarData() in some way, or the data returned by the JAR analyzer. jar itself can no longer be used.
* </pre>
* <p/>
* Note: that the actual data is separated from this class by design to minimise the chance of forgetting to close the
* JAR file. The {@link org.apache.maven.shared.jar.JarData} class exposed, as well as any data returned by actual
* analyzers that use this class, can be used safely once this class is out of scope.
*
* @see org.apache.maven.shared.jar.identification.JarIdentificationAnalysis#analyze(JarAnalyzer)
* @see org.apache.maven.shared.jar.classes.JarClassesAnalysis#analyze(JarAnalyzer)
*/
public class JarAnalyzer
{
/**
* Pattern to filter JAR entries for class files.
*
* @todo why are inner classes and other potentially valid classes omitted? (It flukes it by finding everything after $)
*/
private static final Pattern CLASS_FILTER = Pattern.compile( "[A-Za-z0-9]*\\.class$" );
/**
* Pattern to filter JAR entries for Maven POM files.
*/
private static final Pattern MAVEN_POM_FILTER = Pattern.compile( "META-INF/maven/.*/pom\\.xml$" );
/**
* Pattern to filter JAR entries for text files that may contain a version.
*/
private static final Pattern VERSION_FILTER = Pattern.compile( "[Vv][Ee][Rr][Ss][Ii][Oo][Nn]" );
/**
* The associated JAR file.
*/
private final JarFile jarFile;
/**
* Contains information about the data collected so far.
*/
private final JarData jarData;
/**
* Constructor. Opens the JAR file, so should be matched by a call to {@link #closeQuietly()}.
*
* @param file the JAR file to open
* @throws java.io.IOException if there is a problem opening the JAR file, or reading the manifest. The JAR file
* will be closed if this occurs.
*/
public JarAnalyzer( File file )
throws IOException
{
try
{
this.jarFile = new JarFile( file );
}
catch ( ZipException e )
{
ZipException ioe = new ZipException( "Failed to open file " + file + " : " + e.getMessage() );
ioe.initCause( e );
throw ioe;
}
// Obtain entries list.
List<JarEntry> entries = Collections.list( jarFile.entries() );
// Sorting of list is done by name to ensure a bytecode hash is always consistent.
Collections.sort( entries, new Comparator<JarEntry>()
{
public int compare( JarEntry entry1, JarEntry entry2 )
{
return entry1.getName().compareTo( entry2.getName() );
}
} );
Manifest manifest;
try
{
manifest = jarFile.getManifest();
}
catch ( IOException e )
{
closeQuietly();
throw e;
}
this.jarData = new JarData( file, manifest, entries );
}
/**
* Get the data for an individual entry in the JAR. The caller should closeQuietly the input stream, and should not
* retain the stream as the JAR file may be closed elsewhere.
*
* @param entry the JAR entry to read from
* @return the input stream of the individual JAR entry.
* @throws java.io.IOException if there is a problem opening the individual entry
*/
public InputStream getEntryInputStream( JarEntry entry )
throws IOException
{
return jarFile.getInputStream( entry );
}
/**
* Close the associated JAR file, ignoring any errors that may occur.
*/
public void closeQuietly()
{
try
{
jarFile.close();
}
catch ( IOException e )
{
// not much we can do about it but ignore it
}
}
/**
* Filter a list of JAR entries against the pattern.
*
* @param pattern the pattern to filter against
* @return the list of files found, in {@link java.util.jar.JarEntry} elements
*/
public List<JarEntry> filterEntries( Pattern pattern )
{
List<JarEntry> ret = new ArrayList<JarEntry>();
for ( JarEntry entry : getEntries() )
{
Matcher mat = pattern.matcher( entry.getName() );
if ( mat.find() )
{
ret.add( entry );
}
}
return ret;
}
/**
* Get all the classes in the JAR.
*
* @return the list of files found, in {@link java.util.jar.JarEntry} elements
*/
public List<JarEntry> getClassEntries()
{
return filterEntries( CLASS_FILTER );
}
/**
* Get all the Maven POM entries in the JAR.
*
* @return the list of files found, in {@link java.util.jar.JarEntry} elements
*/
public List<JarEntry> getMavenPomEntries()
{
return filterEntries( MAVEN_POM_FILTER );
}
/**
* Get all the version text files in the JAR.
*
* @return the list of files found, in {@link java.util.jar.JarEntry} elements
*/
public List<JarEntry> getVersionEntries()
{
return filterEntries( VERSION_FILTER );
}
/**
* Get all the contained files in the JAR.
*
* @return the list of files found, in {@link java.util.jar.JarEntry} elements
*/
public List<JarEntry> getEntries()
{
return jarData.getEntries();
}
/**
* Get the file that was opened by this analyzer.
*
* @return the JAR file reference
*/
public File getFile()
{
return jarData.getFile();
}
public JarData getJarData()
{
return jarData;
}
}