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
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
package Torello.Java;

import Torello.Java.HelperPackages.SED.SingleLine;
import Torello.Java.HelperPackages.SED.MultiLine;

import static Torello.Java.C.*;

import java.io.IOException;
import java.io.FilenameFilter;
import java.io.File;

import java.util.List;
import java.util.regex.Pattern;
import java.util.regex.MatchResult;
import java.util.function.Function;

/**
 * Based on the UNIX {@code 'SED'} Command, this class updates files based on
 * <B STYLE='color: red;'><I>either</I></B> {@code String}-Literal matches &amp; replacements, or
 * <B STYLE='color: red;'><I>or</I></B> Regular-Expression matches.
 * 
 * <EMBED CLASS='external-html' DATA-FILE-ID=SED>
 */
@Torello.JavaDoc.StaticFunctional
public class SED
{
    private SED() { }


    // ********************************************************************************************
    // ********************************************************************************************
    // Some Filters
    // ********************************************************************************************
    // ********************************************************************************************


    /** A {@code java.io.FilenameFilter} that looks for JavaDoc Upgrader HTML-Files. */
    public static final FilenameFilter filterFilesHTML = (File dir, String fileName) ->
    {
        if (! fileName.endsWith(".html")) return false;

        if (! StrCmpr.containsAND(dir.getPath(), "upgrade-files", "external-html")) return false;

        return true;
    };

    /** A {@code java.io.FilenameFilter} that looks for {@code '.java'} Files. */
    public static final FilenameFilter filterFilesJava = (File dir, String fileName) ->
    {
        if (! fileName.endsWith(".java")) return false;

        return true;
    };


    // ********************************************************************************************
    // ********************************************************************************************
    // Single-Line String-Match
    // ********************************************************************************************
    // ********************************************************************************************


    /**
     * Convenience Method.
     * <BR />Invokes: {@link #singleLine(Iterable, String, String, boolean, IOExceptionHandler,
     *      Appendable, boolean, Verbosity)}
     * <BR />Passes: <CODE>
     *      <B STYLE='color: blue'>askFirst</B>: TRUE,
     *      <B STYLE='color: blue'>ioeh</B>: null,
     *      <B STYLE='color: blue'>outputSaver</B>: null,
     *      <B STYLE='color: blue'>useUNIXColors</B>: TRUE,
     *      <B STYLE='color: blue'>verbosity</B>: Verbosity.Normal
     * </CODE>
     */
    public static List<FileNode> singleLine
        (Iterable<FileNode> files, String matchStr, String replaceStr)
        throws IOException
    {
        return SingleLine.strMatch
            (files, matchStr, replaceStr, true, null, null, true, Verbosity.Normal);
    }

    /**
     * Makes updates to Text-Files listed in parameter {@code 'files'}.
     * 
     * <BR /><BR />In the example below, all {@code '.java'} Source-Code Files in the Java-HTML
     * Library are loaded into a {@code Vector<FileNode>}.  Using this particular {@code 'SED'}
     * method, each substring that looks like <B STYLE='color: blue'>{@code <B>TRUE</B>}</B> (in
     * all {@code '.java'} Source-Files) is converted to look like
     * <B STYLE='color: blue'><CODE>&lcub;&commat;code TRUE&rcub;</CODE></B>
     * 
     * <DIV CLASS=EXAMPLE>{@code
     * StringBuilder log = new StringBuilder();
     * 
     * // Load all Java-HTML Source-Files (in all packages) into a FileNode-Vector
     * Vector<FileNode> files = FileNode
     *      .createRoot("Torello/")
     *      .loadTree(-1, SED.filterFilesJava, null)
     *      .flattenJustFiles(RTC.VECTOR());
     * 
     * // Invoke this method
     * singleLine(files, "<B>TRUE</B>", "{@code TRUE}", true, null, log, true, Verbosity.Normal);
     * 
     * // Save the Changes that were made to a log
     * FileRW.writeFile(log, "ChangesMade.log.txt");
     * }</DIV>
     * 
     * @param matchStr Any Java {@code String}-Literal.  It is required that this {@code String}
     * not contain any new-line ({@code '\n'}) characters, or an {@code IllegalArgumentException}
     * will throw.
     * 
     * @param replaceStr For all {@code 'files'}, each instance of the {@code String}-Literal
     * parameter {@code 'matchStr'} found in any file will be replaced by {@code 'replaceStr'}.
     * 
     * <BR /><BR />As with parameter {@code 'matchStr'}, if this {@code String} contains any
     * new-line ({@code '\n'}) characters, an {@code IllegalArgumentException} will throw.
     * 
     * @return A list of {@link FileNode} instances whose File-Contents were modified / updated
     * by this method.
     * 
     * @throws IllegalArgumentException If either of the {@code String} parameters
     * {@code 'matchStr'} or {@code 'replaceStr'} contain any newline ({@code '\n'}) characters.
     * 
     * <BR /><BR />If a {@code 'SED'} replacement needs to be done using a {@code String}-Match
     * that spans more than one line, use the Multi-Line method provided in this class.
     * 
     * @throws AppendableError <EMBED CLASS='external-html' DATA-FILE-ID=SED_APPEND_ERROR>
     * @throws IOException     On File-System read / write error.
     */
    public static List<FileNode> singleLine(
            Iterable<FileNode>  files,
            String              matchStr,
            String              replaceStr,
            boolean             askFirst,
            IOExceptionHandler  ioeh,
            Appendable          outputSaver,
            boolean             useUNIXColors,
            Verbosity           verbosity
        )
        throws IOException
    {
        return SingleLine.strMatch
            (files, matchStr, replaceStr, askFirst, ioeh, outputSaver, useUNIXColors, verbosity);
    }


