5月31日

jQuery

ヘッダにライブラリの読み込みを追加する。

	<head>
		<meta charset="utf-8">
		<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.2.2/jquery.min.js"></script>
		<title>Welcome</title>
	</head>

Chromeブラウザでサイトにアクセスし、デベロッパーツールを起動する。
ConsoleからJavaScriptを入力してHTMLを操作できる。

JSPからJavaScriptファイルを読み込むための設定

webappの下にJavaScriptファイル等を格納する resources フォルダを作成する。
さらに、resources フォルダの下に js フォルダを作成する。

jsフォルダに main.js ファイルを作成する。

js

mvc-config.xml に設定を追加。

<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:context="http://www.springframework.org/schema/context"
	xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd
		http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
		http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">

    <context:component-scan base-package="jp.abc"/>


    <mvc:annotation-driven />
	<mvc:resources mapping="/resources/**" location="/resources/" />

	<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
	        <!-- Example: a logical view name of 'showMessage' is mapped to '/WEB-INF/jsp/showMessage.jsp' -->
	        <property name="prefix" value="/WEB-INF/view/"/>
	        <property name="suffix" value=".jsp"/>
	</bean>

</beans>

JSPで JavaScript ファイルを読み込む。

<!DOCTYPE html>
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>

<html>
	<head>
		<meta charset="utf-8">
		<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.2.2/jquery.min.js"></script>
		<script src="<c:url value="/resources/js/main.js" />"></script>
		<title>Welcome</title>
	</head>
	<body>
		<h2>${message}</h2>
		<form:form modelAttribute="formModel">
			<form:input path="input1"/>
			<input type="submit">
		</form:form>
		<div id="footer"></div>
		<input type="button" value="テスト" onclick="testClicked()">
	</body>
</html>

main.js

/**
 *
 */
function testClicked() {
	var footer = $('#footer');
	footer.append('<p>Hello</p>');
}

文字化け対策

