As discussed with Kin-Man, the following patch (for Jasper2)
addresses two issues related to scripting variables exposed by
custom tags (via TagExtraInfo class or TLD):

ISSUE 1:
+++++++
According to the JSP spec, scripting variables with scope AT_BEGIN
or AT_END are supposed to be visible from the begin element or end
element, respectively, of the custom tag that is exposing them, all
the way to the *end* of the page. This currently is not the case.

The attached patch addresses this problem by determining the AT_BEGIN
and AT_END scripting variables of any custom tag and declaring them as
local variables of the _jspService() method.


ISSUE 2:
+++++++
If a custom tag exposing scripting variables is nested inside
itself, its scripting variables get declared multiple times within the
same scope of the generated code, resulting in compilation
errors. This problem has been filed as bug #8926 (Synopsis: "Duplicate
variable definition in generated Java source, related to custom tag
scripting variable").

The attached patch addresses this problem by declaring AT_BEGIN and
AT_END scripting variables once (as local variables of the
_jspService() method, see above), and declaring NESTED scripting
variables only at the outermost nesting level of their custom tag,
where "nesting level" corresponds the number of times the custom tag is
nested inside itself.

Example:
 
  <g:h>
    <a:b> -- nesting level 0, declares NESTED scripting variables
      <c:d>
        <e:f>
          <a:b> -- nesting level 1, saves scripting variables
            <a:b> -- nesting level 2, saves scripting variables
            </a:b> -- restores scripting variables
          </a:b> -- restores scripting variables
          <a:b> -- nesting level 1, saves scripting variables
          </a:b> -- restores scripting variables
        </e:f>
      </c:d>
    </a:b>
    <a:b> -- nesting level 0, does not declare any NESTED scripting variables
    </a:b>
  </g:h>

If <a:b> exposes any NESTED scripting variables, those variables are
going to be declared only once in the generated code, namely by the
*first* <a:b> at nesting level 0.

Any <a:b> with a nesting level > 0 saves (in its begin element) the
current values of all its scripting variables to locale variables (named
for the scripting variable and the custom tag's nesting level) before
synchronizing the scripting variables as defined by the JSP spec. In
its end element, the custom tag restores the original values of the
scripting variables (that is, the values the scripting variables had when
the tag's begin element was encountered).


In addition, this patch stores a custom tag's scripting variables with
the custom tag itself, so the scripting variables don't need to be
determined over and over again.
This has resulted in two new accessor methods for Node.CustomTag:

        public TagVariableInfo[] getTagVariableInfos()
        public VariableInfo[] getVariableInfos()


Let me know if there are any problems with this patch.


Thanks,


Jan
Executing ssh-askpass to query the password...
Warning: Remote host denied X11 forwarding, perhaps xauth program could not be run on 
the server side.
? build.properties
? build
? src/share/org/apache/jasper/compiler/Generator.java.SAVE
cvs server: Diffing .
cvs server: Diffing doc
cvs server: Diffing src
cvs server: Diffing src/bin
cvs server: Diffing src/share
cvs server: Diffing src/share/org
cvs server: Diffing src/share/org/apache
cvs server: Diffing src/share/org/apache/jasper
cvs server: Diffing src/share/org/apache/jasper/compiler
Index: src/share/org/apache/jasper/compiler/Generator.java
===================================================================
RCS file: 
/home/cvs/jakarta-tomcat-jasper/jasper2/src/share/org/apache/jasper/compiler/Generator.java,v
retrieving revision 1.23
diff -u -r1.23 Generator.java
--- src/share/org/apache/jasper/compiler/Generator.java 10 Jun 2002 21:08:30 -0000     
 1.23
+++ src/share/org/apache/jasper/compiler/Generator.java 12 Jun 2002 21:49:23 -0000
@@ -184,13 +184,15 @@
             */
            public void visit(Node.CustomTag n) throws JasperException {
                
+               // Create tag handler pool name and assign it to node
                String name = createTagHandlerPoolName(n.getPrefix(),
-                                                       n.getShortName(),
-                                                       n.getAttributes());
+                                                      n.getShortName(),
+                                                      n.getAttributes());
                n.setTagHandlerPoolName(name);
                if (!names.contains(name)) {
                    names.add(name);
                }
+
                visitBody(n);
            }
 
@@ -235,6 +237,80 @@
        page.visit(new TagHandlerPoolVisitor(tagHandlerPoolNames));
     }
 