    // ********************************************************************************************
    // ********************************************************************************************
    // Single-Line Regular-Expression-Match
    // ********************************************************************************************
    // ********************************************************************************************


    /**
     * Convenience Method.
     * <BR />Invokes: {@link #singleLine(Iterable, Pattern, Function, boolean, IOExceptionHandler,
     *      Appendable, boolean, Verbosity)}
     * <BR />Passes: <CODE>
     *      <B STYLE='color: blue'>askFirst</B>: TRUE,
     *      <B STYLE='color: blue'>ioeh</B>: null,
     *      <B STYLE='color: blue'>outputSaver</B>: null,
     *      <B STYLE='color: blue'>useUNIXColors</B>: TRUE,
     *      <B STYLE='color: blue'>verbosity</B>: Verbosity.Normal
     * </CODE>
     */
    public static List<FileNode> singleLine
        (Iterable<FileNode> files, Pattern regEx, Function<MatchResult, String> replaceFunction)
        throws IOException
    {
        return SingleLine.regExMatch
            (files, regEx, replaceFunction, true, null, null, true, Verbosity.Normal);
    }    

    /**
     * Makes updates to Text-Files listed in parameter {@code 'files'}.  Matches that are eligible
     * for replacement are identified using the Regular-Expression parameter {@code 'regEx'}.
     * 
     * <BR /><BR />Replacement-{@code String's} are obtained by invoking the
     * {@code 'replaceFunction'} parameter.
     * 
     * <BR /><BR />In the example below, all Java HTML Library Source-Files are loaded into a 
     * {@code Vector<FileNode>}, and then iterated by this method.  In each file, any instances of
     * an {@code EMBED}-Tag that contains a {@code FILE-ID} Data-Attribute whose value is wrapped
     * in Double Quotation-Marks has that value extracted and re-inserted without any
     * Quotation-Marks.
     * 
     * <DIV CLASS=EXAMPLE>{@code
     * StringBuilder log = new StringBuilder();
     * 
     * // Load all Java-HTML Source-Files (in all packages) into a FileNode-Vector
     * Vector<FileNode> files = FileNode
     *      .createRoot("Torello/")
     *      .loadTree(-1, filterFilesJava, null)
     *      .flattenJustFiles(RTC.VECTOR());
     * 
     * // Matches an <EMBED> Tag's FILE-ID Data-Attribute.  There are parenthesis placed around the
     * // actual ID-Name.  The contents of the parenthesis are "Reg-Ex Match-Group #1"
     * 
     * Pattern regEx = Pattern.compile("DATA-FILE-ID=\"(\\w+)\"");
     * 
     * // This Function extracts Reg-Ex Group-1, and re-inserts it **WITHOUT** the Double
     * // Quotation-Marks that were placed around it.  Look Closely at Previous Line of Code, the
     * // 'replacer' function does not include the Double-Quotes.
     * 
     * Function<MatchResult, String> replacer = (MatchResult mr) -> "DATA-FILE-ID=" + mr.group(1);
     *
     * // Invoke this method
     * singleLine(files, regEx, replacer, true, null, log, true, Verbosity.Normal);
     * 
     * // Write the changes that have been made to a log-file
     * FileRW.writeFile(log, "ChangesMade.log.txt");
     * }</DIV>
     * 
     * @param regEx Any Java Regular-Expression.  This will be used to match against the text
     * inside each file returned by the {@code Iterable} parameter {@code 'files'}.
     * 
     * <BR /><BR />If this Regular-Expression returns a match that spans more than a single line of
     * text, this method will throw an exception.  To perform matches that span multiple lines of 
     * text, use the Multi-Line methods, provided by this class
     * 
     * @param replaceFunction Any Lambda-Expression or Function-Pointer that accepts a
     * {@code java.util.regex.MatchResult}, and returns a Replacement-{@code String}.
     * 
     * <BR /><BR />If this Replace-Function returns a match that contains any newline {@code '\n'}
     * characters, this method will throw a {@link RegExException}.
     * 
     * @return A list of {@link FileNode} instances whose File-Contents were modified / updated
     * by this method.
     * 
     * @throws RegExExpression Throws if the {@code Pattern} parameter {@code 'regEx'} returns a
     * match that covers more than one line of text in any of the files that are scanned. 
     * 
     * <BR /><BR />This exception will also throw if the Lambda-Target / Function-Pointer parameter
     * {@code 'replaceFunction'} ever returns a {@code String} that spans multiple lines.
     * 
     * <BR /><BR />In both of these cases, the logic simply checks for the presence of a newline
     * {@code '\n'} character.
     * 
     * @throws AppendableError <EMBED CLASS='external-html' DATA-FILE-ID=SED_APPEND_ERROR>
     * @throws IOException     On File-System read / write error.
     */
    public static List<FileNode> singleLine(
            Iterable<FileNode>              files,
            Pattern                         regEx,
            Function<MatchResult, String>   replaceFunction,
            boolean                         askFirst,
            IOExceptionHandler              ioeh,
            Appendable                      outputSaver,
            boolean                         useUNIXColors,
            Verbosity                       verbosity
        )
        throws IOException
    {
        return SingleLine.regExMatch
            (files, regEx, replaceFunction, askFirst, ioeh, outputSaver, useUNIXColors, verbosity);
    }