web.xml に以下の設定を追加する。

	<filter>
		<filter-name>CharacterEncodingFilter</filter-name>
		<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
		<init-param>
			<param-name>encoding</param-name>
			<param-value>UTF-8</param-value>
		</init-param>
		<init-param>
			<param-name>forceEncoding</param-name>
			<param-value>true</param-value>
		</init-param>
	</filter>
	<filter-mapping>
		<filter-name>CharacterEncodingFilter</filter-name>
		<url-pattern>/*</url-pattern>
	</filter-mapping>
</web-app>

5月27日

SpringMVCによるプロジェクトの作成

[新規]-[プロジェクト]-[Spring Legacy Project]を選択して「次へ」をクリック。

プロジェクト名に「SpringMyApp」を入力し、テンプレートで「Simple Spring Web Maven」を選択する。
「完了」をクリックすると、プロジェクトが作成される。

プロジェクトが作成されたら、プロジェクトエクスプローラの「SpringMyApp」を右クリックし、[Maven]-[プロジェクトの更新]を選択する。

画面下のメッセージが100%まで進んで完了したら、再びプロジェクトエクスプローラの「SpringMyApp」を右クリックし、[実行]-[Maven install]を選択する。

コンソールに多数のライブラリがダウンロードされていることを示すメッセージが表示される。

ビルドが完了したら、サーバーにプロジェクトを追加する。

サーバータブの「Tomcat8 サーバー」を右クリックし[追加と削除]を選択する。
左側にある SpringMyApp を選択して「追加」をクリックし、右側に移動して「完了」をクリック。

「Tomcat8 サーバー」を右クリックして[開始]をクリックし、サーバーを起動する。

ブラウザを開いて、「http://localhost:8080/SpringMyApp/ 」にアクセスする。

click to enter と表示されれば、正常に動作している。

コントローラを作成する

プロジェクトエクスプローラで「src/main/java」を右クリックし[新規]-[クラス]を選択する。
以下の箇所を入力し「完了」をクリックする。

パッケージ: jp.abc
名前: MyAppController

生成された MyAppControllerクラスにテキストp.163のコードを記述する。

最初にクラスのアノテーションを追加する。
クラス宣言の前に「@C」だけを入力し、Ctrl+Spaceを押すと補完してくれる。

package jp.abc;

import org.springframework.stereotype.Controller;

@Controller
public class MyAppController {

}

次にメソッドを記述する。

package jp.abc;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;

@Controller
public class MyAppController {

	public String helo(Model model) {
		model.addAttribute("message", "this is sample. ok?");
		return "showMessage";
	}
}

メソッドを記述後に、メソッドのアノテーションを追加する。
メソッド宣言の前の行に「@Re」だけ入力してCtrl+Spaceを押すと補完してくれる。
アノテーションのパラメータも「v」を入力してCtrl+Spaceを押すなどして補完するとよい。

package jp.abc;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

@Controller
public class MyAppController {

	@RequestMapping(value = "/helo", method = RequestMethod.GET)
	public String helo(Model model) {
		model.addAttribute("message", "this is sample. ok?");
		return "showMessage";
	}
}

設定ファイルの変更

mvc-config.xml を開く。

コメントアウトされた部分のXMLコメントを削除して有効にする。
その内部の context:component-scan タグの base-package 要素の値を「jp.abc」に変更する。

<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:context="http://www.springframework.org/schema/context"
	xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd
		http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
		http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">

    <context:component-scan base-package="jp.abc"/>


    <mvc:annotation-driven />

	<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
	        <!-- Example: a logical view name of 'showMessage' is mapped to '/WEB-INF/jsp/showMessage.jsp' -->
	        <property name="prefix" value="/WEB-INF/view/"/>
	        <property name="suffix" value=".jsp"/>
	</bean>

</beans>

設定ファイルを修正したので、サーバーを再起動する。
コンソールに、「情報: Mapped “{[/helo],methods=[GET],…」と出ていれば、リクエストを受け付けることを示している。

http://localhost:8080/SpringMyApp/helo にアクセスすると、「this is sample. ok?」と表示される。

入力フォームを追加する

SpringMVCの場合、入力フォームで入力されたデータを受け取るにはいくつかの方法があるが、まずは保管するクラスを用意する方法で実装する。

[新規]-[クラス]でクラスを追加する。
クラス名は FormModel として「完了」をクリックする。

package jp.abc;

public class FormModel {

}

private メンバーを追加する。

package jp.abc;

public class FormModel {
	private String input1;
}

メニューで[ソース]-[getter および setter の生成]を選択し、input1 にチェックを入れて「OK」をクリックする。

package jp.abc;

public class FormModel {
	private String input1;

	public String getInput1() {
		return input1;
	}

	public void setInput1(String input1) {
		this.input1 = input1;
	}
}

showMessage.jsp にフォームを追加する。

<!DOCTYPE html>
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>

<html>
	<head>
		<meta charset="utf-8">
		<title>Welcome</title>
	</head>
	<body>
		<h2>${message}</h2>
		<form:form modelAttribute="formModel">
			<form:input path="input1"/>
			<input type="submit">
		</form:form>
	</body>
</html>

コントローラを修正する。

package jp.abc;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

@Controller
public class MyAppController {

	@RequestMapping(value = "/helo", method = RequestMethod.GET)
	public String helo(Model model) {
		FormModel fm = new FormModel();
		fm.setInput1("ここに書く");
		model.addAttribute("formModel", fm);
		model.addAttribute("message", "何か書いてください。");
		return "showMessage";
	}

	@RequestMapping(value = "helo", method = RequestMethod.POST)
	public String form(@ModelAttribute FormModel fm, Model model) {
		model.addAttribute("message", "you typed: " + fm.getInput1());
		return "showMessage";
	}
}

http://localhost:8080/SpringMyApp/helo にアクセスすると、入力フォームが表示される。
フォームに入力して送信すると、英数字はきちんと表示されるが、日本語は文字化けする。

5月24日

うらないもどきの続きを作る。

今は名前だけをJSPで表示しているので、誕生日も表示するように修正する。
まずはサーブレット側から誕生日のデータを渡すようにする。

	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		request.setCharacterEncoding("UTF-8");
		response.setContentType("text/html; charset=UTF-8");
		String name = request.getParameter("name");
		String month = request.getParameter("month");
		String date = request.getParameter("date");
		request.setAttribute("name", name);
		request.setAttribute("month", month);
		request.setAttribute("date", date);
		request.getRequestDispatcher("result.jsp")
			   .forward(request, response);
	}

JSP側で誕生日を表示する。

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>うらないもどき 結果</title>
</head>
<body>
<h1>結果</h1>
${month}月${date}日生まれの${name}さんの運勢は?
</body>
</html>

サーブレット側で結果を渡すコードを追加するとともに、うらないの結果を生成するメソッドを用意する。

	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		request.setCharacterEncoding("UTF-8");
		response.setContentType("text/html; charset=UTF-8");
		String name = request.getParameter("name");
		String month = request.getParameter("month");
		String date = request.getParameter("date");
		String result = getResult(month, date);
		request.setAttribute("name", name);
		request.setAttribute("month", month);
		request.setAttribute("date", date);
		request.setAttribute("result", result);
		request.getRequestDispatcher("result.jsp")
			   .forward(request, response);
	}

	private String getResult(String month, String date) {
		return "サイコーです!";
	}

JSP側で結果を表示する。

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>うらないもどき 結果</title>
</head>
<body>
<h1>結果</h1>
${month}月${date}日生まれの${name}さんの運勢は?
<div>${result}</div>
</body>
</html>

誕生日によって異なる結果が表示されるようにする。

	private String getResult(String month, String date) {
		String[] r = {
				"ぼちぼちです",
				"サイコーです!",
				"まあまあです",
				"それなりです",
				"やれやれです",
				"なかなかです",
				"かなりいい感じです",
				"最悪です"
		};
		String s = month + "-" + date;
		int i = s.hashCode() % r.length;
		return r[i];
	}

なんとか度診断も追加してみる。
入力フォームに追加する。

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>うらないもどき</title>
</head>
<body>

<h1>うらないもどき</h1>

<form action="DivineServlet">
<div>
名前:<input type="text" name="name">
</div>
<div>
誕生日:
<select name="month">
<c:forEach begin="1" end="12" varStatus="status">
  <option value="${status.index}">${status.index}</option>
</c:forEach>
</select>
月
<select name="date">
<c:forEach begin="1" end="31" varStatus="status">
  <option value="${status.index}">${status.index}</option>
</c:forEach>
</select>
日
</div>
<div>
なんとか度:<input type="text" name="rate">
</div>
<div>
<input type="submit" value="送信">
</div>
</form>


</body>
</html>

サーブレットにコードを追加する。

	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		request.setCharacterEncoding("UTF-8");
		response.setContentType("text/html; charset=UTF-8");
		String name = request.getParameter("name");
		String month = request.getParameter("month");
		String date = request.getParameter("date");
		String rate = request.getParameter("rate");
		String result = getResult(month, date);
		String rating = getRating(name, rate);
		request.setAttribute("name", name);
		request.setAttribute("month", month);
		request.setAttribute("date", date);
		request.setAttribute("result", result);
		request.setAttribute("rate", rating);
		request.getRequestDispatcher("result.jsp")
			   .forward(request, response);
	}

	private String getRating(String name, String rate) {
		String s = name + "-" + rate;
		int v = s.hashCode() % 100;
		String rating = name + "さんの"
					  + rate + "度は、"
					  + v + "%です。";
		return rating;
	}

結果を表示する。

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>うらないもどき 結果</title>
</head>
<body>
<h1>結果</h1>
${month}月${date}日生まれの${name}さんの運勢は?
<div>${result}</div>
<div>${rate}</div>
</body>
</html>

Spring MVC の準備として、プラグインをインストールする。

メニューの[ヘルプ]-[Eclipseマーケットプレイス]を選択する。
検索キーワードで「STS」を入力して検索を実行。
検索結果に出た「STS 3.7.3 RELEASE」をインストールする。

5月20日

クライアントからパラメータを受け取る

HelloServletでリクエストパラメータを受け取るコードを書いてみる。

リクエストパラメータの送信は、URLにパラメータを追加する。

http://localhost:8080/web/HelloServlet?key1=value1&key2=value2&...
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		Date d = new Date();
		Map<String, String[]> map = request.getParameterMap();
		Set<String> keys = map.keySet();
		for (String key : keys) {
			String[] v = map.get(key);
			System.out.println(key + "=" + v[0]);
		}
		Writer w = response.getWriter();
		w.write("<html><body>");
		w.write("Today is " + d.toString());
		w.write("</body></html>");
	}

クライアントから受け取ったパラメータと値をブラウザに表示するには、System.outに出力していた内容を、Writerに出力するように書き直せばよい。改行させるために、それぞれのキーと値のペアを div タグで囲む。

	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		Date d = new Date();
		Map<String, String[]> map = request.getParameterMap();
		Set<String> keys = map.keySet();
		Writer w = response.getWriter();
		w.write("<html><body>");
		w.write("Today is " + d.toString());
		for (String key : keys) {
			String[] v = map.get(key);
			w.write("<div>");
			w.write(key + "=" + v[0]);
			w.write("</div>");
		}
		w.write("</body></html>");
	}

うらないみたいなWebアプリを作ってみる

名前と誕生日を入力して送信すると、うらないの結果を表示するアプリを作る。
入力画面は input.jsp とする。

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>うらないもどき</title>
</head>
<body>

<h1>うらないもどき</h1>

<form action="DivineServlet">
<div>
名前:<input type="text" name="name">
</div>
<div>
誕生日:
<select name="month">
<c:forEach begin="1" end="12" varStatus="status">
  <option value="${status.index}">${status.index}</option>
</c:forEach>
</select>
月
<select name="date">
<c:forEach begin="1" end="31" varStatus="status">
  <option value="${status.index}">${status.index}</option>
</c:forEach>
</select>
日
</div>
<div>
<input type="submit" value="送信">
</div>

</form>


</body>
</html>

「占う」の英語が「Divine」なので、リクエストの受け取り側をDivineServletとする。
とりあえず、パラメータを受け取って表示するだけのサーブレットを作る。

package jp.abc;

import java.io.IOException;
import java.io.Writer;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
 * Servlet implementation class DivineServlet
 */
