ソースコード(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" ...