001package Torello.Java;
002
003import java.io.IOException;
004import java.util.*;
005
006/**
007 * A utility that attempts to mimic the UNIX command <CODE>'grep'</CODE>.
008 * <EMBED CLASS='external-html' DATA-FILE-ID=GREP>
009 */
010@Torello.JavaDoc.StaticFunctional
011public class GREP
012{
013    private GREP() { }
014
015    /**
016     * Convenience Method.
017     * <BR />Automatically Selects: {@link RTC#VECTOR()}
018     * <BR />Invokes: {@link #search(RTC, Iterator, SearchAndPrint, IOExceptionHandler)}
019     */
020    public static Vector<FileNode> search
021        (Iterable<FileNode> iterable, SearchAndPrint sp, IOExceptionHandler ioeh)
022    { return search(RTC.VECTOR(), iterable.iterator(), sp, ioeh); }
023
024    /**
025     * Convenience Method.
026     * <BR />Automatically Selects: {@link RTC#VECTOR()}
027     * <BR />Invokes: {@link #search(RTC, Iterator, SearchAndPrint, IOExceptionHandler)}.
028     */
029    public static Vector<FileNode> search
030        (Iterator<FileNode> iter, SearchAndPrint sp, IOExceptionHandler ioeh)
031    { return search(RTC.VECTOR(), iter, sp, ioeh); }
032
033    /**
034     * This version of GREP shall search every file in a java {@code Iterator<FileNode>}
035     * data-structure.  Any 'directories' (which are not files) returned by this {@code Iterator}
036     * shall be skipped / ignored.
037     *
038     * @param dataStructureChoice <EMBED CLASS='external-html' DATA-FILE-ID=FN_RTC_PARAM>
039     *
040     * @param iter Any Iterator of type {@code Iterator<FileNode>}.  Each file that is returned in
041     * the return set shall be searched for the described matches using {@code 'SearchAndPrint'}
042     * parameter {@code 'sp'}
043     *
044     * @param sp The {@code 'sp'} parameter is the implementation of the file-grep operation that
045     * the user is requesting.  The actions that this SearchAndPrint method-pointer should perform
046     * include:
047     *
048     * <BR /><BR /><UL CLASS=JDUL>
049     * <LI> Test if the file contains a match - using whatever string-testing procedure required.
050     *      Usually either a simple {@code String} token is being searched, or an entire 
051     *      {@code String} regular-expression is used.
052     *      </LI>
053     * 
054     * <LI> Do some kind of output printing to a writable printing parameter to report these
055     *      {@code String}-match results
056     *      </LI>
057     * 
058     * <LI> return either {@code TRUE} or {@code FALSE} to indicate whether or not a particular file
059     *      contained matches.
060     *      </LI>
061     * </UL>
062     *
063     * <BR /><BR /><B><SPAN STYLE="color: red;">IMPORTANT:</B></SPAN> This parameter cannot be
064     * null.  It is the core of the grep-search algorithm.  If the programmer wishes to rely on the
065     * standard 'token-search' or 'regular-expression-search' methods defined in
066     * {@code class 'SearchAndPrint'} - <I>then the programmer should simply use one of the
067     * standard factory methods provided in {@code 'SearchAndPrint'} to build an instance of
068     * this class.</I> 
069     *
070     * @param ioeh When performing these UNIX-GREP styled text-file searches, it becomes imperative
071     * to catch any potential {@code IOException's} so that if one file is failing to load to Java,
072     * the rest of the entire search will not be sacrificed or ceased.  Using this parameter allows
073     * a programmer to log exceptions, or take any user-defined action when any potential
074     * {@code IOException's} occur while reading the files in a directory tree.  Using this
075     * exception-handler allows the GREP-Search to continue, even if reading one particular file
076     * fails.
077     *
078     * <BR /><BR /><B>NOTE:</B> This parameter may be null, and if it is, it will be ignored -
079     * <I>and all exceptions shall suppressed</I>.  This means that no exception information will
080     * be reported back to the user.  If this parameter is null, and if an {@code IOException} is
081     * generated while traversing a particular file for GREP-search, that file will merely be
082     * skipped (gracefully), and its contents will not be searched.
083     *
084     * @return The return-list shall contain a reference of every instance of
085     * {@code FileNode} for which whose contents on the file-system contained a match - as
086     * determined by the {@code SearchAndPrint} instance that is passed to parameter {@code 'sp'}.
087     *
088     * <EMBED CLASS='external-html' DATA-FILE-ID=FN_RTC_RET>
089     *
090     * @see SearchAndPrint
091     * @see FileRW#loadFileToString(String)
092     * @see FileNode#isDirectory
093     * @see FileNode#toString()
094     * @see SearchAndPrint#test(String, String)
095     * @see IOExceptionHandler#accept(FileNode, IOException)
096     */
097    public static <T> T search(
098            RTC<T> dataStructureChoice, Iterator<FileNode> iter, SearchAndPrint sp,
099            IOExceptionHandler ioeh
100        )
101    { 
102        while (iter.hasNext())
103        {
104            FileNode fn = iter.next();
105
106            if (fn.isDirectory) continue;
107
108            try
109            {
110                String  fileContents    = FileRW.loadFileToString(fn.toString());
111                boolean wasMatch        = sp.test(fn.toString(), fileContents);
112
113                if (wasMatch) dataStructureChoice.inserter.accept(fn);
114            }
115
116            catch (IOException e)
117                { if (ioeh != null) ioeh.accept(fn, e); }
118        }
119
120        return dataStructureChoice.finisher.get();
121    }
122
123    /**
124     * This version of GREP shall search a single file only.  The passed parameter 'file' must be
125     * a {@code FileNode} instance that represents a UNIX or MS-DOS file, not a directory.
126     *
127     * @param file This is the file to be searched or "GREPPED."
128     *
129     * @param sp The {@code 'sp'} parameter is the implementation of the file-grep operation
130     * that the user is requesting.  The actions that this SearchAndPrint method-pointer should
131     * perform include:
132     *
133     * <BR /><BR /><UL CLASS=JDUL>
134     * <LI> Test if the file contains a match - using whatever string-testing procedure required.
135     *      Usually either a simple {@code String} token is being searched, or an entire 
136     *      {@code String} regular-expression is used.
137     *      </LI>
138     * 
139     * <LI> Do some kind of output printing to a writable printing parameter to report these
140     *      {@code String}-match results
141     *      </LI>
142     * 
143     * <LI> return either {@code TRUE} or {@code FALSE} to indicate whether or not a particular file
144     *      contained matches.
145     *      </LI>
146     * </UL>
147     *
148     * <BR /><B><SPAN STYLE="color: red;">IMPORTANT:</B></SPAN> This parameter cannot be null.
149     * It is the core of the grep-search algorithm.  If the programmer wishes to rely on the
150     * standard 'token-search' or 'regular-expression-search' methods defined in class
151     * {@code 'SearchAndPrint'} - <I>then the programmer should simply use one of the standard
152     * factory methods provided in {@code 'SearchAndPrint'} to build an instance of this class.</I>
153     *
154     * @param ioeh This parameter may be used, or it may be left null.  It is less emphasized here,
155     * because only a single file is being searched.  That file is specified in the parameters to
156     * this method.  Since there is only one file to search, catching the {@code IOException's}
157     * makes little difference because GREP-search on other files will not be hindered, since there
158     * are no other files being searched.
159     *
160     * @return A value of {@code TRUE} shall indicate that there was a match found.
161     *
162     * @throws FileExpectedException The parameter {@code 'file'} must actually be a 'file' instance
163     * of {@code FileNode}.  If {@code 'file'} is not actually a file, but rather a directory, then
164     * this exception shall throw.
165     *
166     * @see FileExpectedException#check(FileNode)
167     * @see FileRW#loadFileToString(String)
168     * @see FileNode#toString()
169     * @see SearchAndPrint#test(String, String)
170     * @see IOExceptionHandler#accept(FileNode, IOException)
171     */
172    public static boolean searchFile(FileNode file, SearchAndPrint sp, IOExceptionHandler ioeh)
173    {
174        // Can only test files, not directories...
175        FileExpectedException.check(file);
176
177        // Test to see if there is a match
178        try
179            { return sp.test(file.toString(), FileRW.loadFileToString(file.toString())); }
180
181        catch (IOException e)
182            { if (ioeh != null) ioeh.accept(file, e); return false; }
183    }
184}