@WebServlet("/DivineServlet")
public class DivineServlet extends HttpServlet {
	private static final long serialVersionUID = 1L;

	/**
	 * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
	 */
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		String name = request.getParameter("name");
		String month = request.getParameter("month");
		String date = request.getParameter("date");
		Writer w = response.getWriter();
		w.write("<html><body><h1>結果</h1>");
		w.write(name);
		w.write("さん(");
		w.write(month);
		w.write("月");
		w.write(date);
		w.write("日生まれ)の運勢は");
		w.write("サイコーです");
		w.write("</body></html>");
	}

	/**
	 * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
	 */
	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		doGet(request, response);
	}

}

文字化け対策をする。
文字コードを指定するだけでよい。

	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		request.setCharacterEncoding("UTF-8");
		response.setContentType("text/html; charset=UTF-8");
		String name = request.getParameter("name");
		String month = request.getParameter("month");
		String date = request.getParameter("date");
		Writer w = response.getWriter();
		w.write("<html><body><h1>結果</h1>");
		w.write(name);
		w.write("さん(");
		w.write(month);
		w.write("月");
		w.write(date);
		w.write("日生まれ)の運勢は");
		w.write("サイコーです");
		w.write("</body></html>");
	}

Writerにタグを出力するのはめんどくさいので、結果をJSPで表示できるようにする。
結果を表示する result.jsp を作成する。

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>うらないもどき 結果</title>
</head>
<body>
<h1>結果</h1>
</body>
</html>

DivineServlet から result.jsp にフォワードする。

	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		request.setCharacterEncoding("UTF-8");
		response.setContentType("text/html; charset=UTF-8");
		String name = request.getParameter("name");
		String month = request.getParameter("month");
		String date = request.getParameter("date");
		request.getRequestDispatcher("result.jsp")
			   .forward(request, response);
	}

Servletで受け取ったパラメータをJSPに渡す。

	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		request.setCharacterEncoding("UTF-8");
		response.setContentType("text/html; charset=UTF-8");
		String name = request.getParameter("name");
		String month = request.getParameter("month");
		String date = request.getParameter("date");
		request.setAttribute("name", name);
		request.getRequestDispatcher("result.jsp")
			   .forward(request, response);
	}

JSPで表示する。

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>うらないもどき 結果</title>
</head>
<body>
<h1>結果</h1>
${name}
</body>
</html>

5月17日

Webアプリケーション開発の基本

EclipseでWebアプリケーションを開発するには、そのためのプロジェクトを作成する。
基本的なWebアプリケーションの場合、「動的Webプロジェクト」を作成する。

