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}