001package org.unix4j.unix.tail;
002
003import java.io.File;
004import java.util.Arrays;
005import java.util.List;
006
007import org.unix4j.command.AbstractCommand;
008import org.unix4j.context.ExecutionContext;
009import org.unix4j.io.FileInput;
010import org.unix4j.io.Input;
011import org.unix4j.line.Line;
012import org.unix4j.line.SimpleLine;
013import org.unix4j.processor.DefaultInputProcessor;
014import org.unix4j.processor.InputProcessor;
015import org.unix4j.processor.LineProcessor;
016import org.unix4j.processor.MultipleInputLineProcessor;
017import org.unix4j.processor.RedirectInputLineProcessor;
018import org.unix4j.unix.Tail;
019import org.unix4j.util.FileUtil;
020
021/**
022 * Implementation of the {@link Tail tail} command.
023 */
024class TailCommand extends AbstractCommand<TailArguments> {
025        public TailCommand(TailArguments arguments) {
026                super(Tail.NAME, arguments);
027                if (arguments.isCountSet() && arguments.getCount() < 0) {
028                        throw new IllegalArgumentException("count cannot be negative: " + arguments);
029                }
030        }
031
032        @Override
033        public LineProcessor execute(ExecutionContext context, final LineProcessor output) {
034                final TailArguments args = getArguments(context);
035                
036                //input from file(s)?
037                if (args.isFilesSet()) {
038                        final List<FileInput> inputs = FileInput.multiple(args.getFiles());
039                        return getFileInputProcessor(inputs, context, output, args);
040                } else if (args.isPathsSet()) {
041                        final List<File> files = FileUtil.expandFiles(context.getCurrentDirectory(), args.getPaths());
042                        final List<FileInput> inputs = FileInput.multiple(files);
043                        return getFileInputProcessor(inputs, context, output, args);
044                } else if (args.isInputsSet()) {
045                        final List<Input> inputs = Arrays.asList(args.getInputs());
046                        return getFileInputProcessor(inputs, context, output, args);
047                }
048                
049                //read from standard input
050                return getStandardInputProcessor(context, output, args);
051        }
052        
053        private AbstractTailProcessor getStandardInputProcessor(ExecutionContext context, LineProcessor output, TailArguments args) {
054                if (args.isChars()) {
055                        if (args.isCountFromStart()) {
056                                return new TailCharsFromStartProcessor(this, context, output);
057                        } else {
058                                return new TailCharsFromEndProcessor(this, context, output);
059                        }
060                } else {
061                        if (args.isCountFromStart()) {
062                                return new TailLinesFromStartProcessor(this, context, output);
063                        } else {
064                                return new TailLinesFromEndProcessor(this, context, output);
065                        }
066                }
067        }
068        
069        private LineProcessor getFileInputProcessor(List<? extends Input> inputs, final ExecutionContext context, final LineProcessor output, TailArguments args) {
070                final AbstractTailProcessor tailProcessor = getStandardInputProcessor(context, output, args);
071                if (inputs.size() <= 1 || args.isSuppressHeaders()) {
072                        return new RedirectInputLineProcessor(inputs, tailProcessor);
073                } else {
074                        //write header line per file
075                        final InputProcessor inputProcessor = new DefaultInputProcessor() {
076                                private boolean firstFile = true;
077                                @Override
078                                public void begin(Input input, LineProcessor standardInputProcessor) {
079                                        if (firstFile) {
080                                                firstFile = false;
081                                        } else {
082                                                output.processLine(Line.EMPTY_LINE);
083                                        }
084                                        final String fileInfo = input instanceof FileInput ? ((FileInput)input).getFileInfo(context.getCurrentDirectory()) : input.toString();
085                                        output.processLine(new SimpleLine("==> " + fileInfo + " <=="));
086                                }
087
088                @Override
089                public void finish(Input input, LineProcessor output) {
090                    super.finish(input, output);
091                    tailProcessor.resetCountersAndFlush();
092                }
093            };
094                        return new MultipleInputLineProcessor(inputs, inputProcessor, tailProcessor);
095                }
096        }
097
098}