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