package fr.lteconsulting.hexacssmaven;
import java.util.Set;
import org.apache.maven.plugin.logging.Log;
import com.helger.commons.charset.CCharset;
import com.helger.css.ECSSVersion;
import com.helger.css.decl.CSSDeclaration;
import com.helger.css.decl.CSSMediaQuery;
import com.helger.css.decl.CSSMediaRule;
import com.helger.css.decl.CSSSelector;
import com.helger.css.decl.CSSSelectorSimpleMember;
import com.helger.css.decl.CSSStyleRule;
import com.helger.css.decl.CSSSupportsRule;
import com.helger.css.decl.CascadingStyleSheet;
import com.helger.css.decl.ICSSSelectorMember;
import com.helger.css.decl.ICSSTopLevelRule;
import com.helger.css.reader.CSSReader;
import com.helger.css.writer.CSSWriter;
import com.helger.css.writer.CSSWriterSettings;
/**
* Rewrites CSS files, with minifying and optionnally pruning unused css classes.
*
* @author Arnaud Tournier
* (c) LTE Consulting - 2015
* http://www.lteconsulting.fr
*
*/
public class CssRewriter
{
private final Log log;
private final Set<String> usedClassNames;
public CssRewriter( Set<String> usedClassNames, boolean doPrune, Log log )
{
if( doPrune )
this.usedClassNames = usedClassNames;
else
this.usedClassNames = null;
this.log = log;
}
public String process( String input )
{
CascadingStyleSheet css = CSSReader.readFromString( input, CCharset.CHARSET_UTF_8_OBJ, ECSSVersion.CSS30 );
CascadingStyleSheet out = new CascadingStyleSheet();
for( ICSSTopLevelRule rule : css.getAllRules() )
{
ICSSTopLevelRule outRule = filterRule( rule );
if( outRule != null )
out.addRule( outRule );
}
CSSWriterSettings settings = new CSSWriterSettings( ECSSVersion.CSS30, true );
settings.setRemoveUnnecessaryCode( true );
return new CSSWriter( settings ).getCSSAsString( out );
}
private ICSSTopLevelRule filterRule( ICSSTopLevelRule rule )
{
Class<?> c = rule.getClass();
if( c == CSSMediaRule.class )
{
return filterMediaRule( (CSSMediaRule) rule );
}
else if( c == CSSStyleRule.class )
{
return filterStyleRule( (CSSStyleRule) rule );
}
else if( c == CSSSupportsRule.class )
{
return null;
}
else
{
return rule;
}
}
private CSSMediaRule filterMediaRule( CSSMediaRule rule )
{
CSSMediaRule outRule = new CSSMediaRule();
for( CSSMediaQuery q : rule.getAllMediaQueries() )
outRule.addMediaQuery( q );
for( ICSSTopLevelRule r : rule.getAllRules() )
{
ICSSTopLevelRule out = filterRule( r );
if( out != null )
outRule.addRule( out );
}
if( outRule.getRuleCount() == 0 )
return null;
return outRule;
}
private CSSStyleRule filterStyleRule( CSSStyleRule rule )
{
CSSStyleRule outRule = new CSSStyleRule();
for( CSSSelector sel : ((CSSStyleRule) rule).getAllSelectors() )
{
boolean addSelector = true;
if( usedClassNames != null )
{
for( ICSSSelectorMember member : sel.getAllMembers() )
{
if( member instanceof CSSSelectorSimpleMember )
{
CSSSelectorSimpleMember m = (CSSSelectorSimpleMember) member;
String value = m.getValue();
if( !value.startsWith( "." ) )
continue;
if( usedClassNames == null || usedClassNames.contains( value.substring( 1 ) ) )
{
addSelector = true;
log.debug( "Found used class name '" + value + "' in selector" );
break;
}
}
}
}
if( addSelector )
outRule.addSelector( sel );
}
if( outRule.getSelectorCount() == 0 )
return null;
for( CSSDeclaration declaration : rule.getAllDeclarations() )
outRule.addDeclaration( declaration );
return outRule;
}
}