This pure Java Server Pages (JSP) Tag Library implementation of the number guess game is designed to be nearly identical in features and appearance to the Qwicap implementation of the game. Because of the vast differences between the tag library and Qwicap approaches to web application development, this example bears no similarity in structure to the Qwicap example, and only a limited conceptual similarity to the pure JSP example.
Contents
- User Experience
- Java Server Page "number-guess.jsp"
- Style Sheet "number-guess.css"
- File "WEB-INF/tlguess.tld"
- File "WEB-INF/web.xml"
- File "tagsguess/BadInput.java"
- File "tagsguess/DoneSection.java"
- File "tagsguess/GuessCount.java"
- File "tagsguess/GuessNo.java"
- File "tagsguess/GuessResult.java"
- File "tagsguess/GuessValue.java"
- File "tagsguess/HistoryRows.java"
- File "tagsguess/HistorySection.java"
- File "tagsguess/InputError.java"
- File "tagsguess/InputErrorMessage.java"
- File "tagsguess/InputSection.java"
- File "tagsguess/NumberGuessGame.java"
- File "tagsguess/TheNumber.java"
—[ Return To: “Comparison of Number Guess Implementations” ]—
User Experience
Game In Progress, Bad Input
Game Complete
Java Server Page "number-guess.jsp"
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <%@ taglib prefix="guess" uri="/WEB-INF/tlguess.tld" %> <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> <head> <meta http-equiv="Content-Type" content='text/html; charset=UTF-8'/> <meta http-equiv="Content-Style-Type" content="text/css"/> <link rel="stylesheet" media="screen" type="text/css" title="Preferred" href="number-guess.css"/> <title>JSP Tag Library Number Guess</title> </head> <body> <h1>JSP Tab Library Number Guess</h1> <div class='content'> <guess:InputSection> <div class='guess'> <p>A random number between 0 and 100 (inclusive) has been selected.</p> <guess:InputError> <div class='bad-field-error-message'> <guess:InputErrorMessage/> </div> </guess:InputError> <form method='post' action='number-guess.jsp'> <label <guess:InputError>class='bad-field'</guess:InputError>>Guess the number: <input type='text' size='6' name='guess' <guess:InputError>value='<guess:BadInput/>'</guess:InputError>/> </label> <input type='submit' value='Guess'/> </form> </div> </guess:InputSection> <guess:DoneSection> <div class='done'> <p>Correct! The number was <guess:TheNumber/>. You guessed it in <guess:GuessCount/> attempts.</p> <form method='post' action='number-guess.jsp'> <input type='submit' value='Play Again'/> <input type='hidden' name='restart' value='restart'/> </form> </div> </guess:DoneSection> <guess:HistorySection> <div class='history'> <table class='history'> <thead> <tr> <th>No.</th> <th>Guess</th> <th>Result</th> </tr> </thead> <tbody> <guess:HistoryRows> <tr> <td class='no'><guess:GuessNo/></td> <td class='guess'><guess:GuessValue/></td> <td class='result'><guess:GuessResult/></td> </tr> </guess:HistoryRows> </tbody> </table> </div> </guess:HistorySection> </div> </body> </html>
Style Sheet "number-guess.css"
body { color: black; background-color: #d3c692; margin: 4% 6% 3% 6%; font-family: verdana, arial, helvetica, sans-serif; } div.content { background-color: #ffffee; border: 1px solid; padding: 1em 2em; text-align: center; } div.guess input[type="submit"] { width: 12ex; } table.history { margin-left: auto; margin-right: auto; border-top: 1px solid gray; border-right: 1px solid gray; border-spacing: 0; border-collapse: collapse; empty-cells: show; } table.history th { padding: 0.2em 1em; border-left: 1px solid white; border-bottom: 1px solid gray; background-color: gray; color: white; } table.history th:first-child { border-left: 1px solid gray; } table.history td { padding: 0.2em 1em; border-left: 1px solid gray; border-bottom: 1px solid gray; text-align: center; } div.bad-field-error-message { border: 1px solid; background: #ecc6c6; padding: 0.5em 2em; margin-top: 1em; margin-bottom: 1em; margin-left: auto; margin-right: auto; } *.bad-field { color: red; }
File "WEB-INF/tlguess.tld"
<?xml version="1.0" encoding="UTF-8" ?> <taglib xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-jsptaglibrary_2_0.xsd" version="2.0"> <description>A tag library implementing a number guess game.</description> <tlib-version>1.0</tlib-version> <short-name>NumberGuessTagLibrary</short-name> <uri>/tagsguess</uri> <tag> <name>BadInput</name> <tag-class>tagsguess.BadInput</tag-class> <body-content>empty</body-content> <description>Prints the most recent input, if it was bad.</description> </tag> <tag> <name>DoneSection</name> <tag-class>tagsguess.DoneSection</tag-class> <body-content>JSP</body-content> <description> If the game is complete, the body of this tag is included, otherwise it is omitted. </description> </tag> <tag> <name>GuessCount</name> <tag-class>tagsguess.GuessCount</tag-class> <body-content>empty</body-content> <description>Prints the number of guesses made during this game.</description> </tag> <tag> <name>GuessNo</name> <tag-class>tagsguess.GuessNo</tag-class> <body-content>empty</body-content> <description> When used inside a tagsguess.HistoryRows tag, prints the number of the guess in the current row of history. </description> </tag> <tag> <name>GuessResult</name> <tag-class>tagsguess.GuessResult</tag-class> <body-content>empty</body-content> <description> When used inside a tagsguess.HistoryRows tag, prints the result of the guess in the current row of history. </description> </tag> <tag> <name>GuessValue</name> <tag-class>tagsguess.GuessValue</tag-class> <body-content>empty</body-content> <description> When used inside a tagsguess.HistoryRows tag, prints the value of the guess in the current row of history. </description> </tag> <tag> <name>HistoryRows</name> <tag-class>tagsguess.HistoryRows</tag-class> <body-content>JSP</body-content> <description>Iterates through the guess history, from most recent guess, to least recent.</description> </tag> <tag> <name>HistorySection</name> <tag-class>tagsguess.HistorySection</tag-class> <body-content>JSP</body-content> <description> If there is guess history available, the body of this tag is included, otherwise it is omitted. </description> </tag> <tag> <name>InputError</name> <tag-class>tagsguess.InputError</tag-class> <body-content>JSP</body-content> <description> If the most recent input was bad, the body of this tag is included, otherwise it is omitted. </description> </tag> <tag> <name>InputErrorMessage</name> <tag-class>tagsguess.InputErrorMessage</tag-class> <body-content>empty</body-content> <description> Retrieves the error message associated with the most recent input. </description> </tag> <tag> <name>InputSection</name> <tag-class>tagsguess.InputSection</tag-class> <body-content>JSP</body-content> <description> If the game is in progress (and therefore accepting input), the body of this tag is included, otherwise it is omitted. </description> </tag> <tag> <name>TheNumber</name> <tag-class>tagsguess.TheNumber</tag-class> <body-content>empty</body-content> <description> Prints the number which the user is/was trying to guess. </description> </tag> </taglib>
File "WEB-INF/web.xml"
<?xml version="1.0" encoding="ISO-8859-1"?> <!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" "http://java.sun.com/j2ee/dtds/web-app_2_3.dtd"> <web-app> <display-name>Number Guess Application Implemented With Tag Library</display-name> <description> Sample application demonstrating the implementation of a number guess game by means of a custom tag library. </description> <session-config> <session-timeout>10</session-timeout> </session-config> <taglib> <taglib-uri>/WEB-INF/lib/tlguess.jar</taglib-uri> <taglib-location>/WEB-INF/tlguess.tld</taglib-location> </taglib> </web-app>
tagsguess/BadInput.java
package tagsguess; import javax.servlet.jsp.JspException; import javax.servlet.jsp.tagext.TagSupport; import java.io.IOException; public class BadInput extends TagSupport { public int doStartTag() throws JspException { try { pageContext.getOut().print(NumberGuessGame.getCurrentInstance(pageContext).getMostRecentInput()); return SKIP_BODY; } catch (IOException e) { throw new JspException(e); } } }
tagsguess/DoneSection.java
package tagsguess; import javax.servlet.jsp.tagext.BodyTagSupport; public class DoneSection extends BodyTagSupport { public int doStartTag() { if (NumberGuessGame.getCurrentInstance(pageContext).getIsDone()) return EVAL_BODY_INCLUDE; return SKIP_BODY; } }
tagsguess/GuessCount.java
package tagsguess; import javax.servlet.jsp.tagext.TagSupport; import javax.servlet.jsp.JspException; import java.io.IOException; public class GuessCount extends TagSupport { public int doStartTag() throws JspException { try { pageContext.getOut().print(NumberGuessGame.getCurrentInstance(pageContext).getGuessCount()); return SKIP_BODY; } catch (IOException e) { throw new JspException(e); } } }
tagsguess/GuessNo.java
package tagsguess; import javax.servlet.jsp.tagext.TagSupport; import javax.servlet.jsp.JspException; import java.io.IOException; public class GuessNo extends TagSupport { public int doStartTag() throws JspException { try { final HistoryRows Row = (HistoryRows) findAncestorWithClass(this, HistoryRows.class); pageContext.getOut().print(Row.getGuessNo()); return SKIP_BODY; } catch (IOException e) { throw new JspException(e); } } }
tagsguess/GuessResult.java
package tagsguess; import javax.servlet.jsp.tagext.TagSupport; import javax.servlet.jsp.JspException; import java.io.IOException; public class GuessResult extends TagSupport { public int doStartTag() throws JspException { try { final HistoryRows Row = (HistoryRows) findAncestorWithClass(this, HistoryRows.class); pageContext.getOut().print(Row.getGuessResult()); return SKIP_BODY; } catch (IOException e) { throw new JspException(e); } } }
tagsguess/GuessValue.java
package tagsguess; import javax.servlet.jsp.tagext.TagSupport; import javax.servlet.jsp.JspException; import java.io.IOException; public class GuessValue extends TagSupport { public int doStartTag() throws JspException { try { final HistoryRows Row = (HistoryRows) findAncestorWithClass(this, HistoryRows.class); pageContext.getOut().print(Row.getGuess()); return SKIP_BODY; } catch (IOException e) { throw new JspException(e); } } }
tagsguess/HistoryRows.java
package tagsguess; import javax.servlet.jsp.tagext.BodyTagSupport; public class HistoryRows extends BodyTagSupport { private NumberGuessGame Game; private int Index; public int doStartTag() { Game = NumberGuessGame.getCurrentInstance(pageContext); Index = Game.getGuessCount() - 1; return EVAL_BODY_INCLUDE; } public int getGuessNo() { return Index + 1; } public int getGuess() { return Game.getGuess(Index); } public String getGuessResult() { final int Guess = getGuess(); final int TheNumber = Game.getTheNumber(); String Result = "Correct!"; if (Guess < TheNumber) Result = "Too Low"; else if (Guess > TheNumber) Result = "Too High"; return Result; } public int doAfterBody() { if (--Index >= 0) return EVAL_BODY_AGAIN; // EVAL_BODY_TAG; return SKIP_BODY; } public void release() { Game = null; } }
tagsguess/HistorySection.java
package tagsguess; import javax.servlet.jsp.tagext.BodyTagSupport; public class HistorySection extends BodyTagSupport { public int doStartTag() { if (NumberGuessGame.getCurrentInstance(pageContext).getHasHistory()) return EVAL_BODY_INCLUDE; return SKIP_BODY; } }
tagsguess/InputError.java
package tagsguess; import javax.servlet.jsp.tagext.BodyTagSupport; public class InputError extends BodyTagSupport { public int doStartTag() { if (NumberGuessGame.getCurrentInstance(pageContext).getInputWasBad()) return EVAL_BODY_INCLUDE; return SKIP_BODY; } }
tagsguess/InputErrorMessage.java
package tagsguess; import javax.servlet.jsp.tagext.TagSupport; import javax.servlet.jsp.JspException; import java.io.IOException; public class InputErrorMessage extends TagSupport { public int doStartTag() throws JspException { try { pageContext.getOut().print(NumberGuessGame.getCurrentInstance(pageContext).getInputErrorMessage()); return SKIP_BODY; } catch (IOException e) { throw new JspException(e); } } }
tagsguess/InputSection.java
package tagsguess; import javax.servlet.jsp.tagext.BodyTagSupport; public class InputSection extends BodyTagSupport { public int doStartTag() { NumberGuessGame Game = NumberGuessGame.getCurrentInstance(pageContext); final String GuessStr = pageContext.getRequest().getParameter("guess"); if (GuessStr != null) Game.play(GuessStr); else if (pageContext.getRequest().getParameter("restart") != null) Game = NumberGuessGame.startNewInstance(pageContext); return Game.getInProgress() ? EVAL_BODY_INCLUDE : SKIP_BODY; } }
tagsguess/NumberGuessGame.java
package tagsguess; import javax.servlet.jsp.PageContext; import javax.servlet.http.HttpSession; import java.util.ArrayList; import java.util.Random; public class NumberGuessGame { private static final String GameAttributeName = "GuessGame"; private final ArrayList Hist = new ArrayList(); private final int TheNumber; private boolean IsDone = false; private String BadInputErrorMsg; private String MostRecentInput; public static NumberGuessGame getCurrentInstance ( final PageContext PageCtxt ) { NumberGuessGame Game = (NumberGuessGame) PageCtxt.getSession().getAttribute(GameAttributeName); if (Game == null) Game = startNewInstance(PageCtxt); return Game; } public static NumberGuessGame startNewInstance ( final PageContext PageCtxt ) { final HttpSession Sess = PageCtxt.getSession(); final NumberGuessGame Game = new NumberGuessGame(); Sess.setAttribute(GameAttributeName, Game); return Game; } public NumberGuessGame() { TheNumber = new Random().nextInt(101); } public void play ( final String GuessStr ) { if (IsDone) return; MostRecentInput = GuessStr; BadInputErrorMsg = null; try { final int Guess = Integer.parseInt(GuessStr); if (Guess < 0 || Guess > 100) { BadInputErrorMsg = "The guess must be in the range 0 to 100 (inclusive). The number \"" + Guess + "\" is not in that range."; } else { Hist.add(new Integer(Guess)); if (Guess == TheNumber) IsDone = true; } } catch (NumberFormatException e) { BadInputErrorMsg = "The guess \"" + GuessStr + "\" is not a number."; } } public int getTheNumber() { return TheNumber; } public boolean getInProgress() { return !IsDone; } public boolean getHasHistory() { return getGuessCount() > 0; } public int getGuessCount() { return Hist.size(); } public int getGuess ( final int Index ) { return ((Integer) Hist.get(Index)).intValue(); } public boolean getIsDone() { return IsDone; } public boolean getInputWasBad() { return BadInputErrorMsg != null; } public String getInputErrorMessage() { return BadInputErrorMsg; } public String getMostRecentInput() { return MostRecentInput; } }
tagsguess/TheNumber.java
package tagsguess; import javax.servlet.jsp.tagext.TagSupport; import javax.servlet.jsp.JspException; import java.io.IOException; public class TheNumber extends TagSupport { public int doStartTag() throws JspException { try { pageContext.getOut().print(NumberGuessGame.getCurrentInstance(pageContext).getTheNumber()); return SKIP_BODY; } catch (IOException e) { throw new JspException(e); } } }