+    /*
+     * For every custom tag, declares its scripting variables with AT_BEGIN
+     * and AT_END scopes.
+     */
+    private void declareAtBeginAtEndScriptingVariables(Node.Nodes page)
+           throws JasperException {
+
+       class ScriptingVariableDeclarationVisitor extends Node.Visitor {
+
+           /*
+            * Vector keeping track of which scripting variables have already
+            * been declared
+            */
+           private Vector scriptVars;
+
+           /*
+            * Constructor.
+            */
+           public ScriptingVariableDeclarationVisitor() {
+               scriptVars = new Vector();
+           }
+
+           public void visit(Node.CustomTag n) throws JasperException {
+
+               TagVariableInfo[] tagVarInfos = n.getTagVariableInfos();
+               VariableInfo[] varInfos = n.getVariableInfos();
+
+               if ((varInfos == null) && (tagVarInfos == null)) {
+                   visitBody(n);
+               }
+
+               if (varInfos != null) {
+                   for (int i=0; i<varInfos.length; i++) {
+                       int scope = varInfos[i].getScope();
+                       String varName = varInfos[i].getVarName();
+                       if (((scope == VariableInfo.AT_BEGIN)
+                                            || (scope == VariableInfo.AT_END))
+                               && varInfos[i].getDeclare()
+                               && !scriptVars.contains(varName)) {
+                           out.printin(varInfos[i].getClassName());
+                           out.print(" ");
+                           out.print(varName);
+                           out.println(" = null;");
+                           scriptVars.add(varName);
+                       }
+                   }
+               } else {
+                   for (int i=0; i<tagVarInfos.length; i++) {
+                       int scope = tagVarInfos[i].getScope();
+                       String varName = tagVarInfos[i].getNameGiven();
+                       if (varName == null) {
+                           varName = n.getTagData().getAttributeString(
+                                        tagVarInfos[i].getNameFromAttribute());
+                       }
+                       if (((scope == VariableInfo.AT_BEGIN)
+                                            || (scope == VariableInfo.AT_END))
+                               && tagVarInfos[i].getDeclare()
+                               && !scriptVars.contains(varName)) {
+                           out.printin(tagVarInfos[i].getClassName());
+                           out.print(" ");
+                           out.print(varName);
+                           out.println(" = null;");
+                           scriptVars.add(varName);
+                       }
+                   }
+               }
+
+               visitBody(n);
+           }
+       }
+
+       page.visit(new ScriptingVariableDeclarationVisitor());
+    }
+
     /**
      * Generates the destroy() method which is responsible for calling the
      * release() method on every tag handler in any of the tag handler pools.
@@ -376,6 +452,10 @@
         if (maxTagNesting > 0) {
            out.printil("JspxState _jspxState = new JspxState();");
         }
+       out.println();
+
+       declareAtBeginAtEndScriptingVariables(page);
+       out.println();
 
        out.printil("try {");
        out.pushIndent();
@@ -470,14 +550,26 @@
        private ServletWriter out;
        private MethodsBuffer methodsBuffer;
 
+       /*
+        * Maps temporary scripting variable to parent of custom tag that
+        * declared it
+        */
+       private Hashtable tmpVars;
+
+       // Maps NESTED scripting var to parent of custom tag that declared it
+       private Hashtable nestedVars;
+
        /**
         * Constructor.
         */
