001package Torello.Java;
002
003import static Torello.Java.C.*;
004
005import java.io.IOException;
006import java.io.FilenameFilter;
007import java.io.File;
008
009import java.util.List;
010import java.util.regex.Pattern;
011import java.util.regex.MatchResult;
012import java.util.function.Function;
013
014import Torello.JavaDoc.LinkJavaSource;
015
016/**
017 * Based on the UNIX {@code 'SED'} Command, this class updates files based on
018 * <B STYLE='color: red;'><I>either</I></B> {@code String}-Literal matches &amp; replacements, or
019 * <B STYLE='color: red;'><I>or</I></B> Regular-Expression matches.
020 * 
021 * <EMBED CLASS='external-html' DATA-FILE-ID=SED>
022 */
023@Torello.JavaDoc.StaticFunctional
024public class SED
025{
026    private SED() { }
027
028
029    // ********************************************************************************************
030    // ********************************************************************************************
031    // Some Filters
032    // ********************************************************************************************
033    // ********************************************************************************************
034
035
036    /** A {@code java.io.FilenameFilter} that looks for JavaDoc Upgrader HTML-Files. */
037    public static final FilenameFilter filterFilesHTML = (File dir, String fileName) ->
038    {
039        if (! fileName.endsWith(".html")) return false;
040
041        if (! StrCmpr.containsAND(dir.getPath(), "upgrade-files", "external-html")) return false;
042
043        return true;
044    };
045
046    /** A {@code java.io.FilenameFilter} that looks for {@code '.java'} Files. */
047    public static final FilenameFilter filterFilesJava = (File dir, String fileName) ->
048    {
049        if (! fileName.endsWith(".java")) return false;
050
051        return true;
052    };
053
054
055    // ********************************************************************************************
056    // ********************************************************************************************
057    // Single-Line String-Match
058    // ********************************************************************************************
059    // ********************************************************************************************
060
061
062    /**
063     * Convenience Method.
064     * <BR />Invokes: {@link #singleLine(Iterable, String, String, boolean, IOExceptionHandler,
065     *      Appendable, boolean, Verbosity)}
066     * <BR />Passes: <CODE>
067     *      <B STYLE='color: blue'>askFirst</B>: TRUE,
068     *      <B STYLE='color: blue'>ioeh</B>: null,
069     *      <B STYLE='color: blue'>outputSaver</B>: null,
070     *      <B STYLE='color: blue'>useUNIXColors</B>: TRUE,
071     *      <B STYLE='color: blue'>verbosity</B>: Verbosity.Normal
072     * </CODE>
073     */
074    @LinkJavaSource(handle="SingleLineStrMatch")
075    @LinkJavaSource(handle="PrintingRecSingleLine")
076    public static List<FileNode> singleLine
077        (Iterable<FileNode> files, String matchStr, String replaceStr)
078        throws IOException
079    {
080        return SingleLineStrMatch.match
081            (files, matchStr, replaceStr, true, null, null, true, Verbosity.Normal);
082    }
083
084    /**
085     * Makes updates to Text-Files listed in parameter {@code 'files'}.
086     * 
087     * <BR /><BR />In the example below, all {@code '.java'} Source-Code Files in the Java-HTML
088     * Library are loaded into a {@code Vector<FileNode>}.  Using this particular {@code 'SED'}
089     * method, each substring that looks like <B STYLE='color: blue'>{@code <B>TRUE</B>}</B> (in
090     * all {@code '.java'} Source-Files) is converted to look like
091     * <B STYLE='color: blue'><CODE>&lcub;&commat;code TRUE&rcub;</CODE></B>
092     * 
093     * <DIV CLASS=EXAMPLE>{@code
094     * StringBuilder log = new StringBuilder();
095     * 
096     * // Load all Java-HTML Source-Files (in all packages) into a FileNode-Vector
097     * Vector<FileNode> files = FileNode
098     *      .createRoot("Torello/")
099     *      .loadTree(-1, SED.filterFilesJava, null)
100     *      .flattenJustFiles(RTC.VECTOR());
101     * 
102     * // Invoke this method
103     * singleLine(files, "<B>TRUE</B>", "{@code TRUE}", true, null, log, true, Verbosity.Normal);
104     * 
105     * // Save the Changes that were made to a log
106     * FileRW.writeFile(log, "ChangesMade.log.txt");
107     * }</DIV>
108     * 
109     * @param matchStr Any Java {@code String}-Literal.  It is required that this {@code String}
110     * not contain any new-line ({@code '\n'}) characters, or an {@code IllegalArgumentException}
111     * will throw.
112     * 
113     * @param replaceStr For all {@code 'files'}, each instance of the {@code String}-Literal
114     * parameter {@code 'matchStr'} found in any file will be replaced by {@code 'replaceStr'}.
115     * 
116     * <BR /><BR />As with parameter {@code 'matchStr'}, if this {@code String} contains any
117     * new-line ({@code '\n'}) characters, an {@code IllegalArgumentException} will throw.
118     * 
119     * @return A list of {@link FileNode} instances whose File-Contents were modified / updated
120     * by this method.
121     * 
122     * @throws IllegalArgumentException If either of the {@code String} parameters
123     * {@code 'matchStr'} or {@code 'replaceStr'} contain any newline ({@code '\n'}) characters.
124     * 
125     * <BR /><BR />If a {@code 'SED'} replacement needs to be done using a {@code String}-Match
126     * that spans more than one line, use the Multi-Line method provided in this class.
127     * 
128     * @throws AppendableError <EMBED CLASS='external-html' DATA-FILE-ID=SED_APPEND_ERROR>
129     * @throws IOException     On File-System read / write error.
130     */
131    @LinkJavaSource(handle="SingleLineStrMatch")
132    @LinkJavaSource(handle="PrintingRecSingleLine")
133    public static List<FileNode> singleLine(
134            Iterable<FileNode>  files,
135            String              matchStr,
136            String              replaceStr,
137            boolean             askFirst,
138            IOExceptionHandler  ioeh,
139            Appendable          outputSaver,
140            boolean             useUNIXColors,
141            Verbosity           verbosity
142        )
143        throws IOException
144    {
145        return SingleLineStrMatch.match
146            (files, matchStr, replaceStr, askFirst, ioeh, outputSaver, useUNIXColors, verbosity);
147    }
148
149
150    // ********************************************************************************************
151    // ********************************************************************************************
152    // Single-Line Regular-Expression-Match
153    // ********************************************************************************************
154    // ********************************************************************************************
155
156
157    /**
158     * Convenience Method.
159     * <BR />Invokes: {@link #singleLine(Iterable, Pattern, Function, boolean, IOExceptionHandler,
160     *      Appendable, boolean, Verbosity)}
161     * <BR />Passes: <CODE>
162     *      <B STYLE='color: blue'>askFirst</B>: TRUE,
163     *      <B STYLE='color: blue'>ioeh</B>: null,
164     *      <B STYLE='color: blue'>outputSaver</B>: null,
165     *      <B STYLE='color: blue'>useUNIXColors</B>: TRUE,
166     *      <B STYLE='color: blue'>verbosity</B>: Verbosity.Normal
167     * </CODE>
168     */
169    @LinkJavaSource(handle="SingleLineRegExMatch")
170    @LinkJavaSource(handle="PrintingRecSingleLine")
171    public static List<FileNode> singleLine
172        (Iterable<FileNode> files, Pattern regEx, Function<MatchResult, String> replaceFunction)
173        throws IOException
174    {
175        return SingleLineRegExMatch.match
176            (files, regEx, replaceFunction, true, null, null, true, Verbosity.Normal);
177    }    
178
179    /**
180     * Makes updates to Text-Files listed in parameter {@code 'files'}.  Matches that are eligible
181     * for replacement are identified using the Regular-Expression parameter {@code 'regEx'}.
182     * 
183     * <BR /><BR />Replacement-{@code String's} are obtained by invoking the
184     * {@code 'replaceFunction'} parameter.
185     * 
186     * <BR /><BR />In the example below, all Java HTML Library Source-Files are loaded into a 
187     * {@code Vector<FileNode>}, and then iterated by this method.  In each file, any instances of
188     * an {@code EMBED}-Tag that contains a {@code FILE-ID} Data-Attribute whose value is wrapped
189     * in Double Quotation-Marks has that value extracted and re-inserted without any
190     * Quotation-Marks.
191     * 
192     * <DIV CLASS=EXAMPLE>{@code
193     * StringBuilder log = new StringBuilder();
194     * 
195     * // Load all Java-HTML Source-Files (in all packages) into a FileNode-Vector
196     * Vector<FileNode> files = FileNode
197     *      .createRoot("Torello/")
198     *      .loadTree(-1, filterFilesJava, null)
199     *      .flattenJustFiles(RTC.VECTOR());
200     * 
201     * // Matches an <EMBED> Tag's FILE-ID Data-Attribute.  There are parenthesis placed around the
202     * // actual ID-Name.  The contents of the parenthesis are "Reg-Ex Match-Group #1"
203     * 
204     * Pattern regEx = Pattern.compile("DATA-FILE-ID=\"(\\w+)\"");
205     * 
206     * // This Function extracts Reg-Ex Group-1, and re-inserts it **WITHOUT** the Double
207     * // Quotation-Marks that were placed around it.  Look Closely at Previous Line of Code, the
208     * // 'replacer' function does not include the Double-Quotes.
209     * 
210     * Function<MatchResult, String> replacer = (MatchResult mr) -> "DATA-FILE-ID=" + mr.group(1);
211     *
212     * // Invoke this method
213     * singleLine(files, regEx, replacer, true, null, log, true, Verbosity.Normal);
214     * 
215     * // Write the changes that have been made to a log-file
216     * FileRW.writeFile(log, "ChangesMade.log.txt");
217     * }</DIV>
218     * 
219     * @param regEx Any Java Regular-Expression.  This will be used to match against the text
220     * inside each file returned by the {@code Iterable} parameter {@code 'files'}.
221     * 
222     * <BR /><BR />If this Regular-Expression returns a match that spans more than a single line of
223     * text, this method will throw an exception.  To perform matches that span multiple lines of 
224     * text, use the Multi-Line methods, provided by this class
225     * 
226     * @param replaceFunction Any Lambda-Expression or Function-Pointer that accepts a
227     * {@code java.util.regex.MatchResult}, and returns a Replacement-{@code String}.
228     * 
229     * <BR /><BR />If this Replace-Function returns a match that contains any newline {@code '\n'}
230     * characters, this method will throw a {@link RegExException}.
231     * 
232     * @return A list of {@link FileNode} instances whose File-Contents were modified / updated
233     * by this method.
234     * 
235     * @throws RegExExpression Throws if the {@code Pattern} parameter {@code 'regEx'} returns a
236     * match that covers more than one line of text in any of the files that are scanned. 
237     * 
238     * <BR /><BR />This exception will also throw if the Lambda-Target / Function-Pointer parameter
239     * {@code 'replaceFunction'} ever returns a {@code String} that spans multiple lines.
240     * 
241     * <BR /><BR />In both of these cases, the logic simply checks for the presence of a newline
242     * {@code '\n'} character.
243     * 
244     * @throws AppendableError <EMBED CLASS='external-html' DATA-FILE-ID=SED_APPEND_ERROR>
245     * @throws IOException     On File-System read / write error.
246     */
247    @LinkJavaSource(handle="SingleLineRegExMatch")
248    @LinkJavaSource(handle="PrintingRecSingleLine")
249    public static List<FileNode> singleLine(
250            Iterable<FileNode>              files,
251            Pattern                         regEx,
252            Function<MatchResult, String>   replaceFunction,
253            boolean                         askFirst,
254            IOExceptionHandler              ioeh,
255            Appendable                      outputSaver,
256            boolean                         useUNIXColors,
257            Verbosity                       verbosity
258        )
259        throws IOException
260    {
261        return SingleLineRegExMatch.match
262            (files, regEx, replaceFunction, askFirst, ioeh, outputSaver, useUNIXColors, verbosity);
263    }
264
265
266    // ********************************************************************************************
267    // ********************************************************************************************
268    // Multi-Line String-Match
269    // ********************************************************************************************
270    // ********************************************************************************************
271
272
273    /**
274     * Convenience Method.
275     * <BR />Invokes: {@link #multiLine(Iterable, String, String, boolean, IOExceptionHandler,
276     *      Appendable, boolean, Verbosity)}
277     * <BR />Passes: <CODE>
278     *      <B STYLE='color: blue'>askFirst</B>: TRUE,
279     *      <B STYLE='color: blue'>ioeh</B>: null,
280     *      <B STYLE='color: blue'>outputSaver</B>: null,
281     *      <B STYLE='color: blue'>useUNIXColors</B>: TRUE,
282     *      <B STYLE='color: blue'>verbosity</B>: Verbosity.Normal
283     * </CODE>
284     */
285    @LinkJavaSource(handle="MultiLineStrMatch")
286    @LinkJavaSource(handle="PrintingRecMultiLine")
287    public static List<FileNode> multiLine
288        (Iterable<FileNode> files, String matchStr, String replaceStr)
289        throws IOException
290    {
291        return MultiLineStrMatch.match
292            (files, matchStr, replaceStr, true, null, null, true, Verbosity.Normal);
293    }
294
295
296    /**
297     * Makes updates to Text-Files listed in parameter {@code 'files'}.
298     * 
299     * <BR /><BR /><B CLASS=JDDescLabel>Single-Line &amp; Multi-Line:</B>
300     * 
301     * <BR />If either parameters {@code 'matchStr'} or {@code 'replaceStr'} receive a
302     * {@code String} that does not actually contain any new-line characters ({@code '\n'}),
303     * <I>this method will not throw any exceptions.  All User-Requested Text-Updates will be
304     * processed to completion!</I>
305     * 
306     * <BR /><BR />This somewhat in contrast to method {@code 'singleLine'} which will, indeed, 
307     * throw an {@code IllegalArgumentException} if either {@code 'matchStr'} or
308     * {@code 'replaceStr'} contain newline characters.
309     * 
310     * @param matchStr Any Java {@code String}-Literal.
311     * 
312     * @param replaceStr For all {@code 'files'}, each instance of the {@code String}-Literal
313     * parameter {@code 'matchStr'} found in any file will be replaced by {@code 'replaceStr'}.
314     * 
315     * @return A list of {@link FileNode} instances whose File-Contents were modified / updated
316     * by this method.
317     * 
318     * @throws AppendableError <EMBED CLASS='external-html' DATA-FILE-ID=SED_APPEND_ERROR>
319     * @throws IOException     On File-System read / write error.
320     */
321    @LinkJavaSource(handle="MultiLineStrMatch")
322    @LinkJavaSource(handle="PrintingRecMultiLine")
323    public static List<FileNode> multiLine(
324            Iterable<FileNode>  files,
325            String              matchStr,
326            String              replaceStr,
327            boolean             askFirst,
328            IOExceptionHandler  ioeh,
329            Appendable          outputSaver,
330            boolean             useUNIXColors,
331            Verbosity           verbosity
332        )
333        throws IOException
334    {
335        return MultiLineStrMatch.match
336            (files, matchStr, replaceStr, askFirst, ioeh, outputSaver, useUNIXColors, verbosity);
337    }
338
339
340    // ********************************************************************************************
341    // ********************************************************************************************
342    // Multi-Line Regular-Expression Match
343    // ********************************************************************************************
344    // ********************************************************************************************
345
346
347    /**
348     * Convenience Method.
349     * <BR />Invokes: {@link #multiLine(Iterable, Pattern, Function, boolean, IOExceptionHandler,
350     *      Appendable, boolean, Verbosity)}
351     * <BR />Passes: <CODE>
352     *      <B STYLE='color: blue'>askFirst</B>: TRUE,
353     *      <B STYLE='color: blue'>ioeh</B>: null,
354     *      <B STYLE='color: blue'>outputSaver</B>: null,
355     *      <B STYLE='color: blue'>useUNIXColors</B>: TRUE,
356     *      <B STYLE='color: blue'>verbosity</B>: Verbosity.Normal
357     * </CODE>
358     */
359    @LinkJavaSource(handle="MultiLineRegExMatch")
360    @LinkJavaSource(handle="PrintingRecMultiLine")
361    public static List<FileNode> multiLine
362        (Iterable<FileNode> files, Pattern regEx, Function<MatchResult, String> replaceFunction)
363        throws IOException
364    {
365        return MultiLineRegExMatch.match
366            (files, regEx, replaceFunction, true, null, null, true, Verbosity.Normal);
367    }
368
369    /**
370     * Makes updates to Text-Files listed in parameter {@code 'files'}.
371     * 
372     * <BR /><BR /><B CLASS=JDDescLabel>Single-Line &amp; Multi-Line:</B>
373     * 
374     * <BR />When unsure whether to use the {@code 'singleLine'} versus the {@code 'multiLine'}
375     * variants class {@coce 'SED'}, given a {@code 'regEx'} that might match either one or many
376     * lines of text - <I>always use the Multi-Line Version (this method) to avoid exception
377     * throws!</I>
378     * 
379     * <BR /><BR />The main differences between methods {@code 'singleLine'} and
380     * {@code 'multiLine'} lays with the formatting of the User-Output Text that is printed by this
381     * method as matches are found.
382     * 
383     * <BR /><BR />This method can easily handle falling back to printing only one line of
384     * Matching-Text.  However, {@code 'singleLine'} is simply incapable of printing a Multi-Line
385     * Regular-Expression Match, and will instead throw an exception if such a match is identified.
386     * 
387     * @param regEx Any Java Regular-Expression.  This will be used to match against the text
388     * inside each file returned by the {@code Iterable} parameter {@code 'files'}.
389     * 
390     * <BR /><BR />The Regular-Expression passed may match any number of characters or lines in the
391     * Source-File.  If just one line of the original file is matched by {@code 'regEx'}, this is
392     * perfectly fine and will not produce any exception throws.
393     * 
394     * <BR /><BR />This method's User Output-Text Printing-Mechanism is written to handle 
395     * {@code 'regEx'} Match-Text of any size.
396     * 
397     * @param replaceFunction Any Lambda-Expression or Function-Pointer that accepts a
398     * {@code java.util.regex.MatchResult}, and returns a Replacement-{@code String}.
399     * 
400     * @return A list of {@link FileNode} instances whose File-Contents were modified / updated
401     * by this method.
402     * 
403     * @throws AppendableError <EMBED CLASS='external-html' DATA-FILE-ID=SED_APPEND_ERROR>
404     * @throws IOException     On File-System read / write error.
405     */
406    @LinkJavaSource(handle="MultiLineRegExMatch")
407    @LinkJavaSource(handle="PrintingRecMultiLine")
408    public static List<FileNode> multiLine(
409            Iterable<FileNode>              files,
410            Pattern                         regEx,
411            Function<MatchResult, String>   replaceFunction,
412            boolean                         askFirst,
413            IOExceptionHandler              ioeh,
414            Appendable                      outputSaver,
415            boolean                         useUNIXColors,
416            Verbosity                       verbosity
417        )
418        throws IOException
419    {
420        return MultiLineRegExMatch.match(
421            files, regEx, replaceFunction, askFirst, ioeh, outputSaver,
422            useUNIXColors, verbosity
423        );
424    }
425}