Custom for loop tag
[nobr]Hi all,
I am trying to implement a custom "for loop tag". But it seems to me that there is a type conversion problem. I've tried many ways to fixed it but I still cannot get it to work. I've dug through my books, searched the internet and this forum but I can't found anythig useful. I am really lost now! Can anyone help me with this? Many many many thanks!!
loopTag.jsp
<%@ taglib prefix="myTag" uri="/WEB-INF/myTag.tld" %>
<%int num=5; %>
<myTag:loop index="i" count="<%=num%>">
body1here: i expr: <%=i%> i property: <jsp:getProperty name="i" property="value"/> <br>
</myTag:loop>
myTag.tld
<?xml version="1.0" encoding="ISO-8859-1" ?>
<!DOCTYPE taglib PUBLIC"-//Sun Microsystems, Inc.//DTD JSP Tag Library 1.1//EN""http://java.sun.com/j2ee/dtds/web-jsptaglibrary_1_1.dtd">
<taglib>
<tlibversion>1.0</tlibversion>
<jspversion>1.1</jspversion>
<shortname>simple</shortname>
<tag>
<name>loop</name>
<tagclass>myTag.LoopTag</tagclass>
<bodycontent>JSP</bodycontent>
<info>for loop</info>
<attribute>
<name>index</name>
<required>true</required>
</attribute>
<attribute>
<name>count</name>
<required>true</required>
<rtexprvalue>true</rtexprvalue>
</attribute>
</tag>
</taglib>
LoopTag.java
package myTag;
import javax.servlet.jsp.*;
import javax.servlet.jsp.tagext.*;
import java.util.Hashtable;
import java.io.Writer;
import java.io.IOException;
publicclass LoopTagextends BodyTagSupport{
String index;
int count;
int i=0;
publicvoid setIndex(String index){
this.index=index;
}
publicvoid setCount(String count){
this.count=Integer.parseInt(count);
}
publicint doStartTag()throws JspException{
return EVAL_BODY_TAG;
}
publicvoid doInitBody()throws JspException{
pageContext.setAttribute(index, i);
i++;
}
publicint doAfterBody()throws JspException{
try{
if (i >= count){
bodyContent.writeOut(bodyContent.getEnclosingWriter());
return SKIP_BODY;
}
else{
pageContext.setAttribute(index, i);
}
i++;
return EVAL_BODY_TAG;
}
catch (IOException ex){
thrownew JspTagException(ex.toString());
}
}
}
Error message:
type Exception report
message
description The server encountered an internal error () that prevented it from fulfillingthis request.
exception
org.apache.jasper.JasperException: Unable to compileclassfor JSP
An error occurred at line: 4 in the jsp file: /loopTag.jsp
Generated servlet error:
The method setCount(String) in the type LoopTag is not applicablefor the arguments (int)
An error occurred at line: 5 in the jsp file: /loopTag.jsp
Generated servlet error:
i cannot be resolved
org.apache.jasper.compiler.DefaultErrorHandler.javacError(DefaultErrorHandler.java:84)
org.apache.jasper.compiler.ErrorDispatcher.javacError(ErrorDispatcher.java:328)
org.apache.jasper.compiler.JDTCompiler.generateClass(JDTCompiler.java:409)
org.apache.jasper.compiler.Compiler.compile(Compiler.java:288)
org.apache.jasper.compiler.Compiler.compile(Compiler.java:267)
org.apache.jasper.compiler.Compiler.compile(Compiler.java:255)
org.apache.jasper.JspCompilationContext.compile(JspCompilationContext.java:556)
org.apache.jasper.servlet.JspServletWrapper.service(JspServletWrapper.java:293)
org.apache.jasper.servlet.JspServlet.serviceJspFile(JspServlet.java:314)
org.apache.jasper.servlet.JspServlet.service(JspServlet.java:264)
javax.servlet.http.HttpServlet.service(HttpServlet.java:802)
note The full stack trace of the root cause is available in the Apache Tomcat/5.5.11 logs.
[/nobr]
Your tag needs to expose a variable.
[url http://java.sun.com/j2ee/1.4/docs/tutorial/doc/JSPTags6.html#wp90424]Declaring Tag Variables for Tag Handlers [/url]
[url http://java.sun.com/j2ee/1.4/docs/tutorial/doc/JSPTags7.html#wp90587]Tag Handlers for Tags That Define Variables [/url]
You need to add something like the following to the tld:
<variable>
<name-from-attribute>index</name-from-attribute>
<variable-class>java.lang.Integer</variable-class>
<declare>true</declare>
<scope>NESTED</scope>
</variable>
Oh, it is complaining that <%= num%> does not evaluate to a String.
So either make the variable an Integer, or the expression something like <%= "" + num %> (cheap way out)
Maybe this is unwarranted, but why re-invent the wheel? JSTL includes a very useful forEach loop. Take a look at theirs, and see how they do it ;-)
[nobr]> So either make the variable an Integer, or the
> wheel? JSTL includes a very useful forEach loop.
Thanks for replying me! I fixed the type conversion problem. And I am very close to finishing this tag except that I have problem accessing the index variable of the loop inside the loop.
Another problem is that the loop print out 5 times on the first execution, but only print out 1 time in the following executions. I can't figure out why it would do that.
Can any Custom Tag Expert/Genius help me with this "For-Loop" tag? This should be a piece of cake for you guys, but it is very confusing for a newbie like me. Many Thanks!!!
loopTag.jsp
<%@ taglib prefix="myTag" uri="/WEB-INF/myTag.tld" %>
<% int num=5; %>
<myTag:loop index="i" count="<%= num %>">
index : <myTag:printIndex></myTag:printIndex> <br>
</myTag:loop>
myTag.tld
<?xml version="1.0" encoding="ISO-8859-1" ?>
<!DOCTYPE taglib PUBLIC "-//Sun Microsystems, Inc.//DTD JSP Tag Library 1.1//EN" "http://java.sun.com/j2ee/dtds/web-jsptaglibrary_1_1.dtd">
<taglib>
<tlibversion>1.0</tlibversion>
<jspversion>1.1</jspversion>
<shortname>simple</shortname>
<tag>
<name>loop</name>
<tagclass>myTag.LoopTag</tagclass>
<bodycontent>JSP</bodycontent>
<info>for loop</info>
<attribute>
<name>index</name>
<required>true</required>
<rtexprvalue>true</rtexprvalue>
</attribute>
<attribute>
<name>count</name>
<required>true</required>
<rtexprvalue>true</rtexprvalue>
</attribute>
</tag>
<tag>
<name>printIndex</name>
<tagclass>myTag.LoopPrintTag</tagclass>
<bodycontent>JSP</bodycontent>
<info>for loop print</info>
</tag>
</taglib>
LoopTag.java
package myTag;
import javax.servlet.jsp.*;
import javax.servlet.jsp.tagext.*;
import java.util.Hashtable;
import java.io.Writer;
import java.io.IOException;
public class LoopTag extends BodyTagSupport {
String index;
int count;
int i=0;
public void setIndex(String index){
this.index=index;
}
public String getIndex(){
return String.valueOf(pageContext.getAttribute(index));
}
public void setCount(int count){
this.count=count;
}
public int doStartTag() throws JspException {
return EVAL_BODY_TAG;
}
public void doInitBody() throws JspException {
pageContext.setAttribute(index, i);
i++;
}
public int doAfterBody() throws JspException {
try {
if (i >= count) {
bodyContent.writeOut(bodyContent.getEnclosingWriter());
return SKIP_BODY;
}
else{
pageContext.setAttribute(index, i);
}
i++;
return EVAL_BODY_TAG;
}
catch (IOException ex) {
throw new JspTagException(ex.toString());
}
}
}
LoopPrintTag.java
package myTag;
import javax.servlet.jsp.*;
import javax.servlet.jsp.tagext.*;
import java.io.*;
import javax.servlet.*;
public class LoopPrintTag extends BodyTagSupport {
public int doStartTag() throws JspTagException {
LoopTag parent = (LoopTag)findAncestorWithClass(this, LoopTag.class);
if (parent == null) {
throw new JspTagException("print not inside loop");
}
return(EVAL_BODY_TAG);
}
public int doAfterBody() {
LoopTag parent = (LoopTag)findAncestorWithClass(this, LoopTag.class);
try {
JspWriter out = pageContext.getOut();
out.print(" i = " + parent.getIndex());
} catch(IOException ioe) {
System.out.println("Error in printTag: " + ioe);
}
return(SKIP_BODY);
}
}
Output: (first execution)
index :
index :
index :
index :
index :
Output: (following execution)
index :[/nobr]
> > wheel? JSTL includes a very useful forEach loop.By the way, I looked at the JSTL forEach loop, it is very nice. But unfortunately, I am not allowed to use JSTL in this project, because the client server doesn't have JSTL installed.
you need to reset your variable "i" in the doStartTag: i = 0;
> you need to reset your variable "i" > in the doStartTag: i = 0;Nice, it works!!! Thank you so much!!But I am still struggling to access the "loop index " inside the loop. Can anyone give me some directions?
> But I am still struggling to access the "loop index "
> inside the loop. Can anyone give me some directions?
Thank God! I finally figured out what I did wrong!
My print statement should be placed in the doStartTag() method.
Hope this can help somebody who encounters the same problem. :)
LoopPrintTag.java
package myTag;
import javax.servlet.jsp.*;
import javax.servlet.jsp.tagext.*;
import java.io.*;
import javax.servlet.*;
public class LoopPrintTag extends BodyTagSupport {
public int doStartTag() throws JspTagException {
LoopTag parent = (LoopTag)findAncestorWithClass(this, LoopTag.class);
if (parent == null) {
throw new JspTagException("print not inside loop");
}
else{
try {
JspWriter out = pageContext.getOut();
out.print(" i = " + parent.getIndex());
} catch(IOException ioe) {
System.out.println("Error in printTag: " + ioe);
}
}
return(SKIP_BODY);
}
}
Output:
index : i = 0
index : i = 1
index : i = 2
index : i = 3
index : i = 4
[nobr]I took the custom for-loop one step further and now it can be nested with in other for-loop.
Hope this is the last time I have to reinvent the wheel. Now I REALLY apprecate JSTL!!
loopTag.jsp
<%@ taglib prefix="myTag" uri="/WEB-INF/myTag.tld" %>
<% int row = 5; %>
<% int col = 5; %>
<table border=0>
<myTag:loop index="i" count="<%= row %>">
<tr>
<myTag:loop index="j" count="<%= col %>">
<td>index: i=<myTag:printIndex parentLevel="2" /> j=<myTag:printIndex parentLevel="1" />, <br></td>
</myTag:loop>
</tr>
</myTag:loop>
</table>
myTag.tld
<?xml version="1.0" encoding="ISO-8859-1" ?>
<!DOCTYPE taglib PUBLIC "-//Sun Microsystems, Inc.//DTD JSP Tag Library 1.1//EN" "http://java.sun.com/j2ee/dtds/web-jsptaglibrary_1_1.dtd">
<taglib>
<tlibversion>1.0</tlibversion>
<jspversion>1.1</jspversion>
<shortname>simple</shortname>
<tag>
<name>loop</name>
<tagclass>myTag.LoopTag</tagclass>
<bodycontent>JSP</bodycontent>
<info>for loop</info>
<attribute>
<name>index</name>
<required>true</required>
<rtexprvalue>true</rtexprvalue>
</attribute>
<attribute>
<name>count</name>
<required>true</required>
<rtexprvalue>true</rtexprvalue>
</attribute>
</tag>
<tag>
<name>printIndex</name>
<tagclass>myTag.LoopPrintTag</tagclass>
<bodycontent>JSP</bodycontent>
<info>for loop print</info>
<attribute>
<name>parentLevel</name>
<required>true</required>
<rtexprvalue>true</rtexprvalue>
</attribute>
</tag>
</taglib>
LoopTag.java
package myTag;
import javax.servlet.jsp.*;
import javax.servlet.jsp.tagext.*;
import java.util.Hashtable;
import java.io.Writer;
import java.io.IOException;
public class LoopTag extends BodyTagSupport {
String index;
int count;
int i=0;
public void setIndex(String index){
this.index=index;
}
public String getIndex(){
return String.valueOf(pageContext.getAttribute(index));
}
public void setCount(int count){
this.count=count;
}
public int doStartTag() throws JspException {
i = 0;
return EVAL_BODY_TAG;
}
public void doInitBody() throws JspException {
pageContext.setAttribute(index, i);
i++;
}
public int doAfterBody() throws JspException {
try {
if (i >= count) {
bodyContent.writeOut(bodyContent.getEnclosingWriter());
return SKIP_BODY;
}
else{
pageContext.setAttribute(index, i);
}
i++;
return EVAL_BODY_TAG;
}
catch (IOException ex) {
throw new JspTagException(ex.toString());
}
}
}
LoopPrintTag.java
package myTag;
import javax.servlet.jsp.*;
import javax.servlet.jsp.tagext.*;
import java.io.*;
import javax.servlet.*;
public class LoopPrintTag extends BodyTagSupport {
private int parentLevel = 1;
public int doStartTag() throws JspTagException {
LoopTag parent = (LoopTag)findAncestorWithClass(this, LoopTag.class);
for(int i=1; i<parentLevel; i++){
parent = (LoopTag)findAncestorWithClass(parent, LoopTag.class);
}
if (parent == null) {
throw new JspTagException("print not inside loop");
}
else{
try {
JspWriter out = pageContext.getOut();
out.print(parent.getIndex());
} catch(IOException ioe) {
System.out.println("Error in printTag: " + ioe);
}
}
return(SKIP_BODY);
}
public void setParentLevel(int _parentLevel){
if(_parentLevel > 0){
parentLevel = _parentLevel;
}
}
}
Output:
index: i=0 j=0, index: i=0 j=1, index: i=0 j=2, index: i=0 j=3, index: i=0 j=4,
index: i=1 j=0, index: i=1 j=1, index: i=1 j=2, index: i=1 j=3, index: i=1 j=4,
index: i=2 j=0, index: i=2 j=1, index: i=2 j=2, index: i=2 j=3, index: i=2 j=4,
index: i=3 j=0, index: i=3 j=1, index: i=3 j=2, index: i=3 j=3, index: i=3 j=4,
index: i=4 j=0, index: i=4 j=1, index: i=4 j=2, index: i=4 j=3, index: i=4 j=4,[/nobr]