-       public GenerateVisitor(ServletWriter out, MethodsBuffer methodsBuffer) {
+       public GenerateVisitor(ServletWriter out,
+                              MethodsBuffer methodsBuffer) {
            this.out = out;
            this.methodsBuffer = methodsBuffer;
            handlerInfos = new Hashtable();
            tagVarNumbers = new Hashtable();
+           tmpVars = new Hashtable();
+           nestedVars = new Hashtable();
        }
 
        /**
@@ -976,16 +1068,8 @@
 
         public void visit(Node.CustomTag n) throws JasperException {
 
-           TagLibraryInfo tagLibInfo = (TagLibraryInfo)
-               pageInfo.getTagLibraries().get(n.getPrefix());
-           TagInfo tagInfo = tagLibInfo.getTag(n.getShortName());
-
-           // Get info on scripting variables created/manipulated by tag
-           VariableInfo[] varInfos = tagInfo.getVariableInfo(n.getTagData());
-           TagVariableInfo[] tagVarInfos = tagInfo.getTagVariableInfos();
-
-           Hashtable handlerInfosByShortName
-               = (Hashtable) handlerInfos.get(n.getPrefix());
+           Hashtable handlerInfosByShortName = (Hashtable)
+               handlerInfos.get(n.getPrefix());
            if (handlerInfosByShortName == null) {
                handlerInfosByShortName = new Hashtable();
                handlerInfos.put(n.getPrefix(), handlerInfosByShortName);
@@ -993,8 +1077,11 @@
            TagHandlerInfo handlerInfo = (TagHandlerInfo)
                handlerInfosByShortName.get(n.getShortName());
            if (handlerInfo == null) {
-               handlerInfo = new TagHandlerInfo(n, tagInfo.getTagClassName(),
-                                                ctxt.getClassLoader(), err);
+               handlerInfo = new TagHandlerInfo(
+                                           n,
+                                           n.getTagInfo().getTagClassName(),
+                                           ctxt.getClassLoader(),
+                                           err);
                handlerInfosByShortName.put(n.getShortName(), handlerInfo);
            }
 
@@ -1008,8 +1095,9 @@
            // to a method.
            ServletWriter outSave = null;
            MethodsBuffer methodsBufferSave = null;
-           if (n.isScriptless() && varInfos == null &&
-                       (tagVarInfos == null || tagVarInfos.length == 0)) {
+           if (n.isScriptless() && n.getVariableInfos() == null &&
+                       (n.getTagVariableInfos() == null
+                        || n.getTagVariableInfos().length == 0)) {
                // The tag handler and its body code can reside in a separate
                // method if it is scriptless and does not have any scripting
                // variable defined.
@@ -1073,20 +1161,19 @@
            }
 
            // Generate code for start tag, body, and end tag
-           generateCustomStart(n, varInfos, tagVarInfos, handlerInfo,
-                               tagHandlerVar, tagEvalVar);
+           generateCustomStart(n, handlerInfo, tagHandlerVar, tagEvalVar);
 
            String tmpParent = parent;
            parent = tagHandlerVar;
            visitBody(n);
 
            parent = tmpParent;
-           generateCustomEnd(n, varInfos, tagVarInfos,
-                             handlerInfo.getTagHandlerClass(), tagHandlerVar,
-                             tagEvalVar);
+           generateCustomEnd(n, handlerInfo.getTagHandlerClass(),
+                             tagHandlerVar, tagEvalVar);
 
-           if (n.isScriptless() && varInfos == null &&
-                       (tagVarInfos == null || tagVarInfos.length == 0)) {
+           if (n.isScriptless() && n.getVariableInfos() == null &&
+                       (n.getTagVariableInfos() == null
+                        || n.getTagVariableInfos().length == 0)) {
                // Generate end of method
                out.popIndent();
                out.printil("}");
@@ -1202,23 +1289,33 @@
        }
 
        private void generateCustomStart(Node.CustomTag n,
-                                        VariableInfo[] varInfos,
-                                        TagVariableInfo[] tagVarInfos,
                                         TagHandlerInfo handlerInfo,
                                         String tagHandlerVar,
                                         String tagEvalVar)
                            throws JasperException {
 
+           Class tagHandlerClass = handlerInfo.getTagHandlerClass();
+
            n.setBeginJavaLine(out.getJavaLine());
            out.printin("/* ----  ");
            out.print(n.getName());
            out.println(" ---- */");
 
-           Class tagHandlerClass = handlerInfo.getTagHandlerClass();
-
             boolean implementsTryCatchFinally =
                 TryCatchFinally.class.isAssignableFrom(tagHandlerClass);
 
+           /*
+            * Declare variables where current contents of scripting variables
+            * will be temporarily saved
+            */
+           declareTemporaryScriptingVariables(n);
+
+           // Declare scripting variables with NESTED scope
+           declareNestedScriptingVariables(n);
+
+           // Save current value of scripting variables if required
+           saveScriptingVariables(n);
+
            out.printin(tagHandlerClass.getName());
            out.print(" ");
            out.print(tagHandlerVar);
@@ -1245,9 +1342,8 @@
            boolean isBodyTag
                = BodyTag.class.isAssignableFrom(tagHandlerClass);
 
-           // Declare and synchronize AT_BEGIN scripting variables
-           syncScriptingVariables(varInfos, tagVarInfos, n.getTagData(),
-                                  VariableInfo.AT_BEGIN, true);
+           // Synchronize AT_BEGIN scripting variables
+           syncScriptingVariables(n, VariableInfo.AT_BEGIN);
  
            if (n.getBody() != null) {
                out.printin("if (");
@@ -1295,23 +1391,21 @@
                }
            }
 
