FunctionInfo createFunctionInfo(TreeNode elem) {

    String name = null;
    String klass = null;
    String signature = null;

    Iterator list = elem.findChildren();
    while (list.hasNext()) {
      TreeNode element = (TreeNode) list.next();
      String tname = element.getName();

      if ("name".equals(tname)) {
        name = element.getBody();
      } else if ("function-class".equals(tname)) {
        klass = element.getBody();
      } else if ("function-signature".equals(tname)) {
        signature = element.getBody();
      } else if ("display-name".equals(tname)
          || // Ignored elements
          "small-icon".equals(tname)
          || "large-icon".equals(tname)
          || "description".equals(tname)
          || "example".equals(tname)) {
      } else {
        if (log.isWarnEnabled()) {
          log.warn(Localizer.getMessage("jsp.warning.unknown.element.in.function", tname));
        }
      }
    }

    return new FunctionInfo(name, klass, signature);
  }
  /*
   * Populates taglib map described in web.xml.
   *
   * This is not kept in sync with o.a.c.startup.TldConfig as the Jasper only
   * needs the URI to TLD mappings from scan web.xml whereas TldConfig needs
   * to scan the actual TLD files.
   */
  private void tldScanWebXml() throws Exception {

    WebXml webXml = null;
    try {
      webXml = new WebXml(ctxt);
      if (webXml.getInputSource() == null) {
        return;
      }

      boolean validate =
          Boolean.parseBoolean(ctxt.getInitParameter(Constants.XML_VALIDATION_TLD_INIT_PARAM));
      String blockExternalString = ctxt.getInitParameter(Constants.XML_BLOCK_EXTERNAL_INIT_PARAM);
      boolean blockExternal;
      if (blockExternalString == null) {
        blockExternal = Constants.IS_SECURITY_ENABLED;
      } else {
        blockExternal = Boolean.parseBoolean(blockExternalString);
      }

      // Parse the web application deployment descriptor
      ParserUtils pu = new ParserUtils(validate, blockExternal);

      TreeNode webtld = null;
      webtld = pu.parseXMLDocument(webXml.getSystemId(), webXml.getInputSource());

      // Allow taglib to be an element of the root or jsp-config (JSP2.0)
      TreeNode jspConfig = webtld.findChild("jsp-config");
      if (jspConfig != null) {
        webtld = jspConfig;
      }
      Iterator<TreeNode> taglibs = webtld.findChildren("taglib");
      while (taglibs.hasNext()) {

        // Parse the next <taglib> element
        TreeNode taglib = taglibs.next();
        String tagUri = null;
        String tagLoc = null;
        TreeNode child = taglib.findChild("taglib-uri");
        if (child != null) tagUri = child.getBody();
        child = taglib.findChild("taglib-location");
        if (child != null) tagLoc = child.getBody();

        // Save this location if appropriate
        if (tagLoc == null) continue;
        if (uriType(tagLoc) == NOROOT_REL_URI) tagLoc = "/WEB-INF/" + tagLoc;
        TldLocation location;
        if (tagLoc.endsWith(JAR_EXT)) {
          location = new TldLocation("META-INF/taglib.tld", ctxt.getResource(tagLoc).toString());
        } else {
          location = new TldLocation(tagLoc);
        }
        mappings.put(tagUri, location);
      }
    } finally {
      if (webXml != null) {
        webXml.close();
      }
    }
  }
  private void init(ErrorDispatcher err) throws JasperException {
    if (initialized) return;

    InputStream is = ctxt.getResourceAsStream(TAG_PLUGINS_XML);
    if (is == null) return;

    TreeNode root = (new ParserUtils()).parseXMLDocument(TAG_PLUGINS_XML, is);
    if (root == null) {
      return;
    }

    if (!TAG_PLUGINS_ROOT_ELEM.equals(root.getName())) {
      err.jspError("jsp.error.plugin.wrongRootElement", TAG_PLUGINS_XML, TAG_PLUGINS_ROOT_ELEM);
    }

    tagPlugins = new HashMap();
    Iterator pluginList = root.findChildren("tag-plugin");
    while (pluginList.hasNext()) {
      TreeNode pluginNode = (TreeNode) pluginList.next();
      TreeNode tagClassNode = pluginNode.findChild("tag-class");
      if (tagClassNode == null) {
        // Error
        return;
      }
      String tagClass = tagClassNode.getBody().trim();
      TreeNode pluginClassNode = pluginNode.findChild("plugin-class");
      if (pluginClassNode == null) {
        // Error
        return;
      }

      String pluginClassStr = pluginClassNode.getBody();
      TagPlugin tagPlugin = null;
      try {
        Class pluginClass = Class.forName(pluginClassStr);
        tagPlugin = (TagPlugin) pluginClass.newInstance();
      } catch (Exception e) {
        throw new JasperException(e);
      }
      if (tagPlugin == null) {
        return;
      }
      tagPlugins.put(tagClass, tagPlugin);
    }
    initialized = true;
  }
  /*
   * Parses the tag file directives of the given TagFile and turns them into a
   * TagInfo.
   *
   * @param elem The <tag-file> element in the TLD @param uri The location of
   * the TLD, in case the tag file is specified relative to it @param jarFile
   * The JAR file, in case the tag file is packaged in a JAR
   *
   * @return TagInfo correspoding to tag file directives
   */
  private TagFileInfo createTagFileInfo(TreeNode elem, String uri, URL jarFileUrl)
      throws JasperException {

    String name = null;
    String path = null;

    Iterator list = elem.findChildren();
    while (list.hasNext()) {
      TreeNode child = (TreeNode) list.next();
      String tname = child.getName();
      if ("name".equals(tname)) {
        name = child.getBody();
      } else if ("path".equals(tname)) {
        path = child.getBody();
      } else if ("example".equals(tname)) {
        // Ignore <example> element: Bugzilla 33538
      } else if ("tag-extension".equals(tname)) {
        // Ignore <tag-extension> element: Bugzilla 33538
      } else if ("icon".equals(tname)
          || "display-name".equals(tname)
          || "description".equals(tname)) {
        // Ignore these elements: Bugzilla 38015
      } else {
        if (log.isWarnEnabled()) {
          log.warn(Localizer.getMessage("jsp.warning.unknown.element.in.tagfile", tname));
        }
      }
    }

    if (path.startsWith("/META-INF/tags")) {
      // Tag file packaged in JAR
      // See https://issues.apache.org/bugzilla/show_bug.cgi?id=46471
      // This needs to be removed once all the broken code that depends on
      // it has been removed
      ctxt.setTagFileJarUrl(path, jarFileUrl);
    } else if (!path.startsWith("/WEB-INF/tags")) {
      err.jspError("jsp.error.tagfile.illegalPath", path);
    }

    TagInfo tagInfo =
        TagFileProcessor.parseTagFileDirectives(parserController, name, path, jarFileUrl, this);
    return new TagFileInfo(name, path, tagInfo);
  }
  /*
   * Returns the value of the uri element of the given TLD, or null if the
   * given TLD does not contain any such element.
   */
  private String getUriFromTld(String resourcePath, InputStream in) throws JasperException {
    // Parse the tag library descriptor at the specified resource path
    TreeNode tld = new ParserUtils().parseXMLDocument(resourcePath, in);
    TreeNode uri = tld.findChild("uri");
    if (uri != null) {
      String body = uri.getBody();
      if (body != null) return body;
    }

    return null;
  }
  String[] createInitParam(TreeNode elem) {
    String[] initParam = new String[2];

    Iterator list = elem.findChildren();
    while (list.hasNext()) {
      TreeNode element = (TreeNode) list.next();
      String tname = element.getName();
      if ("param-name".equals(tname)) {
        initParam[0] = element.getBody();
      } else if ("param-value".equals(tname)) {
        initParam[1] = element.getBody();
      } else if ("description".equals(tname)) {
        // Do nothing
      } else {
        if (log.isWarnEnabled()) {
          log.warn(Localizer.getMessage("jsp.warning.unknown.element.in.initParam", tname));
        }
      }
    }
    return initParam;
  }
  TagVariableInfo createVariable(TreeNode elem) {
    String nameGiven = null;
    String nameFromAttribute = null;
    String className = "java.lang.String";
    boolean declare = true;
    int scope = VariableInfo.NESTED;

    Iterator list = elem.findChildren();
    while (list.hasNext()) {
      TreeNode element = (TreeNode) list.next();
      String tname = element.getName();
      if ("name-given".equals(tname)) nameGiven = element.getBody();
      else if ("name-from-attribute".equals(tname)) nameFromAttribute = element.getBody();
      else if ("variable-class".equals(tname)) className = element.getBody();
      else if ("declare".equals(tname)) {
        String s = element.getBody();
        if (s != null) declare = JspUtil.booleanValue(s);
      } else if ("scope".equals(tname)) {
        String s = element.getBody();
        if (s != null) {
          if ("NESTED".equals(s)) {
            scope = VariableInfo.NESTED;
          } else if ("AT_BEGIN".equals(s)) {
            scope = VariableInfo.AT_BEGIN;
          } else if ("AT_END".equals(s)) {
            scope = VariableInfo.AT_END;
          }
        }
      } else if ("description".equals(tname)
          || // Ignored elements
          false) {
      } else {
        if (log.isWarnEnabled()) {
          log.warn(Localizer.getMessage("jsp.warning.unknown.element.in.variable", tname));
        }
      }
    }
    return new TagVariableInfo(nameGiven, nameFromAttribute, className, declare, scope);
  }
  /*
   * Scan the TLD contents in the specified input stream and add any new URIs
   * to the map.
   *
   * @param resourcePath  Path of the resource
   * @param entryName     If the resource is a JAR file, the name of the entry
   *                      in the JAR file
   * @param stream        The input stream for the resource
   * @throws IOException
   */
  private void tldScanStream(String resourcePath, String entryName, InputStream stream)
      throws IOException {
    try {
      // Parse the tag library descriptor at the specified resource path
      String uri = null;

      boolean validate =
          Boolean.parseBoolean(ctxt.getInitParameter(Constants.XML_VALIDATION_TLD_INIT_PARAM));
      String blockExternalString = ctxt.getInitParameter(Constants.XML_BLOCK_EXTERNAL_INIT_PARAM);
      boolean blockExternal;
      if (blockExternalString == null) {
        blockExternal = Constants.IS_SECURITY_ENABLED;
      } else {
        blockExternal = Boolean.parseBoolean(blockExternalString);
      }

      ParserUtils pu = new ParserUtils(validate, blockExternal);
      TreeNode tld = pu.parseXMLDocument(resourcePath, stream);
      TreeNode uriNode = tld.findChild("uri");
      if (uriNode != null) {
        String body = uriNode.getBody();
        if (body != null) uri = body;
      }

      // Add implicit map entry only if its uri is not already
      // present in the map
      if (uri != null && mappings.get(uri) == null) {
        TldLocation location;
        if (entryName == null) {
          location = new TldLocation(resourcePath);
        } else {
          location = new TldLocation(entryName, resourcePath);
        }
        mappings.put(uri, location);
      }
    } catch (JasperException e) {
      // Hack - makes exception handling simpler
      throw new IOException(e);
    }
  }
  private TagLibraryValidator createValidator(TreeNode elem) throws JasperException {

    String validatorClass = null;
    Map initParams = new Hashtable();

    Iterator list = elem.findChildren();
    while (list.hasNext()) {
      TreeNode element = (TreeNode) list.next();
      String tname = element.getName();
      if ("validator-class".equals(tname)) validatorClass = element.getBody();
      else if ("init-param".equals(tname)) {
        String[] initParam = createInitParam(element);
        initParams.put(initParam[0], initParam[1]);
      } else if ("description".equals(tname)
          || // Ignored elements
          false) {
      } else {
        if (log.isWarnEnabled()) {
          log.warn(Localizer.getMessage("jsp.warning.unknown.element.in.validator", tname));
        }
      }
    }

    TagLibraryValidator tlv = null;
    if (validatorClass != null && !validatorClass.equals("")) {
      try {
        Class tlvClass = ctxt.getClassLoader().loadClass(validatorClass);
        tlv = (TagLibraryValidator) tlvClass.newInstance();
      } catch (Exception e) {
        err.jspError("jsp.error.tlvclass.instantiation", validatorClass, e);
      }
    }
    if (tlv != null) {
      tlv.setInitParameters(initParams);
    }
    return tlv;
  }
  /*
   * Populates taglib map described in web.xml.
   */
  protected void processWebDotXml(Map tmpMappings) throws Exception {

    InputStream is = null;

    try {
      // Acquire input stream to web application deployment descriptor
      String altDDName = (String) ctx.getAttribute(Constants.ALT_DD_ATTR);
      if (altDDName != null) {
        try {
          is = new FileInputStream(altDDName);
        } catch (FileNotFoundException e) {
          log.warn(Localizer.getMessage("jsp.error.internal.filenotfound", altDDName));
        }
      } else {
        is = ctx.getResourceAsStream(WEB_XML);
        if (is == null) {
          log.warn(Localizer.getMessage("jsp.error.internal.filenotfound", WEB_XML));
        }
      }

      if (is == null) {
        return;
      }

      // Parse the web application deployment descriptor
      TreeNode webtld = null;
      // altDDName is the absolute path of the DD
      if (altDDName != null) {
        webtld = new ParserUtils().parseXMLDocument(altDDName, is);
      } else {
        webtld = new ParserUtils().parseXMLDocument(WEB_XML, is);
      }

      // Allow taglib to be an element of the root or jsp-config (JSP2.0)
      TreeNode jspConfig = webtld.findChild("jsp-config");
      if (jspConfig != null) {
        webtld = jspConfig;
      }
      Iterator taglibs = webtld.findChildren("taglib");
      while (taglibs.hasNext()) {

        // Parse the next <taglib> element
        TreeNode taglib = (TreeNode) taglibs.next();
        String tagUri = null;
        String tagLoc = null;
        TreeNode child = taglib.findChild("taglib-uri");
        if (child != null) tagUri = child.getBody();
        child = taglib.findChild("taglib-location");
        if (child != null) tagLoc = child.getBody();

        // Save this location if appropriate
        if (tagLoc == null) continue;
        if (uriType(tagLoc) == NOROOT_REL_URI) tagLoc = "/WEB-INF/" + tagLoc;
        String tagLoc2 = null;
        if (tagLoc.endsWith(JAR_FILE_SUFFIX)) {
          tagLoc = ctx.getResource(tagLoc).toString();
          tagLoc2 = "META-INF/taglib.tld";
        }
        tmpMappings.put(tagUri, new String[] {tagLoc, tagLoc2}); // SYNC
      }
    } finally {
      if (is != null) {
        try {
          is.close();
        } catch (Throwable t) {
        }
      }
    }
  }
  TagAttributeInfo createAttribute(TreeNode elem, String jspVersion) {
    String name = null;
    String type = null;
    String expectedType = null;
    String methodSignature = null;
    boolean required = false,
        rtexprvalue = false,
        reqTime = false,
        isFragment = false,
        deferredValue = false,
        deferredMethod = false;

    Iterator list = elem.findChildren();
    while (list.hasNext()) {
      TreeNode element = (TreeNode) list.next();
      String tname = element.getName();

      if ("name".equals(tname)) {
        name = element.getBody();
      } else if ("required".equals(tname)) {
        String s = element.getBody();
        if (s != null) required = JspUtil.booleanValue(s);
      } else if ("rtexprvalue".equals(tname)) {
        String s = element.getBody();
        if (s != null) rtexprvalue = JspUtil.booleanValue(s);
      } else if ("type".equals(tname)) {
        type = element.getBody();
        if ("1.2".equals(jspVersion)
            && (type.equals("Boolean")
                || type.equals("Byte")
                || type.equals("Character")
                || type.equals("Double")
                || type.equals("Float")
                || type.equals("Integer")
                || type.equals("Long")
                || type.equals("Object")
                || type.equals("Short")
                || type.equals("String"))) {
          type = "java.lang." + type;
        }
      } else if ("fragment".equals(tname)) {
        String s = element.getBody();
        if (s != null) {
          isFragment = JspUtil.booleanValue(s);
        }
      } else if ("deferred-value".equals(tname)) {
        deferredValue = true;
        type = "javax.el.ValueExpression";
        TreeNode child = element.findChild("type");
        if (child != null) {
          expectedType = child.getBody();
          if (expectedType != null) {
            expectedType = expectedType.trim();
          }
        } else {
          expectedType = "java.lang.Object";
        }
      } else if ("deferred-method".equals(tname)) {
        deferredMethod = true;
        type = "javax.el.MethodExpression";
        TreeNode child = element.findChild("method-signature");
        if (child != null) {
          methodSignature = child.getBody();
          if (methodSignature != null) {
            methodSignature = methodSignature.trim();
          }
        } else {
          methodSignature = "java.lang.Object method()";
        }
      } else if ("description".equals(tname)
          || // Ignored elements
          false) {;
      } else {
        if (log.isWarnEnabled()) {
          log.warn(Localizer.getMessage("jsp.warning.unknown.element.in.attribute", tname));
        }
      }
    }

    if (isFragment) {
      /*
       * According to JSP.C-3 ("TLD Schema Element Structure - tag"),
       * 'type' and 'rtexprvalue' must not be specified if 'fragment' has
       * been specified (this will be enforced by validating parser).
       * Also, if 'fragment' is TRUE, 'type' is fixed at
       * javax.servlet.jsp.tagext.JspFragment, and 'rtexprvalue' is fixed
       * at true. See also JSP.8.5.2.
       */
      type = "javax.servlet.jsp.tagext.JspFragment";
      rtexprvalue = true;
    }

    if (!rtexprvalue && type == null) {
      // According to JSP spec, for static values (those determined at
      // translation time) the type is fixed at java.lang.String.
      type = "java.lang.String";
    }

    return new TagAttributeInfo(
        name,
        required,
        type,
        rtexprvalue,
        isFragment,
        null,
        deferredValue,
        deferredMethod,
        expectedType,
        methodSignature);
  }
  private TagInfo createTagInfo(TreeNode elem, String jspVersion) throws JasperException {

    String tagName = null;
    String tagClassName = null;
    String teiClassName = null;

    /*
     * Default body content for JSP 1.2 tag handlers (<body-content> has
     * become mandatory in JSP 2.0, because the default would be invalid for
     * simple tag handlers)
     */
    String bodycontent = "JSP";

    String info = null;
    String displayName = null;
    String smallIcon = null;
    String largeIcon = null;
    boolean dynamicAttributes = false;

    Vector attributeVector = new Vector();
    Vector variableVector = new Vector();
    Iterator list = elem.findChildren();
    while (list.hasNext()) {
      TreeNode element = (TreeNode) list.next();
      String tname = element.getName();

      if ("name".equals(tname)) {
        tagName = element.getBody();
      } else if ("tagclass".equals(tname) || "tag-class".equals(tname)) {
        tagClassName = element.getBody();
      } else if ("teiclass".equals(tname) || "tei-class".equals(tname)) {
        teiClassName = element.getBody();
      } else if ("bodycontent".equals(tname) || "body-content".equals(tname)) {
        bodycontent = element.getBody();
      } else if ("display-name".equals(tname)) {
        displayName = element.getBody();
      } else if ("small-icon".equals(tname)) {
        smallIcon = element.getBody();
      } else if ("large-icon".equals(tname)) {
        largeIcon = element.getBody();
      } else if ("icon".equals(tname)) {
        TreeNode icon = element.findChild("small-icon");
        if (icon != null) {
          smallIcon = icon.getBody();
        }
        icon = element.findChild("large-icon");
        if (icon != null) {
          largeIcon = icon.getBody();
        }
      } else if ("info".equals(tname) || "description".equals(tname)) {
        info = element.getBody();
      } else if ("variable".equals(tname)) {
        variableVector.addElement(createVariable(element));
      } else if ("attribute".equals(tname)) {
        attributeVector.addElement(createAttribute(element, jspVersion));
      } else if ("dynamic-attributes".equals(tname)) {
        dynamicAttributes = JspUtil.booleanValue(element.getBody());
      } else if ("example".equals(tname)) {
        // Ignored elements
      } else if ("tag-extension".equals(tname)) {
        // Ignored
      } else {
        if (log.isWarnEnabled()) {
          log.warn(Localizer.getMessage("jsp.warning.unknown.element.in.tag", tname));
        }
      }
    }

    TagExtraInfo tei = null;
    if (teiClassName != null && !teiClassName.equals("")) {
      try {
        Class teiClass = ctxt.getClassLoader().loadClass(teiClassName);
        tei = (TagExtraInfo) teiClass.newInstance();
      } catch (Exception e) {
        err.jspError("jsp.error.teiclass.instantiation", teiClassName, e);
      }
    }

    TagAttributeInfo[] tagAttributeInfo = new TagAttributeInfo[attributeVector.size()];
    attributeVector.copyInto(tagAttributeInfo);

    TagVariableInfo[] tagVariableInfos = new TagVariableInfo[variableVector.size()];
    variableVector.copyInto(tagVariableInfos);

    TagInfo taginfo =
        new TagInfo(
            tagName,
            tagClassName,
            bodycontent,
            info,
            this,
            tei,
            tagAttributeInfo,
            displayName,
            smallIcon,
            largeIcon,
            tagVariableInfos,
            dynamicAttributes);
    return taginfo;
  }
  /*
   * @param ctxt The JSP compilation context @param uri The TLD's uri @param
   * in The TLD's input stream @param jarFileUrl The JAR file containing the
   * TLD, or null if the tag library is not packaged in a JAR
   */
  private void parseTLD(JspCompilationContext ctxt, String uri, InputStream in, URL jarFileUrl)
      throws JasperException {
    Vector tagVector = new Vector();
    Vector tagFileVector = new Vector();
    Hashtable functionTable = new Hashtable();

    // Create an iterator over the child elements of our <taglib> element
    ParserUtils pu = new ParserUtils();
    TreeNode tld = pu.parseXMLDocument(uri, in);

    // Check to see if the <taglib> root element contains a 'version'
    // attribute, which was added in JSP 2.0 to replace the <jsp-version>
    // subelement
    this.jspversion = tld.findAttribute("version");

    // Process each child element of our <taglib> element
    Iterator list = tld.findChildren();

    while (list.hasNext()) {
      TreeNode element = (TreeNode) list.next();
      String tname = element.getName();

      if ("tlibversion".equals(tname) // JSP 1.1
          || "tlib-version".equals(tname)) { // JSP 1.2
        this.tlibversion = element.getBody();
      } else if ("jspversion".equals(tname) || "jsp-version".equals(tname)) {
        this.jspversion = element.getBody();
      } else if ("shortname".equals(tname) || "short-name".equals(tname))
        this.shortname = element.getBody();
      else if ("uri".equals(tname)) this.urn = element.getBody();
      else if ("info".equals(tname) || "description".equals(tname)) this.info = element.getBody();
      else if ("validator".equals(tname)) this.tagLibraryValidator = createValidator(element);
      else if ("tag".equals(tname)) tagVector.addElement(createTagInfo(element, jspversion));
      else if ("tag-file".equals(tname)) {
        TagFileInfo tagFileInfo = createTagFileInfo(element, uri, jarFileUrl);
        tagFileVector.addElement(tagFileInfo);
      } else if ("function".equals(tname)) { // JSP2.0
        FunctionInfo funcInfo = createFunctionInfo(element);
        String funcName = funcInfo.getName();
        if (functionTable.containsKey(funcName)) {
          err.jspError("jsp.error.tld.fn.duplicate.name", funcName, uri);
        }
        functionTable.put(funcName, funcInfo);
      } else if ("display-name".equals(tname)
          || // Ignored elements
          "small-icon".equals(tname)
          || "large-icon".equals(tname)
          || "listener".equals(tname)) {;
      } else if ("taglib-extension".equals(tname)) {
        // Recognized but ignored
      } else {
        if (log.isWarnEnabled()) {
          log.warn(Localizer.getMessage("jsp.warning.unknown.element.in.taglib", tname));
        }
      }
    }

    if (tlibversion == null) {
      err.jspError("jsp.error.tld.mandatory.element.missing", "tlib-version", uri);
    }
    if (jspversion == null) {
      err.jspError("jsp.error.tld.mandatory.element.missing", "jsp-version", uri);
    }

    this.tags = new TagInfo[tagVector.size()];
    tagVector.copyInto(this.tags);

    this.tagFiles = new TagFileInfo[tagFileVector.size()];
    tagFileVector.copyInto(this.tagFiles);

    this.functions = new FunctionInfo[functionTable.size()];
    int i = 0;
    Enumeration enumeration = functionTable.elements();
    while (enumeration.hasMoreElements()) {
      this.functions[i++] = (FunctionInfo) enumeration.nextElement();
    }
  }