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);
  }
  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);
  }
  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);
  }
  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;
  }
  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();
    }
  }