1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
package Torello.Java;

import Torello.HTML.TagNode;
import Torello.HTML.TextNode;
import Torello.HTML.HTMLNode;
import Torello.HTML.HTMLPage;
import Torello.HTML.Util;

import java.util.Vector;

class TextToHTML 
{
    // Used in method below
    private static final String[] MATCH_STRS_1 = { "<", ">", "\n\r", "\r\n", "\n", "\r" };
    private static final String[] MATCH_STRS_2 = { "<", ">" };
    private static final String[] MATCH_STRS_3 = { "\n\r", "\r\n", "\n", "\r" };

    private static final String REPLACER(int i, String s)
    {
        switch (s)
        {
            case "<" : return "&lt;";
            case ">" : return "&gt;";
            case "\n" :
            case "\r" :
            case "\n\r" : 
            case "\r\n" : return "<BR />\n";
            default: throw new UnreachableError();
        }
    }


    static String convert(
            String text,
            final boolean   preFormat,
            final boolean   escapeHTMLElements,
            final boolean   useCSSClasses
        )
    {
        // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
        // Old Way, much less efficient
        // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***

        // If the input text, itself, has HTML elements, then those have to be "escaped" to
        // properly render.  If the intention was to have them rendered has HTML Elements
        // (not text), then this boolean should be false.

        // if (escapeHTMLElements) text = text.replace("<", "&lt;").replace(">", "&gt;");

        // With "Pre-Formatted Text" - there is no need to add "<BR />" where line-breaks
        // occur CRLF will automatically be inserted courtesy of the browser

        // if (! preFormat) text = text.replaceAll("\n|\r\n|\r", "<BR />\n");


        // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
        // Quite a bit faster
        // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***

        String[] matchStrs = null;

        // Replaces both '<', '>' **AND** '\n', '\r', '\n\r', '\r\n' (all at once)
        if (escapeHTMLElements && (! preFormat)) matchStrs = MATCH_STRS_1;

        // Replaces ONLY '<' and '>'
        else if (escapeHTMLElements && preFormat) matchStrs = MATCH_STRS_2;

        // Replaces ONLY '\n', '\r', '\n\r', '\r\n'
        else if ((! escapeHTMLElements) && (! preFormat)) matchStrs = MATCH_STRS_3;

        // NOW RUN IT...
        if (matchStrs != null)
            text = StrReplace.r(text, matchStrs, TextToHTML::REPLACER);

        final String html = StrReplace.r(
            text,
            C.charCodesArr, 
            useCSSClasses ? C.htmlSpansCSSClasses : C.htmlSpansStyleAttributes
        );

        if (! escapeHTMLElements) return html;


        // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
        // Added Aug. 6th, 2025 - If Multiple Open <SPAN> - close them !!!
        // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***

        final Vector<HTMLNode> v = HTMLPage.getPageTokens(html, false);

        TagNode tn      = null;
        int     curOpen = 0;

        for (int i = 0; i < v.size(); i++)

            if (    ((tn = v.elementAt(i).ifTagNode()) != null)
                &&  tn.tok.equals("span")
            )
            {
                if (tn.isClosing)
                {
                    if (curOpen > 1)
                        v.setElementAt(closedSPAN(curOpen), i);

                    else if (curOpen == 0)
                        v.setElementAt(EMPTY_NODE, i);

                    curOpen = 0;
                }

                else curOpen++;
            }

        return Util.pageToString(v);
    }


    private static final TextNode EMPTY_NODE    = new TextNode("");
    private static final TextNode S2            = new TextNode("</SPAN></SPAN>");
    private static final TextNode S3            = new TextNode("</SPAN></SPAN></SPAN>");

    private static TextNode closedSPAN(int n)
    {
        if (n == 2)         return S2;
        else if (n == 3)    return S3;

        final StringBuilder sb = new StringBuilder();

        for (int i=0; i < n; i++) sb.append("</SPAN>");

        return new TextNode(sb.toString());
    }
}