ソースコード(Eclipse WTPプロジェクト) : ExamWebService.zip
こちらで実際に動く物を試せます : http://hondou.homedns.org/ExamWebService/suggestion.html
下図のように、テキストボックスに百人一首の一部を入れると、前方一致する百人一首が候補として提示されるような AJAX AJAX しているアプリを作ってみる
package com.snail.exam;
import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
public class WakaSuggestionWebService {
private static String FILE_NAME = "/srv/ftp/data/hyakunin.txt";
/** 和歌の対応表のキャッシュ. */
private static Map<String, WakaBean> cache = null;
/** キャッシュが有効な時刻. */
private static long cacheUpdate = 0;
/**
* 和歌の対応表("かな" -> "漢字+詠み人")を読み出します. 一旦読み込むと5分間はキャッシュを返します。
*
* @return 和歌の対応表
*/
private synchronized Map<String, WakaBean> getWakaMap() {
if (cacheUpdate > System.currentTimeMillis()) {
// キャッシュ有効時間内
return cache;
}
// 出現順を保存するために、LinkedHashMapを使う
cache = new LinkedHashMap<String, WakaBean>();
try {
BufferedReader br =
new BufferedReader(
new InputStreamReader(
new FileInputStream(FILE_NAME),"UTF-8"));
String kanji, kana, author;
while (((kanji = br.readLine()) != null)
&& ((kana = br.readLine()) != null)
&& ((author = br.readLine()) != null)) {
cache.put(kana, new WakaBean(kana, kanji, author));
}
br.close();
cacheUpdate = System.currentTimeMillis() + 5 * 60 * 1000;
} catch (IOException e) {
// 読み込みに失敗した場合は何もしない
// ただし、キャッシュ有効時間の更新も行わない
e.printStackTrace();
e = null;
}
return cache;
}
/**
* fragment で始まる和歌を返します
*
* @param fragment
* 和歌の一部
* @return fragment で始まる和歌の配列
* @throws Exception
*/
public WakaBean[] suggestWaka(final String fragment) {
List<WakaBean> retList = new LinkedList<WakaBean>();
for (Map.Entry<String, WakaBean> entry : getWakaMap().entrySet()) {
String kana = entry.getKey();
if (kana.startsWith(fragment)) {
retList.add(entry.getValue());
}
}
return retList.toArray(new WakaBean[0]);
}
public WakaSuggestionWebService() {
super();
}
}
package com.snail.exam;
import java.io.Serializable;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
public class WakaBean implements Serializable {
private static final long serialVersionUID = -8204110091872591615L;
private String kana;
private String kanji;
private String author;
public String getKana() {
return kana;
}
public WakaBean(){
super();
}
public WakaBean(String kana, String kanji, String author){
super();
this.kana = kana;
this.kanji = kanji;
this.author = author;
}
public void setKana(String kana) {
try {
this.kana = URLEncoder.encode(kana, "UTF-8");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
}
public String getKanji() {
return kanji;
}
public void setKanji(String kanji) {
try {
this.kanji = URLEncoder.encode(kanji, "UTF-8");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
}
public String getAuthor() {
return author;
}
public void setAuthor(String author) {
try {
this.author = URLEncoder.encode(author, "UTF-8");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
}
}
<!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>Suggestion Example</title>
<style type="text/css">
.suggestionListON {
width: 500pt;
border: ridge;
}
.suggestionListOFF {
border: none;
}
.candidateON {
color: yellow;
background-color: black;
}
.candidateOFF {
color: black;
background-color: white;
}
</style>
<script type="text/javascript" src="scripts/prototype.js"></script>
<script type="text/javascript" src="scripts/ws.js"></script>
<script type="text/javascript">
// <!--
//------------------------------------------------------------------------
function init(){
Event.observe('tBox', 'keydown', keyPress);
}
//------------------------------------------------------------------------
var lastEvaluated = null;
function updateSuggestionList(){
var input = $('tBox').value;
if(input == ''){
return true;
}
if(input == lastEvaluated){
return true;
}
lastEvaluated = input;
var MyHandler = Class.create();
Object.extend(MyHandler.prototype,WS.Handler.prototype);
Object.extend(MyHandler.prototype,{
on_request : function(envelope){
},
on_response : function(envelope){
},
on_error : function(call,envelope){
alert("ERROR");
}
});
var call = new WS.Call('/ExamWebService/services/WakaSuggestionWebService');
call.add_handler(new MyHandler());
var nsuri = 'http://exam.snail.com';
var qn_op = new WS.QName('suggestWaka', nsuri);
call.invoke_rpc(qn_op, [ {
name : 'fragment',
value : $('tBox').value
} ], null, function(call, envelope) {
var wakalist = envelope.get_body().get_all_children()[0].get_all_children();
var kana = new Array(wakalist.length);
for( var wakaNo = 0 ; wakaNo < wakalist.length ; wakaNo++ ){
var wakaBean = wakalist[wakaNo].get_all_children();
for( var elemNo = 0 ; elemNo < wakaBean.length ; elemNo++ ){
var name = wakaBean[elemNo].element.tagName;
var value = wakaBean[elemNo].get_value();
if( name == 'kana' ){
kana[wakaNo] = value;
}else if( name == 'kanji' ){
kanji = value;
}else if( name == 'author' ){
author = value;
}
}
}
var inputText = $('tBox').value;
if( kana.length == 1 ){
var candidate = kana[0];
if( inputText == candidate ){
$('suggestionList').innerHTML = '';
$('suggestionList').className = "suggestionListOFF";
return;
}
}
var suggestion = '';
for(var cnt = 0 ; cnt < kana.length ; cnt++){
suggestion += '<div';
suggestion += ' onmouseover="mousein(this);"';
suggestion += ' onmouseout="mouseout(this);"';
suggestion += ' class="candidateOFF"';
suggestion += ' onClick="setValue(this.innerHTML);">';
suggestion += kana[cnt];
suggestion += '</div>';
}
$('suggestionList').innerHTML = suggestion;
$('suggestionList').className = "suggestionListON";
// $('soap').innerHTML = "RAW SOAP ENVELOPE:<br/>" + arguments[2].escapeHTML();
});
}
//------------------------------------------------------------------------
// Mouse Operation
function setValue(selectedValue){
$('tBox').value = selectedValue;
$('suggestionList').innerHTML = "";
$('suggestionList').className = "suggestionListOFF";
}
function mousein(row){
var candidateList = $('suggestionList').childNodes;
for( var cnt = 0 ; cnt < candidateList.length ; cnt++ ){
candidateList[cnt].className = "candidateOFF";
}
row.className = 'candidateON';
}
function mouseout(row){
row.className = 'candidateOFF';
}
//------------------------------------------------------------------------
// Key operation
function keyPress(event){
var candidateList = $('suggestionList').childNodes;
var cnt;
if( candidateList.length == 0 ){
return;
}
switch(event.keyCode){
case Event.KEY_RETURN :
// RETURN Key (13) is pressed
for( cnt = 0 ; cnt < candidateList.length ; cnt++ ){
if(candidateList[cnt].className == "candidateON"){
// find selected row
$('tBox').value = candidateList[cnt].innerHTML;
$('suggestionList').innerHTML = '';
return;
}
}
break;
case Event.KEY_UP :
// UP Key (38) is pressed
for( cnt = 0 ; cnt < candidateList.length ; cnt++ ){
if(candidateList[cnt].className == "candidateON"){
// find selected row
candidateList[cnt].className = "candidateOFF";
if( (cnt-1) == -1 ){
candidateList[candidateList.length - 1].className = "candidateON";
}else{
candidateList[cnt-1].className = "candidateON";
}
return;
}
}
candidateList[candidateList.length - 1].className = "candidateON";
break;
case Event.KEY_DOWN :
// DOWN Key (40) is pressed
for(cnt = 0 ; cnt < candidateList.length ; cnt++ ){
if(candidateList[cnt].className == "candidateON"){
// find selected row
candidateList[cnt].className = "candidateOFF";
if( (cnt+1) == candidateList.length ){
candidateList[0].className = "candidateON";
}else{
candidateList[cnt+1].className = "candidateON";
}
return;
}
}
candidateList[0].className = "candidateON";
break;
}
}
//------------------------------------------------------------------------
// -->
</script>
</head>
<body onLoad="init();">
<div><input type="text" id="tBox"
onkeyup="updateSuggestionList();" autocomplete="off"
style="width: 500pt" /> <br />
<div id="suggestionList" class="suggestionListOFF"></div>
</div>
</body>
</html>
基本的に、テキストボックスと、候補を展開する <div> のみ
<body> <div><input type="text" id="tBox" onkeyup="updateSuggestionList();" autocomplete="off" style="width: 500pt" onfocus="isWakaInputBoxOnFocus=true" onblur="isWakaInputBoxOnFocus=false" /> <br /> <div id="suggestionList" class="suggestionListOFF"></div> </div> </body>
document.onkeypress = keyPress; function keyPress(event){
document.onkeypress = keyPress; function keyPress(){
function init(){ Event.observe('tBox', 'keydown', keyPress); } ... function keyPress(event){ ... } ... </script> </head> <body onLoad="init();"> <div><input type="text" id="tBox" ...