001package Torello.Java;
002
003import java.util.function.*;
004import java.util.regex.*;
005import java.io.*;
006
007import static Torello.Java.C.*;
008
009/**
010 * A functional-interface for specifying a regular-expression, and how to print the results,
011 * to the <CODE>GREP</CODE> Tool.
012 * 
013 * <EMBED CLASS='external-html' DATA-FILE-ID=SEARCH_AND_PRNT>
014 */
015@FunctionalInterface
016public interface SearchAndPrint extends java.io.Serializable
017{
018    /** <EMBED CLASS='external-html' DATA-FILE-ID=SVUIDFI>  */
019    public static final long serialVersionUID = 1;
020
021    /**
022     * <EMBED CLASS='external-html' DATA-FILE-ID=FUNC_INTER_METH>
023     *
024     * @param fileName The name of any file, from the File-System.
025     *
026     * @param fileContents The contents of that file as a {@code String}.
027     *
028     * @return A {@code TRUE} value should mean that the {@code FileNode} has passed the match test.
029     * A return value of {@code FALSE} should indicate that the {@code FileNode} did not contain any
030     * matches for the {@code GREP} search routine.
031     *
032     * <BR /><BR /><B><SPAN STYLE="color: red;">IMPORTANT NOTE:</B></SPAN> This method is also
033     * expected to perform any printing and match-evaluating that needs to be performed when
034     * {@code GREP}'ING a directory, or directory-tree, of files.  This is in addition to the
035     * simple job of returning a true/false boolean indicating whether a match occurred.
036     */
037    public boolean test(String fileName, String fileContents) throws IOException;
038
039
040    // ********************************************************************************************
041    // ********************************************************************************************
042    // ALL, String-Token
043    // ********************************************************************************************
044    // ********************************************************************************************
045
046
047    /**
048     * This static-factory 'builder' method merely returns a C-Styled Function-Pointer, which in
049     * Java is referred to as a {@code FunctionalInterface} and may also be called a Lambda-Target.
050     * 
051     * <BR /><BR />The function that is returned will simply look through the contents of a file
052     * which here is just called {@code 'fileContents'} (in the Method Body Code below) for a
053     * particular substring {@code 'token.'}
054     *
055     * <BR /><BR /><B CLASS=JDDescLabel>Matches Returned:</B>
056     * 
057     * <BR />All Regular-Expression Matches found in {@code 'fileContents'} shall be sent to the
058     * User-Provided {@code Appendable} parameter {@code 'a'}.
059     * 
060     * @param token The UNIX-GREP command scours a single-file, or all files in a directory or tree
061     * of directories for a particular {@code String}-token.  This parameter {@code 'token'} is the
062     * {@code String} that the {@code GREP} Search-Logic will be using to search the files in the
063     * File-System.
064     *
065     * @param ignoreCase This parameter will inform the Search-Logic to ignore case sensitivity
066     * during the string comparisons of file-content.
067     *
068     * @param a Uses {@code java.lang.Appendable} to receive output-text information/messages.
069     * If this parameter is 'null', then output will not be printed, with the intention being,
070     * {@code GREP's} return value {@code Vector<FileNode>} is sufficient of a return-value.
071     *
072     * <EMBED CLASS='external-html' DATA-FILE-ID=APPENDABLE>
073     *
074     * @param useUNIXColorChars If both of the following are true, then UNIX Color-Code Characters
075     * will be appended to the output stream.
076     *
077     * @return An instance of {@code 'SearchAndPrint'} that obeys these search-criteria, and
078     * sends output to the specified destination.
079     */
080    public static SearchAndPrint ALL
081        (String token, boolean ignoreCase, boolean useUNIXColorChars, Appendable a)
082    {
083        // FAIL-FAST: Before building a Lambda-Predicate, make sure the testing-supplies work.
084        if (token == null)  throw new NullPointerException
085            ("String-Parameter 'token' was passed null to static-factory method 'ALL'.");
086
087        // Build & Generate an instance of 'SearchAndPrint', a @FunctionalInterface that is highly
088        // similar to java.util.function.BiPredicate (but throws IOException), and return it.
089
090        return (String fileName, String fileContents) ->
091            findAndPrint
092                (fileName, fileContents, token, -1, ignoreCase, true, useUNIXColorChars, a);
093    }
094
095
096    // ********************************************************************************************
097    // ********************************************************************************************
098    // All, Regular-Expression
099    // ********************************************************************************************
100    // ********************************************************************************************
101
102
103    /**
104     * This static-factory 'builder' method merely returns a C-Styled Function-Pointer, which in
105     * Java is referred to as a {@code FunctionalInterface} and may also be called a Lambda-Target.
106     * 
107     * <BR /><BR />The function that is returned will simply look through the contents of a file
108     * which here is just called {@code 'fileContents'} (in the Method Body Code below) using a
109     * Regular-Expression Matcher, which here is {@code Pattern} parameter {@code 'regEx'}.
110     *
111     * <BR /><BR /><B CLASS=JDDescLabel>Matches Returned:</B>
112     * 
113     * <BR />All Regular-Expression Matches found in {@code 'fileContents'} shall be sent to the
114     * User-Provided {@code Appendable} parameter {@code 'a'}.
115     * 
116     * @param regEx The UNIX-GREP command scours a single-file, or all files in a directory or tree
117     * of directories for a match using a regular-expression matcher.  This parameter 
118     * {@code regEx}.
119     *
120     * @param a Uses {@code java.lang.Appendable} to receive output-text information/messages.
121     * If this parameter is 'null', then output will not be printed, with the intention being,
122     * {@code GREP's} return value {@code Vector<FileNode>} is sufficient of a return-value.
123     *
124     * <EMBED CLASS='external-html' DATA-FILE-ID=APPENDABLE>
125     *
126     * @param useUNIXColorChars If both of the following are true, then UNIX Color-Code Characters
127     * will be appended to the output stream.
128     *
129     * @return An instance of {@code 'SearchAndPrint'} that obeys these search-criteria, and sends
130     * output to the specified destination.
131     */
132    public static SearchAndPrint ALL(Pattern regEx, boolean useUNIXColorChars, Appendable a)
133    {
134        // FAIL-FAST: Before building a Lambda-Predicate, make sure the testing-supplies work.
135        if (regEx == null)  throw new NullPointerException(
136            "Regular-Expression Parameter 'regEx' was passed null to static-factory method " +
137            "'ALL'."
138        );
139
140        // Build & Generate an instance of 'SearchAndPrint', a @FunctionalInterface that is highly
141        // similar to java.util.function.BiPredicate (but throws IOException), and return it.
142
143        return (String fileName, String fileContents) ->
144            findAndPrint(fileName, fileContents, regEx, -1, useUNIXColorChars, a);
145    }
146
147
148    // ********************************************************************************************
149    // ********************************************************************************************
150    // FIRSTN, String-Token
151    // ********************************************************************************************
152    // ********************************************************************************************
153
154
155    /**
156     * This static-factory 'builder' method merely returns a C-Styled Function-Pointer, which in
157     * Java is referred to as a {@code FunctionalInterface} and may also be called a Lambda-Target.
158     * 
159     * <BR /><BR />The function that is returned will simply look through the contents of a file
160     * which here is just called {@code 'fileContents'} (in the Method Body Code below) for a
161     * particular substring {@code 'token'}.
162     *
163     * <BR /><BR /><B CLASS=JDDescLabel>Matches Returned:</B>
164     * 
165     * <BR />The first {@code 'n'} number of matches shall be sent to the User-Provided
166     * {@code Appendable} parameter {@code 'a'}.
167     * 
168     * @param token The UNIX-GREP command scours a single-file, or all files in a directory or tree
169     * of directories for a particular {@code String}-token.  This parameter {@code 'token'} is the
170     * {@code String} that the {@code GREP} Search-Logic will be using to search the files in the
171     * File-System.
172     *
173     * @param n This will put a 'maximum-count' on the number of {@code String}-token-matches
174     * that the {@code GREP} Class / Command will return.  Here, {@code 'n'} must be a
175     * positive-integer (greater-than 0), or an {@code 'NException'} will throw.
176     *
177     * @param ignoreCase This parameter will inform the Search-Logic to ignore case sensitivity
178     * during the {@code String} comparisons of file-content.
179     *
180     * @param a Uses {@code java.lang.Appendable} to receive output-text information/messages.
181     * If this parameter is 'null', then output will not be printed, with the intention being,
182     * {@code GREP's} return value {@code Vector<FileNode>} is sufficient of a return-value.
183     *
184     * <EMBED CLASS='external-html' DATA-FILE-ID=APPENDABLE>
185     *
186     * @param useUNIXColorChars If both of the following are true, then UNIX Color-Code
187     * Characters will be appended to the output stream.
188     *
189     * @return An instance of {@code 'SearchAndPrint'} that obeys these search-criteria, and
190     * sends output to the specified destination.
191     *
192     * @throws NException If {@code 'n'} is a negative integer, or zero.
193     */
194    public static SearchAndPrint FIRSTN
195        (String token, int n, boolean ignoreCase, boolean useUNIXColorChars, Appendable a)
196    {
197        // FAIL-FAST: Before building a Lambda-Predicate, make sure the testing-supplies work.
198        if (token == null)  throw new NullPointerException
199            ("String-Parameter 'token' was passed null to static-factory method 'FIRSTN'.");
200
201        // FAIL-FAST: Simple check on the value of 'n' - to make sure n is not negative, or zero.
202        NException.check(n);
203
204        // Build & Generate an instance of 'SearchAndPrint', a @FunctionalInterface that is highly
205        // similar to java.util.function.BiPredicate (but throws IOException), and return it.
206
207        return (String fileName, String fileContents) ->
208            findAndPrint(fileName, fileContents, token, n, ignoreCase, true, useUNIXColorChars, a);
209    }
210
211
212    // ********************************************************************************************
213    // ********************************************************************************************
214    // FIRSTN, Regular-Expression
215    // ********************************************************************************************
216    // ********************************************************************************************
217
218
219    /**
220     * This static-factory 'builder' method merely returns a C-Styled Function-Pointer, which in
221     * Java is referred to as a {@code FunctionalInterface} and may also be called a Lambda-Target.
222     * 
223     * <BR /><BR />The function that is returned will simply look through the contents of a file
224     * which here is just called {@code 'fileContents'} (in the Method Body Code below) using a
225     * Regular-Expression Matcher, which here is {@code Pattern} parameter {@code 'regEx'}.
226     *
227     * <BR /><BR /><B CLASS=JDDescLabel>Matches Returned:</B>
228     * 
229     * <BR />The first {@code 'n'} number of matches shall be sent to the User-Provided
230     * {@code Appendable} parameter {@code 'a'}.
231     * 
232     * @param regEx The UNIX-GREP command scours a single-file, or all files in a directory or tree
233     * of directories for a match using a regular-expression matcher.  This parameter 
234     * {@code 'regEx'}.
235     *
236     * @param n This will put a 'maximum-count' on the number of regular-expression matches that
237     * the {@code GREP} Class / Command will return.  Here, {@code 'n'} must be a positive-integer
238     * (greater-than 0), or an {@code 'NException'} will throw.
239     *
240     * @param a Uses {@code java.lang.Appendable} to receive output-text information/messages.
241     * If this parameter is 'null', then output will not be printed, with the intention being,
242     * {@code GREP's} return value {@code Vector<FileNode>} is sufficient of a return-value.
243     *
244     * <EMBED CLASS='external-html' DATA-FILE-ID=APPENDABLE>
245     *
246     * @param useUNIXColorChars If both of the following are true, then UNIX Color-Code Characters
247     * will be appended to the output stream.
248     *
249     * @return An instance of {@code 'SearchAndPrint'} that obeys these search-criteria, and sends
250     * output to the specified destination.
251     *
252     * @throws NException If {@code 'n'} is a negative integer, or zero.
253     */
254    public static SearchAndPrint FIRSTN
255        (Pattern regEx, int n, boolean useUNIXColorChars, Appendable a)
256    { 
257        // FAIL-FAST: Before building a Lambda-Predicate, make sure the testing-supplies work.
258        if (regEx == null)  throw new NullPointerException(
259            "Regular-Expression Parameter 'regEx' was passed null to static-factory method " +
260            "'FIRSTN'."
261        );
262
263        // FAIL-FAST: Simple check on the value of 'n' - to make sure n is not negative, or zero.
264        NException.check(n);
265
266        // Build & Generate an instance of 'SearchAndPrint', a @FunctionalInterface that is highly
267        // similar to java.util.function.BiPredicate (but throws IOException), and return it.
268
269        return (String fileName, String fileContents) -> 
270            findAndPrint(fileName, fileContents, regEx, n, useUNIXColorChars, a);
271    }
272
273
274    // ********************************************************************************************
275    // ********************************************************************************************
276    // LASTN, String-Token
277    // ********************************************************************************************
278    // ********************************************************************************************
279
280
281    /**
282     * This static-factory 'builder' method merely returns a C-Styled Function-Pointer, which in
283     * Java is referred to as a {@code FunctionalInterface} and may also be called a Lambda-Target.
284     * 
285     * <BR /><BR />The function that is returned will simply look through the contents of a file
286     * which here is just called {@code 'fileContents'} (in the Method Body Code below), for a
287     * particular substring {@code 'token'}.
288     * 
289     * <BR /><BR /><B CLASS=JDDescLabel>Matches Returned:</B>
290     * 
291     * <BR />The last {@code 'n'} number of matches shall be sent to the User-Provided
292     * {@code Appendable} parameter {@code 'a'}.
293     * 
294     * @param token The UNIX-GREP command scours a single-file, or all files in a directory or tree
295     * of directories for a particular {@code String}-token.  This parameter {@code 'token'} is the
296     * {@code String} that the {@code GREP} Search-Logic will be using to search the files in the
297     * File-System.
298     * 
299     * @param n This will put a 'maximum-count' on the number of {@code String}-token-matches that
300     * the {@code GREP} Class / Command will return. Here, {@code 'n'} must be a positive-integer
301     * (greater-than 0), or an {@code 'NException'} will throw.
302     * 
303     * @param ignoreCase This parameter will inform the Search-Logic to ignore case sensitivity
304     * during the {@code String} comparisons of file-content.
305     * 
306     * @param a Uses {@code java.lang.Appendable} to receive output-text information/messages.  If
307     * this parameter is 'null', then output will not be printed, with the intention being,
308     * {@code GREP's} return value {@code Vector<FileNode>} is sufficient of a return-value.
309     * 
310     * <EMBED CLASS='external-html' DATA-FILE-ID=APPENDABLE>
311     * 
312     * @param useUNIXColorChars If both of the following are true, then UNIX Color-Code Characters
313     * will be appended to the output stream.
314     * 
315     * @return An instance of {@code 'SearchAndPrint'} that obeys these search-criteria, and sends
316     * output to the specified destination.
317     * 
318     * @throws NException If {@code 'n'} is a negative integer, or zero.
319     */
320    public static SearchAndPrint LASTN
321        (String token, int n, boolean ignoreCase, boolean useUNIXColorChars, Appendable a)
322    {
323        // FAIL-FAST: Before building a Lambda-Predicate, make sure the testing-supplies work.
324        if (token == null)  throw new NullPointerException
325            ("String-Parameter 'token' was passed null to static-factory method 'FIRSTN'.");
326
327        // FAIL-FAST: Simple check on the value of 'n' - to make sure n is not negative, or zero.
328        NException.check(n);
329
330        // Build & Generate an instance of 'SearchAndPrint', a @FunctionalInterface that is highly
331        // similar to java.util.function.BiPredicate (but throws IOException), and return it.
332
333        return (String fileName, String fileContents) ->
334            findAndPrint
335                (fileName, fileContents, token, n, ignoreCase, false, useUNIXColorChars, a);
336    }
337
338
339    // ********************************************************************************************
340    // ********************************************************************************************
341    // PROTECTED TOP-LEVEL DISPATCH METHODS
342    // ********************************************************************************************
343    // ********************************************************************************************
344
345
346    /**
347     * This will search through a file using a regular-expression.
348     *
349     * @param numMatches This is the number of matches to print before exiting.  Each match will
350     * decrement the counter by 1, and this method shall exit once this reaches zero, or once the
351     * number of matches has exceeded this count.
352     *
353     * <BR /><BR /><B>NOTE:</B> Setting this parameter to a negative number will disable the
354     * counter, and all matches will be returned.
355     *
356     * @return {@code TRUE} if there was match in the file, and {@code FALSE} otherwise.
357     */
358    static boolean findAndPrint(
359            String fileName, String fileContents, Pattern regEx, int numMatches,
360            boolean useUNIXColorChars, Appendable a
361        )
362        throws IOException
363    {
364        Matcher m   = regEx.matcher(fileContents);
365        boolean ret = false;
366
367        while ((numMatches != 0) && m.find())
368        {
369            ret = true;
370
371            if (a != null) a.append(
372                BRED + "File: " + RESET + BYELLOW + fileName + RESET + ", " +
373                BRED + "Line: " + RESET + StrPrint.lineOrLines
374                    (fileContents, m.start(), m.end() - m.start(), BCYAN) + '\n'
375            );
376
377            if (numMatches > 0) numMatches--;
378        }
379
380        return ret;
381    }
382
383    /**
384     * This will search through a file by looking for a particular {@code String} 'token.'
385     *=
386     * @param numMatches This is the number of matches to print before exiting.  Each match will
387     * decrement the counter by 1, and this method shall exit once this reaches zero, or once the
388     * number of matches has exceeded this count.
389     *
390     * <BR /><BR /><B>NOTE:</B> Setting this parameter to a negative number will disable the
391     * counter, and all matches will be returned.
392     *
393     * @param forwardOrReverse When this parameter is {@code TRUE}, then the search shall begin at
394     * the start of the file-contents, and continue forward towards the End-Of-File.  When this
395     * parameter is {@code FALSE}, the {@code String}-Token Matching shall start at the End-Of-File,
396     * and work towards the beginning.
397     *
398     * @return {@code TRUE} if there was match in the file, and {@code FALSE} otherwise.
399     */
400    static boolean findAndPrint(
401            String fileName, String fileContents, String token, int numMatches, boolean ignoreCase,
402            boolean forwardOrReverse, boolean useUNIXColorChars, Appendable a
403        )
404        throws IOException
405    {
406        int     pos = forwardOrReverse ? 0 : (fileContents.length() - 1);
407        int     len = token.length();
408        boolean ret = false;
409
410        // IGNORE-CASE
411        if (ignoreCase)
412        {
413            // Search Forward, from beginning of fileContents
414            if (forwardOrReverse)
415
416                while (     (numMatches != 0)
417                        &&  ((pos = StrIndexOf.first_CI(fileContents, pos, -1, token)) != -1)
418                )
419                {
420                    ret = true;
421
422                    if (a != null) a.append(
423                        BRED + "File: " + RESET + BYELLOW + fileName + RESET + ", " +
424                        BRED + "Line: " + RESET + StrPrint.lineOrLines
425                            (fileContents, pos, len, BCYAN) + '\n'
426                    );
427
428                    pos += len;
429                    if (numMatches > 0) numMatches--;
430                }
431
432            // Search Backwards, from ending of fileContents
433            else
434
435                while (     (numMatches != 0)
436                        &&  ((pos = StrIndexOf.left(fileContents, pos, token)) != -1)
437                )
438                {
439                    ret = true;
440
441                    if (a != null) a.append(
442                        BRED + "File: " + RESET + BYELLOW + fileName + RESET + ", " +
443                        BRED + "Line: " + RESET + StrPrint.lineOrLines
444                            (fileContents, pos, len, BCYAN) + '\n'
445                    );
446
447                    pos--;
448                    if (numMatches > 0) numMatches--;
449                }
450        }   
451
452        // else DO-NOT IGNORE-CASE
453        else
454        {
455            // Search Forward, from beginning of fileContents
456            if (forwardOrReverse)
457
458                while (     (numMatches != 0)
459                        &&  ((pos = fileContents.indexOf(token, pos)) != -1)
460                )
461                {
462                    ret = true;
463
464                    if (a != null) a.append(
465                        BRED + "File: " + RESET + BYELLOW + fileName + RESET + ", " +
466                        BRED + "Line: " + RESET + StrPrint.lineOrLines
467                            (fileContents, pos, len, BCYAN) + '\n'
468                    );
469
470                    pos += len;
471                    if (numMatches > 0) numMatches--;
472                }
473
474            // Search Backwards, from ending of fileContents
475            else
476                while (     (numMatches != 0)
477                        &&  ((pos = StrIndexOf.left(fileContents, pos, token)) != -1)
478                )
479                {
480                    ret = true;
481
482                    if (a != null) a.append(
483                        BRED + "File: " + RESET + BYELLOW + fileName + RESET + ", " +
484                        BRED + "Line: " + RESET + StrPrint.lineOrLines
485                            (fileContents, pos, len, BCYAN) + '\n'
486                    );
487
488                    pos--;
489                    if (numMatches > 0) numMatches--;
490                }
491        }
492
493        return ret;
494    }
495}