class CommonCrawlSentenceSource extends SentenceSource {

  private static final int MIN_LENGTH = 15;
  private static final int MAX_LENGTH = 250;
  
  private final List<CommonCrawlSentence> sentences;
  private final XZInputStream xzIn;

  private int tooShort = 0;
  private int tooLong = 0;
  private int empty = 0;
  private int wrongStartChar = 0;
  private int wrongEndChar = 0;
  private int count = 0;
  private int lineCount = 0;

  CommonCrawlSentenceSource(InputStream input, Language language, Pattern filter) throws IOException {
    super(language, filter);
    sentences = new ArrayList<>();
    xzIn = new XZInputStream(input);
  }

  @Override
  public boolean hasNext() {
    fillSentences();
    return sentences.size() > 0;
  }

  @Override
  public Sentence next() {
    fillSentences();
    if (sentences.isEmpty()) {
      throw new NoSuchElementException();
    }
    CommonCrawlSentence ccSentence = sentences.remove(0);
    return new Sentence(ccSentence.sentence, getSource(), null, null, ccSentence.articleCount);
  }

  @Override
  public String getSource() {
    return "commoncrawl";
  }

  private void fillSentences() {<FILL_FUNCTION_BODY>}

  private void printStats() {
    System.out.println("lines            : " + lineCount);
    System.out.println("indexed sentences: " + count);
    System.out.println("tooShort         : " + tooShort);
    System.out.println("tooLong          : " + tooLong);
    System.out.println("empty            : " + empty);
    System.out.println("wrongStartChar   : " + wrongStartChar);
    System.out.println("wrongEndChar     : " + wrongEndChar);
  }

  private static class CommonCrawlSentence {
    final String sentence;
    final int articleCount;
    CommonCrawlSentence(String sentence, int articleCount) {
      this.sentence = sentence;
      this.articleCount = articleCount;
    }
  }
}

    byte[] buffer = new byte[8192];
    int n;
    try {
      while (sentences.isEmpty() && (n = xzIn.read(buffer)) != -1) {
        String buf = new String(buffer, 0, n);  // TODO: not always correct, we need to wait for line end first?
        String[] lines = buf.split("\n");
        for (String line : lines) {
          lineCount++;
          line = line.trim();
          if (line.isEmpty()) {
            empty++;
            continue;
          }
          boolean startLower = Character.isLowerCase(line.charAt(0));
          if (startLower) {
            //System.out.println("IGNORING, lc start: " + line);
            wrongStartChar++;
          } else if (line.length() < MIN_LENGTH) {
            //System.out.println("IGNORING, too short: " + line);
            tooShort++;
          } else if (line.length() > MAX_LENGTH) {
            //System.out.println("IGNORING, too long (" + line.length() + "): " + line);
            tooLong++;
          } else if (line.endsWith(".") || line.endsWith("!") || line.endsWith("?") || line.endsWith(":")) {
            //System.out.println(line);
            sentences.add(new CommonCrawlSentence(line, count++));
          } else {
            wrongEndChar++;
            // well, this way we also miss headlines and the sentences that are split
            // over more than one line, but I see now simple way to merge them...
            //System.out.println("IGNORING, wrong end char: " + line);
          }
        }
      }
    } catch (IOException e) {
      printStats();
      throw new RuntimeException(e);
    }
  
<methods>public abstract java.lang.String getSource() ,public abstract boolean hasNext() ,public abstract org.languagetool.dev.dumpcheck.Sentence next() ,public void remove() ,public java.lang.String toString() <variables>static final int MAX_SENTENCE_LENGTH,static final int MIN_SENTENCE_LENGTH,static final int MIN_SENTENCE_TOKEN_COUNT,private final non-sealed java.util.regex.Pattern acceptPattern,private int ignoreCount,private final non-sealed org.languagetool.tokenizers.Tokenizer wordTokenizer