    // ********************************************************************************************
    // ********************************************************************************************
    // Multi-Line String-Match
    // ********************************************************************************************
    // ********************************************************************************************


    /**
     * Convenience Method.
     * <BR />Invokes: {@link #multiLine(Iterable, String, String, boolean, IOExceptionHandler,
     *      Appendable, boolean, Verbosity)}
     * <BR />Passes: <CODE>
     *      <B STYLE='color: blue'>askFirst</B>: TRUE,
     *      <B STYLE='color: blue'>ioeh</B>: null,
     *      <B STYLE='color: blue'>outputSaver</B>: null,
     *      <B STYLE='color: blue'>useUNIXColors</B>: TRUE,
     *      <B STYLE='color: blue'>verbosity</B>: Verbosity.Normal
     * </CODE>
     */
    public static List<FileNode> multiLine
        (Iterable<FileNode> files, String matchStr, String replaceStr)
        throws IOException
    {
        return MultiLine.strMatch
            (files, matchStr, replaceStr, true, null, null, true, Verbosity.Normal);
    }

    /**
     * Makes updates to Text-Files listed in parameter {@code 'files'}.
     * 
     * <BR /><BR /><B CLASS=JDDescLabel>Single-Line &amp; Multi-Line:</B>
     * 
     * <BR />If either parameters {@code 'matchStr'} or {@code 'replaceStr'} receive a
     * {@code String} that does not actually contain any new-line characters ({@code '\n'}),
     * <I>this method will not throw any exceptions.  All User-Requested Text-Updates will be
     * processed to completion!</I>
     * 
     * <BR /><BR />This somewhat in contrast to method {@code 'singleLine'} which will, indeed, 
     * throw an {@code IllegalArgumentException} if either {@code 'matchStr'} or
     * {@code 'replaceStr'} contain newline characters.
     * 
     * @param matchStr Any Java {@code String}-Literal.
     * 
     * @param replaceStr For all {@code 'files'}, each instance of the {@code String}-Literal
     * parameter {@code 'matchStr'} found in any file will be replaced by {@code 'replaceStr'}.
     * 
     * @return A list of {@link FileNode} instances whose File-Contents were modified / updated
     * by this method.
     * 
     * @throws AppendableError <EMBED CLASS='external-html' DATA-FILE-ID=SED_APPEND_ERROR>
     * @throws IOException     On File-System read / write error.
     */
    public static List<FileNode> multiLine(
            Iterable<FileNode>  files,
            String              matchStr,
            String              replaceStr,
            boolean             askFirst,
            IOExceptionHandler  ioeh,
            Appendable          outputSaver,
            boolean             useUNIXColors,
            Verbosity           verbosity
        )
        throws IOException
    {
        return MultiLine.strMatch
            (files, matchStr, replaceStr, askFirst, ioeh, outputSaver, useUNIXColors, verbosity);
    }