-
-           // Declare and synchronize NESTED scripting variables
-           syncScriptingVariables(varInfos, tagVarInfos, n.getTagData(),
-                                  VariableInfo.NESTED, true);
+           // Synchronize NESTED scripting variables
+           syncScriptingVariables(n, VariableInfo.NESTED);
 
            // Synchronize AT_BEGIN scripting variables
-           syncScriptingVariables(varInfos, tagVarInfos, n.getTagData(),
-                                  VariableInfo.AT_BEGIN, false);
+           syncScriptingVariables(n, VariableInfo.AT_BEGIN);
        };
        
        private void generateCustomEnd(Node.CustomTag n,
-                                      VariableInfo[] varInfos,
-                                      TagVariableInfo[] tagVarInfos,
-                                      Class tagHandlerClass,
+                                      Class tagHandlerClass, 
                                       String tagHandlerVar,
                                       String tagEvalVar) {
 
+           VariableInfo[] varInfos = n.getVariableInfos();
+           TagVariableInfo[] tagVarInfos = n.getTagVariableInfos();
+
            boolean implementsIterationTag = 
                IterationTag.class.isAssignableFrom(tagHandlerClass);
            boolean implementsBodyTag = 
@@ -1327,8 +1421,7 @@
            }
 
            // Synchronize AT_BEGIN scripting variables
-           syncScriptingVariables(varInfos, tagVarInfos, n.getTagData(),
-                                  VariableInfo.AT_BEGIN, false);
+           syncScriptingVariables(n, VariableInfo.AT_BEGIN);
 
            if (n.getBody() != null) {
                if (implementsBodyTag) {
@@ -1373,35 +1466,234 @@
                out.print(tagHandlerVar);
                out.println(");");
                 out.popIndent();
-                out.printil("}");
+                out.println("}");
             } else {
                 out.printin(n.getTagHandlerPoolName());
-                out.println(".reuse(");
-               out.printin(tagHandlerVar);
+                out.print(".reuse(");
+               out.print(tagHandlerVar);
                out.println(");");
            }
 
-           // Declare and synchronize AT_END variables
-           syncScriptingVariables(varInfos, tagVarInfos, n.getTagData(),
-                                  VariableInfo.AT_END, true);
+           // Synchronize AT_END variables
+           syncScriptingVariables(n, VariableInfo.AT_END);
+
+           restoreScriptingVariables(n);
 
            n.setEndJavaLine(out.getJavaLine());
        }
 
