8月24日

復習として新規にWebアプリを作成する

入力フォームを用意(URLは、 http://localhost:8080/Rating/form )

「○○」さんの「○○」度は? [判定]

判定ボタンを押すと、

「○○さんの ○○度は、○○%です。」

と表示する。

プロジェクト名: Rating
テンプレート: Simple Spring Web Maven

プロジェクトを作成したら、以下を実行。

  1. [Maven]-[プロジェクトの更新]
  2. [実行]-[maven install]

文字化け対策

文字化け対策のために、web.xml を編集。
最後の <web-app> タグの手前に以下の内容を追加。
src/main/webapp/WEB-INF/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>

パッケージの設定

使用するパッケージ名を mvc-config.xml に設定。
src/main/webapp/WEB-INF/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 />

	<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>

ルートコンテキストの動作確認

サーバータブの Tomcat7 サーバーに Ratingプロジェクトを追加して Tomcat7 サーバーを再起動。
http://localhost:8080/Rating/ にアクセスすれば、click to enter が表示される。

FormModelの作成

javaリソースの src/java/main を右クリックし、クラスを新規作成する。
パッケージ名: jp.abc
クラス名; FormModel

FormModel.java

package jp.abc;

public class FormModel {
	private String name;
	private String title;
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
	public String getTitle() {
		return title;
	}
	public void setTitle(String title) {
		this.title = title;
	}
}

ビューテンプレートの作成

form.jsp を以下のように作成する。

<!DOCTYPE html>

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

<html>
	<head>
		<meta charset="utf-8">
		<title>なんとか度判定</title>
	</head>
	<body>
		<h1>なんとか度判定</h1>
		<div style="font-size: 24pt;">${result}</div>
		<h2>${message}</h2>
		<form:form modelAttribute="formModel">
		<form:input path="name"/>さんの<form:input path="title"/>度
		<br />
		<input type="submit" value="判定" />
		</form:form>
	</body>
</html>

コントローラを作成する

買い物メモのコントローラを作成する。
テキストでは MyAppController を再利用しているが、ここでは新しく RatingController を作成する。
※開発する際に、名前は重要!

Javaリソースの src/java/main の下にある jp.abc パッケージを右クリックして [新規]-[クラス] を選択。

名前: RatingController

まずは、GET メソッドに対応する部分だけ入力する。

RatingController.java

package jp.abc;

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

@Controller
public class RatingController {

	@RequestMapping(value = "/form", method = RequestMethod.GET)
	public String form(Model model) {
        model.addAttribute("message", "名前と判定したいなにかを入力してください。");
        FormModel fm = new FormModel();
        model.addAttribute("formModel", fm);
		return "form";
	}

}

http://localhost:8080/Rating/form にアクセスする。
なんとか度判定の画面が表示されればOK。

次に POST メソッドに対応する部分を実装する。

RatingController.java

	@RequestMapping(value = "/form", method = RequestMethod.POST)
	public String rating(@Valid @ModelAttribute FormModel formModel, BindingResult result, Model model) {
            model.addAttribute("message", "名前と判定したいなにかを入力してください。");
       	    String s = formModel.getName() + " さんの "
       			 + formModel.getTitle() + "度は、";
       	    float r = (Math.abs(s.hashCode()) % 1001) / 10;
       	    s = s + r + "% です。";
       	    model.addAttribute("result", s);
       	    FormModel fm = new FormModel();
            model.addAttribute("formModel", fm);
	    return "form";
	}

入力チェックの追加

バリデーション用ライブラリをロードする

パッケージエクスプローラで、Memoプロジェクトの一番下にあるファイル pom.xml を開く。
エディタの下にある「pom.xml」タブを選択する。
ファイルの最後の の手前に以下の内容を追加する。

pom.xml

        <dependency>
            <groupId>javax.validation</groupId>
            <artifactId>validation-api</artifactId>
            <version>1.1.0.Final</version>
        </dependency>
        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-validator</artifactId>
            <version>5.0.1.Final</version>
        </dependency>
    </dependencies>
</project>

保存すると、自動的にプロジェクトがビルドされ、ライブラリをダウンロードしてくれる。

JSPの変更

form.jsp にエラーメッセージを表示するためのタグを追加する。

form.jsp

		<h1>なんとか度判定</h1>
		<div style="font-size: 24pt;">${result}</div>
		<h2>${message}</h2>
		<form:form modelAttribute="formModel">
		<form:errors path="*" cssStyle="color:red" element="div" />
		<form:input path="name"/>さんの<form:input path="title"/>度
		<br />
		<input type="submit" value="判定" />
		</form:form>

コントローラの修正

入力チェックのための@Validアノテーションと、結果を通知するための BindingResult引数を追加する。

RatingController.java

	@RequestMapping(value = "/form", method = RequestMethod.POST)
	public String rating(@Valid @ModelAttribute FormModel formModel, BindingResult result, Model model) {
        model.addAttribute("message", "名前と判定したいなにかを入力してください。");
        if (result.hasErrors()) {
            model.addAttribute("title", "Sample [ERROR]");
            model.addAttribute("message", "値を再チェックしてください");
        } else {
        	String s = formModel.getName() + " さんの "
        			 + formModel.getTitle() + "度は、";
        	float r = (Math.abs(s.hashCode()) % 1001) / 10;
        	s = s + r + "% です。";
        	model.addAttribute("result", s);
        	FormModel fm = new FormModel();
            model.addAttribute("formModel", fm);
        }
		return "form";
	}

FormModelの変更

FormModelに入力チェック方法を示すアノテーションを追加する。

FormModel.java

	@NotEmpty
	private String name;
	@NotEmpty
	private String title;

エラーメッセージをカスタマイズする

@NotEmpty などのアノテーションに message 引数を指定する。

FormModel.java

	@NotEmpty(message = "名前は必須です")
	private String name;
	@NotEmpty(message = "判定内容は必須です")
	private String title;

7月10日

バリデーションの利用

バリデーション用に新しいプロジェクトを作成する。

プロジェクトの作成と初期設定

プロジェクト名: Memo
テンプレート: Simple Spring Web Maven

プロジェクトを作成したら、以下を実行。

  1. [Maven]-[プロジェクトの更新]
  2. [実行]-[maven install]

文字化け対策のために、web.xml を編集。
最後の <web-app> タグの手前に以下の内容を追加。
src/main/webapp/WEB-INF/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>

使用するパッケージ名を mvc-config.xml に設定。
src/main/webapp/WEB-INF/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 />

	<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>

サーバータブの Tomcat7 サーバーに Memo プロジェクトを追加して Tomcat7 サーバーを再起動。
http://localhost:8080/Memo/ にアクセスすれば、click to enter が表示される。

FormModelの作成

javaリソースの src/java/main を右クリックし、クラスを新規作成する。
パッケージ名: jp.abc
クラス名; FormModel

FormModel.java

package jp.abc;

import java.util.Date;

public class FormModel {
	private String item;
	private Integer price;
	private Date buydate;
	private String memo;
	public String getItem() {
		return item;
	}
	public void setItem(String item) {
		this.item = item;
	}
	public Integer getPrice() {
		return price;
	}
	public void setPrice(Integer price) {
		this.price = price;
	}
	public Date getBuydate() {
		return buydate;
	}
	public void setBuydate(Date buydate) {
		this.buydate = buydate;
	}
	public String getMemo() {
		return memo;
	}
	public void setMemo(String memo) {
		this.memo = memo;
	}
}

ビューテンプレートの変更

showMessage.jsp を以下のように変更する。

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

<html>
	<head>
		<meta charset="utf-8">
		<title>${title}</title>
		<style type="text/css">
		h1 { font-size: 16pt;background-color: #ccccff;padding:3px; }
		p { color: #000066; }
		</style>
	</head>
	<body>
		<h1>${title}</h1>
		<p>${message}</p>
		<table>
		<form:form modelAttribute="formModel">
			<tr>
				<td><form:label path="item">商品名</form:label></td>
				<td><form:input path="item" size="20" /></td>
			</tr>
			<tr>
				<td><form:label path="price">金額</form:label></td>
				<td><form:input path="price" size="20" /></td>
			</tr>
			<tr>
				<td><form:label path="memo">メモ</form:label></td>
				<td><form:textarea path="memo" cols="20" rows="5" /></td>
			</tr>
			<tr><td><input type="submit"></td></tr>
		</form:form>
		</table>
		<hr />
		<c:if test="${datalist != null}" >
		<table border="1">
			<tr><th>商品名</th><th>価格</th></tr>
			<c:forEach var="obj" items="${datalist}" varStatus="status">
				<tr>
					<td>${obj.item}</td>
					<td>${obj.price}</td>
				</tr>
			</c:forEach>
		</table>
		</c:if>
	</body>
</html>

コントローラを作成する

買い物メモのコントローラを作成する。
テキストでは MyAppController を再利用しているが、ここでは新しく MemoController を作成する。
※開発する際に、名前は重要!

Javaリソースの src/java/main の下にある jp.abc パッケージを右クリックして [新規]-[クラス] を選択。

名前: MemoController

まずは、GET メソッドに対応する部分だけ入力する。

MemoController.java

package jp.abc;

import java.util.ArrayList;
import java.util.List;

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 MemoController {
	private List<FormModel> buylist = new ArrayList<FormModel>();

	@RequestMapping(value = "/list", method = RequestMethod.GET)
	public String list(Model model) {
		model.addAttribute("title", "Sample");
		model.addAttribute("message", "買い物メモです。");
		FormModel fm = new FormModel();
		model.addAttribute("formModel", fm);
		model.addAttribute("datalist", buylist);
		return "showMessage";
	}
}

http://localhost:8080/Memo/list にアクセスする。
買い物メモの画面が表示されればOK。

次に POST メソッドに対応する部分を実装する。

MemoController.java

	@RequestMapping(value = "/list", method = RequestMethod.POST)
	public String form(@ModelAttribute FormModel formModel, BindingResult result, Model model) {
		buylist.add(formModel);
		model.addAttribute("title", "Sample");
		String res = "<ol>";
		res += "<li>" + formModel.getItem() + "</li>";
		res += "<li>" + formModel.getPrice() + "</li>";
		res += "<li>" + formModel.getMemo() + "</li>";
		res += "<li>" + Calendar.getInstance().getTime() + "</li>";
		res += "</ol>";
		res += "<p>" + buylist.size() + "</p>";
		model.addAttribute("message", res);
		model.addAttribute("datalist", buylist);
		model.addAttribute("formModel", new FormModel());
		return "showMessage";
	}

バリデーション用ライブラリをロードする

パッケージエクスプローラで、Memoプロジェクトの一番下にあるファイル pom.xml を開く。
エディタの下にある「pom.xml」タブを選択する。
ファイルの最後の <dependencies> の手前に以下の内容を追加する。

pom.xml

		<dependency>
			<groupId>javax.validation</groupId>
			<artifactId>validation-api</artifactId>
			<version>1.1.0.Final</version>
		</dependency>
		<dependency>
			<groupId>org.hibernate</groupId>
			<artifactId>hibernate-validator</artifactId>
			<version>5.0.1.Final</version>
		</dependency>
	</dependencies>
</project>

保存すると、自動的にプロジェクトがビルドされ、ライブラリをダウンロードしてくれる。

JSPの変更

showMessage.jsp にエラーメッセージを表示するためのタグを追加する。

showMessage.jsp

		<form:form modelAttribute="formModel">
			<tr><td></td><td><form:errors path="*" element="div" /></td></tr>
			<tr>
				<td><form:label path="item">商品名</form:label></td>
				<td><form:input path="item" size="20" /></td>
			</tr>

コントローラの変更

GETメソッドはバリデーションは関係なので、POSTメソッドを処理するメソッドを変更する。

まず、バリデーションの対象となる最初の引数 FormModel に @Valid アノテーションを追加する。

MemoController.java

	@RequestMapping(value = "/list", method = RequestMethod.POST)
	public String form(@Valid @ModelAttribute FormModel formModel, BindingResult result, Model model) {

エラーがあったときには、異なるタイトルとメッセージを表示するように変更する。
判断は、BindingResult#hasErrors()メソッドを使用する。
MemoController.java

	@RequestMapping(value = "/list", method = RequestMethod.POST)
	public String form(@Valid @ModelAttribute FormModel formModel, BindingResult result, Model model) {
		if (result.hasErrors()) {
			model.addAttribute("title", "Sample [ERROR]");
			model.addAttribute("message", "値を再チェックしてください");
		} else {
			buylist.add(formModel);
			model.addAttribute("title", "送信内容");
			String res = "<ol>";
			res += "<li>" + formModel.getItem() + "</li>";
			res += "<li>" + formModel.getPrice() + "</li>";
			res += "<li>" + formModel.getMemo() + "</li>";
			res += "<li>" + Calendar.getInstance().getTime() + "</li>";
			res += "</ol>";
			res += "<p>" + buylist.size() + "</p>";
			model.addAttribute("message", res);
			model.addAttribute("formModel", new FormModel());
		}
		model.addAttribute("datalist", buylist);
		return "showMessage";
	}

エラーメッセージのカスタマイズ

エラーメッセージを変更するには、アノテーションに message 引数を追加するのが簡単。

FormModel.java

public class FormModel {
	@NotEmpty(message="商品名は必須です。")
	private String item;
	@Min(value=0, message="{value}以上の値が必要です。")
	private Integer price;
	private Date buydate;
	private String memo;

個別にメッセージを表示する

現状では1箇所にまとめてエラーメッセージを表示しているが、これを入力フィールド毎に変更できる。
<form:errors> タグの path 属性の * を、それぞれの変数名で指定すればよい。

showMessage.jsp

		<form:form modelAttribute="formModel">
			<tr>
				<td><form:label path="item">商品名</form:label></td>
				<td><form:input path="item" size="20" />
				<form:errors path="item" cssStyle="color:red" /></td>
			</tr>
			<tr>
				<td><form:label path="price">金額</form:label></td>
				<td><form:input path="price" size="20" />
				<form:errors path="price" cssStyle="color:red" /></td>
			</tr>
			<tr>
				<td><form:label path="memo">メモ</form:label></td>
				<td><form:textarea path="memo" cols="20" rows="5" />
				<form:errors path="memo" cssStyle="color:red" /></td>
			</tr>
			<tr><td><input type="submit"></td></tr>
		</form:form>

7月6日

フォームを使いこなす(続き)

複数のチェックボックスをまとめて作る

前回は複数のチェックボックスをまとめて作成するところまで作ったが、クエリ送信でエラーが発生していた。

エラーメッセージでは items must not be null と表示されていることから、${checkItems} が null であると推測できる。

コントローラで値を設定するように修正する。

MyAppController.java

	@RequestMapping(value = "/helo", method = RequestMethod.POST)
	public String form(@ModelAttribute FormModel formModel, Model model){
		String res = "<ul><li>" + formModel.getInput1()
				+ "</li><li>" + formModel.getPass1()
				+ "</li><li>" + formModel.getArea1()
				+ "</li><li>" + formModel.isCheck1()
				+ "</li></ul>";
		model.addAttribute("title", "sample");
		model.addAttribute("message", res);
		model.addAttribute("formModel", formModel);
		model.addAttribute("checkItems", getList());
		return "showMessage";
	}

チェックされた要素をメッセージに追加する。

MyAppController.java

	@RequestMapping(value = "/helo", method = RequestMethod.POST)
	public String form(@ModelAttribute FormModel formModel, Model model){
		String res = "<ul><li>" + formModel.getInput1()
				+ "</li><li>" + formModel.getPass1()
				+ "</li><li>" + formModel.getArea1()
				+ "</li><li>" + formModel.isCheck1()
				+ "</li></ul>";
		String[] selected = formModel.getChecks();
		res = res + "<ol>";
		for (String sel : selected) {
			res += "<li>" + sel + "</li>";
		}
		res += "</ol>";
		model.addAttribute("title", "sample");
		model.addAttribute("message", res);
		model.addAttribute("formModel", formModel);
		model.addAttribute("checkItems", getList());
		return "showMessage";
	}

ラベルと値を設定する

チェックボックスのラベルと値を設定するために、クラスを追加する。
javaリソースの下の src/main/java にある jp.abc パッケージを右クリックし、[新規]-[クラス]を選択する。
新規クラスのダイアログで名前に ListDataModel と入力し「完了」をクリック。
テキスト p.214 のコードを記述する。

ListDataModel.java

package jp.abc;

public class ListDataModel {
	private String label;
	private String data;
	public String getLabel() {
		return label;
	}
	public void setLabel(String label) {
		this.label = label;
	}
	public String getData() {
		return data;
	}
	public void setData(String data) {
		this.data = data;
	}
	public ListDataModel(String label, String data) {
		this.label = label;
		this.data = data;
	}
}

JSPの <form:checkboxes> の内部を変更する。

				<tr><td>
					<form:checkboxes items="${checkItems}" path="checks"
					itemLabel="label" itemValue="data" delimiter=" "/>
				</td></tr>

MyAppControllerでは、ListDataModel を格納した List を作るように getList() を修正する。

MyAppController.java

package jp.abc;

import java.util.ArrayList;
import java.util.List;

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 {

	private List<ListDataModel> getList() {
		List<ListDataModel> list = new ArrayList<ListDataModel>();
		list.add(new ListDataModel("まっく", "Mac OS X"));
		list.add(new ListDataModel("うぃんどうず", "Windows"));
		list.add(new ListDataModel("りなくす", "Linux"));
		return list;
	}

	@RequestMapping(value = "/helo", method = RequestMethod.GET)
	public String helo(Model model) {
		model.addAttribute("title", "ModelAndView sample");
		model.addAttribute("message", "これはModelAndViewのテストです。");
		FormModel fm = new FormModel();
		fm.setCheck1(true);
		fm.setChecks(new String[]{"Windows"});
		model.addAttribute("formModel", fm);
		model.addAttribute("checkItems", getList());
		return "showMessage";
	}

	@RequestMapping(value = "/helo", method = RequestMethod.POST)
	public String form(@ModelAttribute FormModel formModel, Model model){
		String res = "<ul><li>" + formModel.getInput1()
				+ "</li><li>" + formModel.getPass1()
				+ "</li><li>" + formModel.getArea1()
				+ "</li><li>" + formModel.isCheck1()
				+ "</li></ul>";
		String[] selected = formModel.getChecks();
		res = res + "<ol>";
		for (String sel : selected) {
			res += "<li>" + sel + "</li>";
		}
		res += "</ol>";
		model.addAttribute("title", "sample");
		model.addAttribute("message", res);
		model.addAttribute("formModel", formModel);
		model.addAttribute("checkItems", getList());
		return "showMessage";
	}
}

ラジオボタンの利用

ラジオボタンは、<form:radiobutton> タグを使用する。

ラジオボタンのために、FormModel に radio1 を追加する。

FormModel.java

package jp.abc;

public class FormModel {
	private String input1;
	private String pass1;
	private String area1;
	private boolean check1;
	private String[] checks;
	private String radio1;

	public String getInput1() {
		return input1;
	}
	public void setInput1(String input1) {
		this.input1 = input1;
	}
	public String getPass1() {
		return pass1;
	}
	public void setPass1(String pass1) {
		this.pass1 = pass1;
	}
	public String getArea1() {
		return area1;
	}
	public void setArea1(String area1) {
		this.area1 = area1;
	}
	public boolean isCheck1() {
		return check1;
	}
	public void setCheck1(boolean check1) {
		this.check1 = check1;
	}
	public String[] getChecks() {
		return checks;
	}
	public void setChecks(String[] checks) {
		this.checks = checks;
	}
	public String getRadio1() {
		return radio1;
	}
	public void setRadio1(String radio1) {
		this.radio1 = radio1;
	}
}

JSP にラジオボタンのタグを追加する。

			<form:form modelAttribute="formModel">
				<tr><td>input1:<form:input path="input1"/></td></tr>
				<tr><td>pass1:<form:password path="pass1" showPassword="on"/></td></tr>
				<tr><td>area1:<form:textarea path="area1" cols="40" rows="3"></form:textarea></td></tr>
				<tr><td><form:checkbox path="check1" label="checkbox 1"/></td></tr>
				<tr><td>
					<form:checkboxes items="${checkItems}" path="checks"
					itemLabel="label" itemValue="data" delimiter=" "/>
				</td></tr>
				<tr><td><form:radiobutton path="radio1" name="radio1"
					label="男性" value="male"/>
					<form:radiobutton path="radio1" name="radio1"
					label="女性" value="female"/>
				<tr><td><input type="submit" ></td></tr>
			</form:form>

GETメソッドを処理する helo() メソッドでは、1行だけ追加する。

MyAppController.java

	@RequestMapping(value = "/helo", method = RequestMethod.GET)
	public String helo(Model model) {
		model.addAttribute("title", "ModelAndView sample");
		model.addAttribute("message", "これはModelAndViewのテストです。");
		FormModel fm = new FormModel();
		fm.setCheck1(true);
		fm.setChecks(new String[]{"Windows"});
		fm.setRadio1("male");
		model.addAttribute("formModel", fm);
		model.addAttribute("checkItems", getList());
		return "showMessage";
	}

POSTメソッドを処理する form() メソッドでも1行だけの追加。

MyAppController.java

	@RequestMapping(value = "/helo", method = RequestMethod.POST)
	public String form(@ModelAttribute FormModel formModel, Model model){
		String res = "<ul><li>" + formModel.getInput1()
				+ "</li><li>" + formModel.getPass1()
				+ "</li><li>" + formModel.getArea1()
				+ "</li><li>" + formModel.isCheck1()
				+ "</li></ul>";
		String[] selected = formModel.getChecks();
		res = res + "<ol>";
		for (String sel : selected) {
			res += "<li>" + sel + "</li>";
		}
		res += "</ol>";
		res += "selected: " + formModel.getRadio1();
		model.addAttribute("title", "sample");
		model.addAttribute("message", res);
		model.addAttribute("formModel", formModel);
		model.addAttribute("checkItems", getList());
		return "showMessage";
	}

ラジオボタンをまとめて作成する

複数のラジオボタンをまとめて作成するには、 <form:radiobuttons> タグを使用する。

まずは複数のラジオボタン用に、FormModel に radio2 を追加し、getter/setter を用意する。
FormModel.java

package jp.abc;

public class FormModel {
	private String input1;
	private String pass1;
	private String area1;
	private boolean check1;
	private String[] checks;
	private String radio1;
	private String radio2;

	public String getInput1() {
		return input1;
	}
	public void setInput1(String input1) {
		this.input1 = input1;
	}
	public String getPass1() {
		return pass1;
	}
	public void setPass1(String pass1) {
		this.pass1 = pass1;
	}
	public String getArea1() {
		return area1;
	}
	public void setArea1(String area1) {
		this.area1 = area1;
	}
	public boolean isCheck1() {
		return check1;
	}
	public void setCheck1(boolean check1) {
		this.check1 = check1;
	}
	public String[] getChecks() {
		return checks;
	}
	public void setChecks(String[] checks) {
		this.checks = checks;
	}
	public String getRadio1() {
		return radio1;
	}
	public void setRadio1(String radio1) {
		this.radio1 = radio1;
	}
	public String getRadio2() {
		return radio2;
	}
	public void setRadio2(String radio2) {
		this.radio2 = radio2;
	}
}

JSP に、複数のラジオボタンを表示するタグを追加する。

showMessage.jsp

				<tr><td><form:radiobutton path="radio1" name="radio1"
					label="男性" value="male"/>
					<form:radiobutton path="radio1" name="radio1"
					label="女性" value="female"/>
				</td></tr>
				<tr><td><form:radiobuttons path="radio2" name="radio2"
						items="${radiolist}" itemLabel="label" itemValue="data"
						delimiter=" " />
				</td></tr>
				<tr><td><input type="submit" ></td></tr>

GETメソッドを処理する helo() メソッドでは、FormModel の値を設定し、radiolist の名前で ListDataModel の List を渡してやる。

MyController.java

	@RequestMapping(value = "/helo", method = RequestMethod.GET)
	public String helo(Model model) {
		model.addAttribute("title", "ModelAndView sample");
		model.addAttribute("message", "これはModelAndViewのテストです。");
		FormModel fm = new FormModel();
		fm.setCheck1(true);
		fm.setChecks(new String[]{"Windows"});
		fm.setRadio1("male");
		fm.setRadio2("Linux");
		model.addAttribute("formModel", fm);
		model.addAttribute("checkItems", getList());
		model.addAttribute("radiolist", getList());
		return "showMessage";
	}

POST メソッドを処理する form() メソッドでは、選択された値を表示する1行の追加と、radiolist の名前で ListDataModel の List を渡してやる1行の追加のみ。

MyAppController.jav

	@RequestMapping(value = "/helo", method = RequestMethod.POST)
	public String form(@ModelAttribute FormModel formModel, Model model){
		String res = "<ul><li>" + formModel.getInput1()
				+ "</li><li>" + formModel.getPass1()
				+ "</li><li>" + formModel.getArea1()
				+ "</li><li>" + formModel.isCheck1()
				+ "</li></ul>";
		String[] selected = formModel.getChecks();
		res = res + "<ol>";
		for (String sel : selected) {
			res += "<li>" + sel + "</li>";
		}
		res += "</ol>";
		res += "<br />selected: " + formModel.getRadio1();
		res += "<br />selected: " + formModel.getRadio2();
		model.addAttribute("title", "sample");
		model.addAttribute("message", res);
		model.addAttribute("formModel", formModel);
		model.addAttribute("checkItems", getList());
		model.addAttribute("radiolist", getList());
		return "showMessage";
	}

SELECTによる選択リストの作成

複数項目をリスト表示する <select>タグは、<form:select> タグで作成できる。

まず、FormModel に select 用のメンバーを用意する。

FormModel.java

package jp.abc;

public class FormModel {
	private String input1;
	private String pass1;
	private String area1;
	private boolean check1;
	private String[] checks;
	private String radio1;
	private String radio2;
	private String select1;

	public String getInput1() {
		return input1;
	}
	public void setInput1(String input1) {
		this.input1 = input1;
	}
	public String getPass1() {
		return pass1;
	}
	public void setPass1(String pass1) {
		this.pass1 = pass1;
	}
	public String getArea1() {
		return area1;
	}
	public void setArea1(String area1) {
		this.area1 = area1;
	}
	public boolean isCheck1() {
		return check1;
	}
	public void setCheck1(boolean check1) {
		this.check1 = check1;
	}
	public String[] getChecks() {
		return checks;
	}
	public void setChecks(String[] checks) {
		this.checks = checks;
	}
	public String getRadio1() {
		return radio1;
	}
	public void setRadio1(String radio1) {
		this.radio1 = radio1;
	}
	public String getRadio2() {
		return radio2;
	}
	public void setRadio2(String radio2) {
		this.radio2 = radio2;
	}
	public String getSelect1() {
		return select1;
	}
	public void setSelect1(String select1) {
		this.select1 = select1;
	}
}

JSP の、radiobuttons と submit の間に SELECT用のタグを追加する。

				<tr><td><form:radiobuttons path="radio2" name="radio2"
						items="${radiolist}" itemLabel="label" itemValue="data"
						delimiter=" " />
				</td></tr>
				<tr><td><form:select path="select1" name="select1"
					items="${optionlist}" itemLabel="label" itemValue="data"
					size="5" multiple="false" />
				</td></tr>
				<tr><td><input type="submit" ></td></tr>

GETメソッドを処理する helo() メソッドでは、FormModel の値を設定し、optionlist の名前で ListDataModel の List を渡してやる。

	@RequestMapping(value = "/helo", method = RequestMethod.GET)
	public String helo(Model model) {
		model.addAttribute("title", "ModelAndView sample");
		model.addAttribute("message", "これはModelAndViewのテストです。");
		FormModel fm = new FormModel();
		fm.setCheck1(true);
		fm.setChecks(new String[]{"Windows"});
		fm.setRadio1("male");
		fm.setRadio2("Linux");
		fm.setSelect1("Windows");
		model.addAttribute("formModel", fm);
		model.addAttribute("checkItems", getList());
		model.addAttribute("radiolist", getList());
		model.addAttribute("optionlist", getList());
		return "showMessage";
	}

POST メソッドを処理する form() メソッドでは、選択された値を表示する1行の追加と、optionlist の名前で ListDataModel の List を渡してやる1行の追加のみ。

	@RequestMapping(value = "/helo", method = RequestMethod.POST)
	public String form(@ModelAttribute FormModel formModel, Model model){
		String res = "<ul><li>" + formModel.getInput1()
				+ "</li><li>" + formModel.getPass1()
				+ "</li><li>" + formModel.getArea1()
				+ "</li><li>" + formModel.isCheck1()
				+ "</li></ul>";
		String[] selected = formModel.getChecks();
		res = res + "<ol>";
		for (String sel : selected) {
			res += "<li>" + sel + "</li>";
		}
		res += "</ol>";
		res += "<br />selected: " + formModel.getRadio1();
		res += "<br />selected: " + formModel.getRadio2();
		res += "<br />selected: " + formModel.getSelect1();
		model.addAttribute("title", "sample");
		model.addAttribute("message", res);
		model.addAttribute("formModel", formModel);
		model.addAttribute("checkItems", getList());
		model.addAttribute("radiolist", getList());
		model.addAttribute("optionlist", getList());
		return "showMessage";
	}

リストで複数項目を選択可能にする

<form:select> タグの multiple属性をtrueにすることで、リストの複数項目を選択可能になる。

複数選択したデータを受け取るために、FormModel に select2 を追加し、getter/setterを生成する。

FormModel.java

package jp.abc;

public class FormModel {
	private String input1;
	private String pass1;
	private String area1;
	private boolean check1;
	private String[] checks;
	private String radio1;
	private String radio2;
	private String select1;
	private String[] select2;

	public String getInput1() {
		return input1;
	}
	public void setInput1(String input1) {
		this.input1 = input1;
	}
	public String getPass1() {
		return pass1;
	}
	public void setPass1(String pass1) {
		this.pass1 = pass1;
	}
	public String getArea1() {
		return area1;
	}
	public void setArea1(String area1) {
		this.area1 = area1;
	}
	public boolean isCheck1() {
		return check1;
	}
	public void setCheck1(boolean check1) {
		this.check1 = check1;
	}
	public String[] getChecks() {
		return checks;
	}
	public void setChecks(String[] checks) {
		this.checks = checks;
	}
	public String getRadio1() {
		return radio1;
	}
	public void setRadio1(String radio1) {
		this.radio1 = radio1;
	}
	public String getRadio2() {
		return radio2;
	}
	public void setRadio2(String radio2) {
		this.radio2 = radio2;
	}
	public String getSelect1() {
		return select1;
	}
	public void setSelect1(String select1) {
		this.select1 = select1;
	}
	public String[] getSelect2() {
		return select2;
	}
	public void setSelect2(String[] select2) {
		this.select2 = select2;
	}
}

JSP の <form:select タグの multiple 属性を true にしたものを追加する。

showMessage.jsp

				<tr><td><form:select path="select1" name="select1"
					items="${optionlist}" itemLabel="label" itemValue="data"
					size="5" multiple="false" />
				</td></tr>
				<tr><td><form:select path="select2" name="select2"
					items="${optionlist}" itemLabel="label" itemValue="data"
					size="5" multiple="true" />
				</td></tr>
				<tr><td><input type="submit" ></td></tr>

コントローラで、GETメソッドを処理する helo() メソッドの変更箇所は1行のみ。

MyAppController.java

	@RequestMapping(value = "/helo", method = RequestMethod.GET)
	public String helo(Model model) {
		model.addAttribute("title", "ModelAndView sample");
		model.addAttribute("message", "これはModelAndViewのテストです。");
		FormModel fm = new FormModel();
		fm.setCheck1(true);
		fm.setChecks(new String[]{"Windows"});
		fm.setRadio1("male");
		fm.setRadio2("Linux");
		fm.setSelect1("Windows");
		fm.setSelect2(new String[]{"Linux"});
		model.addAttribute("formModel", fm);
		model.addAttribute("checkItems", getList());
		model.addAttribute("radiolist", getList());
		model.addAttribute("optionlist", getList());
		return "showMessage";
	}

POSTメソッドを処理する form() メソッドでは、選択された複数の項目を表示するコードを追加する。

MyAppController.java

	@RequestMapping(value = "/helo", method = RequestMethod.POST)
	public String form(@ModelAttribute FormModel formModel, Model model){
		String res = "<ul><li>" + formModel.getInput1()
				+ "</li><li>" + formModel.getPass1()
				+ "</li><li>" + formModel.getArea1()
				+ "</li><li>" + formModel.isCheck1()
				+ "</li></ul>";
		String[] selected = formModel.getChecks();
		res = res + "<ol>";
		for (String sel : selected) {
			res += "<li>" + sel + "</li>";
		}
		res += "</ol>";
		res += "<br />selected: " + formModel.getRadio1();
		res += "<br />selected: " + formModel.getRadio2();
		res += "<br />selected: " + formModel.getSelect1();
		selected = formModel.getSelect2();
		res = res + "<ol>";
		for (String sel : selected) {
			res += "<li>" + sel + "</li>";
		}
		res += "</ol>";
		model.addAttribute("title", "sample");
		model.addAttribute("message", res);
		model.addAttribute("formModel", formModel);
		model.addAttribute("checkItems", getList());
		model.addAttribute("radiolist", getList());
		model.addAttribute("optionlist", getList());
		return "showMessage";
	}

7月3日

フォームを使いこなす

練習用に新しいプロジェクトを作成してそこにフォームを作成する。
メニューで[新規]-[プロジェクト]を選択。
ウィザードで[Spring]-[Springプロジェクト]を選択。

プロジェクト名: Forms
テンプレート: Simple Spring Web Maven

「完了」をクリックしてプロジェクトを作成する。

Forms プロジェクトを右クリックし、[Maven]-[プロジェクトの更新]を選択。

プロジェクトの更新が終わったら、もう一度 Forms プロジェクトを右クリックし、[実行]-[Maven install]を選択。

Tomcat7サーバーを右クリックして「追加および除去」を選択。
Formsプロジェクトを選択して右側に移動する。
Tomcat7サーバーを再開し、 http://localhost:8080/Forms/ にアクセスする。

「click to enter」と表示されればOK。

文字化け対策をする

src/main/webapp/Web-INF の下にある web.xl を開く。

前回と同様に、web.xml の最後の </web-app> の手前に以下の内容を追加する。
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>

FormModelを作成する

プロジェクトエクスプローラで Forms の下にある Javaリソース を展開し、src/main/java を右クリック。
[新規]-[クラス]を選択する。

新規Javaクラスのダイアログで
パッケージ名: jp.abc
名前: FormModel
を入力し「完了」をクリック。

FormModel クラスが作成されるので、インスタンス変数を追加する。
FormModel.java

package jp.abc;

public class FormModel {
	private String input1;
	private String pass1;
	private String area1;
}

メニューバーで[ソース]-[getterおよびsetterの生成]を選択する。
ダイアログで「すべて選択」をクリックし、挿入ポイントで「最後のメンバー」を選択。
「OK」をクリックすると、getter/setterが生成される。

showMessage.jspの変更

テキストp.204 リスト2-4 のとおりに変更する。
taglib ディレクティブが書かれていないので、テキストp.182を参照して追加する。
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>${title}</title>
	</head>
	<body>
		<h1>${title}</h1>
		<p>${message}</p>
		<table>
			<form:form modelAttribute="formModel">
				<tr><td><form:input path="input1"/></td></tr>
				<tr><td><form:password path="pass1" showPassword="on"/></td></tr>
				<tr><td><form:textarea path="area1" cols="40" rows="3"></form:textarea></td></tr>
				<tr><td><input type="submit" ></td></tr>
			</form:form>
		</table>
	</body>
</html>

mvc-config.xmlの変更

src/main/webapp/WEB-INF の下にある mvc-config.xml を開く。
context:component-scan タグの部分がコメントアウトされているのを修正し、base-package の部分を jp.abc に書き換えて保存する。

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 />

	<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>

Controllerを作成する

Javaリソースの下の src/main/java 内の jp.abc パッケージを右クリックし、[新規]-[クラス]を選択する。
名前に MyAppController を入力して「完了」をクリック。
以下のコードを入力する。

MyAppController.java

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) {
		return "showMessage";
	}
}

Tomcat7サーバーを再起動し、http://localhost:8080/Forms/helo にアクセスする。
と、エラーが発生した。
エラーメッセージを確認すると、formModel がないという感じのメッセージがあるので、MyAppControllerをきちんと実装する。

MyAppController.java

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("title", "ModelAndView sample");
		model.addAttribute("message", "これはModelAndViewのテストです。");
		FormModel fm = new FormModel();
		model.addAttribute("formModel", fm);
		return "showMessage";
	}
}

これで http://localhost:8080/Forms/helo にアクセスすると、エラーなく showMessage.jsp が表示されるようになった。

「クエリ送信」ボタンをクリックすると、POSTメソッドがサポートされてないというエラーが表示される。
MyAppController.java に、POSTメソッドを受け付けるためのメソッドを追加する。

MyAppController.java

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) {
		model.addAttribute("title", "ModelAndView sample");
		model.addAttribute("message", "これはModelAndViewのテストです。");
		FormModel fm = new FormModel();
		model.addAttribute("formModel", fm);
		return "showMessage";
	}

	@RequestMapping(value = "/helo", method = RequestMethod.POST)
	public String form(@ModelAttribute FormModel formModel, Model model){
		String res = "<ul><li>" + formModel.getInput1()
				+ "</li><li>" + formModel.getPass1()
				+ "</li><li>" + formModel.getArea1()
				+ "</li></ul>";
		model.addAttribute("title", "sample");
		model.addAttribute("message", res);
		return "showMessage";
	}
}

