/*
 * @(#)Asciitab.java
 *
 * Summary: prepares ASCII table in HTML.
 *
 * Copyright: (c) 2001-2009 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.5+
 *
 * Created with: IntelliJ IDEA IDE.
 *
 * Version History:
 *  1.1 2009-03-22 - add style markup, character entities
 */
package com.mindprod.palette;

import com.mindprod.entities.InsertEntities;

import java.io.FileWriter;
import java.io.IOException;

/**
 * prepares ASCII table in HTML.
 *
 * @author Roedy Green, Canadian Mind Products
 * @version 1.1 2009-03-22 - add style markup, character entities
 * @since 2001
 */
public final class Asciitab
    {
    // ------------------------------ CONSTANTS ------------------------------

    /**
     * Non displayed copyright message
     */
    @SuppressWarnings( { "UnusedDeclaration" } )
    private static final String EMBEDDED_COPYRIGHT =
            "copyright (c) 2001-2009 Roedy Green, Canadian Mind Products, http://mindprod.com";

    /**
     * boilerplace  used to prepare descriptions
     */
    private static final String GLUE1 = " <span class=\"acronym\">";

    /**
     * boilerplace  used to prepare descriptions
     */
    private static final String GLUE2 = "</span>, <span class=\"means\">";

    // -------------------------- STATIC METHODS --------------------------

    /**
     * how you display this character in HTML
     *
     * @param c the character you want to render
     *
     * @return html for rendering the character
     */
    private static String asHTML( char c )
        {
        if ( c < 32 )
            {
            /* control char */
            return "&#x" + toLZ( c + 0x2400, 4, 16 ) + ";";
            }
        else if ( c > 126 )
            {
            /* high ascii */
            // decimal form: "&#" + Integer.toString( c ) + ";";
            return InsertEntities.insertHTMLEntities( String.valueOf( c ) );
            }
        else
            {
            switch ( c )
                {
                case 32:
                    return "&nbsp;";
                case 38:
                    return "&amp;";
                case 34:
                    return "&quot;";
                case 60:
                    return "&lt;";
                case 62:
                    return "&gt;";
                /* ordinary char */
                default:
                    return String.valueOf( c );
                }
            }
        }

    /**
     * how you display this character in HTML, quoted so you can see the code
     *
     * @param c the character you want to render
     *
     * @return html for rendering the character
     */
    private static String asQuotedHTML( char c )
        {
        String result = asHTML( c );
        if ( result.charAt( 0 ) == '&' )
            {
            return "&amp;" + result.substring( 1 );
            }
        else
            {
            return result;
            }
        }

    /**
     * Get description of a character
     *
     * @param c character to describe
     *
     * @return string destribing character, or &nbsp; if none available.
     */
    @SuppressWarnings( { "OctalInteger" } )
    private static String descs( char c )
        {
        if ( 'A' <= c && c <= 'Z' )
            {
            return "upper case " + String.valueOf( c );
            }
        else if ( 'a' <= c && c <= 'z' )
            {
            return "lower case " + String.valueOf( c );
            }
        else if ( '0' <= c && c <= '9' )
                {
                return "digit " + String.valueOf( c );
                }
            else
                {
                switch ( c )
                    {
                    case 0:
                        return "^@" +
                               GLUE1 +
                               "NUL" +
                               GLUE2 +
                               "<span class=\"ac\">nul</span></span>";
                    case 1:
                        return "^A" +
                               GLUE1 +
                               "SOH" +
                               GLUE2 +
                               "<span class=\"ac\">s</span>tart <span class=\"ac\">o</span>f <span class=\"ac\">h</span>eader</span>";
                    case 2:
                        return "^B" +
                               GLUE1 +
                               "STX" +
                               GLUE2 +
                               "<span class=\"ac\">s</span>tart of <span class=\"ac\">t</span>e<span class=\"ac\">x</span>t</span>";
                    case 3:
                        return "^C" +
                               GLUE1 +
                               "ETX" +
                               GLUE2 +
                               "<span class=\"ac\">e</span>nd of <span class=\"ac\">t</span>e<span class=\"ac\">x</span>t</span>";
                    case 4:
                        return "^D" +
                               GLUE1 +
                               "EOT" +
                               GLUE2 +
                               "<span class=\"ac\">e</span>nd <span class=\"ac\">o</span>f <span class=\"ac\">t</span>ransmission</span>";
                    case 5:
                        return "^E" +
                               GLUE1 +
                               "ENQ" +
                               GLUE2 +
                               "<span class=\"ac\">e</span><span class=\"ac\">nq</span>uiry</span>";
                    case 6:
                        return "^F" +
                               GLUE1 +
                               "ACK" +
                               GLUE2 +
                               "<span class=\"ac\">ack</span>nowledege</span>";
                    case 7:
                        return "^G" +
                               GLUE1 +
                               "BEL" +
                               GLUE2 +
                               "<span class=\"ac\">bel</span>l</span>";
                    case 8:
                        return "^H" +
                               GLUE1 +
                               "BS" +
                               GLUE2 +
                               "<span class=\"ac\">b</span>ack <span class=\"ac\">s</span>pace</span> [\\b]";
                    case 9:
                        return "^I" +
                               GLUE1 +
                               "HT" +
                               GLUE2 +
                               "<span class=\"ac\">h</span>orizonal <span class=\"ac\">t</span>ab</span> [\\t]";
                    case 10:
                        return "^J" +
                               GLUE1 +
                               "LF" +
                               GLUE2 +
                               "<span class=\"ac\">l</span>ine <span class=\"ac\">f</span>eed</span> [\\n]";
                    case 11:
                        return "^K" +
                               GLUE1 +
                               "VT" +
                               GLUE2 +
                               "<span class=\"ac\">v</span>ertical <span class=\"ac\">t</span>ab</span>";
                    case 12:
                        return "^L" +
                               GLUE1 +
                               "FF" +
                               GLUE2 +
                               "<span class=\"ac\">f</span>orm <span class=\"ac\">f</span>eed</span> [\\f]";
                    case 13:
                        return "^M" +
                               GLUE1 +
                               "CR" +
                               GLUE2 +
                               "<span class=\"ac\">c</span>arriage <span class=\"ac\">r</span>eturn</span> [\\r]";
                    case 14:
                        return "^N" +
                               GLUE1 +
                               "SO" +
                               GLUE2 +
                               "<span class=\"ac\">s</span>hift <span class=\"ac\">o</span>ut</span>";
                    case 15:
                        return "^O" +
                               GLUE1 +
                               "SI" +
                               GLUE2 +
                               "<span class=\"ac\">s</span>hift <span class=\"ac\">i</span>n</span>";
                    case 16:
                        return "^P" +
                               GLUE1 +
                               "DLE" +
                               GLUE2 +
                               "<span class=\"ac\">d</span>ata <span class=\"ac\">l</span>ink <span class=\"ac\">e</span>scape</span>";
                    case 17:
                        return "^Q" +
                               GLUE1 +
                               "DC1" +
                               GLUE2 +
                               "<span class=\"ac\">d</span>evice <span class=\"ac\">c</span>ontrol <span class=\"ac\">1</span></span>,"
                               + " XON resume transmission";
                    case 18:
                        return "^R" +
                               GLUE1 +
                               "DC2" +
                               GLUE2 +
                               "<span class=\"ac\">d</span>evice <span class=\"ac\">c</span>ontrol <span class=\"ac\">2</span></span>";
                    case 19:
                        return "^S" +
                               GLUE1 +
                               "DC3" +
                               GLUE2 +
                               "<span class=\"ac\">d</span>evice <span class=\"ac\">c</span>ontrol <span class=\"ac\">3</span></span>,"
                               + " XOFF pause transmission";
                    case 20:
                        return "^T" +
                               GLUE1 +
                               "DC4" +
                               GLUE2 +
                               "<span class=\"ac\">d</span>evice <span class=\"ac\">c</span>ontrol <span class=\"ac\">4</span></span>";
                    case 21:
                        return "^U" +
                               GLUE1 +
                               "NAK" +
                               GLUE2 +
                               "<span class=\"ac\">n</span>egative <span class=\"ac\">a</span>c<span class=\"ac\">k</span>nowledge</span>";
                    case 22:
                        return "^V" +
                               GLUE1 +
                               "SYN" +
                               GLUE2 +
                               "<span class=\"ac\">syn</span>chronise</span>";
                    case 23:
                        return "^W" +
                               GLUE1 +
                               "ETB" +
                               GLUE2 +
                               "<span class=\"ac\">e</span>nd <span class=\"ac\">t</span>ext <span class=\"ac\">b</span>lock</span>";
                    case 24:
                        return "^X" +
                               GLUE1 +
                               "CAN" +
                               GLUE2 +
                               "<span class=\"ac\">can</span>cel</span>";
                    case 25:
                        return "^Y" +
                               GLUE1 +
                               "EM" +
                               GLUE2 +
                               "<span class=\"ac\">e</span>nd <span class=\"ac\">m</span>essage</span>";
                    case 26:
                        return "^Z" +
                               GLUE1 +
                               "SUB" +
                               GLUE2 +
                               "<span class=\"ac\">sub</span>stitute</span>";
                    case 27:
                        return "^[" +
                               GLUE1 +
                               "ESC" +
                               GLUE2 +
                               "<span class=\"ac\">esc</span>ape</span>";
                    case 28:
                        return "^\\" +
                               GLUE1 +
                               "FS" +
                               GLUE2 +
                               "<span class=\"ac\">f</span>ile <span class=\"ac\">s</span>eparator</span>,"
                               + " usually used to groups of records.";
                    case 29:
                        return "^]" +
                               GLUE1 +
                               "GS" +
                               GLUE2 +
                               "<span class=\"ac\">g</span>roup <span class=\"ac\">s</span>eparator</span>,"
                               + " usually used to separate fields.";
                    case 30:
                        return "^^" +
                               GLUE1 +
                               "RS" +
                               GLUE2 +
                               "<span class=\"ac\">r</span>ecord <span class=\"ac\">s</span>eparator</span>,"
                               + " usually used to separate records.";
                    case 31:
                        return "^_" +
                               GLUE1 +
                               "US" +
                               GLUE2 +
                               "<span class=\"ac\">u</span>nit <span class=\"ac\">s</span>eparator</span>,"
                               + " usually used to separate subfields.";

                    case 32:
                        return "space";
                    case 33:
                        return "bang, exclamation";
                    case 34:
                        return "quote";
                    case 35:
                        return "sharp, number sign";
                    case 36:
                        return "dollar sign";
                    case 37:
                        return "percent";
                    case 38:
                        return "ampersand";
                    case 39:
                        return "apostrophe";
                    case 40:
                        return "left parenthesis";
                    case 41:
                        return "right parenthesis";
                    case 42:
                        return "star, asterisk";
                    case 43:
                        return "plus";
                    case 44:
                        return "comma";
                    case 45:
                        return "minus";
                    case 46:
                        return "period";
                    case 47:
                        return "slash, ,<b>not backslash!</b>";
                    case 58:
                        return "colon";
                    case 59:
                        return "semicolon";
                    case 60:
                        return "less than";
                    case 61:
                        return "equals";
                    case 62:
                        return "greater than";
                    case 63:
                        return "question mark";
                    case 64:
                        return "at sign";
                    case 91:
                        return "left square bracket";
                    case 92:
                        return "backslash, <b>not slash!</b>";
                    case 93:
                        return "right square bracket";
                    case 94:
                        return "hat, circumflex";
                    case 95:
                        return "underscore";
                    case 96:
                        return "grave, rhymes with have";
                    case 123:
                        return "left curly brace";
                    case 124:
                        return "vertical bar";
                    case 125:
                        return "right curly brace";
                    case 126:
                        return "tilde";
                    case 127:
                        return "<span class=\"acronym\">DEL</span>, <span class=\"means\"><span class=\"ac\">del</span>ete</span>";

                    // the following are OCTAL because that'show PostScript specifies them
                    // &xxx; codes are for Latin-1
                    case 0241:
                        return "PostScript " + "(&iexcl;) exclamdown";
                    case 0242:
                        return "PostScript " + "(&cent;) cent";
                    case 0243:
                        return "PostScript " + "(&pound;) sterling";
                    case 0244:
                        return "PostScript " + "(/) fraction";
                    case 0245:
                        return "PostScript " + "(&yen;) yen";
                    case 0246:
                        return "PostScript " + "(&#131;) florin";
                    case 0247:
                        return "PostScript " + "(&sect;) section";
                    case 0250:
                        return "PostScript " + "(&curren;) currency";
                    case 0251:
                        return "PostScript " + "(') quotesingle";
                    case 0252:
                        return "PostScript " + "(&#147;) quotedblleft";
                    case 0253:
                        return "PostScript " + "(&laquo;) guillemotleft";
                    case 0254:
                        return "PostScript " + "(&lt;) guilsinglleft";
                    case 0255:
                        return "PostScript " + "(&gt;) guilsinglright";
                    case 0256:
                        return "PostScript " + "fi ligature";
                    case 0257:
                        return "PostScript " + "fl ligature;";
                    case 0261:
                        return "PostScript " + "(&#150;) endash";
                    case 0262:
                        return "PostScript " + "(&#134;) dagger";
                    case 0263:
                        return "PostScript " + "(&middot;) periodcentered";
                    case 0266:
                        return "PostScript " + "(&para;) paragraph";
                    case 0267:
                        return "PostScript " + "(&#149;) bullet";
                    case 0270:
                        return "PostScript " + "(,) quotesinglbase";
                    case 0271:
                        return "PostScript " + "(&#132;) quotedblbase";
                    case 0272:
                        return "PostScript " + "(&#148;) quotedblright";
                    case 0273:
                        return "PostScript " + "(&raquo;) guillemotright";
                    case 0274:
                        return "PostScript " + "(&#133;) ellipsis";
                    case 0275:
                        return "PostScript " + "(&#137;) perthousand";
                    case 0277:
                        return "PostScript " + "(&iquest;) questiondown";
                    case 0301:
                        return "PostScript " + "(`) grave";
                    case 0302:
                        return "PostScript " + "(&acute;) acute";
                    case 0303:
                        return "PostScript " + "(^) circumflex";
                    case 0304:
                        return "PostScript " + "(~) tilde";
                    case 0305:
                        return "PostScript " + "(&macr;) macron, overbar accent";
                    case 0306:
                        return "PostScript "
                               + "(<sup>u</sup>) breve, flattened u-shaped accent";
                    case 0307:
                        return "PostScript " + "(&#183;) dotaccent";
                    case 0310:
                        return "PostScript " + "(&uml;) dieresis";
                    case 0312:
                        return "PostScript " + "(&#176;) ring";
                    case 0313:
                        return "PostScript " + "(&cedil;) cedilla";
                    case 0315:
                        return "PostScript " + "(&#148;) hungarumlaut";
                    case 0316:
                        return "PostScript " + "(,) ogonek, reverse comma";
                    case 0317:
                        return "PostScript "
                               + "(<sup>v</sup>) caron, flattened v-shaped accent";
                    case 0320:
                        return "PostScript " + "(&#151;) emdash";
                    case 0341:
                        return "PostScript " + "(&AElig;) AE";
                    case 0343:
                        return "PostScript " + "(&ordf;) ordfeminine";
                    case 0350:
                        return "PostScript " + "(L/) Lslash, L with / overstrike";
                    case 0351:
                        return "PostScript " + "(&Oslash;) Oslash";
                    case 0352:
                        return "PostScript " + "(&#140;) OE";
                    case 0353:
                        return "PostScript " + "(&ordm;) ordmasculine";
                    case 0361:
                        return "PostScript " + "(&aelig;) ae";
                    case 0365:
                        return "PostScript " + "(1) dotlessi, i without dot";
                    case 0370:
                        return "PostScript " + "(l/) l with / overstrike";
                    case 0371:
                        return "PostScript " + "(&oslash;) oslash";
                    case 0372:
                        return "PostScript " + "(&#156;) oe";
                    case 0373:
                        return "PostScript " + "(&szlig;) germandbls";
                    default:
                        return "&nbsp;";
                    }
                }
        }

    /**
     * Generate ascii table
     *
     * @param filename filename where to put the generated table
     */
    @SuppressWarnings( { "SameParameterValue" } )
    private static void generateAsciitab( String filename )
        {
        try
            {
            FileWriter p = new FileWriter( filename );
            p.write( "<cseignore>\n" );
            p.write( "<table class=\"standard\">\n" );
            p.write(
                    "<colgroup><col align=\"center\" class=\"behold\"><col span=\"3\" align=\"right\"><col span=\"2\" align=\"left\"></colgroup>\n" );
            p.write(
                    "<thead><tr><th colspan=\"6\">ASCII and Latin-1 Character Table</th></tr>" );
            p.write(
                    "<tr><th>Char</th><th>Dec</th><th>Hex</th><th>Octal</th><th>HTML</th><th>Notes</th></tr></thead>\n<tbody>\n" );
            for ( char c = 0; c <= 0xff; c++ )
                {
                p.write( "<tr>" );

                /* displayable char */
                p.write( "<td class=\"behold\">" );
                p.write( asHTML( c ) );
                p.write( "</td>" );

                /* decimal */
                p.write( "<td class=\"decimal\">" );
                p.write( Integer.toString( c ) );
                p.write( "</td>" );

                /* hex */
                p.write( "<td class=\"hexentity\">" );
                p.write( "0x" + toLZ( c, 2, 16 ) );
                p.write( "</td>" );

                /* octal */
                p.write( "<td class=\"octal\">" );
                p.write( toLZ( c, 4, 8 ) );
                p.write( "</td>" );

                /* HTML entity */
                p.write( "<td class=\"entity\">" );
                p.write( asQuotedHTML( c ) );
                p.write( "</td>" );

                /* notes */
                p.write( "<td>" );
                p.write( descs( c ) );
                p.write( "</td>" );

                p.write( "</tr>\n" );
                }// end for
            p.write( "</tbody></table>" );
            p.write( "</cseignore>\n" );
            p.close();
            }
        catch ( IOException e )
            {
            System.err.println( e );
            }
        }// end generateAsciitab

    /**
     * Convert an integer to a String, with left zeroes.
     *
     * @param i    the integer to be converted
     * @param len  len the length of the resulting string
     * @param base base 10=decimal 16=hex 8=octal
     *
     * @return String representation of the int e.g. 007
     */
    private static String toLZ( int i, int len, int base )
        {
        // Since String is final, we could not add this method there.
        String s = Integer.toString( i, base );
        if ( s.length() > len )
            {
            return s.substring( 0, len );
            }
        else if ( s.length() < len )
            // pad on left with zeros
            {
            return "000000000000000000000000000".substring( 0,
                    len - s.length() )
                   + s;
            }
        else
            {
            return s;
            }
        }// end toLZ

    // --------------------------- main() method ---------------------------

    /**
     * generates table C:\temp\ascii.html
     *
     * @param args not used
     */
    public static void main( String[] args )
        {
        generateAsciitab( "E:/mindprod/jgloss/include/asciitab.html" );
        }// end main
    }