-       private void syncScriptingVariables(VariableInfo[] varInfos,
-                                           TagVariableInfo[] tagVarInfos,
-                                           TagData tagData,
-                                           int scope,
-                                           boolean declare) {
+       /*
+        * Declares any NESTED scripting variables of the given custom tag,
+        * if the given custom tag is not nested inside itself (i.e, has a
+        * nesting level of zero). In addition, a NESTED scripting variable is 
+        * declared only if it has not already been declared in the same scope
+        * in the generated code, that is, if this custom tag's parent is
+        * different from the parent of the custom tag that may already have
+        * declared this variable.
+        */
+       private void declareNestedScriptingVariables(Node.CustomTag n) {
+           if (n.getCustomNestingLevel() > 0) {
+               return;
+           }
+
+           TagVariableInfo[] tagVarInfos = n.getTagVariableInfos();
+           VariableInfo[] varInfos = n.getVariableInfos();
            if ((varInfos == null) && (tagVarInfos == null)) {
                return;
            }
+
            if (varInfos != null) {
                for (int i=0; i<varInfos.length; i++) {
-                   if (varInfos[i].getScope() == scope) {
-                       if (declare && varInfos[i].getDeclare()) {
-                           out.printin(varInfos[i].getClassName() + " ");
+                   if ((varInfos[i].getScope() == VariableInfo.NESTED)
+                           && varInfos[i].getDeclare()) {
+                       String name = varInfos[i].getVarName();
+                       Node parent = (Node) nestedVars.get(name);
+                       if ((parent == null) || (parent != n.getParent())) {
+                           out.printin(varInfos[i].getClassName());
+                           out.print(" ");
+                           out.print(name);
+                           out.println(";");
+                           nestedVars.put(name, n.getParent());
+                       }
+                   }
+               }
+           } else {
+               for (int i=0; i<tagVarInfos.length; i++) {
+                   if ((tagVarInfos[i].getScope() == VariableInfo.NESTED)
+                           && tagVarInfos[i].getDeclare()) {
+                       String name = tagVarInfos[i].getNameGiven();
+                       if (name == null) {
+                           name = n.getTagData().getAttributeString(
+                                    tagVarInfos[i].getNameFromAttribute());
                        }
+                       Node parent = (Node) nestedVars.get(name);
+                       if ((parent == null) || (parent != n.getParent())) {
+                           out.printin(tagVarInfos[i].getClassName());
+                           out.print(" ");
+                           out.print(name);
+                           out.println(";");
+                           nestedVars.put(name, n.getParent());
+                       }
+                   }
+               }
+           }
+       }
+
+       /*
+        * For every scripting variable exposed by this custom tag, declares
+        * a variable where the current value of the scripting variable may
+        * be saved, so it can later be restored in this custom tag's end
+        * element.
+        */
+       private void declareTemporaryScriptingVariables(Node.CustomTag n) {
+           if (n.getCustomNestingLevel() == 0) {
+               return;
+           }
+
+           TagVariableInfo[] tagVarInfos = n.getTagVariableInfos();
+           VariableInfo[] varInfos = n.getVariableInfos();
+           if ((varInfos == null) && (tagVarInfos == null)) {
+               return;
+           }
+
+           if (varInfos != null) {
+               for (int i=0; i<varInfos.length; i++) {
+                   String tmpVarName = "_jspx_" + varInfos[i].getVarName()
+                       + "_" + n.getCustomNestingLevel();
+                   Node parent = (Node) tmpVars.get(tmpVarName);
+                   if ((parent == null) || (parent != n.getParent())) {
+                       out.printin(varInfos[i].getClassName());
+                       out.print(" ");
+                       out.print(tmpVarName);
+                       out.println(";");
+                       tmpVars.put(tmpVarName, n.getParent());
+                   }
+               }
+           } else {
+               for (int i=0; i<tagVarInfos.length; i++) {
+                   String varName = tagVarInfos[i].getNameGiven();
+                   if (varName == null) {
+                       varName = n.getTagData().getAttributeString(
+                                       tagVarInfos[i].getNameFromAttribute());
+                   }
+                   String tmpVarName = "_jspx_" + varName + "_"
+                       + n.getCustomNestingLevel();
+                   Node parent = (Node) tmpVars.get(tmpVarName);
+                   if ((parent == null) || (parent != n.getParent())) {
+                       out.printin(tagVarInfos[i].getClassName());
+                       out.print(" ");
+                       out.print(tmpVarName);
+                       out.println(";");
+                       tmpVars.put(tmpVarName, n.getParent());
+                   }
+               }
+           }
+       }
+
+       /*
+        * For each scripting variable of a custom tag with a nesting level
+        * greater than 0, save its value to a temporary variable so that the
+        * scripting variable can be synchronized inside the nested custom tag
+        * without affecting the value it had at the start element of the
+        * custom tag, which will be restored when the end element of the
+        * custom tag is reached.
+        */
+       private void saveScriptingVariables(Node.CustomTag n) {
+           if (n.getCustomNestingLevel() == 0) {
+               return;
+           }
+
+           TagVariableInfo[] tagVarInfos = n.getTagVariableInfos();
+           VariableInfo[] varInfos = n.getVariableInfos();
+           if ((varInfos == null) && (tagVarInfos == null)) {
+               return;
+           }
+
+           if (varInfos != null) {
+               for (int i=0; i<varInfos.length; i++) {
+                   String varName = varInfos[i].getVarName();
+                   String tmpVarName = "_jspx_" + varName + "_"
+                       + n.getCustomNestingLevel();
+                   out.printin(tmpVarName);
+                   out.print(" = ");
+                   out.print(varName);
+                   out.println(";");
+               }
+           } else {
+               for (int i=0; i<tagVarInfos.length; i++) {
+                   String varName = tagVarInfos[i].getNameGiven();
+                   if (varName == null) {
+                       varName = n.getTagData().getAttributeString(
+                                tagVarInfos[i].getNameFromAttribute());
+                   }
+                   String tmpVarName = "_jspx_" + varName + "_"
+                       + n.getCustomNestingLevel();
+                   out.printin(tmpVarName);
+                   out.print(" = ");
+                   out.print(varName);
+                   out.println(";");
+               }
+           }
+       }
+
+       /*
+        * For each scripting variable of a custom tag with a nesting level
+        * greater than 0, restore its original value that was saved in the
+        * start element of the custom tag.
+        */
+       private void restoreScriptingVariables(Node.CustomTag n) {
+           if (n.getCustomNestingLevel() == 0) {
+               return;
+           }
+
+           TagVariableInfo[] tagVarInfos = n.getTagVariableInfos();
+           VariableInfo[] varInfos = n.getVariableInfos();
+           if ((varInfos == null) && (tagVarInfos == null)) {
+               return;
+           }
+
+           if (varInfos != null) {
+               for (int i=0; i<varInfos.length; i++) {
+                   String varName = varInfos[i].getVarName();
+                   String tmpVarName = "_jspx_" + varName + "_"
+                       + n.getCustomNestingLevel();
+                   out.printin(varName);
+                   out.print(" = ");
+                   out.print(tmpVarName);
+                   out.println(";");
+               }
+           } else {
+               for (int i=0; i<tagVarInfos.length; i++) {
+                   String varName = tagVarInfos[i].getNameGiven();
+                   if (varName == null) {
+                       varName = n.getTagData().getAttributeString(
+                                tagVarInfos[i].getNameFromAttribute());
+                   }
+                   String tmpVarName = "_jspx_" + varName + "_"
+                       + n.getCustomNestingLevel();
+                   out.printin(varName);
+                   out.print(" = ");
+                   out.print(tmpVarName);
+                   out.println(";");
+               }
+           }
+       }
+
+       /*
+        * Synchronizes the scripting variables of the given custom tag for
+        * the given scope.
+        */
+       private void syncScriptingVariables(Node.CustomTag n, int scope) {
+           TagVariableInfo[] tagVarInfos = n.getTagVariableInfos();
+           VariableInfo[] varInfos = n.getVariableInfos();
+
+           if ((varInfos == null) && (tagVarInfos == null)) {
+               return;
+           }
+
+           if (varInfos != null) {
+               for (int i=0; i<varInfos.length; i++) {
+                   if (varInfos[i].getScope() == scope) {
                        out.printin(varInfos[i].getVarName());
                        out.print(" = (");
                        out.print(varInfos[i].getClassName());
@@ -1412,14 +1704,11 @@
                }
            } else {
                for (int i=0; i<tagVarInfos.length; i++) {
-                   String name = tagVarInfos[i].getNameGiven();
-                   if (name == null) {
-                       name = tagData.getAttributeString(
-                                        tagVarInfos[i].getNameFromAttribute());
-                   }
                    if (tagVarInfos[i].getScope() == scope) {
-                       if (declare && tagVarInfos[i].getDeclare()) {
-                           out.printin(tagVarInfos[i].getClassName() + " ");
+                       String name = tagVarInfos[i].getNameGiven();
+                       if (name == null) {
+                           name = n.getTagData().getAttributeString(
+                                        tagVarInfos[i].getNameFromAttribute());
                        }
                        out.printin(name);
                        out.print(" = (");
Index: src/share/org/apache/jasper/compiler/JspDocumentParser.java
===================================================================
RCS file: 
/home/cvs/jakarta-tomcat-jasper/jasper2/src/share/org/apache/jasper/compiler/JspDocumentParser.java,v
retrieving revision 1.1.1.1
diff -u -r1.1.1.1 JspDocumentParser.java
--- src/share/org/apache/jasper/compiler/JspDocumentParser.java 28 Mar 2002 18:46:15 
-0000      1.1.1.1
+++ src/share/org/apache/jasper/compiler/JspDocumentParser.java 12 Jun 2002 21:49:23 
+-0000
@@ -62,7 +62,7 @@
 
 import java.io.*;
 import java.util.Hashtable;
-import javax.servlet.jsp.tagext.TagLibraryInfo;
+import javax.servlet.jsp.tagext.*;
 import javax.xml.parsers.SAXParserFactory;
 import javax.xml.parsers.ParserConfigurationException;
 import javax.xml.parsers.SAXParser;
@@ -401,13 +401,14 @@
         if (tagLibInfo == null) {
             return null;
        }
-        if (tagLibInfo.getTag(shortName) == null) {
+        TagInfo tagInfo = tagLibInfo.getTag(shortName);
+       if (tagInfo == null) {
            throw new SAXException(err.getString("jsp.error.bad_tag",
                                                 shortName, prefix));
        }
        
        return new Node.CustomTag(attrs, start, qName, prefix, shortName,
-                                 parent);
+                                 tagInfo, parent);
     }
 
     /*
Index: src/share/org/apache/jasper/compiler/Node.java
===================================================================
RCS file: 
/home/cvs/jakarta-tomcat-jasper/jasper2/src/share/org/apache/jasper/compiler/Node.java,v
retrieving revision 1.12
diff -u -r1.12 Node.java
--- src/share/org/apache/jasper/compiler/Node.java      8 Jun 2002 00:14:35 -0000      
 1.12
+++ src/share/org/apache/jasper/compiler/Node.java      12 Jun 2002 21:49:23 -0000
@@ -62,7 +62,7 @@
 
 import java.util.*;
 import java.io.CharArrayWriter;
-import javax.servlet.jsp.tagext.TagData;
+import javax.servlet.jsp.tagext.*;
 import org.xml.sax.Attributes;
 import org.apache.jasper.JasperException;
 
@@ -670,13 +670,19 @@
        private boolean hasIncludeAction;
        private boolean hasSetProperty;
        private String tagHandlerPoolName;
+       private TagInfo tagInfo;
+       private VariableInfo[] varInfos;
+       private int nestingLevel;
 
        public CustomTag(Attributes attrs, Mark start, String name,
-                        String prefix, String shortName, Node parent) {
+                        String prefix, String shortName,
+                        TagInfo tagInfo, Node parent) {
            super(attrs, start, parent);
            this.name = name;
            this.prefix = prefix;
            this.shortName = shortName;
+           this.tagInfo = tagInfo;
+           this.nestingLevel = computeCustomNestingLevel();
        }
 
        public void accept(Visitor v) throws JasperException {
@@ -714,6 +720,7 @@
        
        public void setTagData(TagData tagData) {
            this.tagData = tagData;
+           this.varInfos = tagInfo.getVariableInfo(tagData);
        }
 
        public TagData getTagData() {
@@ -752,14 +759,68 @@
            return hasSetProperty;
        }
 
+       public void setTagHandlerPoolName(String s) {
+           tagHandlerPoolName = s;
+       }
+
        public String getTagHandlerPoolName() {
            return tagHandlerPoolName;
        }
 
-       public void setTagHandlerPoolName(String s) {
-           tagHandlerPoolName = s;
+       public TagInfo getTagInfo() {
+           return tagInfo;
+       }
+
+       public TagVariableInfo[] getTagVariableInfos() {
+           return tagInfo.getTagVariableInfos();
        }
 
+       public VariableInfo[] getVariableInfos() {
+           return varInfos;
+       }
+
+       /*
+        * Gets this custom tag's nesting level.
+        */
+       public int getCustomNestingLevel() {
+           return nestingLevel;
+       }
+
+       /*
+        * Computes this custom tag's nesting level, which corresponds to the
+        * number of times this custom tag is nested inside itself.
+        *
+        * Example:
+        * 
+        *  <g:h>
+        *    <a:b> -- nesting level 0
+        *      <c:d>
+        *        <e:f>
+        *          <a:b> -- nesting level 1
+        *            <a:b> -- nesting level 2
+        *            </a:b>
+        *          </a:b>
+        *          <a:b> -- nesting level 1
+        *          </a:b>
+        *        </e:f>
+        *      </c:d>
+        *    </a:b>
+        *  </g:h>
+        * 
+        * @return Custom tag's nesting level
+        */
+       private int computeCustomNestingLevel() {
+           int n = 0;
+           Node p = parent;
+           while (p != null) {
+               if ((p instanceof Node.CustomTag)
+                       && name.equals(((Node.CustomTag) p).name)) {
+                   n++;
+               }
+               p = p.parent;
+           }
+           return n;
+       }
     }
 
     /**
Index: src/share/org/apache/jasper/compiler/Parser.java
===================================================================
RCS file: 
/home/cvs/jakarta-tomcat-jasper/jasper2/src/share/org/apache/jasper/compiler/Parser.java,v
retrieving revision 1.4
diff -u -r1.4 Parser.java
--- src/share/org/apache/jasper/compiler/Parser.java    7 Jun 2002 20:04:27 -0000      
 1.4
+++ src/share/org/apache/jasper/compiler/Parser.java    12 Jun 2002 21:49:24 -0000
@@ -704,7 +704,8 @@
            reader.reset(start);
            return false;
        }
-        if (tagLibInfo.getTag(shortTagName) == null) {
+        TagInfo tagInfo = tagLibInfo.getTag(shortTagName);
+       if (tagInfo == null) {
            err.jspError(start, "jsp.error.bad_tag", shortTagName, prefix);
        }
 
@@ -716,7 +717,7 @@
        if (reader.matches("/>")) {
            // EmptyElemTag ::= '<' Name ( S Attribute )* S? '/>'#
            new Node.CustomTag(attrs, start, tagName, prefix, shortTagName,
-                              parent);
+                              tagInfo, parent);
            return true;
        }
        
@@ -729,10 +730,10 @@
        // Looking for a body, it still can be empty; but if there is a
        // a tag body, its syntax would be dependent on the type of
        // body content declared in TLD.
-       String bc = ((TagLibraryInfo) 
taglibs.get(prefix)).getTag(shortTagName).getBodyContent();
+       String bc = tagInfo.getBodyContent();
 
        Node tagNode = new Node.CustomTag(attrs, start, tagName, prefix,
-                                         shortTagName, parent);
+                                         shortTagName, tagInfo, parent);
        // There are 3 body content types: empty, jsp, or tag-dependent.
        if (bc.equalsIgnoreCase(TagInfo.BODY_CONTENT_EMPTY)) {
            if (!reader.matchesETag(tagName)) {
cvs server: Diffing src/share/org/apache/jasper/core
cvs server: Diffing src/share/org/apache/jasper/logging
cvs server: Diffing src/share/org/apache/jasper/resources
cvs server: Diffing src/share/org/apache/jasper/runtime
cvs server: Diffing src/share/org/apache/jasper/servlet
cvs server: Diffing src/share/org/apache/jasper/util
cvs server: Diffing src/share/org/apache/jasper/xmlparser

--
To unsubscribe, e-mail:   <mailto:[EMAIL PROTECTED]>
For additional commands, e-mail: <mailto:[EMAIL PROTECTED]>

Reply via email to