@Override
  public final RuleMatch[] match(final AnalyzedSentence text) {
    final List<RuleMatch> ruleMatches = new ArrayList<RuleMatch>();
    final AnalyzedTokenReadings[] tokens = text.getTokens();
    String prevToken = "";
    String prevPrevToken = "";
    boolean prevWhite = false;
    int pos = 0;
    int prevLen = 0;
    for (int i = 0; i < tokens.length; i++) {
      final String token = tokens[i].getToken();
      final boolean isWhite = tokens[i].isWhitespace() || tokens[i].isFieldCode();
      pos += token.length();
      String msg = null;
      int fixLen = 0;
      String suggestionText = null;
      if (isWhite && isLeftBracket(prevToken)) {
        msg = messages.getString("no_space_after");
        suggestionText = prevToken;
        fixLen = 1;
      } else if (!isWhite
          && prevToken.equals(",")
          && isNotQuoteOrHyphen(token)
          && containsNoNumber(prevPrevToken)
          && containsNoNumber(token)
          && !",".equals(prevPrevToken)) {
        msg = messages.getString("missing_space_after_comma");
        suggestionText = ", ";
      } else if (prevWhite) {
        if (isRightBracket(token)) {
          msg = messages.getString("no_space_before");
          suggestionText = token;
          fixLen = 1;
        } else if (token.equals(",")) {
          msg = messages.getString("space_after_comma");
          suggestionText = ",";
          fixLen = 1;
          // exception for duplicated comma (we already have another rule for that)
          if (i + 1 < tokens.length && ",".equals(tokens[i + 1].getToken())) {
            msg = null;
          }
        } else if (token.equals(".")) {
          msg = messages.getString("no_space_before_dot");
          suggestionText = ".";
          fixLen = 1;
          // exception case for figures such as ".5" and ellipsis
          if (i + 1 < tokens.length && isNumberOrDot(tokens[i + 1].getToken())) {
            msg = null;
          }
        }
      }
      if (msg != null) {
        final int fromPos = tokens[i - 1].getStartPos();
        final int toPos = tokens[i - 1].getStartPos() + fixLen + prevLen;
        // TODO: add some good short comment here
        final RuleMatch ruleMatch = new RuleMatch(this, fromPos, toPos, msg);
        ruleMatch.setSuggestedReplacement(suggestionText);
        ruleMatches.add(ruleMatch);
      }
      prevPrevToken = prevToken;
      prevToken = token;
      prevWhite = isWhite && !tokens[i].isFieldCode(); // OOo code before comma/dot
      prevLen = tokens[i].getToken().length();
    }

    return toRuleMatchArray(ruleMatches);
  }