001package Torello.Java;
002
003import java.util.Vector;
004import java.util.HashMap;
005import java.io.File;
006
007/**
008 * A UNIX Terminal Color-Codes implementation for printing colored text to terminal.
009 * 
010 * <BR /><BR /><EMBED CLASS='external-html' DATA-FILE-ID=UNIX_COLORS>
011 */
012@Torello.JavaDoc.StaticFunctional
013public class C
014{
015    private C() { }
016
017    /**
018     * If Java is not running on a UNIX machine, the terminal output that contains "color" will not function.
019     * If it does not, then the Shell color commands will default to empty, zero-length strings.
020     * 
021     * <BR /><BR /><B CLASS=JDDescLabel>WINDOWS SUPPORT:</B>
022     * 
023     * <BR />Since Windows Release 1909, which was the 2019 Version of Windows 10, MS-DOS Command
024     * Prompt Windows will also support ANSI Color-Code Escape Sequences;
025     */
026    public static final boolean colorANSI;
027
028    static
029    {
030        String  osName  = System.getProperty("os.name");
031        String  osArch  = System.getProperty("os.arch");
032        String  osVers  = System.getProperty("os.version");
033        boolean windows = StrCmpr.containsIgnoreCase(osName, "win");
034        boolean unix    = StrCmpr.containsOR_CI(osName, "nix", "nux", "aix");
035        float   version = -1;
036
037        if (windows)
038
039            try
040                { version = Float.parseFloat(osVers); }
041
042            catch (Exception e) { }
043
044        /*
045        // Finding out where this class works is more difficult than you realize
046        System.out.println(
047            "os.name:    " + osName + '\n' +
048            "os.arch:    " + osArch + '\n' +
049            "os.version: " + osVers + '\n' +
050            "version: "    + version
051        );
052        Q.BP();
053        */
054
055        if (windows)    colorANSI = (version >= 10);
056        else if (unix)  colorANSI = true;
057
058        else if (StrCmpr.containsIgnoreCase(osName, "max"))             colorANSI = false;
059        else if (StrCmpr.containsIgnoreCase(osName, "sunos"))           colorANSI = false;
060        else                                                            colorANSI = true;
061    }
062
063
064    public static final String RESET            = colorANSI ? "\u001B[0m"  : "";
065
066    public static final String BLACK            = colorANSI ? "\u001B[30m" : "";
067    public static final String RED              = colorANSI ? "\u001B[31m" : "";
068    public static final String GREEN            = colorANSI ? "\u001B[32m" : "";
069    public static final String YELLOW           = colorANSI ? "\u001B[33m" : "";
070    public static final String BLUE             = colorANSI ? "\u001B[34m" : "";
071    public static final String PURPLE           = colorANSI ? "\u001B[35m" : "";
072    public static final String CYAN             = colorANSI ? "\u001B[36m" : "";
073    public static final String WHITE            = colorANSI ? "\u001B[37m" : "";
074
075    public static final String BLACK_BKGND      = colorANSI ? "\u001B[40m" : "";
076    public static final String RED_BKGND        = colorANSI ? "\u001B[41m" : "";
077    public static final String GREEN_BKGND      = colorANSI ? "\u001B[42m" : "";
078    public static final String YELLOW_BKGND     = colorANSI ? "\u001B[43m" : "";
079    public static final String BLUE_BKGND       = colorANSI ? "\u001B[44m" : "";
080    public static final String PURPLE_BKGND     = colorANSI ? "\u001B[45m" : "";
081    public static final String CYAN_BKGND       = colorANSI ? "\u001B[46m" : "";
082    public static final String WHITE_BKGND      = colorANSI ? "\u001B[47m" : "";
083
084    public static final String BBLACK           = colorANSI ? "\u001B[90m" : "";
085    public static final String BRED             = colorANSI ? "\u001B[91m" : "";
086    public static final String BGREEN           = colorANSI ? "\u001B[92m" : "";
087    public static final String BYELLOW          = colorANSI ? "\u001B[93m" : "";
088    public static final String BBLUE            = colorANSI ? "\u001B[94m" : "";
089    public static final String BPURPLE          = colorANSI ? "\u001B[95m" : "";
090    public static final String BCYAN            = colorANSI ? "\u001B[96m" : "";
091    public static final String BWHITE           = colorANSI ? "\u001B[97m" : "";
092
093    public static final String BBLACK_BKGND     = colorANSI ? "\u001B[100m" : "";
094    public static final String BRED_BKGND       = colorANSI ? "\u001B[101m" : "";
095    public static final String BGREEN_BKGND     = colorANSI ? "\u001B[102m" : "";
096    public static final String BYELLOW_BKGND    = colorANSI ? "\u001B[103m" : "";
097    public static final String BBLUE_BKGND      = colorANSI ? "\u001B[104m" : "";
098    public static final String BPURPLE_BKGND    = colorANSI ? "\u001B[105m" : "";
099    public static final String BCYAN_BKGND      = colorANSI ? "\u001B[106m" : "";
100    public static final String BWHITE_BKGND     = colorANSI ? "\u001B[107m" : "";
101
102    private static final String CSS_DEFINITIONS_FILE =
103        "data-files" + File.separator + "C-CSS.sdat";
104
105    private static final String HTML_SPANS_FILE =
106        "data-files" + File.separator + "C-SPANS.sarrdat";
107
108    @SuppressWarnings("rawtypes")
109    private static final Vector v = LFEC.readObjectFromFile_JAR
110        (Torello.Java.C.class, HTML_SPANS_FILE, true, Vector.class);
111
112
113    // This list has:
114    // "</SPAN>" (Maps to: C.RESET)
115    // "<SPAN CLASS=SC1>" through SC8 (Maps to: C.BLACK ... C.WHITE)
116    // "<SPAN CLASS=SB1>" through SB8 (Maps to: C.BLACK_BKGND ... C.WHITE_BKGND)
117    // "<SPAN CLASS=BC1>" through BC8 (Maps to: C.BBLACK ... C.BWHITE)
118    // "<SPAN CLASS=BB1>" through BB8 (Maps to: C.BBLACK_BKGND ... C.BWHITE_BKGND)
119
120    @SuppressWarnings("unchecked")
121    private static final String[] htmlSpansCSSClasses = (String[]) v.elementAt(0);
122
123    // This list is identical to the one above, but uses **INLINE** STYLE ATTRIBUTES:
124    // C.RESET          ==> "</SPAN>"
125    // C.BLACK          ==> "<SPAN style='color: black;'>"
126    // C.BLACK_BKGND    ==> "<SPAN style='background: black;'>"
127    // C.BBLACK         ==> "<SPAN style='color: black;  font-weight: bold;'>"
128    // C.BBLACK_BKGN    ==> "<SPAN style='background: brightblack;'>"
129
130    @SuppressWarnings("unchecked")
131    private static final String[] htmlSpansStyleAttributes = (String[]) v.elementAt(1);
132
133    // Efficiently (using HashMap) converts a "Char Code String" to an array position
134    // The "Char Code String" may be any one of the 33 String defined in this class.
135    // The array position is an index into the PREVIOUS TWO ARRAYS.
136
137    @SuppressWarnings("unchecked")
138    private static final HashMap<String, Integer> charCodesMap =
139        (HashMap<String, Integer>) v.elementAt(2);
140
141    // less efficient version, 'StrRepace' requires this...  Note - these Strings are all
142    // extremely short.  There isn't a way to improve StrReplace in this regard - it *MUST*
143    // check for *ALL* of the codes...
144
145    private static final String[] charCodesArr = (String[]) v.elementAt(3);
146
147
148    /**
149     * This will convert a UNIX 'Character Code' into an HTML Tag if it is necessary to convert
150     * UNIX colored-text into HTML.
151     * 
152     * <BR /><BR /><B CLASS=JDDescLabel>CSS-Definitions:</B>
153     * 
154     * <BR />This method returns an HTML {@code SPAN}-Tag that contains an inline 
155     * {@code CSS-CLASS}.  It is (hopefully) obvious that the definitiions for any / all
156     * {@code CSS-CLASSES} that are used will need to be provided on the page.
157     * 
158     * <BR /><BR />The method {@link #getCSSDefinitions()} will return the complete definition
159     * page for all {@code CSS CLASSES} that are employed by this method.
160     * 
161     * <BR /><BR />You may also view the contents of the CSS Definitions Below:
162     * 
163     * <BR /><BR /><B><A HREF="hilite-files/C.css.html">Shell.C CSS Definitions</A></B>
164     * 
165     * @param charCode Any one of the 33 codes defined in this class.
166     * 
167     * @return An HTML {@code <SPAN CLASS=...>} element that may be used as a substitute for
168     * one of the codes defined in this class.
169     * 
170     * @throws IllegalArgumentException If the {@code String} that is passed to parameter
171     * {@code 'charCode'} is not one of the defined codes in this class.
172     * 
173     * @see #getCSSDefinitions()
174     */
175    public static String span(String charCode)
176    {
177        Integer arrPos = charCodesMap.get(charCode);
178
179        if (arrPos == null) throw new IllegalArgumentException(
180            "The value passed to parameter 'charCode' is not one of the defined codes in " +
181            "this class."
182        );
183
184        return htmlSpansCSSClasses[arrPos];
185    }
186
187    /**
188     * This will convert a UNIX 'Character Code' into an HTML Tag if it is necessary to convert
189     * UNIX colored-text into HTML.
190     * 
191     * <BR /><BR /><B CLASS=JDDescLabel>CSS Inline-Style:</B>
192     *
193     * <BR />This method returns an HTML {@code SPAN}-Tag that contains an <B>inline
194     * {@code STYLE}-Attribute</B>.  Remember, if you are converting large Text-{@code String's}
195     * into HTML using inlne {@code STYLE}-Attributes, your output could potentially grow very
196     * large, and rather quickly.
197     * 
198     * <BR /><BR />Using {@code CSS-CLASSES} provided by method {@link #span(java.lang.String)}
199     * <I>will make your generated HTML <B>somewhat</B> more efficient</I>.  If you do, you must
200     * remember to import the CSS Definitions for these classes somewhere in your HTML-File.
201     * 
202     * @param charCode Any one of the 33 codes defined in this class.
203     * 
204     * @return An HTML {@code <SPAN STYLE=...>} element that may be used as a substitute for
205     * one of the color-codes defined in this class.
206     * 
207     * @throws IllegalArgumentException If the {@code String} that is passed to parameter
208     * {@code 'charCode'} is not one of the defined codes in this class.
209     */
210    public static String spanInlineStyle(String charCode)
211    {
212        Integer arrPos = charCodesMap.get(charCode);
213
214        if (arrPos == null) throw new IllegalArgumentException(
215            "The value passed to parameter 'charCode' is not one of the defined codes in " +
216            "this class."
217        );
218
219        return htmlSpansStyleAttributes[arrPos];
220    }
221
222    /**
223     * Convenience Method.
224     * <BR />Invokes: {@link #toHTML(String, boolean, boolean, boolean)}
225     */
226    public static String toHTML(String text) { return toHTML(text, false, true, false); }
227
228    // Used in method below
229    private static final String[] MATCH_STRS_1 = { "<", ">", "\n\r", "\r\n", "\n", "\r" };
230    private static final String[] MATCH_STRS_2 = { "<", ">" };
231    private static final String[] MATCH_STRS_3 = { "\n\r", "\r\n", "\n", "\r" };
232
233    private static final String REPLACER(int i, String s)
234    {
235        switch (s)
236        {
237            case "<" : return "&lt;";
238            case ">" : return "&gt;";
239            case "\n" :
240            case "\r" :
241            case "\n\r" : 
242            case "\r\n" : return "<BR />\n";
243            default: throw new UnreachableError();
244        }
245    }
246
247    /**
248     * Converts the instances of these escape-sequences that are found inside of Java
249     * {@code String's} that were generated using these ANSI UNIX color escape sequences, and
250     * produces a valid HTML {@code String} that contains HTML
251     * {@code <SPAN STYLE="color-information">} replacements!
252     *
253     * <BR /><BR /><B CLASS=JDDescLabel>New-Line Characters:</B>
254     *
255     * <BR />Any new-line Character-Sequences such as {@code '\n'} or {@code '\r\n'} will be
256     * replaced with HTML {@code <BR />} elements.
257     * 
258     * @param text This should be any string, usually one that is saved from a
259     * {@code 'StorageWriter'}, although any text that includes these UNIX Color Escape Codes
260     * is fine.
261     * 
262     * @param preFormat When this parameter receives {@code FALSE}, everywhere in the input
263     * text-{@code String} that a {@code CRLF} (new-line) occurs, that newline will be replaced
264     * by an HTML {@code <BR />} element, in addition to the original newline.
265     * 
266     * <BR /><BR />This parameter is referring to the CSS {@code 'white-space: pre'} setting,
267     * which can be used.  Althought, sometimes going with the plain-old-vanilla {@code <BR />}
268     * tag can also be advisable.
269     * 
270     * @param escapeHTMLElements Whenever HTML is sent to the input-parameter 'text' - if the
271     * intention is to render the HTML using the browser, this parameter should be
272     * {@code FALSE}.  If it is intended to allow the UI to "show the HTML" like it were
273     * text-to-be-viewed, then each and every greater-than-symbol {@code '>'} and also every
274     * less-than-symbol {@code '<'} will be escaped.  This is done to prevent the browser from
275     * trying to parse the text as HTML.
276     * 
277     * @param useCSSClasses When this parameter receives {@code TRUE}, all returned
278     * {@code <SPAN STYLE=...>} elements shall be converted to using a simplified
279     * {@code CSS Class Name}.  The {@code CLASS} definitions for the returned {@code String}
280     * can be retrieved by simply calling the method: {@link #getCSSDefinitions()}.
281     * 
282     * <BR /><BR /><B><SPAN STYLE="color: red;">IMPORTANT:</SPAN></B> If this parameter does
283     * receive a {@code TRUE} value, it is imperitive to include the {@code CSS STYLE}
284     * definitions that are returned by the above mentioned method, or else the colors shall
285     * not be visible.
286     * 
287     * <BR /><BR />You may view the contents of the CSS Definitions Below:
288     * 
289     * <BR /><BR /><B><A HREF="hilite-files/C.css.html">Shell.C CSS Definitions</A></B>
290     * 
291     * @return Every UNIX-ANSI color escape-sequence that is found/identified in this text will
292     * be replaced with an HTML
293     * {@code <SPAN STYLE="color: a-color; background: a-background-color">} element.
294     */ 
295    public static String toHTML
296        (String text, boolean preFormat, boolean escapeHTMLElements, boolean useCSSClasses)
297    {
298        // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
299        // Old Way, much less efficient
300        // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
301
302        // If the input text, itself, has HTML elements, then those have to be "escaped" to
303        // properly render.  If the intention was to have them rendered has HTML Elements
304        // (not text), then this boolean should be false.
305
306        // if (escapeHTMLElements) text = text.replace("<", "&lt;").replace(">", "&gt;");
307
308        // With "Pre-Formatted Text" - there is no need to add "<BR />" where line-breaks
309        // occur CRLF will automatically be inserted courtesy of the browser
310
311        // if (! preFormat) text = text.replaceAll("\n|\r\n|\r", "<BR />\n");
312
313
314        // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
315        // Quite a bit faster
316        // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
317
318        String[] matchStrs = null;
319
320        // Replaces both '<', '>' **AND** '\n', '\r', '\n\r', '\r\n' (all at once)
321        if (escapeHTMLElements && (! preFormat))            matchStrs = MATCH_STRS_1;
322
323        // Replaces ONLY '<' and '>'
324        else if (escapeHTMLElements && preFormat)           matchStrs = MATCH_STRS_2;
325
326        // Replaces ONLY '\n', '\r', '\n\r', '\r\n'
327        else if ((! escapeHTMLElements) && (! preFormat))   matchStrs = MATCH_STRS_3;
328
329        // NOW RUN IT...
330        if (matchStrs != null) text = StrReplace.r(text, matchStrs, C::REPLACER);
331
332        return StrReplace.r(
333            text, charCodesArr, 
334            useCSSClasses ? htmlSpansCSSClasses : htmlSpansStyleAttributes
335        );
336    }
337
338    /**
339     * If the {@code 'useCSSDefinitions'} option is selected with the
340     * {@link #toHTML(String, boolean, boolean, boolean)} method, then the {@code String}
341     * returned from this method shall provide the {@code CSS Style} definitions needed to use
342     * the colors provided by {@code toHTML(...)}
343     * 
344     * @return This shall visit the internal data-files for this JAR distribution, and return a
345     * list of {@code CSS Style} definitions that will colorize the HTML produced by an
346     * invocation of {@code toHTML()}.
347     * 
348     * <BR /><BR />You may view the contents of the CSS Definitions Below:
349     * 
350     * <BR /><BR /><B><A HREF="hilite-files/C.css.html">Shell.C CSS Definitions</A></B>
351     */
352    public static String getCSSDefinitions()
353    {
354        /*
355        return LFEC.readObjectFromFile_JAR
356            (Torello.Data.DataFileLoader.class, CSS_DEFINITIONS_FILE, true, String.class);
357        */
358
359        return LFEC.readObjectFromFile_JAR
360            (Torello.Java.C.class, CSS_DEFINITIONS_FILE, true, String.class);
361    }
362
363    /**
364     * Convenience Method.
365     * <BR />Creates an {@code '.html'}-file from the output produced by
366     * {@link #toHTML(String, boolean, boolean, boolean)}
367     * <BR />Invokes: {@link #getCSSDefinitions()}
368     * <BR />And Invokes: {@link #toHTML(String, boolean, boolean, boolean)}
369     * <BR />Passes: {@code TRUE} to parameter {@code 'useCSSClasses'}
370     */
371    public static String toHTML
372        (String text, boolean preFormat, boolean escapeHTMLElements, String titleStr)
373    {
374        return
375            "<HTML>\n" +
376            "<HEAD>\n" +
377            "<TITLE>" + titleStr + "</TITLE>\n" +
378            "<STYLE TYPE='text/css'>\n" + '\n' +
379
380            // This method simply copies the CSS-Definitions File out of the Java-HTML JAR, and
381            // into a String that is returned to the user.  Make sure to wrap that String
382            // inside of a <STYLE>...</STYLE> HTML-Tag.  Also - set the background color
383
384            getCSSDefinitions() +
385
386            "BODY { background: black; color: white; }\n" +
387            "</STYLE>\n" +
388            "</HEAD>\n" +
389            "<BODY>\n" +
390            (preFormat ? "<PRE>\n" : "") +
391
392            // NOTE: This just calls the other variant of 'toHTML' - but ensures that 'true' is
393            // passed to the 'useCSSClasses' parameter.
394
395            toHTML(text, preFormat, escapeHTMLElements, true) + 
396
397            (preFormat ? "</PRE>\n" : "") +
398            "</BODY>\n" +
399            "</HTML>";
400    }
401}