Webアプリケーションを作成する場合、Eclipseのパースペクティブを「JavaEE」に切り替えると便利。
メニューから[ウィンドウ]-[パースペクティブ]-[パースペクティブを開く]-[その他]を選択し、ダイアログで「JavaEE」を選択する。

動的Webプロジェクト作成後、「新規サーブレット」を作成する。
名前:HelloServlet
スタブは doGet() のみを選択する。

生成された HelloServlet のコードに、テキストの「リスト16-7」のコードを記述する。

画面下側のフレーム内にある「サーバー」タブを選択する。

Webアプリケーションを動作させるためのサーバーを作成する。
「使用可能なサーバーがありません。・・・」をクリックする。

「Tomcat8サーバー」を選択して「次へ」をクリック。
「すべて追加」または「web」プロジェクトを選択して「追加」をクリックし、「web」を右側に移動する。
「完了」をクリック。

サーバータブにTomcat8サーバーが現れるので、右クリックして「開始」をクリックする。

Eclipseのツールバーの「Webブラウザーを開く」(地球のアイコン)をクリックしてWebブラウザを開く。
アドレスバーに「http://localhost:8080/web/HelloServlet」を入力してエンターすると、現在時刻が表示される。

JSP

JSPのサンプルを作成する。
プロジェクト名を右クリックし[新規]-[JSPファイル]を選択する。
ファイル名を「index.jsp」とする。
「次へ」をクリックするとテンプレートが表示されるので、そのまま「完了」をクリックし、JSPファイルを生成する。
WebContentフォルダの下に「index.jsp」が作られている。

JSPの基本

このあたりを参考にする。
サーブレット(Servlet) / JSP入門

JSPには以下の要素がある。
・スクリプトレット
・式
・宣言
・コメント

簡単なJSPの例

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>JSPサンプル</title>
</head>
<body>

<h1>JSPサンプル</h1>

<h2>スクリプトレット</h2>
<%
	out.println(new java.util.Date());
	for (int i = 0; i < 5; i++) {
		out.println("<div>i=" + i + "</div>");
	}
%>

<h2>式</h2>
<%= 5*5 %>

<h2>宣言</h2>
<%!
	int count = 0;
%>
<%=count %>

<h2>コメント</h2>
<!-- HTMLコメント -->
<%-- JSPコメント --%>

</body>
</html>

JSTL

JSTLは、JSP Standard Tag Libraryのことで、よく使われているタグライブラリ。

\\KGAKUSEI1\share\澤田\jstl\lib にある、以下の2ファイルをコピーする。
・jstl.jar
・standard.jar

プロジェクト内の WebContent/WEB-INF/lib に貼り付ける。

JSTLを使ったサンプルコードを index.jsp に追加する。

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>JSPサンプル</title>
</head>
<body>

<h1>JSPサンプル</h1>

<h2>スクリプトレット</h2>
<%
	out.println(new java.util.Date());
	for (int i = 0; i < 5; i++) {
		out.println("<div>i=" + i + "</div>");
	}
%>

<h2>式</h2>
<%= 5*5 %>

<h2>宣言</h2>
<%!
	int count = 0;
%>
<%=count %>

<h2>コメント</h2>
<!-- HTMLコメント -->
<%-- JSPコメント --%>

<h2>JSTL</h2>
<c:set var="data" value="てすと!!" />
<c:out value="${data}" />


</body>
</html>

文字化けしている人は、以下の設定を確認する。
[ウィンドウ]-[設定]
左側のツリーで[Web]-[JSPファイル]を選択する。
エンコードを「UTF-8」に設定して「OK」する。

5月13日

レッドブルを購入するテストがうまくいかなかったので、購入できるかどうかを調べてみるために、isPurchasable()を追加してみる。

	@Test
	public void testレッドブルを購入する() {
		VendingMachine vm = new VendingMachine();
		RedBull r = (RedBull) vm.purchase("レッドブル");
		assertNull(r);
		vm.insert(200);
		boolean b = vm.isPurchasable("レッドブル");
		assertEquals(true, b);
		r = (RedBull) vm.purchase("レッドブル");
		assertNotNull(r);
	}

購入できないといわれるので、原因がどこかを考える。

200円を投入しているけど、200円玉などない!ので、お金が入ってない!

	@Test
	public void testレッドブルを購入する() {
		VendingMachine vm = new VendingMachine();
		RedBull r = (RedBull) vm.purchase("レッドブル");
		assertNull(r);
		vm.insert(100);
		vm.insert(100);
		boolean b = vm.isPurchasable("レッドブル");
		assertEquals(true, b);
		r = (RedBull) vm.purchase("レッドブル");
		assertNotNull(r);
	}

100円を2回投入すれば買えるようになった。

最後に、3種類の飲み物を購入してみる。
売上高とおつりを確認して終わりとする。

	@Test
	public void test複数の飲み物を購入する() {
		VendingMachine vm = new VendingMachine();
		vm.insert(1000);
		Coke c = (Coke) vm.purchase("コーラ");
		assertNotNull(c);
		Water w = (Water) vm.purchase("水");
		assertNotNull(w);
		RedBull r = (RedBull) vm.purchase("レッドブル");
		assertNotNull(r);
		int sa = vm.getSaleAmount();
		assertEquals(420, sa);
		int amount = vm.getAmount();
		assertEquals(580, amount);
		int change = vm.refund();
		assertEquals(580, change);
	}

テキスト16章

ストリームは、Javaがデータの入出力を行うための概念。

まずはファイルから文字列を読み書きするサンプルを作成してみる。

Filer.java

package tdd;

public class Filer {
	public long read(String name) {
		return 0;
	}
}

ファイルを読み込んでバイト数を返すメソッドread()を作ってみる。

テストを書く。

package tdd;

import static org.junit.Assert.*;

import java.io.IOException;

