1 package org.blackquill.engine
3 // BlackQuill Copyright (C) 2013 set.minami<set.minami@gmail.com>
4 // License MIT see also LISENCE.txt
7 import org.apache.commons.logging._
8 import scala.collection.immutable.List
9 import scala.collection.mutable.LinkedHashMap
10 import scala.collection.mutable.Stack
11 import scala.collection.mutable.ListMap
12 import scala.collection.SortedSet
13 import scala.util.matching.Regex
16 import org.blackquill.engine._
19 private val log:Log = LogFactory.getLog(classOf[BQParser])
21 private val Syntax = LinkedHashMap(
22 "^(.*?\\\\,)((>.*?\\\\,)+?)\\\\,(.*?)$$" -> ("blockquote",surroundByBlockquoteTAG _),
23 "^(.*?\\\\,)>(.*?)\\\\,\\\\,(.*?)$$" -> ("blockquote",surroundByGeneralTAG _),
24 "^(.*?)((\\s+\\d+?\\.\\s.+?\\\\,\\s*?)+?)(.*?)$$" -> ("ol",surroundByListTAG _),
25 "^(.*?)((\\s+(?:\\*|\\+|\\-)\\s.+?\\\\,\\s*?)+?)(.*?)$$" -> ("ul",surroundByListTAG _),
26 "^(.*?)\\*\\*(.+?)\\*\\*(.*?)$$" -> ("em",surroundByGeneralTAG _),
27 "^(.*?)\\*(.+?)\\*(.*?)$$" -> ("i",surroundByGeneralTAG _),
28 "^(.*\\\\,)(.*?)\\\\,(\\-+|=+)\\s*\\\\,(.*)$$" -> ("h",surroundByHeadTAGUnderlineStyle _),
29 "^(.*?)(#{1,6})\\s(.+?)(\\s#{1,6}?){0,1}\\\\,(.*?)$$" -> ("h",surroundByHeadTAG _)
30 //"^(.*?)(\\\\,.+?\\\\,)(.*?)$$" -> ("p",surroundByAbstructTAG _)
33 private def surroundByBlockquoteTAG(doc:String, regex:String, TAG:String):String = {
34 val p = new Regex(regex, "before","inTAG","midInTag","following")
35 val m = p findFirstMatchIn(doc)
41 val mid = m.get.group("inTAG")
42 log info "***-->" + mid
44 if(m.get.group("before") != None){bef = m.get.group("before")}else{bef = ""}
45 if(m.get.group("following") != None){fol = m.get.group("following")}else{fol = ""}
49 val mat = """(>(.+?\\,))+?""".r.findAllMatchIn(mid)
52 contentStr += mt.group(2)
57 log info "-->" + contentStr
58 return surroundByBlockquoteTAG(bef, regex, TAG) +
59 "<blockquote>" + contentStr + "</blockquote>" + surroundByBlockquoteTAG(fol, regex, TAG)
64 private def surroundByListTAG(doc:String, regex:String, TAG:String):String = {
65 val p = new Regex(regex, "before","elements","element","following")
66 val m = p findFirstMatchIn(doc)
72 if(m.get.group("before") != None){bef = m.get.group("before")}else{bef = ""}
73 if(m.get.group("following") != None){fol = m.get.group("following")}else{fol = ""}
74 s = m.get.group("elements")
83 var styles:((Int) => String) = null
88 styles = (index:Int) => {
89 (index/indentWidth)%3 match{
96 sign = "[\\*|\\+|\\-]"
99 styles = (index:Int) => {
100 (index/indentWidth)%4 match{
102 case 2 => "decimal-leading-zero"
103 case 3 => "upper-latin"
104 case 0 => "lower-latin"
111 var docList = List[String]()
112 for(elem <- s"""(\\s+?$sign\\s.+?\\\\,)+?""".r.findAllMatchIn(s)){
113 docList = elem.group(1)::docList
118 def _surroundByListTAG(doc:List[String],TAG:String,indent:Int):TreeNode[String] = {
119 var tree = new TreeNode[String]("")
120 if(doc.isEmpty){return tree}
122 tree.add(new TreeNode("<" + sp + s""" style=\"list-style-type:${styles(indent)}\">"""))
124 var list = List.empty[Tuple3[String,Int,String]]
126 val m = s"""((\\s+?)$sign\\s(.+?)\\,)""".r.findFirstMatchIn(elem)
127 list = (m.get.group(1),m.get.group(2).size,m.get.group(3))::list
130 var restStr = List[String]()
131 if(list.isEmpty){return new TreeNode("")
132 }else{for(e <- list.reverse.tail){restStr = e._1::restStr}}
134 restStr = restStr.reverse
135 for(elem <- list.reverse){
137 tree.add(new TreeNode("<" + sp + s""" style=\"list-style-type:${styles(elem._2)}\">"""))
138 }else if(elem._2 < i){
139 tree.add(new TreeNode[String](s"</$sp>"*((i - elem._2)/indentWidth)))
141 tree.add(new TreeNode[String](s"<$TAG>" + elem._3 + s"</$TAG>\\,"))
145 restStr = List[String]("")
147 restStr = restStr.tail
151 tree.add(new TreeNode(s"</$sp>"*((i - indent)/indentWidth + 1)))
154 val r1 = s"""(\\s*)${sign}.*?\\,""".r
155 val wS1 = r1.findFirstMatchIn(s)
157 val r2 = s"""(\\s*)${sign}.*(\\\\,<br />.*?</blockquote>\\\\,)""".r
158 val wS2 = r2.findFirstMatchIn(s)
161 var wS:Option[Regex.Match] = null
162 if(wS2 != None){wS = wS2}else if(wS1 != None){wS = wS1}
164 for(e <- _surroundByListTAG(docList.reverse,"li",wS.get.group(1).size)){
167 if(wS == wS2){str += wS.get.group(2)}
169 log debug "---->" + str
170 surroundByListTAG(bef,regex,TAG) + str + surroundByListTAG(fol,regex,TAG)
174 private def surroundByHeadTAGUnderlineStyle(doc:String, regex:String, TAG:String):String = {
175 if(doc == ""){return doc}
177 log debug "-->" + doc
178 val p = new Regex(regex, "before","inTAG","style","following")
179 val m = p findFirstMatchIn(doc)
185 if(m.get.group("before") != None){bef = m.get.group("before")}else{bef = ""}
186 if(m.get.group("following") != None){fol = m.get.group("following")}else{fol = ""}
187 contentStr = m.get.group("inTAG")
189 if(m.get.group("style").contains("-")){
195 return surroundByHeadTAGUnderlineStyle(bef, regex, TAG) +
196 s"<$headSign>$contentStr</$headSign>\\," + surroundByHeadTAGUnderlineStyle(fol, regex, TAG)
201 private def surroundByHeadTAG(doc:String, regex:String, TAG:String):String = {
202 if(doc == ""){return doc}
204 log debug "--> " + doc
205 val p = new Regex(regex, "before","startHead","inTAG","endHead","following")
206 val m = p findFirstMatchIn(doc)
212 if(m.get.group("before") != None){bef = m.get.group("before")}else{bef = ""}
213 if(m.get.group("following") != None){fol = m.get.group("following")}else{fol = ""}
214 contentStr = m.get.group("inTAG")
216 val headSize = m.get.group("startHead").size
217 val endHead = m.get.group("endHead")
218 log debug "-->" + endHead
219 val headTAG = TAG + headSize
221 val m2 = """^\s(#+?)$$""".r("wantSize").findFirstMatchIn(endHead)
223 val size = m2.get.group("wantSize")
224 if(size.size != headSize){
225 contentStr += " " + size
226 log warn "Curious header expression was found. " +
227 s" BlackQuill represents this <$headTAG>$contentStr</$headTAG> ."
232 return surroundByHeadTAG(bef,regex,TAG) +
233 s"<${headTAG}>$contentStr</${headTAG}>\\," +
234 surroundByHeadTAG(fol,regex,TAG)
239 private def surroundByGeneralTAG(doc:String, regex:String, TAG:String):String = {
240 if(doc == ""){return doc}
242 val p = new Regex(regex,"before","inTAG","following")
243 val m = p findFirstMatchIn(doc)
247 if(m.get.group("before") != None){bef = m.get.group("before")}else{bef = ""}
248 if(m.get.group("following") != None){fol = m.get.group("following")}else{fol = ""}
249 return surroundByGeneralTAG(bef,regex,TAG) +
250 s"<${TAG}>" + m.get.group("inTAG") + s"</${TAG}>" +
251 surroundByGeneralTAG(fol,regex,TAG)
256 private def constructHEADER(doc:String):String = {
258 val titleTAG = "title"
259 var title = "NO TITLE"
261 val p = new Regex("""(?i)#{1,6}\s(.+?)\\,""")
262 val m = p findFirstMatchIn(doc)
263 if(m != None){title = m.get.group(1)}
265 s"<${headTAG}>\n<${titleTAG}>${title}</${titleTAG}>\n</${headTAG}>"
268 def toHTML(markdown:String):String = {
269 val docType = "<!DOCTYPE html>"
273 for(k <- Syntax keys){
274 md = Syntax(k)._2(md, k, Syntax(k)._1)
276 val header = constructHEADER(markdown)
277 s"${docType}\n${header}\n<${htmlTAG}>\n<${bodyTAG}>\n${md}\n</${bodyTAG}>\n</${htmlTAG}>"