package org.jboss.seam.forge.shell.plugins.builtin; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; import java.util.List; import org.jboss.seam.forge.resources.DirectoryResource; import org.jboss.seam.forge.resources.Resource; import org.jboss.seam.forge.shell.plugins.Alias; import org.jboss.seam.forge.shell.plugins.DefaultCommand; import org.jboss.seam.forge.shell.plugins.Help; import org.jboss.seam.forge.shell.plugins.Option; import org.jboss.seam.forge.shell.plugins.PipeIn; import org.jboss.seam.forge.shell.plugins.PipeOut; import org.jboss.seam.forge.shell.plugins.Plugin; import org.jboss.seam.forge.shell.plugins.Topic; import org.jboss.seam.forge.shell.util.GeneralUtils; /** * A simple port of the unix 'wc' command for forge. * * @author Mike Brock . */ @Alias("wc") @Topic("File & Resources") @Help("word, line, character and byte count") public class WordCountPlugin implements Plugin { @DefaultCommand public void run( @PipeIn final InputStream pipeIn, @Option(name = "lines", shortName = "l", flagOnly = true) boolean lineCount, @Option(name = "words", shortName = "w", flagOnly = true) boolean wordCount, @Option(name = "characters", shortName = "c", flagOnly = true) boolean charCount, @Option(description = "file ...", required = false) Resource<?>[] resources, final PipeOut out ) throws IOException { if (!(lineCount || wordCount || charCount)) { wordCount = true; } if (resources != null) { List<String> results = new ArrayList<String>(); for (Resource<?> r : resources) { if (r instanceof DirectoryResource) { System.err.println("wc: " + r.getName() + ": is a directory."); continue; } InputStream instream = r.getResourceInputStream(); try { Results countResults = count(instream, lineCount, wordCount, charCount); results.addAll(countResults.getResults(r.getName())); } finally { instream.close(); } } Results x = new Results(); x.countLines = lineCount; x.countWords = wordCount; x.countChars = charCount; printOutResults(out, x.getColumns(), results); } else if (pipeIn != null) { Results countResults = count(pipeIn, lineCount, wordCount, charCount); printOutResults(out, countResults.getColumns(), countResults.getResults("<pipe>")); } } private static void printOutResults(PipeOut out, int cols, List<String> results) { boolean[] colJust = new boolean[cols]; for (int i = 0; i < cols; i++) { colJust[i] = true; } colJust[cols - 1] = false; GeneralUtils.printOutTables(results, colJust, out, null); } private static Results count(InputStream stream, boolean lines, boolean words, boolean chars) throws IOException { byte[] buffer = new byte[1024]; int read; Results res = new Results(); res.countLines = lines; res.countWords = words; res.countChars = chars; boolean capture = false; byte c; while ((read = stream.read(buffer)) != -1) { res.characters += read; for (int i = 0; i < read; i++) { if (Character.isWhitespace(c = buffer[i])) { if (capture) { capture = false; res.words++; } /** * Handle CRLF */ if (c == '\r') { if ((i + 1 < read) && (buffer[i + 1] == '\n')) { i++; c = '\n'; } } if (c == '\n') { res.lines++; } } else { capture = true; } } } return res; } private static class Results { int words; int characters; int lines; boolean countWords; boolean countChars; boolean countLines; int getColumns() { int cols = countWords ? 1 : 0; cols += countChars ? 1 : 0; cols += countLines ? 1 : 0; return cols + 1; } public int getValue(int index) { // note: the lack of breaks here is intentional, the cases are supposed // to fall-through, // to the next case if the condition is not satisfied. switch (index) { case 0: if (countLines) { return lines; } case 1: if (countWords) { return words; } case 2: if (countChars) { return characters; } } return 0; } List<String> getResults(String name) { int cols = getColumns(); List<String> results = new ArrayList<String>(cols); for (int i = 0; i < cols - 1; i++) { results.add(" " + String.valueOf(getValue(i))); } results.add(name); return results; } } }