Scala+DBMS+Web スカラ座の夜

2011年8月21日

Web フレームワークを作る!!  3日目 #2

Filed under: Framework,Scala — admin @ 4:07 PM

動的ページ生成

class=”seedo:ews.HelloWorld:tb” 属性を実行する機能の実装です。

ExecClassクラスは、フルパスのクラス名をコンストラクタにして生成します。
Class.forName によってクラスを特定して、
newInstance.asInstanceOf[AnyRef] によってオブジェクトを生成します。

この処理の流れはJavaのリフレクションと同じ。

このコードだとクラスローダーを使って任意のクラス名の文字列からClassインスタンスを取得することはしていないので、実行したいクラスは予めクラスファイルかJarファイルをTOMCATに設定します。

最初のコードはシンプルにしたいのでクラスローダーの機能は省略。

package ews
import java.lang.reflect.{Field,InvocationTargetException,Method,Type}
import java.math.BigDecimal
import java.util.{Calendar,Date}
import java.sql.{CallableStatement, PreparedStatement, ResultSet,Timestamp}
import javax.persistence.{Column,Entity,Id,Table,Lob,UniqueConstraint,SequenceGenerator}
import seedo.Util

/**
 * The function is executed specifying the class.
 */
class ExecClass(className:String
    ) {
	val myClass = Class.forName(className)	// The class is gotten from the class name.
	val Obj = myClass.newInstance.asInstanceOf[AnyRef]	// create Object
	val clazz:Class[_] = Obj.getClass	// Class of MyClass
	/**
	 * It executes it specifying the type of the argument of the function.
	 */
	def execStr(methodName:String,value:String) : AnyRef = {
	  try {	// The function with the Int in the argument is called.
		val v = value.toInt
	   	return exec(methodName,v)
	  } catch {
	  	case e:Exception => {}
	  }
	  try {	// The function with the Long in the argument is called.
		  val v = value.toLong
		  return exec(methodName,v)
	  } catch {
	  	case e:Exception => {}
	  }
	  try {	// The function with the Double in the argument is called.
		  val v = value.toDouble
		  return exec(methodName,v)
	  } catch {
	  	case e:Exception => {}
	  }
	  return exec(methodName,value)
	}
	/**
	 * The function with the argument is executed.
	 */
	def exec(methodName:String,value:Any) : AnyRef = {
  	    if(value == null){
	    	val method:Method = clazz.getMethod(methodName)
	    	return method.invoke(Obj)
  	    }
  	    // The function is executed specifying the type of the argument of the function.
	    if(value.isInstanceOf[Int]){// The function with the Int in the argument is called.
		 val method = clazz.getMethod(methodName, classOf[Int])
		 val v = new Integer(value.asInstanceOf[Int])
		 return method.invoke(Obj, v)
	    } else if(value.isInstanceOf[Long]){// The function with the Long in the argument is called.
		 val method = clazz.getMethod(methodName, classOf[Long])
		 val v = new java.lang.Float(value.asInstanceOf[Long])
		 return method.invoke(Obj, v)
	    } else if(value.isInstanceOf[Float]){// The function with the Float in the argument is called.
		 val method = clazz.getMethod(methodName, classOf[Float])
		 val v = new java.lang.Float(value.asInstanceOf[Float])
		 return method.invoke(Obj, v)
	    } else if(value.isInstanceOf[Double]){// The function with the Double in the argument is called.
		 val method = clazz.getMethod(methodName, classOf[Double])
		 val v = new java.lang.Double(value.asInstanceOf[Double])
		 return method.invoke(Obj, v)
	    } else if(value.isInstanceOf[Char]){// The function with the Char in the argument is called.
		 val method = clazz.getMethod(methodName, classOf[Char])
		 val v = new Character(value.asInstanceOf[Char])
		 return method.invoke(Obj, v)
	    } else {	// other class
		 var valClass:Class[_] = value.asInstanceOf[AnyRef].getClass
		 val method = clazz.getMethod(methodName, valClass)
		 return method.invoke(Obj, value.asInstanceOf[AnyRef])
	    }
	}
	/**
	 * The function is executed.
	 */
	def exec(methodName:String) : AnyRef ={
		if(methodName == null)
			return null
		try {
			val method:Method = clazz.getMethod(methodName)
			return method.invoke(Obj)
		} catch {
			case e:InvocationTargetException => {
				throw new Exception(e.getCause)
			}
			case e:Exception => {
				throw new Exception(e)
			}
		}
	}
}