    // ********************************************************************************************
    // ********************************************************************************************
    // Multi-Line Regular-Expression Match
    // ********************************************************************************************
    // ********************************************************************************************


    /**
     * Convenience Method.
     * <BR />Invokes: {@link #multiLine(Iterable, Pattern, Function, boolean, IOExceptionHandler,
     *      Appendable, boolean, Verbosity)}
     * <BR />Passes: <CODE>
     *      <B STYLE='color: blue'>askFirst</B>: TRUE,
     *      <B STYLE='color: blue'>ioeh</B>: null,
     *      <B STYLE='color: blue'>outputSaver</B>: null,
     *      <B STYLE='color: blue'>useUNIXColors</B>: TRUE,
     *      <B STYLE='color: blue'>verbosity</B>: Verbosity.Normal
     * </CODE>
     */
    public static List<FileNode> multiLine
        (Iterable<FileNode> files, Pattern regEx, Function<MatchResult, String> replaceFunction)
        throws IOException
    {
        return MultiLine.regExMatch
            (files, regEx, replaceFunction, true, null, null, true, Verbosity.Normal);
    }

    /**
     * Makes updates to Text-Files listed in parameter {@code 'files'}.
     * 
     * <BR /><BR /><B CLASS=JDDescLabel>Single-Line &amp; Multi-Line:</B>
     * 
     * <BR />When unsure whether to use the {@code 'singleLine'} versus the {@code 'multiLine'}
     * variants class {@coce 'SED'}, given a {@code 'regEx'} that might match either one or many
     * lines of text - <I>always use the Multi-Line Version (this method) to avoid exception
     * throws!</I>
     * 
     * <BR /><BR />The main differences between methods {@code 'singleLine'} and
     * {@code 'multiLine'} lays with the formatting of the User-Output Text that is printed by this
     * method as matches are found.
     * 
     * <BR /><BR />This method can easily handle falling back to printing only one line of
     * Matching-Text.  However, {@code 'singleLine'} is simply incapable of printing a Multi-Line
     * Regular-Expression Match, and will instead throw an exception if such a match is identified.
     * 
     * @param regEx Any Java Regular-Expression.  This will be used to match against the text
     * inside each file returned by the {@code Iterable} parameter {@code 'files'}.
     * 
     * <BR /><BR />The Regular-Expression passed may match any number of characters or lines in the
     * Source-File.  If just one line of the original file is matched by {@code 'regEx'}, this is
     * perfectly fine and will not produce any exception throws.
     * 
     * <BR /><BR />This method's User Output-Text Printing-Mechanism is written to handle 
     * {@code 'regEx'} Match-Text of any size.
     * 
     * @param replaceFunction Any Lambda-Expression or Function-Pointer that accepts a
     * {@code java.util.regex.MatchResult}, and returns a Replacement-{@code String}.
     * 
     * @return A list of {@link FileNode} instances whose File-Contents were modified / updated
     * by this method.
     * 
     * @throws AppendableError <EMBED CLASS='external-html' DATA-FILE-ID=SED_APPEND_ERROR>
     * @throws IOException     On File-System read / write error.
     */
    public static List<FileNode> multiLine(
            Iterable<FileNode>              files,
            Pattern                         regEx,
            Function<MatchResult, String>   replaceFunction,
            boolean                         askFirst,
            IOExceptionHandler              ioeh,
            Appendable                      outputSaver,
            boolean                         useUNIXColors,
            Verbosity                       verbosity
        )
        throws IOException
    {
        return MultiLine.regExMatch(
            files, regEx, replaceFunction, askFirst, ioeh, outputSaver,
            useUNIXColors, verbosity
        );
    }
}