001package org.unix4j.processor;
002
003import org.unix4j.command.ExitValueException;
004import org.unix4j.io.Input;
005import org.unix4j.line.Line;
006
007import java.util.List;
008
009/**
010 * A line processor for multiple inputs processing the same operation for each
011 * input object individually. An operation here is another {@link LineProcessor}
012 * reading the lines passed to it from the standard input.
013 * <p>
014 * The {@link #processLine(Line)} method does nothing and returns false
015 * indicating that the (standard) input is not read by this processor. The
016 * {@link #finish()} method reads the lines from the {@link Input} object passed
017 * to the constructor and passes them as input to the delegate processor
018 * performing the real work.
019 */
020public class MultipleInputLineProcessor implements LineProcessor {
021
022        private final List<? extends Input> inputs;
023        private final InputProcessor processor;
024        private final LineProcessor output;
025        private final LineProcessor nonFinishingOutput;
026
027        /**
028         * Constructor with input objects (usually file operands of the command) and
029         * the input processor of the command that reads from the standard input.
030         * 
031         * @param inputs
032         *            the input devices, usually file operands of the command
033         * @param processor
034         *            the operation applied to every input in the given
035         *            {@code inputs} list
036         */
037        public MultipleInputLineProcessor(List<? extends Input> inputs, InputProcessor processor, final LineProcessor output) {
038                this.inputs = inputs;
039                this.processor = processor;
040                this.output = output;
041                this.nonFinishingOutput = new LineProcessor() {
042                        @Override
043                        public boolean processLine(final Line line) {
044                                return output.processLine(line);
045                        }
046
047                        @Override
048                        public void finish() {
049                                //ignore, we finish only at the very end of all files
050                        }
051                };
052        }
053
054        @Override
055        public boolean processLine(Line line) {
056                return false;// we want no input, we have it already
057        }
058
059        /**
060         * Performs the following operations to process all {@code Input} objects
061         * that have been passed to the constructor:
062         * <ol>
063         * <li>Calls {@link #beginMultiple(List, LineProcessor) beginMultiple(..)}</li>
064         * <li>Iterates over all input objects in sequence</li>
065         * <li>Calls {@link InputProcessor#begin(Input, LineProcessor)}</li>
066         * <li>Calls {@link InputProcessor#processLine(Input, Line, LineProcessor)}
067         * for every line in the current input</li>
068         * <li>Calls {@link InputProcessor#finish(Input, LineProcessor)}</li>
069         * <li>Calls {@link #finishMultiple(List, LineProcessor) finishMultiple(..)}
070         * </li>
071         * </ol>
072         */
073        @Override
074        public void finish() {
075                beginMultiple(inputs, output);
076                for (int i = 0; i < inputs.size(); i++) {
077                        final Input input = inputs.get(i);
078                        try {
079                                processor.begin(input, output);
080                                for (final Line line : input) {
081                                        if (!processor.processLine(input, line, output)) {
082                                                break;// wants no more lines
083                                        }
084                                }
085                                processor.finish(input, nonFinishingOutput);
086                        } catch (ExitValueException e) {
087                                e.setInput(input);
088                                throw e;
089                        }
090                }
091                finishMultiple(inputs, output);
092        }
093
094        /**
095         * Called once at the beginning before iterating over the {@link Input}
096         * objects in the given {@code inputs} list.
097         * <p>
098         * The DEFAULT implementation performs no operation.
099         * 
100         * @param inputs
101         *            the input object being iterated next
102         * @param output
103         *            the output to write to
104         */
105        protected void beginMultiple(List<? extends Input> inputs, LineProcessor output) {
106                // default: no op
107        }
108
109        /**
110         * Called once at the end after iterating over the {@link Input} objects in
111         * the given {@code inputs} list.
112         * <p>
113         * The DEFAULT implementation calls {@link LineProcessor#finish()
114         * output.finish()} and closes all input objects.
115         * 
116         * @param inputs
117         *            the input object being iterated next
118         * @param output
119         *            the output to write to
120         */
121        protected void finishMultiple(List<? extends Input> inputs, LineProcessor output) {
122                output.finish();
123                for (final Input input : inputs) {
124                        input.close();
125                }
126        }
127
128}