package fr.lteconsulting.hexacssmaven;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URISyntaxException;
import java.nio.file.Files;
import java.util.List;
import java.util.Scanner;
import java.util.Set;
import org.apache.maven.artifact.Artifact;
import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugins.annotations.LifecyclePhase;
import org.apache.maven.plugins.annotations.Mojo;
import org.apache.maven.plugins.annotations.Parameter;
import org.apache.maven.plugins.annotations.ResolutionScope;
import org.codehaus.classworlds.ClassRealm;
import org.codehaus.classworlds.ClassWorld;
import org.codehaus.classworlds.DuplicateRealmException;
/**
* Process a CSS file, rename classes according to the mapping file and prune
* all unused classes
*/
@Mojo( name = "process", defaultPhase = LifecyclePhase.PROCESS_SOURCES, requiresDependencyResolution=ResolutionScope.COMPILE )
public class HexaCssProcessMojo extends AbstractMojo
{
private static final String CLASSPATH_MAKER = "classpath:";
@Parameter( defaultValue = "${project.build.directory}" )
private File sourceDirectory;
@Parameter( defaultValue = "${project.build.directory}/${project.build.finalName}" )
private File outputDirectory;
@Parameter
private List<String> includes;
@Parameter( defaultValue = "${project}" )
private org.apache.maven.project.MavenProject mavenProject;
@Parameter( required = true )
private File mappingFile;
@Parameter( defaultValue = "true" )
private Boolean pruneClasses;
// Only used internally to get a reference to the current dependencies
@Parameter( defaultValue = "${project.dependencyArtifacts}", required = true, readonly = true )
private Set<Artifact> dependencyArtifacts;
private void checkFileExists( File file ) throws MojoExecutionException
{
if( file == null )
throw new MojoExecutionException( "The file is not specified !" );
if( !file.exists() )
throw new MojoExecutionException( "The file '" + file.getAbsolutePath() + "' does not exist !" );
}
@Override
public void execute() throws MojoExecutionException
{
getLog().info( "Source directory: " + maybeClasspathPath( sourceDirectory.getAbsolutePath() ) );
getLog().info( "Mapping file: " + mappingFile.getAbsolutePath() );
getLog().info( "Output directory: " + outputDirectory.getAbsolutePath() );
checkFileExists( mappingFile );
try
{
Files.createDirectories( outputDirectory.toPath() );
}
catch( IOException e1 )
{
e1.printStackTrace();
throw new MojoExecutionException( "Cannot create the directory '" + outputDirectory.getAbsolutePath() + "' !" );
}
checkFileExists( outputDirectory );
if( includes != null )
{
for( String include : includes )
{
try
{
getLog().info( "Processing file: " + include );
String sourcePath = maybeClasspathPath( sourceDirectory.getAbsolutePath() + "/" + include );
String sourceFile = loadFile( sourcePath );
if( sourceFile == null )
{
getLog().error( "Cannot load file : " + sourcePath + ". Aborting." );
throw new MojoExecutionException( "Canoot load file : " + sourcePath );
}
CssMapper.processFile( sourceFile, mappingFile.getAbsolutePath(), outputDirectory.getAbsolutePath() + "/" + include, pruneClasses, getLog() );
}
catch( IOException | URISyntaxException e )
{
getLog().error( "Error when processing the file", e );
}
}
getLog().info( "Done." );
}
else
{
getLog().info( "No file specified for processing, nothing to do..." );
}
}
private String loadFile( String sourcePath ) throws URISyntaxException, MojoExecutionException
{
final String sourceContent;
if( sourcePath.startsWith( CLASSPATH_MAKER ) )
{
sourcePath = sourcePath.replaceAll( "\\\\", "/" );
ClassWorld world = new ClassWorld();
ClassRealm realm;
try
{
realm = world.newRealm( "hexacss", null );
for( Artifact artifact : dependencyArtifacts )
{
getLog().debug( "Analysing Artifact " + artifact.getGroupId() + ":" + artifact.getId() + "-" + artifact.getClassifier() );
if( !artifact.getArtifactHandler().isAddedToClasspath() )
continue;
File artifactFile = artifact.getFile();
if(artifactFile==null)
{
getLog().error( "Artifact file is null, maybe dependency resolution has not taken place ! try just 'mvn install' on the whole project" );
}
else
{
URI uri = artifactFile.toURI();
realm.addConstituent( uri.toURL() );
getLog().debug( "Added to realm : " + uri );
}
}
}
catch( DuplicateRealmException e )
{
throw new MojoExecutionException( "Duplicate realm when reading resource " + sourcePath, e );
}
catch( MalformedURLException e )
{
throw new MojoExecutionException( "Malformed URL for resource " + sourcePath, e );
}
String resourceName = sourcePath.substring( CLASSPATH_MAKER.length() );
getLog().info( "Processing a classpath resource : " + resourceName );
try(InputStream is = realm.getResourceAsStream( resourceName );)
{
if(is==null)
throw new MojoExecutionException( "Cannot read resource stream " + resourceName );
sourceContent = new Scanner(is,"UTF-8").useDelimiter("\\A").next();
}
catch( IOException e )
{
throw new MojoExecutionException( "Cannot read resource " + resourceName, e );
}
}
else
{
File file = new File(sourcePath);
checkFileExists( file );
try
{
sourceContent = readFile( file );
}
catch( IOException e )
{
throw new MojoExecutionException( "Cannot read file " + sourcePath, e );
}
}
return sourceContent;
}
private static String readFile( File file ) throws IOException, UnsupportedEncodingException, FileNotFoundException
{
String str = "";
try( FileInputStream fis = new FileInputStream( file ) )
{
byte[] data = new byte[(int) file.length()];
fis.read( data );
str = new String( data, "UTF-8" );
}
return str;
}
private String maybeClasspathPath( String path )
{
int i = path.indexOf( CLASSPATH_MAKER );
if( i >= 0 )
path = path.substring( i );
return path;
}
}