code: package morfologik.tools;
  
  import java.io.BufferedInputStream;
  import java.io.BufferedReader;
  import java.io.Closeable;
  import java.io.Console;
  import java.io.IOException;
  import java.io.InputStreamReader;
  import java.nio.charset.Charset;
  import java.nio.file.Files;
  import java.nio.file.Path;
  import java.util.List;
  
  import com.beust.jcommander.Parameter;
  import com.beust.jcommander.Parameters;
  
  import morfologik.stemming.Dictionary;
  import morfologik.stemming.DictionaryLookup;
  import morfologik.stemming.WordData;
  
  /**
   * Applies a morphological dictionary automaton to the input. 
   */
  @Parameters(
      commandNames = "dict_apply",
      commandDescription = "Applies a dictionary to an input. Each line is considered an input term.")
  public class DictApply extends CliTool {
    private final static String ARG_ENCODING = "--input-charset";
    
    @Parameter(
        names = {"-i", "--input"},
        required = false,
        description = "The input file, each entry in a single line. If not provided, stdin is used.",
        validateValueWith = ValidateFileExists.class)
    private Path input;
  
    @Parameter(
        names = {"-d", "--dictionary"},
        description = "The dictionary (*.dict and a sibling *.info metadata) to apply.", 
        required = true,
        validateValueWith = ValidateFileExists.class)
    private Path dictionary;
  
    @Parameter(
        names = {ARG_ENCODING},
        required = false,
        description = "Character encoding of the input (platform's default).")
    private String inputEncoding;
  
    @Parameter(
        names = {"--skip-tags"},
        required = false,
        description = "Skip tags in the output, only print base forms if found.")
    private boolean skipTags = false;
  
    private abstract class LineSupplier implements Closeable {
      public abstract String nextLine() throws IOException;
  
      @Override
      public void close() throws IOException {
        // No-op by default.
      }
    }
  
    private class ReaderLineSupplier extends LineSupplier {
      private final BufferedReader lineReader;
  
      public ReaderLineSupplier(BufferedReader reader) {
        this.lineReader = reader;
      }
  
      @Override
      public String nextLine() throws IOException {
        return lineReader.readLine();
      }
      
      @Override
      public void close() throws IOException {
        lineReader.close();
      }
    }
  
    DictApply() {
    }
    
    public DictApply(Path dictionary,
                     Path input,
                     String inputEncoding) {
      this.input = checkNotNull(input);
      this.dictionary = checkNotNull(dictionary);
    }
  
    @Override
    public ExitStatus call() throws Exception {
      ExitStatus exitStatus = validateArguments();
      if (exitStatus != null) {
        return exitStatus;
      }
      
      final DictionaryLookup lookup = new DictionaryLookup(Dictionary.read(this.dictionary));
      try (final LineSupplier input = determineInput()) {
        String line;
        while ((line = input.nextLine()) != null) {
          if (line.length() == 0) {
            continue;
          }
  
          List<WordData> wordData = lookup.lookup(line);
          if (wordData.isEmpty()) {
            System.out.println(line + " => [not found]");
          } else {
            for (WordData wd : wordData) {
              CharSequence stem = wd.getStem();
              CharSequence tag = wd.getTag();
              System.out.println(line + " => " +
                  ((skipTags || tag == null) ? stem
                                             : stem + " " + tag));
            }
          }
        }
      }
  
      return ExitStatus.SUCCESS;
    }
  
    private LineSupplier determineInput() throws IOException {
      if (this.input != null) {
        return new ReaderLineSupplier(Files.newBufferedReader(this.input, Charset.forName(inputEncoding)));
      }
  
      final Console c = System.console();
      if (c != null) {
        System.err.println("NOTE: Using Console for input, character encoding is unknown but should be all right.");
        return new LineSupplier() {
          @Override
          public String nextLine() throws IOException {
            return c.readLine();
          }
        };
      }
  
      Charset charset = this.inputEncoding != null ? Charset.forName(this.inputEncoding)
                                                   : Charset.defaultCharset();
      System.err.println("NOTE: Using stdin for input, character encoding set to: " + charset.name() +
          " (use " + ARG_ENCODING + " to override).");
      return new ReaderLineSupplier(
          new BufferedReader(
              new InputStreamReader(
                  new BufferedInputStream(System.in), charset)));
    }
  
    private ExitStatus validateArguments() {
      if (this.input != null) {
        if (this.inputEncoding == null) {
          System.err.println("Input encoding is required if file input is used.");
          return ExitStatus.ERROR_INVALID_ARGUMENTS;
        }
      } else {
        if (System.console() != null && this.inputEncoding != null) {
          System.err.println("Input encoding is only valid with file input or stdin redirection.");
          return ExitStatus.ERROR_INVALID_ARGUMENTS;
        }
      }
  
      return null;
    }
  
    public static void main(String[] args) {
      main(args, new DictApply());
    }
  }
  