/*
* [AnnotateColours.java]
*
* Summary: Annotates hex colour references in Style sheets with colour names.
*
* Copyright: (c) 2009-2017 Roedy Green, Canadian Mind Products, http://mindprod.com
*
* Licence: This software may be copied and used freely for any purpose but military.
* http://mindprod.com/contact/nonmil.html
*
* Requires: JDK 1.8+
*
* Created with: JetBrains IntelliJ IDEA IDE http://www.jetbrains.com/idea/
*
* Version History:
* 1.0 2009-04-13 initial release
*/
package com.mindprod.repair;
import com.mindprod.commandline.CommandLine;
import com.mindprod.common18.EIO;
import com.mindprod.common18.Misc;
import com.mindprod.csv.CSVReader;
import com.mindprod.fastcat.FastCat;
import com.mindprod.filter.AllButSVNDirectoriesFilter;
import com.mindprod.filter.ExtensionListFilter;
import com.mindprod.htmlmacros.macro.Global;
import com.mindprod.htmlmacros.support.ConfigurationForMindprod;
import com.mindprod.hunkio.HunkIO;
import java.io.BufferedReader;
import java.io.EOFException;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.TreeMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import static java.lang.System.*;
/**
* Annotates hex colour references in Style sheets with colour names.
*
* Used after ExtractColours and assigning names to unnamed colours.
*
* @author Roedy Green, Canadian Mind Products
* @version 1.0 2009-04-13 initial release
* @see ExtractColours
* @since 2009-04-13
*/
public class AnnotateColours
{
private static final int FIRST_COPYRIGHT_YEAR = 2009;
/**
* undisplayed copyright notice
*/
@SuppressWarnings( { "UnusedDeclaration" } )
private static final String EMBEDDED_COPYRIGHT =
"Copyright: (c) 2009-2017 Roedy Green, Canadian Mind Products, http://mindprod.com";
/**
* how to use the command line
*/
private static final String USAGE = "\nAnnotateColours needs a space-separated list of filename.csv with optional " +
"-s -q -v switches, Run annotatecolours.btm not annotatecolours.exe.";
/**
* regex to find annotation comments in a style sheet
*/
private static final Pattern COLOUR_COMMENT_PATTERN = Pattern.compile( "/\\*![\\p{Alnum}, \\+\\-]+!\\*/" );
/**
* regex to look for hex colour numbers in a style sheet
*/
private static final Pattern HEX_COLOUR_NUMBER_PATTERN = Pattern.compile( "(;|#\\p{XDigit}{6})" );
/**
* track down colours and their descriptions
*/
private static final TreeMap knownColours = new TreeMap<>();
/**
* name of file to read csv colour/description pairs
*/
private static File csvInput;
/**
* extract unknown colours from the style sheets on the command line
*
* @param args command line list of style sheets
*
* @throws java.io.IOException if trouble reading style sheets
*/
private static void annotateColoursWithComments( final String[] args )
throws IOException
{
CommandLine commandLine = new CommandLine( args,
new AllButSVNDirectoriesFilter(),
new ExtensionListFilter( "css" ) );
if ( commandLine.size() == 0 )
{
throw new IllegalArgumentException( "No files found to process" + USAGE );
}
for ( File file : commandLine )
{
final String contents = HunkIO.readEntireFile( file, HunkIO.UTF8 );
// must be StringBuffer to be compatible with Matcher.
StringBuffer sb = new StringBuffer( contents.length() * 130 / 100 );
final Matcher m = HEX_COLOUR_NUMBER_PATTERN.matcher( contents );
ArrayList descs = new ArrayList<>( 4 );
while ( m.find() )
{
// we found either a hex colour number or a semicolon, # already stripped
final String token = m.group( 1 );
final boolean isSemicolon = token.equals( ";" );
if ( isSemicolon )
{
// insert comment just after semicolon if there were colours to annotate on the line.
if ( descs.size() > 0 )
{
FastCat sb2 = new FastCat( descs.size() * 2 + 2 );
sb2.append( "; /*! " );
for ( String desc : descs )
{
sb2.append( desc );
sb2.append( ", " );
}
sb2.drop(); // remove trailing comma
sb2.append( " !*/" );
final String commentToInsert = sb2.toString();
m.appendReplacement( sb, commentToInsert ); // no need for Matcher.quoteReplacement
descs.clear();
}
// if no pending descs, nothing to do
}
else
{
// this must have been a colour number in the middle of a line.
// just note this colour for later annotation
assert token.startsWith( "#" ) : "malformed hex";
final String anotherColour = token.substring( 1 );
final String desc = knownColours.get( anotherColour );
if ( desc == null )
{
err.println( "Warning: unknown colour encountered "
+ anotherColour
+ " in "
+ EIO.getCanOrAbsPath( file ) );
}
else
{
descs.add( desc );
}
}
} // end loop processing fragments of text
if ( descs.size() != 0 )
{
err.println( "missing semicolon near end of file " + EIO.getCanOrAbsPath( file ) );
descs.clear();
}
m.appendTail( sb );
HunkIO.writeEntireFile( file, sb.toString(), HunkIO.UTF8 );
}
}
/**
* read existing list of known colours
*
* @throws java.io.IOException if trouble reading CSV list of known colours
*/
private static void readKnownColours()
throws IOException
{
// O P E N
final FileReader fr = new FileReader( csvInput );
final BufferedReader br = new BufferedReader( fr,
24 * 1024 /* 24K chars (48K bytes), 75% of allocation is optimal */ );
// reader, separatorChar, quoteChar, commentChars, hideComments, trimQuoted, trimUnquoted,
// allowMultipleLineFields
final CSVReader r = new CSVReader( br,
',',
'\"',
"#",
true,
true /* trimQuoted */,
true /* trimUnquoted */,
false );
final HashMap dupDescDetector = new HashMap<>( 1000 );
try
{
//noinspection InfiniteLoopStatement
while ( true )
{
final String colour = r.get();
if ( colour == null )
{
// bypass blank line.
continue;
}
final String desc = r.get();
// lookup by colour
final String prevDesc = knownColours.put( colour, desc );
if ( prevDesc != null )
{
err.println( "Error: Duplicate entry for colour " + colour + " : " + desc + " : " + prevDesc );
}
// lookup by desc
final String prevColour = dupDescDetector.put( desc, colour );
if ( prevColour != null )
{
err.println( "Warning: Duplicate entry for description " + desc + " : " + colour + " : " +
prevColour );
}
r.skipToNextLine();
}
}
catch ( EOFException e )
{
r.close();
}
}
/**
* remove existing annotation
*
* @param args command line list of style sheets
*
* @throws java.io.IOException if trouble reading style sheets
*/
private static void removeColourAnnotationComments( final String[] args )
throws IOException
{
// remove old comment of form: |*! firebrick !*| or |*! Deeks blue, pansy blue !*|
CommandLine files = new CommandLine( args, new AllButSVNDirectoriesFilter(), new ExtensionListFilter( "css" ) );
for ( File file : files )
{
final String contents = HunkIO.readEntireFile( file, HunkIO.UTF8 );
// must be StringBuffer to be compatible with Matcher, not StringBuilder or FastCat.
StringBuffer sb = new StringBuffer( contents.length() );
final Matcher m = COLOUR_COMMENT_PATTERN.matcher( contents );
while ( m.find() )
{
// remove the annotation comment just found
m.appendReplacement( sb, "" ); // no need for Matcher.quoteReplacement
}
m.appendTail( sb );
HunkIO.writeEntireFile( file, sb.toString(), HunkIO.UTF8 );
}
}
/**
* Extracts lines in files that contain a given string.
* List of all colour names must exist in E:\mindprod\repair\knowncolours.csv.
* Will get a list of missing colour names.
*
* @param args style sheets to scan, e.g. E:\mindprod\mindprod.css
*/
public static void main( String[] args )
{
Global.installConfiguration( new ConfigurationForMindprod() );
csvInput = new File( Global.configuration.getSourceDirWithSlashes(), "repair/knowncolours.csv" );
try
{
// read existing list of known colours
readKnownColours();
// remove existing annotations from all style sheets.
removeColourAnnotationComments( args );
// extract unknown colours from the style sheets on the command line
annotateColoursWithComments( args );
out.println( "Done" );
}
catch ( Exception e )
{
e.printStackTrace( err );
err.println();
}
Misc.trackLastThread();
System.exit( 0 );
} // end main
}