Scala+DBMS+Web スカラ座の夜

2012年2月20日

動的ページのフレームワークの機能拡張

Filed under: Framework,Scala — admin @ 8:01 PM

久しぶりにパーサー部分に機能追加

いままでの動的動作は毎回クラスからオブジェクトを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) : 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
	}
}

Comments

comments

Powered by Facebook Comments

コメントはまだありません

No comments yet.

RSS feed for comments on this post.

Sorry, the comment form is closed at this time.

Powered by WordPress