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

Posted 2011年8月21日 by

ページ置換

いよいよ完成です。

段階的プログラミングの最終版です。

nu.validator.htmlparserを使って、HTMLファイルをパースします。

ReplaceHandlerクラスのなかでHTMLのなかのタグを置換します。

その情報は、ExecTagクラスによって保持します。

package ews

import java.io._
import javax.servlet.http._
import scala.xml._
import scala.collection.JavaConversions._
import nu.validator.htmlparser.common.XmlViolationPolicy
import nu.validator.htmlparser.sax.{HtmlParser,XmlSerializer}
import nu.validator.htmlparser.test.SystemErrErrorHandler
/**
 * The dynamic page is generated on the static HTML page.
 * htmlPath:URL
 */
class Ssp(request : HttpServletRequest, response : HttpServletResponse, htmlPath : String) extends TemplSp {
	var parsed:String = null	//
	// parse HTML file
	private def change(e:InputSource) : String = {
		println("Ssp parse")
	    val pMap = new java.util.HashMap[String,Array[String]]
	    request.getParameterMap.foreach(v => pMap.put(v._1,v._2))	// copy from getParameterMap to HashMap
		val ck = new ReplaceHandler(pMap)	// set ServletRequest getParameterMap function to handler
		val hp = new HtmlParser
		hp.setContentHandler(ck)
		hp.parse(e)
		if(ck != null)
			return ck.toString.replaceAll("","")	// change 
 tag
		else
			return null
	}
	// replace HTML
	def html : String = {
		val htmlFile = new java.io.File(htmlPath)
		if(htmlFile.exists && htmlPath.toLowerCase.endsWith(".html")){
			val input:BufferedInputStream = new BufferedInputStream(new FileInputStream(htmlFile))	// open HTML file
			val inputSource = new InputSource(input)
			parsed = this.change(inputSource)
			return parsed
		}
		null	// There is no file.
	}
	override def toString :String = parsed
}

まずは、通常のハンドラのコード。
ハンドラクラスの中で使うSAXによるタグ解析用のハンドラ関数です。

  • startDocument 未使用
  • endDocument 未使用
  • startElement タグの開始
  • endElement タグの終了
  • characters 要素など

以下ががシンプルなハンドラのコード

クラス構成を示すものなので、このReplaceHandlerクラスにはコンストラクタの引数がないです。

package ews
import java.util.HashMap
import org.xml.sax.helpers.DefaultHandler
import org.xml.sax.Attributes
import scala.collection.JavaConversions._

