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