import org.junit.Test;

public class FilerTest {

	@Test
	public void testRead() {
		Filer f = new Filer();
		long l;
		l = f.read("Coke.java");
		assertEquals(56, l);
	}
}

テストにパスするようにコードを修正する。

package tdd;

public class Filer {
	public long read(String name) {
		return 56;
	}
}

テストを追加する。

package tdd;

import static org.junit.Assert.*;

import java.io.IOException;

import org.junit.Test;

public class FilerTest {

	@Test
	public void testRead() {
		Filer f = new Filer();
		long l;
		l = f.read("Coke.java");
		assertEquals(56, l);
		l = f.read("Drink.java");
		assertEquals(52, l);
	}
}

実際のファイルにアクセスするようコードを修正。

package tdd;

import java.io.FileReader;
import java.io.IOException;

public class Filer {
	public long read(String name) throws IOException {
		long l = 0;
		FileReader r = new FileReader(name);
		return 56;
	}
}

例外をスローするので、テスト側も修正。
FileNotFoundExceptionを無視しないようにするため、RuntimeExceptionを作り直してスローして、テストケースが例外を認識できるようにする。

package tdd;

import static org.junit.Assert.*;

import java.io.File;
import java.io.IOException;

import org.junit.Test;

public class FilerTest {

	@Test
	public void testRead() {
		Filer f = new Filer();
		long l;
		try {
			l = f.read("Coke.java");
			assertEquals(56, l);
			l = f.read("Drink.java");
			assertEquals(52, l);
		} catch (IOException e) {
			e.printStackTrace();
			throw new RuntimeException(e);
		}
	}

}

作業ディレクトリを調べるためのコードを追加。

package tdd;

import static org.junit.Assert.*;

import java.io.File;
import java.io.IOException;

import org.junit.Test;

public class FilerTest {

	@Test
	public void testRead() {
		File cd = new File(".");
		System.out.println(cd.getAbsolutePath());
		Filer f = new Filer();
		long l;
		try {
			l = f.read("Coke.java");
			assertEquals(56, l);
			l = f.read("Drink.java");
			assertEquals(52, l);
		} catch (IOException e) {
			e.printStackTrace();
			throw new RuntimeException(e);
		}
	}

}

作業ディレクトリからの相対パスでファイルを指定するようにテストコードを修正。

package tdd;

import static org.junit.Assert.*;

import java.io.File;
import java.io.IOException;

import org.junit.Test;

public class FilerTest {

	@Test
	public void testRead() {
		File cd = new File(".");
		System.out.println(cd.getAbsolutePath());
		Filer f = new Filer();
		long l;
		try {
			l = f.read("src/tdd/Coke.java");
			assertEquals(56, l);
			l = f.read("src/tdd/Drink.java");
			assertEquals(52, l);
		} catch (IOException e) {
			e.printStackTrace();
			throw new RuntimeException(e);
		}
	}

}

FileNotFoundExceptionが出なくなった。

実際にバイト数を数えるようにFilerを修正する。

Java7から使えるようになった、try-with-resources構文を使用してコードを記述する。

package tdd;

import java.io.FileReader;
import java.io.IOException;

