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
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
package Torello.Java;

import java.util.function.Predicate;
import java.util.function.Function;

class VertAndHorizAbbrev
{
    static String print(
            final String    s,
            final String    hAbbrevStrInput,
            final Integer   maxLineLength,
            final Integer   maxNumLines,
            final boolean   compactConsecutiveBlankLines
        )
    {
        if (maxNumLines != null) if (maxNumLines < 3) throw new IllegalArgumentException(
            "Parameter maxNumLines has been passed [" + maxNumLines + "], but this value must " +
            "be '3' or greater"
        );

        if (
                (maxLineLength == null)
            &&  (maxNumLines == null)
            &&  (compactConsecutiveBlankLines == false)
        )

            throw new IllegalArgumentException(
                "You have passed null to parameters 'maxLineLength' and 'maxNumLines'\n" +
                "You have passed false to 'compactConsecutiveBlankLines'.\n" +
                "There is nothing for this method to do."
            );

        // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
        // NOTE ABOUT THIS METHOD:
        // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
        // 
        // I generally try to avoid excessive use of squiggly-braces '{' and '}'.  When for-loop
        // bodies or if-then-else are painfully-obvious, I let the "Python Mentality" take over and
        // leave them off completely.  I have never programmed in Python, but extra squiggly's whne
        // they are completely unnecessary really make the code less readable, not more.
        //
        // Java is capable of having Good / Proper / Consistent Indentation - not just Python.
        // 
        // HOWEVER - THIS ENTIRE METHOD IS ONE GIANT CONGLOMERATION OF 
        //      * for-loops
        //      * if-then-else
        // 
        // Therefore thereis a Squiggly-Brace at EVERY-SINGLE-LOCATION where one may be placed.
        // I really haven't done this anywhere in Java-HTML, except here.


        // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
        // Initializations
        // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***

        final String[]  lines       = s.split("\n");
        final String    hAbbrevStr  = (hAbbrevStrInput == null) ? "..." : hAbbrevStrInput;

        // This makes it easier to read the if-then-else-for-loop giant-mess
        final Predicate<String> checkHorizAbbrev = (maxLineLength == null)
            ? null
            : (String line) -> line.length() > maxLineLength;

        // NOTE: This value is completely unused / discarded - exxcept in the lamda-expression
        //       directly on the next line.

        final int horizMidPoint = (maxLineLength != null)
            ? (maxLineLength / 2)
            : -1;

        // This is a little unnecessary / "overdone" - but it looks better to me
        final Function<String, String> doHorizAbbrev = (maxLineLength == null)
            ? null
            : (String line) -> Abbrev.print1(line, horizMidPoint, false, hAbbrevStr, maxLineLength);


        // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
        // If Requested, Compact Consecutive Blank-Lines First
        // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
        //
        // MAJOR NOTE: When String.split("\n") is called, NONE OF THE RETURNED STRING'S for the
        //             String[] will actually be Java-String's with any newlines in them!!
        // 
        // IN OTHER WORDS: String.split("\n"), does INDEED remove all newlines from the output
        //                 array!

        int insertPos = 0;

        if (compactConsecutiveBlankLines)
        {
            int     curBlankCount   = 0;
            int     sourcePos       = 0;

            while (sourcePos < lines.length)
            {
                final String line = lines[sourcePos++];

                if (line.trim().length() == 0) curBlankCount++;

                else 
                {
                    if (curBlankCount > 1)
                        // REMEMBER NOT TO APPEND A NEW-LINE AT THE END OF THIS STRING
                        lines[insertPos++] = "[Compacted " + curBlankCount + " Blank Lines]";

                    else if (curBlankCount == 1)
                        // AND, FURTHERMORE, THIS STRING IS JUST A ZERO-LENGTH-STRING
                        // lines[insertPos++] = '[' + lines[sourcePos-1] + ']';
                        // NOTE: When debugging, use the brackets, above, it helps

                        lines[insertPos++] = lines[sourcePos-1];

                    curBlankCount = 0;

                    // add the line, and when debugging, make sure to keep the brackets
                    // lines[insertPos++] = '[' + line + ']';

                    lines[insertPos++] = line;
                }
            }


            // Possible "Trailing Stack of Blank Lines" (which never got a "Compacted Notice"
            // because the loop terminatd before one could be appended)
            // 
            // ONCE-AGAIN: Anytime **ANYTHING** has any amount of "parse" whatsoever, there will
            //             **ALWAYS** be a Trailing-Read where the last "chunk" of the String, 
            //             (or in this case, the String[]-Array) need to be run through whatever
            //             test was going-on inside the loop.

            if (curBlankCount > 1)
                lines[insertPos++] = "[Compacted " + curBlankCount + " Blank Lines]";

            else if (curBlankCount == 1)
                lines[insertPos++] = "";
        }


        // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
        // NOW DO THE COMPACTING - THERE ARE 4 DIFFERENT CASES.
        // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
        // 
        // Horizontal-Abbreviation-Requested: true | false
        // Vertical-Abbreviation-Required:    true | false


        // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
        // VERSION #1: Number of Lines (VERTICAL) is **LESS THAN** 'maxNumLines'
        // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***

        final StringBuilder sb          = new StringBuilder();
        final int           LEN         = compactConsecutiveBlankLines ? insertPos : lines.length;
        final boolean       COMPACTED   = (LEN < lines.length);  // Needed for minor optimization

        if ((maxNumLines == null) || (LEN <= maxNumLines)) // VERTICAL-NOT-REQUIRED
        {
            // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
            // Version 1A: "Max-Line-Length" (HORIZONTAL) has an actual, non-null, value.
            // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
            //
            // NOTE - THAT RIGHT HERE - IT HAS ALREADY BEEN ESTABLISHED:
            //        LEN <= maxNumLines  **OR**  maxNumLines==null (unlimited-vertical)
            //        ==> NO VERTICAL ABBREVIATION

            if (maxLineLength != null) // HORIZONTAL-REQUESTED
            {
                for (int pos=0; pos < LEN; pos++)
                {
                    String line = lines[pos];


                    // Again, the "Check if Horizontal-Abbreviation is necessary" check
                    // was actually turned into a Predicate, solely, to make this part of the
                    // code more readable/intelligble.  (as was the Abbrev-Call turned into a
                    // Java-Function-Pointer-Lambda thingy)

                    if (checkHorizAbbrev.test(line)) line = doHorizAbbrev.apply(line);

                    // HERE - AND ONLY HERE - IS AN ACTUAL NEW-LINE APPENDED
                    sb.append(line);

                    // Do not add the VERY-LAST-NEWLINE, it isn't needed.
                    if (pos < (LEN-1)) sb.append('\n');
                }
            }


            // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
            // Version 1B: "Max-Line-Length" (HORIZONTAL) was passed null (NO HORIZONTAL ABBREV)
            // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
            //
            // NOTE - THAT RIGHT HERE - IT HAS ALREADY BEEN ESTABLISHED:
            //        LEN <= maxNumLines  **OR**  maxNumLines==null (unlimited-vertical)
            //        ==> NO VERTICAL ABBREVIATION

            else // HORIZONTAL-NOT-REQUESTED
            {
                // IMPORTANT POINT TO KEEP IN MIND: Even though NEITHER Horizontal NOR Vertical
                // abbreviation is needed - the user may still have asked that blank lines have 
                // been compacted, so we may as well check for that.
                // 
                // If Horizontal, Vertical and Blank-Line-Compaction are all "false", we can just
                // return the original String 's'

                if (! COMPACTED) return s;

                else for (int pos=0; pos < LEN; pos++)
                {
                    // HERE - AND ONLY HERE - IS AN ACTUAL NEW-LINE APPENDED
                    sb.append(lines[pos]);

                    // Do not add the VERY-LAST-NEWLINE, it isn't needed.
                    if (pos < (LEN-1)) sb.append('\n');
                }
            }

            return sb.toString();
        }


        // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
        // OTHERWISE - VERSION #2: Number of Lines (VERTICAL) is **GREATER THAN** 'maxNumLines'
        // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
        // 
        // maxNumLines is not null at this point, otherewise the if-statement way above this line
        // would have already succeeded, and the method would already have returned.

        final int verticalMidPoint = maxNumLines / 2;

        if (maxLineLength != null) // HORIZONTAL-REQUESTED
        {
            // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
            // Version 2A: "Max-Line-Length" (HORIZONTAL) has an actual, non-null, value.
            // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
            //
            // NOTE, THAT RIGHT HERE IT HAS ALREADY BEEN ESTABLISHED: LEN > maxNumLines
            //       ==> VERTICAL ABBREVIATION REQUIRED

            for (int pos=0; pos < LEN; pos++)
            {
                if (pos == verticalMidPoint)
                {
                    sb.append("[Text Shortened by " + (lines.length - LEN) + " Line(s)]\n");
                    pos = lines.length - verticalMidPoint - 1;
                }    
  
                else
                {
                    String line = lines[pos];
                    if (checkHorizAbbrev.test(line)) line = doHorizAbbrev.apply(line);
    
                    // HERE - AND ONLY HERE - IS AN ACTUAL NEW-LINE APPENDED
                    sb.append(line);

                    // Do not add the VERY-LAST-NEWLINE, it isn't needed.
                    if (pos < (LEN-1)) sb.append('\n');
                }
            }
        }

        else // HORIZONTAL-NOT-REQUESTED
        {
            // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
            // Version 2B: "Max-Line-Length" (HORIZONTAL) was passed null - NO HORIZONTAL ABBREV
            // *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
            //
            // NOTE, THAT RIGHT HERE IT HAS ALREADY BEEN ESTABLISHED: LEN > maxNumLines
            //       ==> VERTICAL ABBREVIATION REQUIRED

            for (int pos=0; pos < LEN; pos++)
            {
                if (pos == verticalMidPoint)
                {
                    sb.append("[Text Shortened by " + (lines.length - LEN) + " Line(s)]\n");
                    pos = lines.length - verticalMidPoint;
                }

                else
                {
                    // HERE - AND ONLY HERE - IS AN ACTUAL NEW-LINE APPENDED
                    sb.append(lines[pos]);

                    // Do not add the VERY-LAST-NEWLINE, it isn't needed.
                    if (pos < (LEN-1)) sb.append('\n');
                }
            }
        }

        return sb.toString();
    }
}