private void directJspTagLib(String text) {

    text = text.substring(TAGLIB_DIRECTIVE.length() + 1, text.length());
    Map<String, String> attrs = new LinkedHashMap<String, String>();
    populateMapWithAttributes(attrs, text);

    String prefix = attrs.get("\"prefix\"");
    String uri = attrs.get("\"uri\"");

    if (uri != null && prefix != null) {

      final String namespace = prefix.substring(1, prefix.length() - 1);
      if (!GroovyPage.DEFAULT_NAMESPACE.equals(namespace)) {
        jspTags.put(namespace, uri.substring(1, uri.length() - 1));
      } else {
        LOG.error(
            "You cannot override the default 'g' namespace with the directive <%@ taglib prefix=\"g\" %>. Please select another namespace.");
      }
    }
  }
  private void endTag() {
    if (!finalPass) return;

    String tagName = scan.getToken().trim();
    String ns = scan.getNamespace();

    if (tagMetaStack.isEmpty())
      throw new GrailsTagException(
          "Found closing Grails tag with no opening [" + tagName + "]",
          pageName,
          getCurrentOutputLineNumber());

    TagMeta tm = tagMetaStack.pop();
    String lastInStack = tm.name;
    String lastNamespaceInStack = tm.namespace;

    // if the tag name is blank then it has been closed by the start tag ie <tag />
    if (GrailsStringUtils.isBlank(tagName)) {
      tagName = lastInStack;
    }

    if (!lastInStack.equals(tagName) || !lastNamespaceInStack.equals(ns)) {
      throw new GrailsTagException(
          "Grails tag [" + lastNamespaceInStack + ":" + lastInStack + "] was not closed",
          pageName,
          getCurrentOutputLineNumber());
    }

    if (GroovyPage.DEFAULT_NAMESPACE.equals(ns) && tagRegistry.isSyntaxTag(tagName)) {
      if (tm.instance instanceof GroovySyntaxTag) {
        GroovySyntaxTag tag = (GroovySyntaxTag) tm.instance;
        tag.doEndTag();
      } else {
        throw new GrailsTagException(
            "Grails tag [" + tagName + "] was not closed", pageName, getCurrentOutputLineNumber());
      }
    } else {
      int bodyTagIndex = -1;
      if (!tm.emptyTag && !tm.bufferMode) {
        bodyTagIndex = tagIndex;
        out.println("})");
        closureLevel--;
      }

      if (tm.bufferMode && tm.bufferPartNumber != -1) {
        if (!bodyVarsDefined.contains(tm.tagIndex)) {
          // out.print("def ");
          bodyVarsDefined.add(tm.tagIndex);
        }
        out.println("createClosureForHtmlPart(" + tm.bufferPartNumber + ", " + tm.tagIndex + ")");
        bodyTagIndex = tm.tagIndex;
        tm.bufferMode = false;
      }

      if (jspTags.containsKey(ns)) {
        String uri = jspTags.get(ns);
        out.println("jspTag = getJspTag('" + uri + "', '" + tagName + "')");
        out.println(
            "if (!jspTag) throw new GrailsTagException('Unknown JSP tag "
                + ns
                + ":"
                + tagName
                + "')");
        out.print("jspTag.doTag(out," + attrsVarsMapDefinition.get(tagIndex) + ",");
        if (bodyTagIndex > -1) {
          out.print("getBodyClosure(" + bodyTagIndex + ")");
        } else {
          out.print("null");
        }
        out.println(")");
      } else {
        if (tm.hasAttributes) {
          out.println(
              "invokeTag('"
                  + tagName
                  + "','"
                  + ns
                  + "',"
                  + getCurrentOutputLineNumber()
                  + ","
                  + attrsVarsMapDefinition.get(tagIndex)
                  + ","
                  + bodyTagIndex
                  + ")");
        } else {
          out.println(
              "invokeTag('"
                  + tagName
                  + "','"
                  + ns
                  + "',"
                  + getCurrentOutputLineNumber()
                  + ",[:],"
                  + bodyTagIndex
                  + ")");
        }
      }
    }

    tm.bufferMode = false;

    tagIndex--;
  }
  @SuppressWarnings({"unchecked", "rawtypes"})
  private void startTag() {
    if (!finalPass) return;

    tagIndex++;

    String text;
    StringBuilder buf = new StringBuilder(scan.getToken());
    String ns = scan.getNamespace();

    boolean emptyTag = false;

    state = scan.nextToken();
    while (state != HTML && state != GEND_TAG && state != GEND_EMPTY_TAG && state != EOF) {
      if (state == GTAG_EXPR) {
        buf.append("${");
        buf.append(scan.getToken().trim());
        buf.append("}");
      } else {
        buf.append(scan.getToken());
      }
      state = scan.nextToken();
    }
    if (state == GEND_EMPTY_TAG) {
      emptyTag = true;
    }

    doNextScan = false;

    text = buf.toString();

    String tagName;
    Map attrs = new LinkedHashMap();

    Matcher m = Pattern.compile("\\s").matcher(text);

    if (m.find()) { // ignores carriage returns and new lines
      tagName = text.substring(0, m.start());
      if (state != EOF) {
        String attrTokens = text.substring(m.start(), text.length());
        populateMapWithAttributes(attrs, attrTokens);
      }
    } else {
      tagName = text;
    }

    if (state == EOF) {
      throw new GrailsTagException(
          "Unexpected end of file encountered parsing Tag ["
              + tagName
              + "] for "
              + className
              + ". Are you missing a closing brace '}'?",
          pageName,
          getCurrentOutputLineNumber());
    }

    flushTagBuffering();

    TagMeta tm = new TagMeta();
    tm.name = tagName;
    tm.namespace = ns;
    tm.hasAttributes = !attrs.isEmpty();
    tm.lineNumber = getCurrentOutputLineNumber();
    tm.emptyTag = emptyTag;
    tm.tagIndex = tagIndex;
    tagMetaStack.push(tm);

    if (GroovyPage.DEFAULT_NAMESPACE.equals(ns) && tagRegistry.isSyntaxTag(tagName)) {
      if (tagContext == null) {
        tagContext = new HashMap<Object, Object>();
        tagContext.put(GroovyPage.OUT, out);
        tagContext.put(GroovyPageParser.class, this);
      }
      GroovySyntaxTag tag = (GroovySyntaxTag) tagRegistry.newTag(tagName);
      tag.init(tagContext);
      tag.setAttributes(attrs);

      if (tag.isKeepPrecedingWhiteSpace() && currentlyBufferingWhitespace) {
        flushBufferedWhiteSpace();
      } else if (!tag.isAllowPrecedingContent() && previousContentWasNonWhitespace) {
        throw new GrailsTagException(
            "Tag ["
                + tag.getName()
                + "] cannot have non-whitespace characters directly preceding it.",
            pageName,
            getCurrentOutputLineNumber());
      } else {
        // If tag does not specify buffering of WS, we swallow it here
        clearBufferedWhiteSpace();
      }

      tag.doStartTag();

      tm.instance = tag;
    } else {
      // Custom taglibs have to always flush the whitespace, there's no
      // "allowPrecedingWhitespace" property on tags yet
      flushBufferedWhiteSpace();

      if (attrs.size() > 0) {
        FastStringWriter buffer = new FastStringWriter();
        buffer.print("[");
        for (Iterator<?> i = attrs.keySet().iterator(); i.hasNext(); ) {
          String name = (String) i.next();
          String cleanedName = name;
          if (name.startsWith("\"") && name.endsWith("\"")) {
            cleanedName = "'" + name.substring(1, name.length() - 1) + "'";
          }
          buffer.print(cleanedName);
          buffer.print(':');

          buffer.print(getExpressionText(attrs.get(name).toString()));
          if (i.hasNext()) {
            buffer.print(',');
          } else {
            buffer.print("]");
          }
        }
        attrsVarsMapDefinition.put(tagIndex, buffer.toString());
        buffer.close();
      }

      if (!emptyTag) {
        tm.bufferMode = true;
      }
    }
  }