チェックボックスの利用

チェックボックスは、<form:checkbox/> タグを使用する。

まずはチェックボックスを使えるようにするために、FormModel にチェックボックス用のフィールドを追加する。

FormModel.java

package jp.abc;

public class FormModel {
	private String input1;
	private String pass1;
	private String area1;
	private boolean check1;

	public String getInput1() {
		return input1;
	}
	public void setInput1(String input1) {
		this.input1 = input1;
	}
	public String getPass1() {
		return pass1;
	}
	public void setPass1(String pass1) {
		this.pass1 = pass1;
	}
	public String getArea1() {
		return area1;
	}
	public void setArea1(String area1) {
		this.area1 = area1;
	}
	public boolean isCheck1() {
		return check1;
	}
	public void setCheck1(boolean check1) {
		this.check1 = check1;
	}
}

アクセスしたときにチェックされた状態にするには、FormModel#setCheck1()でtrueを設定すればよい。

MyAppController.java

	@RequestMapping(value = "/helo", method = RequestMethod.GET)
	public String helo(Model model) {
		model.addAttribute("title", "ModelAndView sample");
		model.addAttribute("message", "これはModelAndViewのテストです。");
		FormModel fm = new FormModel();
		fm.setCheck1(true);
		model.addAttribute("formModel", fm);
		return "showMessage";
	}

