ページ置換
いよいよ完成です。
段階的プログラミングの最終版です。
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 によって置換情報を保持します。
コードは複雑に見えますが、処理としては、
- startElement関数でタグ属性を探す
- endElementでスタートタグと対になるタグを識別
- ec.exec(“setTag”,stackChange.top.getBufChange.toString)により置換元のタグをセット
- ec.exec(“setParameterMap”,pMap)によりパラメータをセット
- ExecClassを生成してexecStr関数を呼び出す
- タグを置換
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 */
コメントを残す
コメントを投稿するにはログインしてください。