/* * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * - Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * - Neither the name of Oracle nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /* * This source code is provided to illustrate the usage of a given feature * or technique and has been deliberately simplified. Additional steps * required for a production-quality application, such as security checks, * input validation, and proper error handling, might not be present in * this sample code. */ import java.io.IOException; import java.io.UncheckedIOException; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.util.Arrays; import java.util.List; import java.util.regex.Pattern; import java.util.stream.Stream; import static java.util.stream.Collectors.toList; /** * Grep prints lines matching a regex. See {@link #printUsageAndExit(String...)} * method for instructions and command line parameters. This sample shows * examples of using next features: * <ul> * <li>Lambda and bulk operations. Working with streams: * map(...),filter(...),flatMap(...),limit(...) methods.</li> * <li>Static method reference for printing values.</li> * <li>New Collections API forEach(...) method.</li> * <li>Try-with-resources feature.</li> * <li>new Files.walk(...), Files.lines(...) API.</li> * <li>Streams that need to be closed.</li> * </ul> * */ public class Grep { private static void printUsageAndExit(String... str) { System.out.println("Usage: " + Grep.class.getSimpleName() + " [OPTION]... PATTERN FILE..."); System.out.println("Search for PATTERN in each FILE. " + "If FILE is a directory then whole file tree of the directory" + " will be processed."); System.out.println("Example: grep -m 100 'hello world' menu.h main.c"); System.out.println("Options:"); System.out.println(" -m NUM: stop analysis after NUM matches"); Arrays.asList(str).forEach(System.err::println); System.exit(1); } /** * The main method for the Grep program. Run program with empty argument * list to see possible arguments. * * @param args the argument list for Grep. * @throws java.io.IOException If an I/O error occurs. */ public static void main(String[] args) throws IOException { long maxCount = Long.MAX_VALUE; if (args.length < 2) { printUsageAndExit(); } int i = 0; //parse OPTIONS while (args[i].startsWith("-")) { switch (args[i]) { case "-m": try { maxCount = Long.parseLong(args[++i]); } catch (NumberFormatException ex) { printUsageAndExit(ex.toString()); } break; default: printUsageAndExit("Unexpected option " + args[i]); } i++; } //parse PATTERN Pattern pattern = Pattern.compile(args[i++]); if (i == args.length) { printUsageAndExit("There are no files for input"); } try { /* * First obtain the list of all paths. * For a small number of arguments there is little to be gained * by producing this list in parallel. For one argument * there will be no parallelism. * * File names are converted to paths. If a path is a directory then * Stream is populated with whole file tree of the directory by * flatMap() method. Files are filtered from directories. */ List<Path> files = Arrays.stream(args, i, args.length) .map(Paths::get) // flatMap will ensure each I/O-based stream will be closed .flatMap(Grep::getPathStream) .filter(Files::isRegularFile) .collect(toList()); /* * Then operate on that list in parallel. * This is likely to give a more even distribution of work for * parallel execution. * * Lines are extracted from files. Lines are filtered by pattern. * Stream is limited by number of matches. Each remaining string is * displayed in std output by method reference System.out::println. */ files.parallelStream() // flatMap will ensure each I/O-based stream will be closed .flatMap(Grep::path2Lines) .filter(pattern.asPredicate()) .limit(maxCount) .forEachOrdered(System.out::println); } catch (UncheckedIOException ioe) { printUsageAndExit(ioe.toString()); } } /** * Flattens file system hierarchy into a stream. This code is not inlined * for the reason of Files.walk() throwing a checked IOException that must * be caught. * * @param path - the file or directory * @return Whole file tree starting from path, a stream with one element - * the path itself - if it is a file. */ private static Stream<Path> getPathStream(Path path) { try { return Files.walk(path); } catch (IOException e) { throw new UncheckedIOException(e); } } /** * Produces a stream of lines from a file. The result is a stream in order * to close it later. This code is not inlined for the reason of * Files.lines() throwing a checked IOException that must be caught. * * @param path - the file to read * @return stream of lines from the file */ private static Stream<String> path2Lines(Path path) { try { return Files.lines(path); } catch (IOException e) { throw new UncheckedIOException(e); } } }