チェックボックスの値をクエリ送信後の画面に繁栄させるには、form()メソッドで出力されるコードに&lt:li>要素を追加する。

MyAppController.java

	@RequestMapping(value = "/helo", method = RequestMethod.POST)
	public String form(@ModelAttribute FormModel formModel, Model model){
		String res = "<ul><li>" + formModel.getInput1()
				+ "</li><li>" + formModel.getPass1()
				+ "</li><li>" + formModel.getArea1()
				+ "</li><li>" + formModel.isCheck1()
				+ "</li></ul>";
		model.addAttribute("title", "sample");
		model.addAttribute("message", res);
		return "showMessage";
	}

複数のチェックボックスをまとめて作る

複数のチェックボックスをまとめて作るには、<form:checkboxes>タグを使用する。

まずは FormModel に、複数のチェックボックスの値を格納するためのメンバーを用意する。

FormModel.java

package jp.abc;

public class FormModel {
	private String input1;
	private String pass1;
	private String area1;
	private boolean check1;
	private String[] checks;

	public String getInput1() {
		return input1;
	}
	public void setInput1(String input1) {
		this.input1 = input1;
	}
	public String getPass1() {
		return pass1;
	}
	public void setPass1(String pass1) {
		this.pass1 = pass1;
	}
	public String getArea1() {
		return area1;
	}
	public void setArea1(String area1) {
		this.area1 = area1;
	}
	public boolean isCheck1() {
		return check1;
	}
	public void setCheck1(boolean check1) {
		this.check1 = check1;
	}
	public String[] getChecks() {
		return checks;
	}
	public void setChecks(String[] checks) {
		this.checks = checks;
	}
}

