protected int parseExpression() {
    char terminationChar = '}';
    char nextTerminationChar = 0;
    boolean startInExpression = true;
    GroovyPageExpressionParser expressionParser =
        new GroovyPageExpressionParser(
            text, end1 - 1, terminationChar, nextTerminationChar, startInExpression);
    int endpos = expressionParser.parse();
    if (endpos != -1) {
      end1 = endpos + 1;
      int expressionEndState = HTML;
      if (state == GTAG_EXPR) {
        expressionEndState = GSTART_TAG;
      }
      return found(expressionEndState, nextTerminationChar == 0 ? 1 : 2);
    }

    throw new GrailsTagException("Unclosed GSP expression", pageName, getLineNumberForToken());
  }
  private void populateMapWithAttributes(Map<String, String> attrs, String attrTokens) {
    attrTokens = attrTokens.trim();
    int startPos = 0;
    while (startPos < attrTokens.length()) {
      // parse name (before '=' character)
      int equalsignPos = attrTokens.indexOf('=', startPos);
      if (equalsignPos == -1) {
        throw new GrailsTagException(
            "Expecting '=' after attribute name (" + attrTokens + ").",
            pageName,
            getCurrentOutputLineNumber());
      }
      String name = attrTokens.substring(startPos, equalsignPos).trim();

      // parse value
      startPos = equalsignPos + 1;
      char ch = attrTokens.charAt(startPos++);
      while (Character.isWhitespace(ch) && startPos < attrTokens.length()) {
        ch = attrTokens.charAt(startPos++);
      }
      if (!(ch == '\'' || ch == '"')) {
        throw new GrailsTagException(
            "Attribute value must be quoted (" + attrTokens + ").",
            pageName,
            getCurrentOutputLineNumber());
      }
      char quoteChar = ch;

      GroovyPageExpressionParser expressionParser =
          new GroovyPageExpressionParser(attrTokens, startPos, quoteChar, (char) 0, false);
      int endQuotepos = expressionParser.parse();
      if (endQuotepos == -1) {
        throw new GrailsTagException(
            "Attribute value quote wasn't closed (" + attrTokens + ").",
            pageName,
            getCurrentOutputLineNumber());
      }

      String val = attrTokens.substring(startPos, endQuotepos);

      if (val.startsWith("${") && val.endsWith("}") && !expressionParser.isContainsGstrings()) {
        val = val.substring(2, val.length() - 1);
      } else if (!(val.startsWith("[") && val.endsWith("]"))) {
        if (val.indexOf('"') == -1) {
          quoteChar = '"';
        }
        String quoteStr;
        // use multiline groovy string if the value contains newlines
        if (val.indexOf('\n') != -1 || val.indexOf('\r') != -1) {
          if (quoteChar == '"') {
            quoteStr = MULTILINE_GROOVY_STRING_DOUBLEQUOTES;
          } else {
            quoteStr = MULTILINE_GROOVY_STRING_SINGLEQUOTES;
          }
        } else {
          quoteStr = String.valueOf(quoteChar);
        }
        val = quoteStr + val + quoteStr;
      }
      attrs.put("\"" + name + "\"", val);
      startPos = endQuotepos + 1;
    }
  }