public class Filer {
	public long read(String name) throws IOException {
		long l = 0;
		try (FileReader r = new FileReader(name);) {
			while (true) {
				int c = r.read();
				if (c < 0) break;
				l++;
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		return l;
	}
}

テストにパスしたので、バイト数を正しく数えているらしい。

ファイルをコピーする機能を追加してみる。
空のメソッドを作成する。

	public long copy(String src, String dst) {
		return 0;
	}

テストを作成。コピーしたバイト数を返すことにする。

	@Test
	public void testCopy() {
		Filer f = new Filer();
		long l;
		l = f.copy("src/tdd/Coke.java", "Coke.java");
		assertEquals(56, l);
	}

copy() メソッドを実装する。

	public long copy(String src, String dst) {
		long l = 0;
		try (FileReader r = new FileReader(src);
			 FileWriter w = new FileWriter(dst);) {
			while (true) {
				int c = r.read();
				if (c < 0) break;
				l++;
				w.write(c);
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		return l;
	}

テストを実行すると、テストにパスする。
パッケージエクスプローラでプロジェクト名を右クリックし、「リフレッシュ」を選択すると、Coke.javaがtddプロジェクトの直下に現れる。

指定したURLのコンテンツをダウンロードするdownload() メソッドの追加。

	public void download(String url, String dst) {
		return;
	}

ダウンロードのテストを作成する。
Yahooのトップページをダウンロードしたらファイルのサイズが1000バイト以上あるだろう?

	@Test
	public void testDownload() {
		Filer f = new Filer();
		long l;
		f.download("http://www.yahoo.co.jp", "yahoo.html");
		File html = new File("yahoo.html");
		assertTrue(html.length() > 1000);
	}

download()メソッドを実装する。
URLクラスのopenStream()メソッドを呼ぶことで、コンテンツにアクセスする入力ストリームを取得できる。
入力ストリームから読み込んだ内容をファイルに出力すればよい。

	public void download(String url, String dst) throws IOException {
		long l = 0;
		URL u = new URL(url);
		try (InputStream is = u.openStream();
			 FileOutputStream os = new FileOutputStream(dst);) {
			while (true) {
				int c = is.read();
				if (c < 0) break;
				l++;
				os.write(c);
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
		return;
	}

パッケージエクスプローラでプロジェクト名を右クリックし、「リフレッシュ」を選択すると、yahoo.htmlがtddプロジェクトの直下に現れる。

ファイルを開くと、HTMLがダウンロードできていることがわかる。

5月10日

ジュースを3種類管理できるようにする。
在庫にレッドブル(値段:200円、名前”レッドブル”)5本を追加する。
在庫に水(値段:100円、名前”水”)5本を追加する。
投入金額、在庫の点で購入可能なドリンクのリストを取得できる。

3種類のドリンクに対応するために、Stockを3個の要素を持つ配列にしてみる。
コンパイルエラーが発生するので、stock変数にアクセスしている部分をすべてstock[0]に変更する。
そうすると、コンパイルエラーは消せる。

package tdd;

public class VendingMachine {
	private int amount = 0;
	private int saleAmount = 0;
	private Stock[] stock;

	public VendingMachine() {
		stock = new Stock[3];
		stock[0] = new Stock();
		stock[0].setCount(5);
		stock[0].setName("コーラ");
		stock[0].setPrice(120);
	}

	public int insert(int money) {
		if (!isAcceptable(money)) return money;
		amount += money;
		return 0;
	}

	private boolean isAcceptable(int money) {
		if (money == 10) return true;
		if (money == 50) return true;
		if (money == 100) return true;
		if (money == 500) return true;
		if (money == 1000) return true;
		return false;
	}

	public int getAmount() {
		return amount;
	}

	public int refund() {
		int a = amount;
		amount = 0;
		return a;
	}

	public Stock getStock() {
		return stock[0];
	}

	public boolean isPurchasable() {
		if (amount < 120) return false;
		if (stock[0].getCount() == 0) return false;
		return true;
	}

	public Coke purchase() {
		if (!isPurchasable()) return null;
		amount -= 120;
		saleAmount += 120;
		int c = stock[0].getCount();
		stock[0].setCount(c - 1);
		return new Coke();
	}

	public int getSaleAmount() {
		return saleAmount;
	}

	public String[] getPurchasableNames() {
		return new String[1];
	}
}

追加したテストがパスするように実装する。

	public String[] getPurchasableNames() {
		if (amount >= 100) {
			String[] s = new String[1];
			s[0] = "水";
			return s;
		}
		return new String[0];
	}

水を購入するテストを追加する。

	@Test
	public void test水を購入する() {
		VendingMachine vm = new VendingMachine();
		Water w = (Water) vm.purchase("水");
		assertNull(w);
		vm.insert(100);
		w = (Water) vm.purchase("水");
		assertNotNull(w);
	}

現状ではpurchase()メソッド内で引数をチェックしてないので、コーラの販売しかできない。
水を購入できるように、実装しなおす必要がある。

抽象クラス Drink を追加して、CokeとWaterがDrinkを継承する。

package tdd;

public abstract class Drink {

}
package tdd;

public class Water extends Drink {

}
package tdd;

public class Coke extends Drink {

}

飲み物を購入するメソッド purchase()に引数を追加して種類を指定できるようにする。
戻り値の型を Drink に変更して、CokeとWaterの両方を購入できるようにする。
水を購入できるかどうかを調べるisPurchasable()メソッドにも引数を追加して、種類を指定できるようにする。

	public boolean isPurchasable(String name) {
		if (name.equals("コーラ")) {
			if (amount < 120) return false;
			if (stock[0].getCount() == 0) return false;
			return true;
		}
		if (name.equals("水")) {
			if (amount < 100) return false;
			if (stock[2].getCount() == 0) return false;
			return true;
		}
		return false;
	}

	public Drink purchase(String name) {
		if (!isPurchasable(name)) return null;
		amount -= 120;
		saleAmount += 120;
		int c = stock[0].getCount();
		stock[0].setCount(c - 1);
		return new Coke();
	}

purchase()メソッドを修正して、テストにパスするように実装する。

	public Drink purchase(String name) {
		if (!isPurchasable(name)) return null;
		if (name.equals("コーラ")) {
			amount -= 120;
			saleAmount += 120;
			int c = stock[0].getCount();
			stock[0].setCount(c - 1);
			return new Coke();
		}
		if (name.equals("水")) {
			amount -= 100;
			saleAmount += 100;
			int c = stock[2].getCount();
			stock[2].setCount(c - 1);
			return new Water();
		}
		return null;
	}

getStock()メソッドも現状はコーラしか参照していないので、複数の飲み物に対応できるように修正する。
まずはテストを変更する。
getStock()メソッドに引数を追加して飲み物の種類を指定できるようにする。

	@Test
	public void testコーラの在庫() {
		VendingMachine vm = new VendingMachine();
		Stock s = vm.getStock("コーラ");
		assertEquals(5, s.getCount());
		assertEquals("コーラ", s.getName());
		assertEquals(120, s.getPrice());
	}

	@Test
	public void test水の在庫() {
		VendingMachine vm = new VendingMachine();
		Stock s = vm.getStock("水");
		assertEquals(5, s.getCount());
		assertEquals("水", s.getName());
		assertEquals(100, s.getPrice());
	}

テストにパスするように実装しなおす。

	public Stock getStock(String name) {
		for (Stock s : stock) {
			if (s.getName().equals(name)) return s;
		}
		return null;
	}

getStock()を実装しなおしたことにより、isPurchasable()とpurchase()の実装を簡素化できるので、両方のメソッドをリファクタリングする。

リファクタリング (refactoring) とは、コンピュータプログラミングにおいて、プログラムの外部から見た動作を変えずにソースコードの内部構造を整理することである。また、いくつかのリファクタリング手法の総称としても使われる。
リファクタリング (プログラミング) – Wikipedia

isPurchasable()メソッドをリファクタリングする。

	public boolean isPurchasable(String name) {
		Stock s = getStock(name);
		if (s == null) return false;
		if (amount < s.getPrice()) return false;
		if (s.getCount() == 0) return false;
		return true;
	}

同様に、purchase()メソッドもリファクタリングできる。
ただし、飲み物の種類によって戻り値として返すDrinkのインスタンスが異なるため、その違いをStockクラスで吸収する。

	public Drink getDrink() {
		if (name.equals("コーラ")) return new Coke();
		if (name.equals("水")) return new Water();
		return null;
	}

StockにgetDrink()を追加したことで、purchase()メソッドをリファクタリングして簡略化できる。

	public Drink purchase(String name) {
		if (!isPurchasable(name)) return null;
		Stock s = getStock(name);
		amount -= s.getPrice();
		saleAmount += s.getPrice();
		int c = s.getCount();
		s.setCount(c - 1);
		return s.getDrink();
	}

うまく動いていない部分があるので、さらにテストを追加する。

	@Test
	public void test購入可能なドリンクのリストを取得() {
		VendingMachine vm = new VendingMachine();
		String[] names = vm.getPurchasableNames();
		assertEquals(0, names.length);
		vm.insert(100);
		names = vm.getPurchasableNames();
		assertEquals(1, names.length);
		assertEquals("水", names[0]);

		vm.insert(50);
		names = vm.getPurchasableNames();
		assertEquals(2, names.length);
	}

getPurchasableNames()を変更してテストにパスさせる。
コレクションフレームワークを使用して実装。

	public String[] getPurchasableNames() {
		ArrayList<String> list = new ArrayList<String>();
		for (Stock s : stock) {
			if (amount >= s.getPrice()) {
				list.add(s.getName());
			}
		}
		return list.toArray(new String[0]);
	}

レッドブルを購入するテストを追加する。

	@Test
	public void testレッドブルを購入する() {
		VendingMachine vm = new VendingMachine();
		RedBull r = (RedBull) vm.purchase("レッドブル");
		assertNull(r);
		vm.insert(200);
		r = (RedBull) vm.purchase("レッドブル");
		assertNotNull(r);
	}

5月6日

自動販売機の在庫を取得するメソッドを追加する。

	@Test
	public void testStock() {
		VendingMachine vm = new VendingMachine();
		Stock s = vm.getStock();
	}

在庫を表現するクラス Stock を作成する。
まずは、名前・価格・在庫数を表すフィールドだけを用意する。

package tdd;

public class Stock {
	private String name;
	private int price;
	private int count;
}

Eclipseではgetter/setterを自動生成できる。
メニューから[ソース]-[getterおよびsetterの生成]を選択する。
ダイアログで「すべて選択」をクリックして全部のフィールドにチェックを入れる。
挿入ポイントは「最後のメンバー」を選択する。
「完了」すると、getter/setterが生成される。

package tdd;

public class Stock {
	private String name;
	private int price;
	private int count;
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public int getPrice() {
		return price;
	}
	public void setPrice(int price) {
		this.price = price;
	}
	public int getCount() {
		return count;
	}
	public void setCount(int count) {
		this.count = count;
	}
}

自動販売機は初期状態で120円のコーラを5本保持しているので、それを確認するテストコードを作成する。

	@Test
	public void testStock() {
		VendingMachine vm = new VendingMachine();
		Stock s = vm.getStock();
		assertEquals(5, s.getCount());
		assertEquals("コーラ", s.getName());
		assertEquals(120, s.getPrice());
	}

そのテストをパスするようにVendingMachineを実装する。

	public Stock getStock() {
		Stock s = new Stock();
		s.setName("コーラ");
		s.setPrice(120);
		s.setCount(5);
		return s;
	}

テストにパスしたら、次のテストを実装する。
次は、購入可能かどうかを投入金額と在庫を元に取得できるようにする。
購入可能かどうかを調べるメソッドを isPurchasable() としてbooleanで結果を返すようにする。
自動販売機では、購入できることを示すランプに対応する。

	@Test
	public void testPurchasable() {
		VendingMachine vm = new VendingMachine();
		boolean b = vm.isPurchasable();
		assertEquals(false, b);
	}

VendingMachineにメソッドを追加する。
コンパイルエラーになっているうえにマウスカーソルを持っていくとメソッドを生成できる。

	public boolean isPurchasable() {
		return false;
	}

メソッドを生成するだけでテストにパスする。
500円を投入したら購入可能になるはずなので、それをテストに追加する。

	@Test
	public void testPurchasable() {
		VendingMachine vm = new VendingMachine();
		boolean b = vm.isPurchasable();
		assertEquals(false, b);
		vm.insert(500);
		b = vm.isPurchasable();
		assertEquals(true, b);
	}

テストがエラーになるので、テストにパスするように実装する。
投入済みの金額が120円以上ならtrueを返すようにする。

	public boolean isPurchasable() {
		if (amount >= 120) return true;
		return false;
	}

テストにパスするので、実際に購入する部分のテストを追加する。
purchase()メソッドで購入の操作を表現する。
購入するとコーラ(Coke)が出てくるはずなので、それをテストで表現する。

	@Test
	public void testPurchasable() {
		VendingMachine vm = new VendingMachine();
		boolean b = vm.isPurchasable();
		assertEquals(false, b);
		vm.insert(500);
		b = vm.isPurchasable();
		assertEquals(true, b);
		Coke c = vm.purchase();
		assertNotNull(c);
	}

Cokeクラスを作成し、purchase()メソッドでCokeのインスタンスを生成して返すようにする。

	public Coke purchase() {
		return new Coke();
	}

テストにパスするので、投入済みの金額が減ったことを確認するテストを追加する。

	@Test
	public void testPurchasable() {
		VendingMachine vm = new VendingMachine();
		boolean b = vm.isPurchasable();
		assertEquals(false, b);
		vm.insert(500);
		b = vm.isPurchasable();
		assertEquals(true, b);
		Coke c = vm.purchase();
		assertNotNull(c);
		int amount = vm.getAmount();
		assertEquals(500 - 120, amount);
	}

テストにパスするようにコードを実装する。

	public Coke purchase() {
		amount -= 120;
		return new Coke();
	}

次は、在庫が減ったことを確認するテストコードを追加する。

	@Test
	public void testPurchasable() {
		VendingMachine vm = new VendingMachine();
		boolean b = vm.isPurchasable();
		assertEquals(false, b);
		vm.insert(500);
		b = vm.isPurchasable();
		assertEquals(true, b);
		Coke c = vm.purchase();
		assertNotNull(c);
		int amount = vm.getAmount();
		assertEquals(500 - 120, amount);
		Stock s = vm.getStock();
		assertEquals(4, s.getCount());
	}

在庫を表す変数は、getStock()メソッドのローカル変数で宣言しているが、これをインスタンス変数に変更する。
コンストラクタ内で初期化する。

package tdd;

public class VendingMachine {
	private int amount = 0;
	private Stock stock;

	public VendingMachine() {
		stock = new Stock();
		stock.setCount(5);
		stock.setName("コーラ");
		stock.setPrice(120);
	}

getStock()メソッドは、インスタンス変数を返すように修正する。

	public Stock getStock() {
		return stock;
	}

購入操作で在庫を減らすコードを追加する。

	public Coke purchase() {
		amount -= 120;
		int c = stock.getCount();
		stock.setCount(c - 1);
		return new Coke();
	}

これでテストにパスする。
次は、売り上げ金額を確認するテストを追加する。
getSaleAmount()メソッドで売り上げ金額を取得できるようにする。

	@Test
	public void testPurchasable() {
		VendingMachine vm = new VendingMachine();
		boolean b = vm.isPurchasable();
		assertEquals(false, b);
		vm.insert(500);
		b = vm.isPurchasable();
		assertEquals(true, b);
		Coke c = vm.purchase();
		assertNotNull(c);
		int amount = vm.getAmount();
		assertEquals(500 - 120, amount);
		Stock s = vm.getStock();
		assertEquals(4, s.getCount());
		int sa = vm.getSaleAmount();
		assertEquals(120, sa);
	}

テストにパスするように実装する。

	public int getSaleAmount() {
		return 120;
	}

自動販売機のインスタンスを生成した直後は売り上げが0のはずなので、テストを追加する。

	@Test
	public void testPurchasable() {
		VendingMachine vm = new VendingMachine();
		int sa = vm.getSaleAmount();
		assertEquals(0, sa);
		boolean b = vm.isPurchasable();
		assertEquals(false, b);
		vm.insert(500);
		b = vm.isPurchasable();
		assertEquals(true, b);
		Coke c = vm.purchase();
		assertNotNull(c);
		int amount = vm.getAmount();
		assertEquals(500 - 120, amount);
		Stock s = vm.getStock();
		assertEquals(4, s.getCount());
		sa = vm.getSaleAmount();
		assertEquals(120, sa);
	}

テストにパスするように実装を修正する。
getSaleAmount()メソッドでは、インスタンス変数 saleAmount の値を返すようにする。
購入の操作の中で、投入金額を減らすと同時に売り上げ金額をプラスする。

package tdd;

public class VendingMachine {
	private int amount = 0;
	private int saleAmount = 0;
	private Stock stock;

	(略)

	public Coke purchase() {
		amount -= 120;
		saleAmount += 120;
		int c = stock.getCount();
		stock.setCount(c - 1);
		return new Coke();
	}

	public int getSaleAmount() {
		return saleAmount;
	}
}

「投入金額が足りない場合もしくは在庫がない場合、購入操作を行っても何もしない。」を実装するために、そのテストを追加する。
お金を入れていない状態では、purchase()メソッドを呼び出してもnullを返して、コーラが出てこないようにする。

	@Test
	public void testPurchase() {
		VendingMachine vm = new VendingMachine();
		Coke c = vm.purchase();
		assertNull(c);
	}

投入金額が120円未満の場合は購入できずにnullを返すようにする。

テストにパスしたら、在庫がない場合も購入できないようにするためのテストを追加する。

	@Test
	public void testPurchase() {
		VendingMachine vm = new VendingMachine();
		Coke c = vm.purchase();
		assertNull(c);
		vm.insert(1000);
		for (int i = 0; i < 5; i++) {
			c = vm.purchase();
			assertNotNull(c);
		}
		c = vm.purchase();
		assertNull(c);
	}
&#91;/java&#93;


VendingMachineに購入可能かどうかを調べるisPurchasable()メソッドがあるので、purchase()メソッド内でもそれを使うようにする。

&#91;java&#93;
	public Coke purchase() {
		if (!isPurchasable()) return null;
		amount -= 120;
		saleAmount += 120;
		int c = stock.getCount();
		stock.setCount(c - 1);
		return new Coke();
	}
&#91;/java&#93;

isPurchasable()メソッドで在庫を確認する。

&#91;java&#93;
	public boolean isPurchasable() {
		if (amount < 120) return false;
		if (stock.getCount() == 0) return false;
		return true;
	}
&#91;/java&#93;

これでテストにパスする。

次の機能拡張に挑戦する!



<blockquote>
ジュースを3種類管理できるようにする。
在庫にレッドブル(値段:200円、名前”レッドブル”)5本を追加する。
在庫に水(値段:100円、名前”水”)5本を追加する。
投入金額、在庫の点で購入可能なドリンクのリストを取得できる。
</blockquote>




	@Test
	public void test購入可能なドリンクのリストを取得() {
		VendingMachine vm = new VendingMachine();
		String[] names = vm.getPurchasableNames();
		assertEquals(0, names.length);
	}