code: package morfologik.tools;
  
  import java.io.BufferedOutputStream;
  import java.io.IOException;
  import java.io.OutputStream;
  import java.nio.ByteBuffer;
  import java.nio.file.Files;
  import java.nio.file.Path;
  
  import morfologik.stemming.Dictionary;
  import morfologik.stemming.DictionaryLookup;
  import morfologik.stemming.WordData;
  
  import com.beust.jcommander.Parameter;
  import com.beust.jcommander.Parameters;
  
  /**
   * Decompiles morphological dictionary automaton back to source state. 
   */
  @Parameters(
      commandNames = "dict_decompile",
      commandDescription = "Decompiles morphological dictionary automaton back to source state.")
  public class DictDecompile extends CliTool {
    @Parameter(
        names = {"-i", "--input"},
        description = "The input dictionary (*.dict and a sibling *.info metadata).", 
        required = true,
        validateValueWith = ValidateFileExists.class)
    private Path input;
  
    @Parameter(
        names = {"-o", "--output"},
        description = "The output file for dictionary data.")
    private Path output;  
  
    @Parameter(
        names = ARG_OVERWRITE,
        description = "Overwrite the output file if it exists.")
    private boolean overwrite;  
  
    @Parameter(
        names = ARG_VALIDATE,
        arity = 1,
        description = "Validate decoded output to make sure it can be re-encoded.")
    private boolean validate = true;  
  
    DictDecompile() {
    }
    
    public DictDecompile(Path input,
                         Path output,
                         boolean overwrite,
                         boolean validate) {
      this.input = checkNotNull(input);
      this.output = output;
      this.overwrite = overwrite;
      this.validate = validate;
    }
  
    @Override
    public ExitStatus call() throws Exception {
      final Dictionary dictionary = Dictionary.read(input);
      final DictionaryLookup lookup = new DictionaryLookup(dictionary);
  
      if (output == null) {
        output = input.resolveSibling(input.getFileName().toString().replaceAll("\\.dict$", "") + ".input");
        if (Files.exists(output) && !overwrite) {
          System.err.println("ERROR: the default output file location already exists. Use --overwrite or remove"
              + " the file manually: " + output.toString());
          return ExitStatus.ERROR_CONFIRMATION_REQUIRED;
        }
      }
  
      final byte separator = dictionary.metadata.getSeparator();
      ByteBuffer stem = ByteBuffer.allocate(0);
      ByteBuffer word = ByteBuffer.allocate(0);
      ByteBuffer tag  = ByteBuffer.allocate(0);
      try (OutputStream os = new BufferedOutputStream(Files.newOutputStream(output))) {
        boolean hasTags = false;
        for (WordData wd : lookup) {
          tag = wd.getTagBytes(tag);
          if (tag.hasRemaining()) {
            hasTags = true;
            break;
          }
        }
  
        for (WordData wd : lookup) {
          stem = wd.getStemBytes(stem);
          word = wd.getWordBytes(word);
          tag = wd.getTagBytes(tag);
  
          write(os, stem);
          os.write(separator);
          write(os, word);
          if (hasTags) {
            os.write(separator);
            write(os, tag);
          }
          os.write('\n');
  
          if (validate && (ensureNoSeparator(stem, separator) ||
                           ensureNoSeparator(word, separator))) {
            System.err.println("ERROR: The stem or word of a dictionary entry contains separator "
                + " byte " + FSAInfo.byteAsChar(separator) + ", this will prevent proper re-encoding."
                + " Add '--validate false' to override. Offending entry: "
                + wd.getStem() + ", "
                + wd.getWord());
            return ExitStatus.ERROR_OTHER;
          }
        }
      }
  
      return ExitStatus.SUCCESS;
    }
  
    private void write(OutputStream os, ByteBuffer bb) throws IOException {
      os.write(bb.array(), bb.arrayOffset() + bb.position(), bb.remaining());
    }
  
    private boolean ensureNoSeparator(ByteBuffer bb, byte marker) {
      byte [] buf = bb.array();
      for (int o = bb.arrayOffset() + bb.position(), i = bb.remaining(); i > 0; i--) {
        if (buf[o] == marker) {
          return true;
        }
      }
      return false;
    }
  
    public static void main(String[] args) {
      main(args, new DictDecompile());
    }
  }
  