class SpanishUnpairedBracketsRule extends GenericUnpairedBracketsRule {

  private static final List<String> ES_START_SYMBOLS = Arrays.asList("[", "(", "{", "“", "«", "\"", "'", "‘");
  private static final List<String> ES_END_SYMBOLS   = Arrays.asList("]", ")", "}", "”", "»", "\"", "'", "’");

  //private static final Pattern NUMBER = Pattern.compile("[\\d,.]*\\d");
  
  private static final Pattern VALID_BEFORE_CLOSING_PARENTHESIS = Pattern
      .compile("\\d+|[a-zA-Z]", Pattern.UNICODE_CASE);
  private static final Pattern NUMBER = Pattern.compile("\\d[\\d., ]+\\d|\\d{1,2}", Pattern.UNICODE_CASE);

  public SpanishUnpairedBracketsRule(ResourceBundle messages) {
    super(messages, ES_START_SYMBOLS, ES_END_SYMBOLS);
  }

  @Override
  public String getId() {
    return "ES_UNPAIRED_BRACKETS";
  }

  @Override
  protected boolean isNoException(final String tokenStr,
      final AnalyzedTokenReadings[] tokens, final int i, final int j,
      final boolean precSpace, final boolean follSpace, UnsyncStack<SymbolLocator> symbolStack) {<FILL_FUNCTION_BODY>}
  
  private boolean isQuote(String tokenStr) {
    return "'".equals(tokenStr) || "’".equals(tokenStr);
  }

  protected List<String> getSuggestions(Supplier<String> text, int startPos, int endPos, Symbol symbol, String otherSymbol) {
    List<String> replacements = new ArrayList<>();
    // add the other symbol together with the original symbol, needs to be moved by the user
    if (symbol.symbolType == Symbol.Type.Closing) {
      replacements.add(otherSymbol + symbol);
    } else {
      replacements.add(symbol + otherSymbol);
    }
    // add the option to remove the original symbol
    replacements.add("");
    return replacements;
  }

}


    if (i < 1) {
      return true;
    }

    final boolean superException = !super.isNoException(tokenStr, tokens, i, j, precSpace, follSpace, symbolStack);
    if (superException) {
      return false;
    }
    
    if ((tokenStr.equals("’") || tokenStr.equals("'"))
        && (tokens[i].hasPosTagStartingWith("N") || tokens[i].hasPosTagStartingWith("A")
        ||tokens[i].hasPosTag("_allow_apostrophe"))) {
      return false;
    }
    
    // Exception for English plural Saxon genitive
    if (i + 1 < tokens.length && (isQuote(tokenStr)) && "s".equalsIgnoreCase(tokens[i + 1].getToken())) {
      return false;
    }

    //degrees, minutes, seconds...
    if (("\"".equals(tokenStr) || "'".equals(tokenStr))
        && NUMBER.matcher(tokens[i - 1].getToken()).matches()
        && !tokens[i].isWhitespaceBefore()
        && ((i > 2 && (tokens[i - 2].getToken().contains("º") || tokens[i - 2].getToken().contains("°")))
        || (i > 4 && (tokens[i - 4].getToken().contains("º") || tokens[i - 4].getToken().contains("°"))))) {
      return false;
    }

    if (i == 1 && tokenStr.equals("»"))
      return false;

    if (i > 1 && tokenStr.equals(")")) {
      boolean isThereOpeningParenthesis = false;
      int k=1;
      while (i-k>0) {
        if (tokens[i-k].getToken().equals(")"))
          break;
        if (tokens[i-k].getToken().equals("(")) {
          isThereOpeningParenthesis=true;
          break;
        }
        k++;
      }
      if (!isThereOpeningParenthesis) {
        final Matcher mValidBeforeClosingParenthesis = VALID_BEFORE_CLOSING_PARENTHESIS
            .matcher(tokens[i - 1].getToken());
        if (mValidBeforeClosingParenthesis.matches())
          return false;
      }
    }

    return true;
  
<methods>public void <init>(java.lang.String, java.util.ResourceBundle, List<java.lang.String>, List<java.lang.String>) ,public void <init>(java.lang.String, java.util.ResourceBundle, List<java.lang.String>, List<java.lang.String>, java.util.regex.Pattern) ,public void <init>(java.util.ResourceBundle, List<java.lang.String>, List<java.lang.String>) ,public void <init>(java.util.ResourceBundle, List<java.lang.String>, List<java.lang.String>, java.util.regex.Pattern) ,public void <init>(java.util.ResourceBundle) ,public java.lang.String getDescription() ,public java.lang.String getId() ,public final org.languagetool.rules.RuleMatch[] match(List<org.languagetool.AnalyzedSentence>) ,public int minToCheckParagraph() <variables>private static final java.util.regex.Pattern NUMERALS_EN,private static final java.util.regex.Pattern PUNCTUATION,private static final java.util.regex.Pattern PUNCTUATION_NO_DOT,private final non-sealed List<java.lang.String> endSymbols,private final non-sealed java.util.regex.Pattern numerals,private final non-sealed java.lang.String ruleId,private final non-sealed List<java.lang.String> startSymbols,private final non-sealed Map<java.lang.String,java.lang.Boolean> uniqueMap