次に showMessage.jsp にチェックボックス用の要素を追加する。
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>${title}</title>
	</head>
	<body>
		<h1>${title}</h1>
		<p>${message}</p>
		<table>
			<form:form modelAttribute="formModel">
				<tr><td><form:input path="input1"/></td></tr>
				<tr><td><form:password path="pass1" showPassword="on"/></td></tr>
				<tr><td><form:textarea path="area1" cols="40" rows="3"></form:textarea></td></tr>
				<tr><td><form:checkbox path="check1" label="checkbox 1"/></td></tr>
				<tr><td>
					<form:checkboxes items="${checkItems}" path="checks" delimiter=" "/>
				</td></tr>
				<tr><td><input type="submit" ></td></tr>
			</form:form>
		</table>
	</body>
</html>

MyAppControllerに複数のチェックボックス用のコードを追加する

getList()メソッドでチェックボックスの要素を生成する。
生成したListをmodelに設定する。

MyAppController.java

package jp.abc;

import java.util.ArrayList;
import java.util.List;

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 {

	private List<String> getList() {
		List<String> list = new ArrayList<String>();
		list.add("Mac OS X");
		list.add("Windows");
		list.add("Linux");
		return list;
	}

	@RequestMapping(value = "/helo", method = RequestMethod.GET)
	public String helo(Model model) {
		model.addAttribute("title", "ModelAndView sample");
		model.addAttribute("message", "これはModelAndViewのテストです。");
		FormModel fm = new FormModel();
		fm.setCheck1(true);
		fm.setChecks(new String[]{"Windows"});
		model.addAttribute("formModel", fm);
		model.addAttribute("checkItems", getList());
		return "showMessage";
	}

	@RequestMapping(value = "/helo", method = RequestMethod.POST)
	public String form(@ModelAttribute FormModel formModel, Model model){
		String res = "<ul><li>" + formModel.getInput1()
				+ "</li><li>" + formModel.getPass1()
				+ "</li><li>" + formModel.getArea1()
				+ "</li><li>" + formModel.isCheck1()
				+ "</li></ul>";
		model.addAttribute("title", "sample");
		model.addAttribute("message", res);
		return "showMessage";
	}
}

