package org.codehaus.mojo.shade;
import org.codehaus.mojo.shade.relocation.Relocator;
import org.codehaus.mojo.shade.resource.ResourceTransformer;
import org.codehaus.plexus.logging.AbstractLogEnabled;
import org.codehaus.plexus.util.IOUtil;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.commons.Remapper;
import org.objectweb.asm.commons.RemappingClassAdapter;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.jar.JarOutputStream;
import java.util.zip.ZipException;
/**
* @author Jason van Zyl
* @plexus.component
*/
public class DefaultShader
extends AbstractLogEnabled
implements Shader
{
public void shade( Set jars,
File uberJar,
List relocators,
List resourceTransformers )
throws IOException
{
Set resources = new HashSet();
MyRemapper remapper = new MyRemapper( relocators );
JarOutputStream jos = new JarOutputStream( new FileOutputStream( uberJar ) );
for ( Iterator i = jars.iterator(); i.hasNext(); )
{
File jar = (File) i.next();
JarFile jarFile = new JarFile( jar );
for ( Enumeration j = jarFile.entries(); j.hasMoreElements(); )
{
JarEntry entry = (JarEntry) j.nextElement();
String name = entry.getName();
InputStream is = jarFile.getInputStream( entry );
if ( entry.isDirectory() )
{
if (!resources.contains(name)) {
jos.putNextEntry( new JarEntry( name ) );
resources.add(name);
}
}
else
{
if ( name.endsWith( ".class" ) )
{
ClassReader cr = new ClassReader( is );
ClassWriter cw = new ClassWriter( cr, 0 );
ClassVisitor cv = new RemappingClassAdapter( cw, remapper );
cr.accept( cv, 0 );
byte[] renamedClass = cw.toByteArray();
// Need to take the .class off for remapping evaluation
String newName = remapper.map( name.substring( 0, name.indexOf( '.' ) ) );
try
{
// Now we put it back on so the class file is written out with the right extension.
jos.putNextEntry( new JarEntry( newName + ".class" ) );
IOUtil.copy( renamedClass, jos );
}
catch ( ZipException e )
{
getLogger().warn( "We have a duplicate " + newName + " in " + jar );
}
}
else
{
boolean resourceTransformed = false;
for ( Iterator k = resourceTransformers.iterator(); k.hasNext(); )
{
ResourceTransformer transformer = (ResourceTransformer) k.next();
if ( transformer.canTransformResource( name ) )
{
transformer.processResource( is );
resourceTransformed = true;
break;
}
}
if ( !resourceTransformed )
{
// Avoid duplicates that aren't accounted for by the resource transformers
if ( resources.contains( name ) )
{
continue;
}
jos.putNextEntry( new JarEntry( name ) );
IOUtil.copy( is, jos );
resources.add( name );
}
}
}
IOUtil.close( is );
}
}
for ( Iterator i = resourceTransformers.iterator(); i.hasNext(); )
{
ResourceTransformer transformer = (ResourceTransformer) i.next();
if ( transformer.hasTransformedResource() )
{
transformer.modifyOutputStream( jos );
}
}
IOUtil.close( jos );
}
class MyRemapper
extends Remapper
{
List relocators;
public MyRemapper( List relocators )
{
this.relocators = relocators;
}
public Object mapValue( Object object )
{
return object;
}
public String map( String name )
{
for ( Iterator i = relocators.iterator(); i.hasNext(); )
{
Relocator r = (Relocator) i.next();
if ( r.canRelocate( name ) )
{
return r.relocate( name );
}
}
return name;
}
}
}