class ReplaceHandler extends DefaultHandler {
  var buf = new StringBuffer
  override def toString :String = buf.toString
  //
  override def  startDocument :Unit ={	/*println("startDocument")*/  }
  override def  endDocument :Unit ={	/*println("endDocument")*/  }
  override def startElement(uri: String, localName: String, qName: String,attrs: Attributes): Unit = {
	var tag = ""
	buf.append(tag)
  }
  override def endElement(uri: String, localName: String, qName: String): Unit = {
	buf.append("")
  }
  override def characters(ch:Array[Char],start:Int,length:Int) : Unit = {
    var tag:String = ""
    for(i

このコードをベースにWebフレームワーク用のハンドラクラスは以下のようになります。

class=”seedo:属性があるタグが入れ子になっても処理できるように
stackChange によって置換情報を保持します。

コードは複雑に見えますが、処理としては、

  1. startElement関数でタグ属性を探す
  2. endElementでスタートタグと対になるタグを識別
  3. ec.exec(“setTag”,stackChange.top.getBufChange.toString)により置換元のタグをセット
  4. ec.exec(“setParameterMap”,pMap)によりパラメータをセット
  5. ExecClassを生成してexecStr関数を呼び出す
  6. タグを置換
package ews

import java.util.HashMap
import org.xml.sax.helpers.DefaultHandler
import org.xml.sax.Attributes
import scala.collection.JavaConversions._
import scala.collection.mutable.Stack
import seedo._

class ReplaceHandler(pMap:HashMap[String,Array[String]]) extends DefaultHandler {
  var buf = new StringBuffer		// Original tag is maintained.
  val stack = new Stack[String]			// For indent inspection of tag
  val stackChange = new Stack[ExecTag]	// For substitution

  override def toString :String = buf.toString
  //
  override def  startDocument :Unit ={	/*println("startDocument")*/  }
  override def  endDocument :Unit ={	/*println("endDocument")*/  }
  override def startElement(uri: String, localName: String, qName: String,attrs: Attributes): Unit = {
	stack.push(qName)
	var tag = "= 3) {	// It doesn't substitute it if there are neither a class name nor a function name.
				stackChange.push(new ExecTag(
						stack.length
						,qName
						,execCommand
						,new StringBuffer	// The tag to be substituted is maintained.
				))
				fullFag = true
			} else {
				println("$$$$$$$$$$ execClass Warning " + attrName + "='" + attrValue + "'")
			}
		}
	}
	tag += ">"
	if(!stackChange.isEmpty){
		stackChange.top.getBufChange.append(tag)
	} else {
		buf.append(tag)
	}
	if(fullFag == true){
		stackChange.top.setStartTagFull(tag)
	}
  }
  override def endElement(uri: String, localName: String, qName: String): Unit = {
    val endTag = ""
    if(stack.top.equals(qName)){	// It is the same as the tag that became a substitution beginning.
		if(!stackChange.isEmpty) {	// Tag to be substituted inside
		  if(stackChange.top.getStartTagDepth == stack.length && qName.equals(stackChange.top.getStartTag) && !stackChange.isEmpty) {
		    // Depth of the same stack as tag that became substitution beginning -> tag end
			stackChange.top.getBufChange.append(endTag)
			var changeTag:String = ""
			try{
				val execCommand = stackChange.top.getExecCommand
				var ec:ExecClass = new ExecClass(execCommand(1))		// 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
					if(execCommand.length == 3){	// Argument none
						changeTag = ec.exec(execCommand(2)).toString
					}else if(execCommand.length == 4){	// There is one argument.
						val value = execCommand(3)
						changeTag = ec.execStr(execCommand(2),execCommand(3)).toString
					}
				}
			} catch {
				case e:NoSuchMethodException => {
					println("Exception "+e.getMessage + " NoSuchMethodException!")
				}
				case e:Exception => {
					println("Exception "+e.getMessage + " class or Method not find!")
				}
			}
			var str:String = null
			if(!stackChange.isEmpty){// Tag inside class="seedo"
				str = stackChange.top.getStartTagFull + changeTag + endTag
			}
			stackChange.pop	// The stack for substitution is liberated.
			if(!stackChange.isEmpty){// class="seedo"
				stackChange.top.getBufChange.append(str)
			} else {
				buf.append(str)	//
			}
		  } else {
			stackChange.top.getBufChange.append(endTag)
		  }
		} else {
			buf.append(endTag)
		}
    	stack.pop
//    	println("-------------stack.length="+stack.length)
    } else {
    	println("$$$$$$$$$$ The correspondence of tag is not taken. startTag(stack.top)=" + stack.top + " endTag=" + qName)
    }
  }
  override def characters(ch:Array[Char],start:Int,length:Int) : Unit = {
    var tag:String = ""
    for(i

結果

結果はこのようになります。

いかがでしょうか。

3日間でWebフレームを作りきりました。

このWebフレームは、最低限の機能しかありませんが、
このコードで誰でもWebフレームを作ることができます。

このコードを発展させて、国産のScala Webフレームワークに育てばいいと考えています。

いっしょに開発しようと思われる方、フレームワークを作りましょう。

ライセンス

ここで公開したソースコードは、LAMPによるライセンスとします。

/*
 * SeeDo
 *  eWave Solutions Inc.(c) 2000-2011, LAMP
 */

Post Details

  • Post Title: Web フレームワークを作る!!  3日目 #3
  • Author: admin
  • Filed As: Framework, Scala
  • Tags:
  • You can follow any responses to this entry through the RSS 2.0 feed. You can leave a response, or trackback from your own site.

コメントを残す