6月29日

プロジェクト SpringMyApp を作成して動かすまで

プロジェクトを作成する

以下の手順でプロジェクトを作成する。(テキストp.39~)

  1. メニューから[新規]-[その他]を選択する。
  2. 「ウィザードを選択」のダイアログで[Spring]-[Srpingプロジェクト]を選択して「次へ」をクリック。
  3. プロジェクト名に「SpringMyApp」を入力する。
  4. テンプレートは「Simple Spring Web Maven」を選択して「完了」をクリック。

プロジェクトを更新する

パッケージエクスプローラで作成したプロジェクトを右クリックし、メニューで[Maven]-[プロジェクトの更新]を選択する。
Eclipse画面の右下でビルド中の表示が消えれば完了。

Maven installを実行する

パッケージエクスプローラで作成したプロジェクトを右クリックし、メニューで[実行]-[Maven install]を選択する。
コンソールに以下のように出力されれば完了。

[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 4.169 s
[INFO] Finished at: 2015-06-29T10:02:49+09:00
[INFO] Final Memory: 12M/220M

Tomcat7サーバーに追加

以下の手順で作成したプロジェクトをTomcat7サーバーに追加する。

  1. Eclipseのサーバータブで、Tomcat7サーバーを右クリック。
  2. メニューで「追加および除去」を選択。
  3. 作成したプロジェクトを選択し「追加」をクリックして右の枠に移動させる。
  4. 「完了」をクリック。
  5. http://localhost:8080/ProjectName にアクセスする。
  6. click to enter と表示されればOK。

コントローラを作成する

プロジェクト配下の特定のURLにアクセスされたときの処理を記述するには、コントローラを作成する必要がある。
以下の手順でコントローラを作成する。(テキストp.160~)

  1. パッケージエクスプローラで作成したプロジェクトを展開する。
  2. 「Javaリソース」を展開する。
  3. 「src/main/java」を右クリックし[新規]-[クラス]を選択する。
  4. パッケージ名に「jp.abc」を入力。
  5. クラス名に「MyAppController」を入力。
  6. 「完了」をクリック。
  7. MyAppController.java のエディタが開く。

MyAppController.javaのコードを編集する。テキストp.163のリスト3-3とほぼ同じ。パッケージ名だけは異なるので注意すること。

MyAppController.java

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を設定する

パッケージエクスプローラで プロジェクト名 > src > main > webapp > WEB-INF の下にある mvc-config.xml を開く。
以下の部分を書き換える。

mvc-config.xml

    <!-- Uncomment and your base-package here:
         <context:component-scan
            base-package="org.springframework.samples.web"/>  -->

書き換え後は以下のとおり。

mvc-config.xml

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

設定ファイルを変更したので、Tomcat7サーバーを再起動する。
http://localhost:8080/(ProjectName)/helo にアクセスすると、「this is sample. ok?」と表示される。

入力フォームを追加する

SpringMVCで入力フォームを作るには、データをやり取りするためのオブジェクトを作成する必要がある。
jp.abc パッケージにFormModelクラスを作成する。

「src/main/java」の下の「jp.abc」を右クリックして[新規]-[クラス]を選択する。
名前に「FormModel」と入力して完了をクリック。
以下のコードを入力する。

FormModel.java

package jp.abc;

public class FormModel {
	private String input1;

	public String getInput1() {
		return input1;
	}

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

次にJSPを編集する。
「src > main > webapp > WEB-INF > view」の下にある showMessage.jsp を開き、以下のようにコードを追加する。2行目の taglib ディレクティブを忘れないように!

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>

MyAppController を編集する。
GETメソッドで呼ばれたときに、FormModelを渡すようにコードを追加する。
また、POSTメソッドで呼ばれたときに、渡された文字列を message に渡すようにする。

MyAppController.java

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";
	}
}