実行させたい関数を呼び出すためのexec関数は、

  1. 関数名だけで引数なしの場合は、def exec(methodName:String)
  2. 引数ありの場合は、def exec(methodName:String,value:Any)

引数なしのexec関数のコードには、例外処理のコードが追加されて、きちんと実装しています。

getMethodinvokeで例外が発生した場合、
特に、InvocationTargetExceptionが発生したときは、
getCause()メソッドでターゲットのクラスからの例外を取得して、
throw new Exception(e.getCause)します。

def exec(methodName:String,value:Any)にも、
この例外処理を入れないといけません。このコードではとりあえず省略。

HTMLパーサしたときに、このクラスを使う場合は、def execStr(methodName:String,value:String) を使います。

execStr関数は、タグ属性で指定された引数文字列から、型、この場合、数字であれば
数値型の型変換します。

  1. Int 整数
  2. Long 整数
  3. Double 実数

型変換の処理は、toIntとかtoLongとかtoDoubleを呼び出して例外でなければ、その数値型で実行して、
例外が発生すれば、文字列として扱うためにString型で実行するという簡単なしかけです。

HelloWorldクラスのtb関数には、引数なし、Double引数、String引数の3つのtb関数を
定義しているので、もし、Intとして成立する数字文字列をタグ属性に書いてしまうと、
HelloWorldクラスには、そんな関数ないっていうことで例外が発生します。
次のLongもないので例外発生し、DoubleではOKとなります。

当然、タグ属性にtb関数以外の関数名が指定されても、そんな関数はないと例外発生となります。

ExecClassクラスの単体テスト用のコードを作ってテストします。

package ews
import org.scalatest.junit.{JUnitSuite,ShouldMatchersForJUnit}
import org.junit.{Test,Before}
import scala.xml._

class ExecClassTest extends JUnitSuite with ShouldMatchersForJUnit{
	var obj:ExecClass = null
	@Before def initialize() {
		obj = new ExecClass("ews.HelloWorld")
	}
	@Test def exec {
		println("Start ---------")
		var result:AnyRef = null
		try {
			result = obj.exec("tb")
		} catch {
		 case e:Exception => {
            println("Exception "+e.getMessage)
		 }
		}
		println(result)
		println(obj.execStr("tb","abcdefg"))
		println(obj.execStr("tb","3.14"))
		println(obj.execStr("tb","2001/1/1"))
		println(obj.execStr("tb","2011/8/20 12:9:59"))
 		println(obj.execStr("tb","2"))
		println(obj.execStr("tb","99999999999999"))
		println("End ---------")
	}
}

このコードを実行できるようにするには、JUNITScalatest の2つのJarファイルをビルドパスに追加します。

私の環境では、以下のバージョンを使っていますが、最新のものでもテストできると思います。

  • junit-4.9b3.jar
  • scalatest-1.2.jar

テストを実行した結果です。

Start ---------
HelloWorld Sun Aug 21 16:16:33 JST 2011
HelloWorld String [abcdefg] Sun Aug 21 16:16:33 JST 2011
HelloWorld Double [3.14] Sun Aug 21 16:16:33 JST 2011
HelloWorld String [2001/1/1] Sun Aug 21 16:16:33 JST 2011
HelloWorld String [2011/8/20 12:9:59] Sun Aug 21 16:16:33 JST 2011
HelloWorld Double [2.0] Sun Aug 21 16:16:33 JST 2011
HelloWorld Double [9.9999999999999E13] Sun Aug 21 16:16:33 JST 2011
End ---------

このtb関数で実行されたかは、HelloWorldの隣に型が出力され、型変換された結果が表示されます。
tb関数では、されに new Date で現在の時刻が表示されています。
いましがたテスト実行したので今日の日時です。

引数なしのtb関数呼び出しでは、例外発生したときに出力するようになっているので、
HelloWorldクラス側で、例外発生させるために、null 変数にtoStirng するような
コードを追加すればテストできます。

いよいよ次は、HTMLパーサによって解析したTAGからclass属性を探して、
TAG置換するコードとなります。

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

No comments yet.

RSS feed for comments on this post. TrackBack URL

Leave a comment

コメントを投稿するにはログインしてください。

Powered by WordPress