001package org.unix4j.unix.cut;
002
003import org.unix4j.command.AbstractCommand;
004import org.unix4j.context.ExecutionContext;
005import org.unix4j.line.Line;
006import org.unix4j.line.SimpleLine;
007import org.unix4j.processor.LineProcessor;
008import org.unix4j.unix.Cut;
009import org.unix4j.util.Range;
010
011/**
012 * Implementation of the {@link Cut cut} command.
013 */
014class CutCommand extends AbstractCommand<CutArguments> {
015
016        public CutCommand(CutArguments arguments) {
017                super(Cut.NAME, arguments);
018        }
019
020        @Override
021        public LineProcessor execute(ExecutionContext context, final LineProcessor output) {
022                final CutArguments args = getArguments(context);
023                //If indexes were specified as an int array, convert to a proper range object
024                if(args.isIndexesSet() && args.isRangeSet()){
025                        throw new IllegalArgumentException("Only one of indexes or range can be set");
026                } else if(args.isIndexesSet()){
027                        args.setRange(Range.of(args.getIndexes()));
028                }
029
030                //Validate options
031                if(args.isDelimiterSet() && !args.isFields()){
032                        throw new IllegalArgumentException("Delimiter can only be specified if cutting by fields");
033                }
034                if(args.isOutputDelimiterSet() && !args.isFields()){
035                        throw new IllegalArgumentException("Output delimiter can only be specified if cutting by fields");
036                }
037
038                return new LineProcessor() {
039                        @Override
040                        public boolean processLine(Line line) {
041                                if (args.isChars()) {
042                                        return cutByChars(line, output);
043                                } else if(args.isFields()){
044                                        return cutByFields(line, output);
045                                } else {
046                                        throw new UnsupportedOperationException("Currently cut only supports cutting by chars or fields...");
047                                }
048                        }
049
050                        @Override
051                        public void finish() {
052                                output.finish();
053                        }
054
055                        private boolean cutByFields(Line line, LineProcessor output) {
056                                final String inputDelim = args.isDelimiterSet() ? args.getDelimiter(): "\\t";
057                                final char outputDelim = args.isOutputDelimiterSet() ? args.getOutputDelimiter(): inputDelim.charAt(0);
058                                final Range range = args.getRange();
059                                //Passing -1 to the split function will cause it to not strip trailing empty strings
060                                final String[] fields = line.getContent().split(inputDelim, -1);
061                                final StringBuilder sb = new StringBuilder();
062
063                                boolean firstDone=false;
064                                for(int i=0; i<fields.length; i++){
065                                        if(range.isWithinRange(i+1)){
066                                                if(firstDone) sb.append(outputDelim);
067                                                sb.append(fields[i]);
068                                                firstDone=true;
069                                        }
070                                }
071                                return output.processLine(new SimpleLine(sb, line.getLineEnding()));
072                        }
073
074                        private boolean cutByChars(Line line, LineProcessor output) {
075                                final Range range = args.getRange();
076                                final char[] chars = line.getContent().toCharArray();
077                                final StringBuilder sb = new StringBuilder();
078
079                                for(int i=0; i<chars.length; i++){
080                                        if(range.isWithinRange(i+1)){
081                                                sb.append(chars[i]);
082                                        }
083                                }
084                                return output.processLine(new SimpleLine(sb, line.getLineEnding()));
085                        }
086                };
087        }
088}