文字化けの対策

日本語文字列を入力して送信すると文字化けが発生する。
フィルターを設定して文字化け対策をする。

「src > main > webapp > WEB-INF」にある web.xml を開き、ソースタブを選択してフィルターの設定を追加する。

最後の </web-app> タグの直前に追加する。

	<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>

6月19日

Webアプリケーション

Webアプリケーションを作る前に、Webアプリケーションとはどういうものかを考えてみる。

WebApp

昨年の設定

プロジェクトを作成する

Eclipseで[新規]-[プロジェクト]を選択
Webの下にある[動的Webプロジェクト]を選択
プロジェクト名: jsp
ターゲットランタイム: Apache Tomcat v7.0
「次へ」→「次へ」→「完了」でプロジェクトを作成する

Eclipseの設定

[ウィンドウ]>[設定]>[Web]>[JSPファイル]を選択
エンコードを「UTF-8」に設定
[ウィンドウ]>[設定]>[Tomcat]を選択
Tomcatバージョンは「バージョン7.x」を選択
Tomcatホームは「C:\All-in-One-Eclipse4.3\tomcat\7」を選択
「OK」をクリック

jspプロジェクトを右クリック
[Tomcatプロジェクト]-[コンテキスト定義を更新]を選択

JSPファイルを作成

パッケージエクスプローラでWebContentを右クリック
[新規]-[JSPファイル]を選択
ファイル名を index.jsp に設定

index.jsp を右クリックして、[実行]-[サーバーで実行]を選択。
ブラウザタブが開いて、index.jsp の内容が表示される。

index.jspの編集

index.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>Hello World!</title>
</head>
<body>

<h1>Hello World!</h1>
<p>
日本語の表示を確認
</p>

<h3>式</h3>
<%=1+1 %>
<br />
<%=new java.util.Date() %>
<!-- HTMLコメント -->
<%-- JSPコメント --%>

<h3>JSP宣言</h3>
<%! int count = 10; %>
<%=count %>

<h3>スクリプトレット</h3>
<% %>

</body>
</html>

6月15日

Mapを使用したクラス

ゲームのタイトルをキーにして会社名を値として保存するクラスの続き。
ひとつのデータを格納して会社名を取得するテストを追加する。

GamesTest.java

	@Test
	public void testGet() {
		Games g = new Games();
		g.add("スーパーマリオブラザーズ", "任天堂");
		String s = g.get("スーパーマリオブラザーズ");
		assertThat(s, is("任天堂"));
	}

このテストにパスするには、Fakeすればよい。

Games.java

import java.util.HashMap;
import java.util.Map;


public class Games {
	private Map<String, String> map = new HashMap<String, String>();

	public void add(String title, String company) {
		map.put(title, company);
	}

	public int getCount() {
		return map.size();
	}

	public String get(String string) {
		return "任天堂";
	}
}

さらにテストを追加する。

	@Test
	public void testGet1() {
		Games g = new Games();
		g.add("パズドラ", "ガンホー");
		String s = g.get("パズドラ");
		assertThat(s, is("ガンホー"));
	}

両方のテストをパスするためには、Mapを使用してきちんと実装するのがよい。

Games.java

	public String get(String key) {
		return map.get(key);
	}

ファイルから読み込む

map.txt を作成して、タイトルと会社名のデータを書き込んでおく。

map.txt

スーパーマリオブラザーズ,任天堂
パズドラ,ガンホー
妖怪ウォッチ,レベルファイブ
ドラゴンクエスト,スクエアエニックス

このファイルのデータを読み込むテストを作成する。

GamesTest.java

	@Test
	public void testLoad() {
		Games g = new Games();
		g.load("map.txt");
		int n = g.getCount();
		assertThat(n, is(4));
	}

Gamesクラスにload()メソッドを追加して、指定されたファイルの内容を読み込み、マップに追加する。
ファイルを読み込む部分は、TextDocumentクラスのload()メソッドが参考になる。

