久しぶりにパーサー部分に機能追加
いままでの動的動作は毎回クラスからオブジェクトをNEWして関数実行していました。
これを、NEWしたオブジェクトに対して関数を複数実行できるように機能拡張。
タグ属性にseedoを定義して、以下のように値を設定する。
クラス名:関数名:引数
クラス名:関数名
<div align="center" class="xxx" seedo="myapp.HelloWorldTop1:tb:2500" id="asse111" />
スタートタグとエンドタグの間の値を関数実行した返り値と置換します。
一度、指定したクラス名はページが有効である間はオブジェクトが保持されているので、
クラス名が同じで別の関数を設定すれば、NEWしたオブジェクトの関数を実行することができます。
<!doctype html>
<HTML>
<HEAD>
<title>Home</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
</HEAD>
<BODY>
<h2>Hello world top</h2>
The current time is
<div class="seedo:myapp.HelloWorldDb1:tb">xxxxx</div>
<HR>
<div align="center" class="seedo:myapp.HelloWorldDb1:tb" />
<HR>
<div align="center" class="xxx" seedo="myapp.HelloWorldTop1:tb:2500" id="asse111" />
<HR>
<table border="1">
<tr>
<th class="xxx" seedo="myapp.HelloWorldTop1:get:2" />
<td seedo="myapp.HelloWorldTop1:getName" />
<td class="xxx" seedo="myapp.HelloWorldTop1:getDept" />
<td class="xxx" seedo="myapp.HelloWorldTop1:getSal" />
<td class="xxx" seedo="myapp.HelloWorldTop1:getJob">job</td>
</tr>
<tr>
<th class="xxx" seedo="myapp.HelloWorldTop1:get:3" />
<td seedo="myapp.HelloWorldTop1:getName" />
<td class="xxx" seedo="myapp.HelloWorldTop1:getDept" />
<td class="xxx" seedo="myapp.HelloWorldTop1:getSal" />
<td class="xxx" seedo="myapp.HelloWorldTop1:getJob">job</td>
</tr>
<tr>
<th class="xxx" seedo="myapp.HelloWorldTop1:get:1" />
<td seedo="myapp.HelloWorldTop1:getName" />
<td class="xxx" seedo="myapp.HelloWorldTop1:getDept" />
<td class="xxx" seedo="myapp.HelloWorldTop1:getSal" />
<td class="xxx" seedo="myapp.HelloWorldTop1:getJob">job</td>
</tr>
<tr>
<th class="xxx" seedo="myapp.HelloWorldTop1:get:0" />
<td seedo="myapp.HelloWorldTop1:getName" />
<td class="xxx" seedo="myapp.HelloWorldTop1:getDept" />
<td class="xxx" seedo="myapp.HelloWorldTop1:getSal" />
<td class="xxx" seedo="myapp.HelloWorldTop1:getJob">job</td>
</tr>
</table>
<HR>
</BODY>
</HTML>
このHTMLをフレームワークで実行すると、
HTMLファイルのなかで使っているHelloWorldTop1 クラスの定義は以下のとおり。
package myapp
import java.util.{HashMap,Date}
import scala.collection.JavaConversions._
import scala.collection.immutable._
import scala.xml._
import seedo.database._
class HelloWorldTop1 extends TmpApp {
var EMPNO:String = "EMPNO"
var ENAME:String = "ENAME"
var SAL:String = "SAL"
var DEPTNO:String = "DEPTNO"
var JOB:String = "JOB"
var MGR:String = "MGR"
var records:Array[Array[Any]] = null
def rc(record:Array[Any]) : Unit ={
EMPNO = if(record(0)!=null){record(0).toString}else{""}
ENAME = if(record(1)!=null){record(1).toString}else{""}
SAL = if(record(2)!=null){record(2).toString}else{""}
DEPTNO = if(record(3)!=null){record(3).toString}else{""}
JOB = if(record(4)!=null){record(4).toString}else{""}
MGR = if(record(5)!=null){record(5).toString}else{""}
}
def recx(record:Array[Any]) :Node = {
var nodes = Queue.empty[Node]
record.foreach{(fx) => nodes += <TD>{fx}</TD>}
return <TR>{nodes}</TR>
}
// Function one argument
def tb(sal:String) : String = {
tb(sal.toInt)
}
def tb(sal:Int) : String = {
val deptno = parameterMap.get("deptno") match {
case v:Array[String] => v(0).toInt
case null => 10
}
var sql:Db = null
try {
sql = new Db
sql.select("EMPNO,ENAME,SAL,DEPTNO,JOB,MGR")
.from("EMP")
.where("SAL > ?",sal)
.or("DEPTNO = ?",deptno)
.orderBy("EMPNO")
records = sql.executeQuery
var nodes = Queue.empty[Node]
records.foreach{(rec) => {
nodes += recx(rec)
rc(rec) // record values 最後のレコードの値が残ることになる。
}
}
val xml = <table border="2">{nodes}</table>
return "HelloWorldDb3 " + new Date + xml.toString
} catch {
case e:Exception =>{
throw e
}
} finally {
if(sql != null)sql.close
}
}
def get(no:Int) : String = {
if(records != null && no < records.length) {
rc(records(no))
return "G E T "+no
} else {
return "Empty"
}
}
def getId : String ={EMPNO}
def getName : String ={ENAME}
def getDept : String ={DEPTNO}
def getJob : String ={JOB}
def getSal : String ={SAL}
def getMgr : String ={MGR}
}
フレームワークのコードは以下のとおり。
package ews.servlet.sp
import java.util.HashMap
import org.xml.sax._
import org.xml.sax.helpers.DefaultHandler
import scala.collection.JavaConversions._
import scala.collection.mutable.Stack
import ews.servlet.session._
/**
* HTMLを直接PrintWriterへ出力する。
*/
class ParserCallback6(pMap:HashMap[String,Array[String]], out:java.io.PrintWriter, session : Session) extends DefaultHandler {
var buf = new StringBuffer
val stackChange = new Stack[ExecTag] // For substitution
var locator:Locator = null
var createClasses = new HashMap[String,ExecClass] // created class
override def toString : String = buf.toString
override def setDocumentLocator (locator:Locator) :Unit= {
this.locator = locator
}
override def startDocument ():Unit= {}
override def endDocument():Unit= {}
// タグスタート
override def startElement ( uri:String, name:String, qualifiedName:String , attrs:Attributes ):Unit={
var tag = "<"
if(name.startsWith("!")){
tag += name
} else {
tag += name.toLowerCase // tagを小文字で統一する
}
var fullFag = false //
var seedoFag = false // Page class object
var end = false // タグが / で閉じているか?
for (idx <- 0 to attrs.getLength - 1){
val attributeType = attrs.getType(idx)
val attrLocalName = attrs.getLocalName(idx)
val attrName = attrs.getQName(idx)
val attrValue = attrs.getValue(idx)
if(attrLocalName.length > 0 && !"#text".equals(attrLocalName)){
if("/".equals(attrLocalName) && attrValue.length == 0){
end = true // tagが閉じた
} else if("!DOCTYPE".equalsIgnoreCase(name) && "html".equalsIgnoreCase(attrLocalName)){
tag += " html"
} else {
if("class".equals(attrName) && attrValue.startsWith("seedo:")){
val execCommand = attrValue.split(":") // Each element is separated.
if(execCommand.length >= 3) { // It doesn't substitute it if there are neither a class name nor a function name.
stackChange.push(new ExecTag(
0
,name
,execCommand
,new StringBuffer // The tag to be substituted is maintained.
,false // class="seedo:...."
))
fullFag = true // 動的タグとして置換状態にある
} else {
println("$$$$$$$$$$ execClass Warning " + attrName + "='" + attrValue + "'")
}
} else if("seedo".equals(attrName) && attrValue.length > 0){ // Servlet class
val execCommand = attrValue.split(":") // Each element is separated.
if(execCommand.length >= 1) { // It doesn't substitute it if there are neither a class name nor a function name.
stackChange.push(new ExecTag(
0
,name
,execCommand
,new StringBuffer // The tag to be substituted is maintained.
,true // SEEDO attribute
))
seedoFag = true
} else {
println("$$$$$$$$$$ execClass Warning " + attrName + "='" + attrValue + "'")
}
} else {
tag += (" " + attrLocalName)
if(attrValue != null && !("selected".equalsIgnoreCase(attrLocalName) || "checked".equalsIgnoreCase(attrLocalName) || "disabled".equalsIgnoreCase(attrLocalName) )) {
tag += ("=\"" + attrValue + "\"")
}
}
}
}
}
tag += ">"
if(fullFag == true || seedoFag == true){ // 動的タグを記録する
stackChange.top.setStartTagFull(tag)
if(end){ // スラでタグが閉じている場合
if(!stackChange.isEmpty){// Tag inside class="seedo"
stackChange.top.getBufChange.append(tag) // 動的タグのオリジナルを設定
tag += exec // タグは、動的タグ生成側でタグ付けする。
stackChange.pop // The stack for substitution is liberated.
}
out.print(tag)
}
} else {
if(stackChange.isEmpty){
out.print(tag)
} else { // 動的タグにネストするタグは置換対象となるため、このタグをバッファに蓄える
stackChange.top.getBufChange.append(tag)
}
}
}
// タグエンド
override def endElement (uri:String , name:String, qualifiedName:String):Unit={
val endTag = "</" + name.toLowerCase +">"
if(stackChange.isEmpty) { // Tag to be substituted inside
out.print(endTag)
} else {
stackChange.top.getBufChange.append(endTag)
if(name.equalsIgnoreCase(stackChange.top.getStartTag)) {
var str:String = null
if(!stackChange.isEmpty){// Tag inside class="seedo"
str = stackChange.top.getStartTagFull + exec + endTag
stackChange.pop // The stack for substitution is liberated.
}
if(stackChange.isEmpty){
out.print(str)
} else {// class="seedo" 動的タグがネストしている
stackChange.top.getBufChange.append(str)
}
}
}
}
// タグ以外
override def characters(ch:Array[Char],start:Int,length:Int) : Unit = {
var str:String = ""
for(i <- start to length+start-1){
str += ch(i)
}
if(stackChange.isEmpty){
out.print(str)
} else {// inside tag class="seedo"
stackChange.top.getBufChange.append(str)
}
}
// 動的タグを実行する
def exec : String = {
// Depth of the same stack as tag that became substitution beginning -> tag end
var changeTag:String = ""
val execCommand = stackChange.top.getExecCommand
if(stackChange.top.getSeedo){ // SEEDO attribute
return create(execCommand)
}
val classname = execCommand(1) // classnamesをNULLで関数呼び出ししてはいけない
var method = ""
try{
var ec:ExecClass = new ExecClass(classname) // Object is generated.
if(ec != null){
ec.exec("setTag",stackChange.top.getBufChange.toString) // TAG before it substitutes it is set.
ec.exec("setParameterMap",pMap) // GetParameterMap method of ServletRequest to acquire parameter
ec.exec("setSession",session) // セッション情報を引き渡す
if(execCommand.length == 3){ // Argument none
method = execCommand(2)
val result = ec.exec(method)
if(result != null)
changeTag = result.toString
} else if(execCommand.length == 4){ // There is one argument.
method = execCommand(2)
val value = execCommand(3)
val result = ec.execStr(method,value)
if(result != null)
changeTag = result.toString
}
}
} catch {
case e:NoSuchMethodException => {
println("Exception "+e.getMessage + " NoSuchMethodException! :" + classname + " " + method)
}
case e:Exception => {
println("Exception "+e.getMessage + " class or Method not find! :" + classname + " " + method)
}
}
return changeTag
}
// Seedo 動的サブレットクラス・オブジェクトを生成する
def create(classnames:Array[java language=".lang.String"][/java]) : String = {
val classname = classnames(0) // classnamesをNULLで関数呼び出ししてはいけない
var method = ""
var changeTag:String = ""
var ec:ExecClass = null
ec = createClasses.get(classname)
try{
if(ec == null){ // はじめてクラス名が現れた
ec = new ExecClass(classname) // Object is generated.
if(ec != null){
createClasses.put(classname, ec) // 生成したクラス・オブジェクトを登録する
ec.exec("setParameterMap",pMap) // GetParameterMap method of ServletRequest to acquire parameter
ec.exec("setSession",session) // セッション情報を引き渡す
}
}
if(ec != null){
if(classnames.length == 2){ // Argument none
method = classnames(1)
val result = ec.exec(method)
if(result != null)
changeTag = result.toString
} else if(classnames.length == 3){ // There is one argument.
method = classnames(1)
val value = classnames(2)
val result = ec.execStr(method,value)
if(result != null)
changeTag = result.toString
}
} else {
println("Exception :Not create object = " + classname + " " + method)
}
} catch {
case e:NoSuchMethodException => {
println("Exception "+e.getMessage + " NoSuchMethodException! :" + classname + " " + method)
}
case e:Exception => {
println("Exception "+e.getMessage + " class or Method not find! :" + classname + " " + method)
}
}
return changeTag
}
}