Games.java

	public boolean load(String path) {
		File f = new File(path);
		if (!f.exists()) return false;
		try (BufferedReader in = new BufferedReader(new FileReader(path))) {
			while (in.ready()) {
				String s = in.readLine();
				String[] sa = s.split(",");
				map.put(sa[0], sa[1]);
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
		return true;
	}

テキストファイルに出力する機能を追加する

格納している内容をテキストファイルに出力し、出力したファイルを読み込んで行数が一致するかを確認するテストを作成する。

GamesTest.java

	@Test
	public void testSave() {
		Games g = new Games();
		g.add("パズドラ", "ガンホー");
		g.save("map1.txt");
		boolean result = g.load("map1.txt");
		assertThat(result, is(true));
		int n = g.getCount();
		assertThat(n, is(1));
	}

保存の処理内容は、TextDocumentクラスのsave()とほぼ同じ。
タイトルと会社名をカンマで区切って出力すればよい。
ループする部分はテキストp.444が参考になる。

Games.java

	public void save(String path) {
		try (PrintWriter out = new PrintWriter(new FileWriter(path))) {
			Set<String> keys = map.keySet();
			for (String key : keys) {
				String v = map.get(key);
				out.println(key + "," + v);
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

要素の削除

指定したタイトルを削除する機能を追加する。
まずテストを作成する。

GamesTest.java

	@Test
	public void testDelete() {
		Games g = new Games();
		g.load("map.txt");
		int n = g.getCount();
		assertThat(n, is(4));
		g.delete("パズドラ");
		n = g.getCount();
		assertThat(n, is(3));
		String s = g.get("パズドラ");
		assertNull(s);
	}

テストにパスするようにGames#delete()を実装する。
要素の削除はテキストには掲載されてないが、Eclipseの補完機能で表示される候補から探せば見つかる。

6月12日

ストリームとコレクション

前回、TextDocumentクラスをテスト駆動開発で作成し始めたので、その続きを実施する。

  1. 指定した行の文字列取得
  2. 指定した行を削除(削除した行の文字列を返す)
  3. 指定した文字列を行として末尾に挿入
  4. 指定した場所に指定した文字列を行として挿入
  5. 出力先ファイル名を指定してファイル出力

指定した場所に指定した文字列を行として挿入

TextDocument.java

	public void addLine(int i, String s) {
		lines.add(i, s);
	}

前回は、4まで実施したが、先頭行に挿入するテストしかやっていないので、別のテストを追加して動作確認を実施する。

まずは、途中に挿入するテストを追加してみる。

	@Test
	public void testAdd2() {
		TextDocument td = new TextDocument();
		td.load("hoge.txt");
		int lines = td.getLines();
		assertThat(lines, is(10));
		td.addLine(3, "1234567890");
		lines = td.getLines();
		assertThat(lines, is(11));
		String s = td.getLine(0);
		assertThat(s, is("ab"));
		s = td.getLine(3);
		assertThat(s, is("1234567890"));
		s = td.getLine(4);
		assertThat(s, is("abcde"));
	}

範囲外の -1 行目を引数で指定した場合は例外が発生する。
例外の発生をテストで書くには、Testアノテーションの引数に発生する例外を記述する。

TextDocumentTest.java

	@Test(expected = IndexOutOfBoundsException.class)
	public void testAdd3() {
		TextDocument td = new TextDocument();
		td.load("hoge.txt");
		td.addLine(-1, "1234567890");
	}

10行しかないときに11行目に追加しようとしても例外が発生する。
まずは、例外が発生してエラーになるテストを書く。
TextDocumentTest.java

	@Test
	public void testAdd4() {
		TextDocument td = new TextDocument();
		td.load("hoge.txt");
		td.addLine(11, "1234567890");
	}

例外が発生してエラーになることを確認してから、アノテーションに expected 引数を追加する。

	@Test(expected = IndexOutOfBoundsException.class)
	public void testAdd4() {
		TextDocument td = new TextDocument();
		td.load("hoge.txt");
		td.addLine(11, "1234567890");
	}

次はファイルに書き込む機能を追加する。
まずはテストを書く。
ファイルに書けたかどうかは、ファイルを読み込んでテストしてみる。
TextDocumentTest.java

	@Test
	public void testSave() {
		TextDocument td = new TextDocument();
		td.load("hoge.txt");
		td.addLine(0, "1234567890");
		td.save("out.txt");
		td.load("out.txt");
		int lines = td.getLines();
		assertThat(lines, is(11));
	}

TextDocumentに、保存の処理を実装する。

	public void save(String path) {
		try (PrintWriter out = new PrintWriter(new FileWriter(path))) {
			for (String s : lines) {
				out.println(s);
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

Mapの使用例

ゲームタイトルをキーにして会社名を保存するクラスを作成する。

Games.java

public class Games {
}

テストを作る。
GamesTest.java

import static org.hamcrest.CoreMatchers.*;
import static org.junit.Assert.*;

import org.junit.Test;


public class GamesTest {

	@Test
	public void testAdd() {
		Games g = new Games();
		g.add("スーパーマリオブラザーズ", "任天堂");
		int n = g.getCount();
		assertThat(n, is(1));
		g.add("パズドラ", "ガンホー");
		n = g.getCount();
		assertThat(n, is(2));
	}

}

テストにパスするように Games を実装したら、さらにテストを追加する。
GamesTest.java

	@Test
	public void testGet() {
		Games g = new Games();
		g.add("スーパーマリオブラザーズ", "任天堂");
		String s = g.get("スーパーマリオブラザーズ");
		assertThat(s, is("任天堂"));
	}

6月8日

テスト駆動開発でストリームとコレクションを復習する

TextDocumentクラスと、テストケースのTextDocumentTestクラスを作成する。

ファイルを読み込む機能を用意する。
そのために、load()メソッドを作成する。
まずはテストを書く。

TextDocumentTest.java

import static org.hamcrest.CoreMatchers.*;
import static org.junit.Assert.*;

import org.junit.Test;

public class TextDocumentTest {

	@Test
	public void testLoad() {
		TextDocument td = new TextDocument();
		boolean b = td.load("hoge.txt");
		assertThat(b, is(true));
	}

}

コンパイルエラーを解消するために、TextDocumentクラスにload()メソッドを追加する。
TextDocument.java

public class TextDocument {
	public boolean load(String path) {
		return false;
	}
}

テストを実行するとエラーになるので、load()メソッドの戻り値をtrueに変更する。(Fake)
TextDocument.java

public class TextDocument {
	public boolean load(String path) {
		return true;
	}
}

テストにパスするのでめでたしめでたし。
テストケースに、パスしないテストを追加する。
TextDocumentTest.java

import static org.hamcrest.CoreMatchers.*;
import static org.junit.Assert.*;

import org.junit.Test;

public class TextDocumentTest {

	@Test
	public void testLoadNG() {
		TextDocument td = new TextDocument();
		boolean b = td.load("aaa.txt");
		assertThat(b, is(false));
	}

	@Test
	public void testLoad() {
		TextDocument td = new TextDocument();
		boolean b = td.load("hoge.txt");
		assertThat(b, is(true));
	}

}

このテストをパスするようにするには、まじめに実装しないといけない。
TextDocument.java

import java.io.File;

public class TextDocument {
	public boolean load(String path) {
		File f = new File(path);
		if (!f.exists()) return false;
		return true;
	}
}

次に、ファイルの行数を取得するメソッドを作ることにする。
hoge.txt は11行なので、テストコードは以下のようになる。
TextDocumentTest.java

	@Test
	public void testLines() {
		TextDocument td = new TextDocument();
		boolean b = td.load("hoge.txt");
		assertThat(b, is(true));
		int lines = td.getLines();
		assertThat(lines, is(11));
	}

このテストをパスするには、Fakeすればよい。
TextDocument.java

import java.io.File;

public class TextDocument {
	public boolean load(String path) {
		File f = new File(path);
		if (!f.exists()) return false;
		return true;
	}

	public int getLines() {
		return 11;
	}
}

Fakeコードではテストにパスしないようにするために、1行だけのファイル 1.txt を用意し、そのファイルの行数を検査するテストを追加する。
TextDocumentTest.java

	@Test
	public void testLines() {
		TextDocument td = new TextDocument();
		boolean b = td.load("hoge.txt");
		assertThat(b, is(true));
		int lines = td.getLines();
		assertThat(lines, is(11));
	}

	@Test
	public void testLines1() {
		TextDocument td = new TextDocument();
		boolean b = td.load("1.txt");
		assertThat(b, is(true));
		int lines = td.getLines();
		assertThat(lines, is(1));
	}

このテストをパスするために、TextDocumentクラスをきちんと実装する。
java.util.List を使用してコレクションフレームワークの復習をする。
List の要素として、ファイルの各行を追加する。

TextDocument.java

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

public class TextDocument {
	private List<String> lines;

	public boolean load(String path) {
		lines = new ArrayList<String>();
		File f = new File(path);
		if (!f.exists()) return false;
		try (BufferedReader in = new BufferedReader(new FileReader(path))) {
			while (in.ready()) {
				String s = in.readLine();
				lines.add(s);
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
		return true;
	}

	public int getLines() {
		return lines.size();
	}
}

ファイル読み込みができた人は、以下の機能を追加しましょう!

  1. 指定した行の文字列取得
  2. 指定した行を削除(削除した行の文字列を返す)
  3. 指定した文字列を行として末尾に挿入
  4. 指定した場所に指定した文字列を行として挿入
  5. 出力先ファイル名を指定してファイル出力

上記機能追加ができたら、さらに以下の機能を追加しましょう!

  1. 文字列検索(行番号と、その行の文字列を、Listで返す)
  2. 文字列置換(検索と同じように返す)

指定した行の文字列を取得

まずはテストを書く。

TextDocumentTest.java

	@Test
	public void testGetLine() {
		TextDocument td = new TextDocument();
		boolean b = td.load("hoge.txt");
		String s = td.getLine(0);
		assertThat(s, is("ab"));
	}

TextDocumentにはgetLine()メソッドがないので、Eclipseに作ってもらう。
TextDocument.java

	public String getLine(int i) {
		return null;
	}

テストを実行するとエラーになるので、テストをパスするように “ab”を返すようにFakeする。
TextDocument.java

	public String getLine(int i) {
		return "ab";
	}

テストを追加する。
TextDocumentTest.java

	@Test
	public void testGetLine() {
		TextDocument td = new TextDocument();
		boolean b = td.load("hoge.txt");
		String s = td.getLine(0);
		assertThat(s, is("ab"));
		s = td.getLine(1);
		assertThat(s, is("abc"));
	}

テストを実行するとエラーになるので、テストにパスするようにコードを修正する。
TextDocument.java

	public String getLine(int i) {
		return lines.get(i);
	}

指定した行を削除

まずはテストを書く。
削除した行の文字列を返す。

TextDocumentTest.java

	@Test
	public void testDelete() {
		TextDocument td = new TextDocument();
		td.load("hoge.txt");
		String s = td.deleteLine(0);
		assertThat(s, is("ab"));
	}

EclipseにdeleteLine()メソッドを作ってもらう。
TextDocument.java

	public String deleteLine(int i) {
		return null;
	}

テストを実行してもパスしないので、”ab”を返すようにFakeする。
TextDocument.java

	public String deleteLine(int i) {
		return "ab";
	}

テストを追加する。
1行目を削除したら、もともと2行目だったものが1行目になるはず。
TextDocumentTest.java

	@Test
	public void testDelete() {
		TextDocument td = new TextDocument();
		td.load("hoge.txt");
		String s = td.deleteLine(0);
		assertThat(s, is("ab"));
		s = td.getLine(0);
		assertThat(s, is("abc"));
	}

TextDocumentのdeleteLine()を修正。

TextDocument.java

	public String deleteLine(int i) {
		return lines.remove(i);
	}

指定した文字列を行として末尾に挿入

まずはテストを書く。
1行追加すれば、行数が増えるはず。

TextDocumentTest.java

	@Test
	public void testAdd() {
		TextDocument td = new TextDocument();
		td.load("hoge.txt");
		int lines = td.getLines();
		assertThat(lines, is(10));
		td.addLine("1234567890");
		lines = td.getLines();
		assertThat(lines, is(11));
	}

EclipseにaddLine()を作ってもらう。
テストするとエラーになるので、テストにパスする実装を追加する。
TextDocument.java

	public void addLine(String s) {
		lines.add(s);
	}

指定した場所に指定した文字列を行として挿入

まずはテストを書く。
どこに追加するのかを引数で渡す。
先頭に1行追加すれば、行数が増えるはず。
先頭行は追加した文字列に一致するはず。
2行目は、もともと1行目だった文字列に一致するはず。

TextDocumentTest.java

	@Test
	public void testAdd1() {
		TextDocument td = new TextDocument();
		td.load("hoge.txt");
		int lines = td.getLines();
		assertThat(lines, is(10));
		td.addLine(0, "1234567890");
		lines = td.getLines();
		assertThat(lines, is(11));
		String s = td.getLine(0);
		assertThat(s, is("1234567890"));
		s = td.getLine(1);
		assertThat(s, is("ab"));
	}

5月15日

24章 ストリームを使いこなす

Writerを使う

WriterSample.java

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

public class WriterSample {

	public static void main(String[] args) throws IOException {
		String path = &quot;hoge.txt&quot;;
		Writer out = new FileWriter(path);
		out.write(&quot;foo&quot;);
		out.write(&quot;bar&quot;);
		out.close();
		System.out.println(&quot;END&quot;);
	}

}

Writerのデコレータを使う

WriterSample2.java

import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;

public class WriterSample2 {

	public static void main(String[] args) throws IOException {
		String path = &quot;hoge.txt&quot;;
		PrintWriter out = new PrintWriter(
				new BufferedWriter(new FileWriter(path)));
		out.println(&quot;foo&quot;);
		out.println(&quot;baa&quot;);
		out.close();
		System.out.println(&quot;END&quot;);
	}
}

Readerを使う

ReaderSample.java

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

public class ReaderSample {

	public static void main(String[] args) throws IOException {
		String path = &quot;hoge.txt&quot;;
		BufferedReader in = new BufferedReader(new FileReader(path));
		String line;
		while ((line = in.readLine()) != null) {
			System.out.println(line);
		}
		in.close();
	}

}

バイナリデータの入出力

OutputStreamを使う

OutputSample.java

import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;


public class OutputSample {

	public static void main(String[] args) throws IOException {
		String path = &quot;btest.bin&quot;;
		OutputStream out = new FileOutputStream(path);
		byte[] b = new byte[] {0x0a, 0x0b, 0x0c};
		out.write(b);
		out.close();
		System.out.println(&quot;END&quot;);
	}

}

InputStreamを使う

InputSample.java

import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;


public class InputSample {

	public static void main(String[] args) throws IOException {
		String path = "btest.bin";
		InputStream in = new FileInputStream(path);
		ByteArrayOutputStream out = new ByteArrayOutputStream();
		int len;
		byte[] b = new byte[1024];
		while (true) {
			len = in.read(b);
			if (len < 0) break;
			out.write(b, 0, len);
		}
		in.close();
		byte[] result = out.toByteArray();
		for (int i = 0; i < result.length; i++) {
			System.out.print(Integer.toHexString(result[i]) + " ");
		}
	}

}

ファイルをコピーする

FileCopy.java

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

public class FileCopy {

	public static void main(String[] args) throws IOException {
		String src = "hoge.txt";
		String dest = "hoge2.txt";
		InputStream in = new FileInputStream(src);
		OutputStream out = new FileOutputStream(dest);
		int len;
		byte[] b = new byte[1024];
		while (true) {
			len = in.read(b);
			if (len < 0) break;
			out.write(b, 0, len);
		}
		in.close();
		out.close();
		System.out.println("END");
	}

}

必ずcloseする

CloseSample.java

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;


public class CloseSample {

	public static void main(String[] args) throws IOException {
		String src = "hoge.txt";
		String dest = "hoge2.txt";
		copy(dest, src);
	}

	public static void copy(String dest, String src) throws IOException {
		InputStream in = null;
		OutputStream out = null;
		try {
			in = new FileInputStream(src);
			out = new FileOutputStream(dest);
			int len;
			byte[] b = new byte[1024];
			while (true) {
				len = in.read(b);
				if (len < 0) break;
				out.write(b, 0, len);
			}
		} finally {
			if (in != null) {
				try {
					in.close();
				} catch (IOException e) {}
			}
			if (out != null) {
				try {
					out.close();
				} catch (IOException e) {}
			}
		}
	}
}

Java7から自動でcloseしてくれる機能が追加された。
try-with-resources文を使うと自動的にcloseしてくれる。
自動でcloseしてくれるオブジェクトは、AutoClosableインタフェースを実装しているクラス。

CloseSample2.java

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

public class CloseSample2 {

	public static void main(String[] args) throws IOException {
		String src = "hoge.txt";
		String dest = "hoge2.txt";
		copy(dest, src);
	}

	public static void copy(String dest, String src) throws IOException {
		try (InputStream in = new FileInputStream(src);
			 OutputStream out = new FileOutputStream(dest)) {
			int len;
			byte[] b = new byte[1024];
			while (true) {
				len = in.read(b);
				if (len < 0) break;
				out.write(b, 0, len);
			}
		}
	}

}

Webページのコピー(ファイル以外の例)

DownloadSample.java を try-with-resources構文で書き換えてみる。

import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URL;

public class DownloadSample {

	public static void main(String[] args) throws IOException {
		String dest = "dest.html";
		URL url = new URL("http://www.yahoo.co.jp/");
		try (
				InputStream in = url.openStream();
				OutputStream out = new FileOutputStream(dest)
			) {
			int len;
			byte[] b = new byte[1024];
			while (true) {
				len = in.read(b);
				if (len < 0) break;
				out.write(b, 0, len);
			}
		}
	}
}

Fileクラスを使う

FileSample.java

import java.io.File;

public class FileSample {

	public static void main(String[] args) {
		File file = new File("C:/All-in-One-Eclipse4.4/workspace/c24/");
		list(file);
	}

	private static void list(File parent) {
		File[] children = parent.listFiles();
		if (children != null) {
			for (int i = 0; i < children.length; i++) {
				File file = children[i];
				if (file.isDirectory()) {
					list(file);
				} else {
					System.out.println(file.getAbsolutePath());
				}
			}
		}
	}
}

Dirコマンドを作ってみよう

Dir.java

import java.io.File;

public class Dir {

	public static void main(String[] args) {
		File file = new File(args[0]);
		dir(file);
	}

	private static void dir(File parent) {
		File[] children = parent.listFiles();
		if (children != null) {
			for (int i = 0; i < children.length; i++) {
				File file = children[i];
				System.out.println(file.getName());
			}
		}
	}

}
  1. 引数なしの場合、現在のディレクトリの内容を表示する
  2. /OS オプションが指定されたらサイズ順にソートする
  3. /OD オプションが指定されたら日付順にソートする

引数なしの場合、現在のディレクトリの内容を表示する

Dir.java

import java.io.File;

public class Dir {

	public static void main(String[] args) {
		String path;
		if (args.length == 0) {
			path = ".";
		} else {
			path = args[0];
		}
		File file = new File(path);
		dir(file);
	}

	private static void dir(File parent) {
		File[] children = parent.listFiles();
		if (children != null) {
			for (int i = 0; i < children.length; i++) {
				File file = children[i];
				System.out.println(file.getName());
			}
		}
	}
}

オプションの取得と条件分岐

Dir.java

import java.io.File;

public class Dir {

	public static void main(String[] args) {
		String path;
		String option = null;
		if (args.length == 0) {
			path = ".";
		} else {
			path = args[0];
			option = getOption(args);
		}
		File file = new File(path);
		dir(file, option);
	}

	private static String getOption(String[] args) {
		for (String s : args) {
			if (s.startsWith("/")) {
				return s;
			}
		}
		return null;
	}

	private static void dir(File parent, String option) {
		File[] children = parent.listFiles();
		if (children != null) {
			if (option == null) {
				for (int i = 0; i < children.length; i++) {
					File file = children[i];
					System.out.println(file.getName());
				}
			}
			if (option.equals("/OD")) {
				// 日付順
			}
			if (option.equals("/OS")) {
				// サイズ順
			}
		}
	}

}