OSDN Git Service

color pen implementing
[blackquill/BlackQuill.git] / src / main / scala / org / blackquill / engine / BQParser.scala
1 package org.blackquill.engine
2
3 // BlackQuill Copyright (C) 2013 set.minami<set.minami@gmail.com>
4 // License MIT see also LISENCE.txt
5 // BQParser.
6
7 import org.apache.commons.logging._
8 import scala.collection.immutable.List
9 import scala.collection.mutable.LinkedHashMap
10 import scala.collection.mutable.HashMap
11 import scala.collection.mutable.Set
12 import scala.collection.mutable.Stack
13 import scala.collection.mutable.ListMap
14 import scala.collection.SortedSet
15 import scala.util.matching.Regex
16 import scala.util.control.Breaks.{break,breakable}
17 import scala.xml._
18
19 import org.blackquill.engine._
20 import org.blackquill.io.FileIO
21
22
23 class BQParser {
24         private val log:Log = LogFactory.getLog(classOf[BQParser])
25
26         private var urlDefMap = new HashMap[String,Tuple5[String,String,String,String,String]]
27         private var footnoteMap = new LinkedHashMap[String,Tuple2[String,String]]
28         private var headerMap = List[Tuple4[Int,Int,String,String]]()
29
30         private val Syntax = LinkedHashMap(
31         //STRONG
32         """^(.*?)(([^(?:\\,)]+?\\,(:(.+?)\\,)+)+)(.*?)$$""" -> ("dl", wordDefinition _),
33         """(.*)\\,~{3,}(?:\{(.+?)\})?(\\,.+\\,)~{3,}\\,(.*)""" -> ("code", fencedCode _),
34         """^(.*?)(?:\[(.+?)\](?:\{(.+?)\})?\\,)?((\|.+?)+?\|)\\,((\|:?\-{3,}:?)+?\|)\\,(((\|.+?\|?)+?\\,)+?)\\,(.*?)$$"""
35         -> ("table",surroundTableTAG _),
36         "^(.*?)`(.*)" -> ("code",surroundByCodeTAG _),
37         "^(.*)<([\\w\\d\\.\\-\\_\\+]+?)@([\\w\\d\\.\\-\\_\\+]+?)>(.*)" -> ("a", autoMailLink _),
38         "^(.*)<((?:https?|ftp):\\/\\/[\\w\\d\\.\\/]+?)>(.*)$$" -> ("a",autoURLLink _),
39         """^(.*)\[\^(.+?)\](.*)$$""" -> ("sup",insertFootnote _),
40         """(.*)\*\[(.*?)\]\{(#?[\w\d]+?)?(/#?[\w\d]+?)?(\(\+?(.*?)?(\|\+?(.*?))?\))?(\[(.*?)\])?\}(.*)"""
41         -> ("span",colorPen _),
42         "^(.*)!\\[(.+?)\\]\\[(.*?)\\](?:\\{(.+?)\\})?(.*)$$" -> ("img",referenceExpander _),
43         "^(.*)\\[(.+?)\\]\\[(.*?)\\](?:\\{(.+?)\\})?(.*)$$" -> ("a",referenceExpander _),
44         "^(.*?)!\\[(.*?)\\]\\((.+?)\\x20*?(?:\"(.+?)\")?(?:\\x20+?(\\d+?%?)?x(\\d+?%?)?)?\\)(?:\\{(.+?)\\})?(.*)$$"
45         -> ("img", putImgTAG _),
46         "^(.*?)\\[(.*?)\\]\\((.+?)\\x20*?(?:\"(.+?)\")?\\)(?:\\{(.+?)\\})?(.*?)$$" -> ("a", surroundaHrefTAG _),
47         "^(.*?\\\\,)(((?:\\x20{4,}|\\t+)(.*?\\\\,))+)(.*?)$$" -> ("code",surroundByPreCodeTAG _),
48         "^(.*?\\\\,)((>.*(?:\\\\,))+?)(.*?)$$" -> ("blockquote",surroundByBlockquoteTAG _),
49         "^(.*?)(((?:\\x20{4,}|\\t+)\\d+?\\.\\x20.+?\\\\,)+)(.*?)$$" -> ("ol",surroundByListTAG _),
50         "^(.*?)(((?:\\x20{4,}|\\t+)(?:\\*|\\+|\\-)\\x20.+?\\\\,)+)(.*?)$$" -> ("ul",surroundByListTAG _),
51         "^(.*?)(#{1,6})\\x20(.+?)(\\x20#{1,6}?(?:\\{(.*?)\\}))?\\\\,(.*?)$$" -> ("h",surroundByHeadTAG _),
52         "^(.*\\\\,)(.*?)(?:\\{(.+?)\\})?\\\\,(\\-+|=+)\\x20*\\\\,(.*?)$$" -> ("h",surroundByHeadTAGUnderlineStyle _),
53         """^(.*?)(\{toc(:.+?)?\})(.*)$$""" -> ("ul",generateTOC _),
54         "^(.*\\\\,)((?:\\-|\\*){3,}|(?:(?:\\-|\\*)\\x20){3,})(.*?)$$" -> ("hr",putHrTAG _),
55         "^(.*?)\\*\\*(.+?)\\*\\*(.*?)$$" -> ("strong",surroundByGeneralTAG _),
56         "^(.*?)\\*(.+?)\\*(.*?)$$" -> ("em",surroundByGeneralTAG _)
57         //WEAK
58         )
59
60         private def colorPen(doc:String, regex:String, TAG:String):String = {
61                 lazy val fontSize = Map[Int,String](0 -> "medium", 1 -> "larger", 2 -> "large", 3 -> "x-large", 4 -> "xx-large",
62                                                                                                                   -1 -> "smaller", -2 -> "small", -3 -> "x-small", -4 -> "xx-small")
63                 lazy val fontWeight = Map(0 -> "normal", 1 -> "bolder", 2 -> "bold" ,-1 -> "light")
64                 if(doc == ""){return ""}
65                 val p = new Regex(regex, "before","content","fcolor","bcolor","fstyle","size","dummy","weight","face","ffamily","following")
66                 val m = p findFirstMatchIn(doc)
67
68                 if(m != None){
69                         val bef = m.get.group("before")
70                         val fol = m.get.group("following")
71
72                         if(Option(m.get.group("content")) != None){
73                                 val content = m.get.group("content")
74                                 val fgColor = if(Option(m.get.group("fcolor")) != None){" color:" + m.get.group("fcolor") + ";"}else{""}
75                                 val bgColor = if(Option(m.get.group("bcolor")) != None){" background-color:" + m.get.group("bcolor").tail + ";"}else{""}
76
77                                 val fSize = if(Option(m.get.group("size")) != None){
78                                   try{
79                                           log info m.get.group("size")
80                                           " font-size:" + fontSize(m.get.group("size").toInt) + ";"
81                                   }catch{ case e:Exception => log info e;" font-size:" + fontSize(0) + ";"}
82                                 }else{""}
83
84                                 val fWeight = if(Option(m.get.group("weight")) != None){
85                                   try{
86                                           " font-weight:" + fontWeight(m.get.group("weight").toInt)
87                                   }catch{ case e:Exception => log info e ;" font-weight:" + fontWeight(0) + ";"}
88                                 }else{
89                                   ""
90                                 }
91
92                                 val fFace = if(Option(m.get.group("ffamily")) != None){
93                                         " font-family:" + m.get.group("ffamily").split(",").mkString("'", "','", "'") + ";"
94                                         }else{""}
95
96                                 return colorPen(bef, regex, TAG) +
97                                                 s"""<span style="$fgColor$bgColor$fSize$fWeight$fFace">$content</span> """ +
98                                                 colorPen(fol, regex, TAG)
99                         }else{
100                                 return colorPen(bef, regex, TAG) + colorPen(fol, regex, TAG)
101                         }
102                 }
103                 doc
104         }
105
106         private def generateTOC(doc:String, regex:String, TAG:String):String = {
107                 log debug doc
108
109                 def _checkRange(start:Option[String],end:Option[String],default:Tuple2[Int,Int]):Tuple2[Int,Int] = {
110                         val s = if(start != None && start.get.toInt >= default._1){start.get.toInt}else{default._1}
111                         val e = if(end != None && end.get.toInt <= default._2){end.get.toInt}else{default._2}
112                         return Tuple2(s,e)
113                 }
114
115                 val text = ""
116                 val p = new Regex(regex, "before","toc","range","following")
117                 val m = p findFirstMatchIn(doc)
118                 var minmax = (1,6)
119                 if(m != None){
120                         val bef = m.get.group("before")
121                         val fol = m.get.group("following")
122                         val tocRange = m.get.group("toc")
123                         if(Option(tocRange) != None){
124                                 val p2 = s":h?([${minmax._1}-${minmax._2}])?\\-h?([${minmax._1}-${minmax._2}])?".r
125                                 val range = p2 findFirstMatchIn(tocRange)
126                                 if(range != None){
127                                   minmax = _checkRange(Option(range.get.group(1)),Option(range.get.group(2)),minmax)
128                                 }else{
129                                   log warn "SYNTAX ERROR:toc header setting"
130                                 }
131
132                         }
133
134                         val hList = makeHeaderMap(doc,minmax._1,minmax._2)
135                         for(e <- hList){
136                           val headSize = e._2
137                           val link = if(e._3 != None){e._3.get}else{s"""${e._2}:${e._4}:${e._1}""" }
138                           headerMap ::= (e._1,headSize,link.toString,e._4)
139                         }
140                         headerMap = headerMap.reverse
141
142                         var i = headerMap.head._2
143                         var toc = ""
144                         var ulNest = 0
145                         for(h <- headerMap){
146                           if(i < h._2){
147                                 toc += """<ul style="list-style:none" >\\,"""
148                                 ulNest += 1
149                           }else if(i > h._2){
150                             toc += """</ul>\\,""" * ulNest
151                             ulNest = 0
152                           }
153                           toc += s"""<li><a href="#${h._3}" ><h${h._2}>${h._4}</h${h._2}></a></li>\\,"""
154                           i = h._2
155                         }
156
157                         val table = """<header>\\,<ul style="list-style:none" id="toc"><nav>\\,""" + toc.mkString("") + "</nav></ul>\\,</header>"
158
159                         return putHeaderID(bef,minmax._1,minmax._2) + table + putHeaderID(fol,minmax._1,minmax._2)
160                 }
161
162                 doc
163         }
164         private def putHeaderID(doc:String,min:Int,max:Int):String ={
165                 if(doc == ""){return ""}
166                 var text = ""
167                 for((e,i) <- doc.split("""\\,""").zipWithIndex){
168                     val p = """<h(\d)\s*?>(.*?)</h\d>""".r
169                     val m = p findFirstMatchIn(e)
170
171                     log debug "###" + e
172                     if(m != None){
173                         val header = m.get.group(1).toInt
174                         if( header >= min && header <= max){
175                                 val id = for(h<-headerMap if h._1 == i)yield{h._3}
176                                 text += s"""<h${header} id="${id.head}">${m.get.group(2)}</h$header>\\,"""
177                         }else{
178                           text += e + """\\,"""
179                         }
180                     }else{text += e + """\\,"""}
181                 }
182                 text
183         }
184
185         private def makeHeaderMap(doc:String,minH:Integer,maxH:Integer):List[Tuple4[Integer,Integer,Option[String],String]] = {
186                 var headList = List[Tuple4[Integer,Integer,Option[String],String]]()
187                 for((line,no) <- doc.split("""\\,""").zipWithIndex){
188                         log info "line=" + line +" No=" + no
189                         val p = """.*?(<h(\d)(.*?)\s*?>(.*?)</h\d>).*""".r
190                         val m = p.findAllMatchIn(line)
191
192                         for(e <- m){
193                                 val p2 = """<h(\d)(?:\s*?id=\"(.*?)\")?\s*?>(.*?)</h\d""".r
194                                 val m2 = p2.findFirstMatchIn(e.group(1))
195                                 val test = m2.get.group(1).toInt
196                                 val id = m2.get.group(2)
197
198                                 if(test >= minH && test <= maxH){
199                                   headList ::= (no, test ,Option(id),m2.get.group(3))
200                                 }
201                         }
202                 }
203                 log info headList
204                 headList.reverse
205         }
206
207         private def wordDefinition(doc:String, regex:String, TAG:String):String = {
208             log info "***" + doc
209                 if(doc == ""){return ""}
210                 val p = new Regex(regex,"before","seq","word","defSeq","def","following")
211                 val m = p.findFirstMatchIn(doc)
212
213                 if(m != None){
214                         val bef = m.get.group("before")
215                         val fol = m.get.group("following")
216                         val p2 = """(((?:\\,)?.*?\\,)(:.+?\\,)+)+?""".r
217                         log info "seq ->" + m.get.group("seq")
218
219                         val m2 = p2.findAllMatchIn(m.get.group("seq"))
220
221                         var dd = ""
222                         for(e <- m2){
223                                 log debug "****" + e
224                                 val p3 = """(?:\\,)?([^\\,]*?)\\,((:(.+?)\\,)+)""".r
225                                 val m3 = p3.findAllMatchIn(e.group(1))
226                                 for(e2 <- m3){
227                                         log debug "###" + e2
228                                         dd += "<dt>" + e2.group(1) + "</dt>"
229                                         val p4 = """:(.+?)\\,""".r
230                                         val m4 = p4.findAllMatchIn(e2.group(2))
231                                         for(e3 <- m4){
232                                                 dd += "<dd>" + e3.group(1) + "</dd>\\,"
233                                         }
234                                 }
235                         }
236                         return wordDefinition(bef,regex,TAG) + "\\," +
237                                 s"""<$TAG>\\,$dd\\,</$TAG>""" +
238                                 wordDefinition(fol, regex,TAG)
239                 }
240                 doc
241         }
242         private def insertFootnote(doc:String, regex:String, TAG:String):String = {
243                 if(doc == ""){return ""}
244
245                 val p = new Regex(regex,"before","footnote","following")
246                 val m = p.findFirstMatchIn(doc)
247                 if(m != None){
248                         val bef = m.get.group("before")
249                         var fnote = m.get.group("footnote")
250                         val fol = m.get.group("following")
251                         if(footnoteMap.contains(fnote)){
252                                 val link = footnoteMap(fnote)._1
253                                 lazy val definition = footnoteMap(fnote)._2
254
255                                 return insertFootnote(bef,regex,TAG) + s"""<$TAG id=\"$fnote\"><a href=\"#$link\" style="text-decoration:none">[$fnote]</a></sup>""" +
256                                         insertFootnote(fol,regex,TAG)
257                         }else{
258                                 log warn "Found lack of Footnotes."
259                         }
260                 }
261                 doc
262         }
263
264         private def gatheringFootnotesDefinition(doc:String):String = {
265                 val p = new Regex("""^(.*)\[\^(.*?)\]:(.*?)\\,(.*)$$""","before","landMark","definition","following")
266                 val m = p findFirstMatchIn(doc)
267
268                 if(m != None){
269                         val bef = m.get.group("before")
270                         val fol = m.get.group("following")
271                         val lMark = m.get.group("landMark")
272                         val define = m.get.group("definition")
273
274                         if(!footnoteMap.contains(lMark)){
275                                 footnoteMap += (lMark->("footnote-"+lMark,define))
276                         }else{
277                                 log warn s"FOUND FOOTNOTE DUPES! @ $lMark: Second definitions was ignored."
278                         }
279                         return gatheringFootnotesDefinition(bef) + gatheringFootnotesDefinition(fol)
280                 }
281                 doc
282         }
283
284         private def insertFootnoteDefinitions(doc:LinkedHashMap[String,Tuple2[String,String]]):String = {
285                 lazy val head = """<footer><nav>\,<h2 id="footnotes">Footnotes</h2><hr />\,<ul style="list-style:none">\,"""
286                 val contents =
287                         for((key,t2) <- doc.toList.reverse)yield(s"""<li id="${t2._1}"><p><em>$key: </em>${t2._2}<a href="#$key">&laquo;</a></p>""")
288                 lazy val tail = "</ul></nav></footer>"
289                 if(contents.size > 0){
290                         return head + contents.mkString("\\,") + tail
291                 }else{""}
292         }
293
294         private def fencedCode(doc:String, regex:String, TAG:String):String = {
295                 val p = new Regex(regex, "before",  "SAttr", "inTAG", "following")
296                 val m = p.findFirstMatchIn(doc)
297
298                 if(m != None){
299                         val bef = m.get.group("before")
300                         val fol = m.get.group("following")
301                         val inCode = m.get.group("inTAG")
302                         var specialAttr = ""
303
304                         if(Option(m.get.group("SAttr")) != None){
305                                 specialAttr = decideClassOrStyle(doc,m.get.group("SAttr"))
306                         }
307
308                         return fencedCode(bef,regex,"code") +
309                                 s"<pre $specialAttr><$TAG>\\\\," + ltgtExpand(inCode) + s"</$TAG></pre>" +
310                                         fencedCode(fol,regex,TAG)
311                 }
312                 doc
313         }
314
315         private def surroundTableTAG(doc:String, regex:String, TAG:String):String = {
316                 def _normalize(text:String):String = {
317                   var retStr = text
318                   if(retStr.startsWith("|")){
319                     retStr = retStr.tail.toString
320                   }
321                   if(retStr.endsWith("|")){
322                     retStr = retStr.init.toString
323                   }
324                   return retStr
325                 }
326
327                 def _getAlign(alignList:List[String],i:Int):String = {
328                   if(i >= alignList.size){""}else{alignList(i)}
329                 }
330
331                 if(doc == ""){return ""}
332
333                 log debug "***" + doc
334                 val p = new Regex(regex, "before","caption","css","headSeq","head","separatorSeq","sep","bodySeq","body","b","following")
335                 val m = p findFirstMatchIn(doc)
336
337                 if(m != None){
338                         val bef = m.get.group("before")
339                         val fol = m.get.group("following")
340                         var head = m.get.group("headSeq")
341                         val sep = m.get.group("separatorSeq")
342                         val body = m.get.group("bodySeq")
343                         var cap = ""
344                         if(Option(m.get.group("caption")) != None){
345                                 cap = s"""<caption>${m.get.group("caption")}</caption>"""
346                         }
347                         var id = ""
348                         if(Option(m.get.group("css")) != None){
349                                 id = decideClassOrStyle(doc,m.get.group("css"))
350                         }
351
352                         if(Option(sep) != None){
353                                 val pSep = """((?:\|)?(:?-{3,}?:?)(?:\|)?)+?""".r
354                                 val mSep = pSep.findAllMatchIn(sep)
355
356                                 var tableList = List[List[String]]()
357                                 var tmpList = List[String]()
358                                 for(mS <- mSep){
359                                         val align = mS.group(2)
360                                         if(align.startsWith(":") && align.endsWith(":")){
361                                                 tmpList ::= """align="center" """
362                                         }else if(align.startsWith(":")){
363                                                 tmpList ::= """align="left" """
364                                         }else if(align.endsWith(":")){
365                                                 tmpList ::= """align="right" """
366                                         }else{
367                                           tmpList ::= ""
368                                         }
369                                 }
370                                 val alignList = tmpList.reverse
371                                 head = _normalize(head)
372                                 log debug head
373                                 val heads = for((h,i) <- head.split("\\|").zipWithIndex)yield(s"""<th ${_getAlign(alignList,i)}>$h</th>\\,""")
374                                 val headList = heads.toList
375                                 if(headList.size != alignList.size){
376                                         log error "Table header is wrong.:" + headList
377                                         exit(-1)
378                                 }
379
380
381                                 log debug headList
382                                 log debug alignList
383
384
385                                 val pTBody = """((((\|)?(.*?)(\|)?)+?)\\,?)+?""".r
386                                 val mTBSeq = pTBody.findAllMatchIn(body)
387                                 var bodyList = List[String]()
388                                 var folTmp = ""
389
390                                 tmpList = List.empty
391                                 for((mTBS,i) <- mTBSeq.zipWithIndex){
392                                         if(mTBS.group(2).contains("|")){
393                                                 val row = _normalize(mTBS.group(2)).split("\\|")
394                                                 val body =  for((c,j) <- row.zipWithIndex)yield(s"""<td ${_getAlign(alignList,j)}>$c</td>\\,""")
395                                                 bodyList ::= "<tr>\\\\," + body.mkString("") + "</tr>\\\\,"
396                                                 }else{
397                                                         folTmp += mTBS.group(2)
398                                                 }
399                                 }
400
401                                 bodyList = bodyList.reverse
402                                 log debug bodyList
403                                 return surroundTableTAG(bef, regex, TAG) +
404                                         s"\\\\,<table $id>\\\\,$cap\\\\,<thead>\\\\," + s"<tr>${headList.mkString("")}</tr></thead>\\\\," +
405                                         s"<tbody>${bodyList.mkString("")}</tbody></table>\\\\," +
406                                         surroundTableTAG(folTmp + fol, regex, TAG)
407
408                         }
409
410                 }
411                 doc
412         }
413
414         private def autoMailLink(doc:String, regex:String, TAG:String):String = {
415                 if(doc == ""){return ""}
416
417                 val p = new Regex(regex, "before","inTAG","domain","following")
418                 val m = p findFirstMatchIn(doc)
419
420                 if(m!= None){
421                         val bef = m.get.group("before")
422                         val fol = m.get.group("following")
423                         val mail = m.get.group("inTAG")
424                         val domain = m.get.group("domain")
425
426                         return autoMailLink(bef, regex, TAG) + "<address>" +
427                                 s"""<script type=\"text/javascript\">\\,document.write('<$TAG href=\\"mailto:$mail')\\,document.write(\"@\")\\,document.write(\"$domain\\">MailMe!</$TAG>\") </script>""" + "</address>" +
428                                 autoMailLink(fol, regex, TAG)
429                 }
430                 doc
431         }
432         private def autoURLLink(doc:String, regex:String, TAG:String):String = {
433                 if(doc == ""){return ""}
434
435                 val p = new Regex(regex, "before","inTAG","following")
436                 val m = p findFirstMatchIn(doc)
437
438                 if(m!= None){
439                         val bef = m.get.group("before")
440                         val fol = m.get.group("following")
441                         val url = m.get.group("inTAG")
442
443                         return autoURLLink(bef, regex, TAG) + s"""<$TAG href=\"$url\">$url</$TAG> """ +
444                                 autoURLLink(fol, regex, TAG)
445                 }
446                 doc
447         }
448         private def putImgTAG(doc:String, regex:String, TAG:String):String = {
449                 if(doc == ""){return ""}
450
451                 val p = new Regex(regex,"before","alt","url","title","resX","resY","css","following")
452                 val m = p findFirstMatchIn(doc)
453
454                 if(m != None){
455                         val bef = m.get.group("before")
456                         val fol = m.get.group("following")
457                         val alt = m.get.group("alt")
458                         val url = m.get.group("url")
459                         return putImgTAG(bef,regex,TAG) + s"""<$TAG src=\"$url\" alt=\"$alt\" ${getTitleName(m.get.group("title"))}""" +
460                                 s"""${getResolutionX(m.get.group("resX"))}${getResolutionY(m.get.group("resY"))}${decideClassOrStyle(doc,m.get.group("css"))}>""" +
461                                 putImgTAG(fol,regex,TAG)
462                 }
463                 doc
464         }
465
466         private def getResolutionX(resX:String):String = Option(resX) match{
467                 case None => return ""
468                 case Some(x) => return s" width=$resX "
469                 case _ =>
470                         log error "unknown parameter has found." + resX
471                         exit(-1)
472         }
473
474         private def getResolutionY(resY:String):String = Option(resY) match{
475                 case None => return ""
476                 case Some(y) => return s" height=$resY "
477                 case _ =>
478                         log error "unknown parameter has found." + resY
479                         exit(-1)
480         }
481         private def searchCSSClassName(doc:String,cssClass:String):Boolean = {
482                 val p = """(?i)<link.*?type=\"text\/css\".*?href=\"(.*?)\".*?>""".r
483                 val m = p findFirstMatchIn(doc)
484
485                 if(m != None){
486                         val fileName = m.get.group(1)
487                         val CSSHandler = FileIO
488                         val CSS = CSSHandler openCSSFile(fileName) mkString("")
489                         log debug CSS
490                         for(line <- CSS.split("""\/\*.*?\*\/""")){
491                                 log debug "***" + line
492                                 if(line.contains(cssClass + " ")){return true}
493                         }
494                 }
495                 false
496         }
497
498         private def surroundByCodeTAG(doc:String, regex:String, TAG:String):String = {
499             def _surroundByCodeTAG(doc:String,regex:String,innerSign:String,TAG:String):String = {
500                 if(doc.contains(innerSign)){
501                         val p = regex.r
502                         val m = p.findFirstMatchIn(doc)
503
504                         if(m != None){
505                                 val bef = m.get.group(1)
506                                 var fol = m.get.group(2)
507                                 var sign = "`"
508                                 log debug "=>" + fol.head + " " + fol
509                                 var follow = ""
510                                 if((fol.head.toString == sign)&&(sign != "``")){
511                                         sign = "``"
512                                         follow = fol.tail
513                                 }else{
514                                         follow = fol
515                                 }
516                                 log debug "**>" + follow
517                                 log debug "==>" + sign
518                                 val p2 = s"""^([^(?:\\,)]+?)$sign(.*)$$""".r
519                                 val m2 = p2.findFirstMatchIn(follow)
520
521                                 if(m2 != None){
522                                         log debug ">>>>" + m2.get.group(1)
523                                         return _surroundByCodeTAG(bef, regex, "`", TAG) + s"<$TAG>" + ltgtExpand(m2.get.group(1)) + s"</$TAG>" +
524                                                 _surroundByCodeTAG(m2.get.group(2), regex, "`", TAG)
525                                 }else{
526                                         if(fol.startsWith("```")){
527                                                 return _surroundByCodeTAG(bef, regex, "`", TAG) + s"<$TAG></$TAG>" +
528                                                         _surroundByCodeTAG(follow.drop(2), regex, "`", TAG)
529                                         }else if(fol.startsWith("`")){
530                                                 return _surroundByCodeTAG(bef, regex, "`", TAG) + s"<$TAG></$TAG>" +
531                                                         _surroundByCodeTAG(follow.drop(0), regex, "`", TAG)
532                                         }else{
533                                                         log warn s"$sign CodeBlock is wrong."
534                                                 return ""
535                                         }
536                                 }
537                         }else{return doc}
538                 }else{return doc}
539             }
540     _surroundByCodeTAG(doc,regex,"`",TAG)
541         }
542
543         private def referenceExpander(doc:String, regex:String, TAG:String):String = {
544                 val m = regex.r.findFirstMatchIn(doc)
545                 def expandAttribute(value:String):String = value match{
546                         case "" => return ""
547                         case _ => return value
548                 }
549
550                 if(m != None){
551                         var key = ""
552                         if(m.get.group(3) != ""){
553                           key = m.get.group(3)
554                         }else{
555                           key = m.get.group(2).toLowerCase()
556                         }
557
558                         if(urlDefMap.contains(key)){
559                                 val tup5 = urlDefMap(key)
560                                 var css = ""
561                                 if(m.get.group(4) == null){
562                                         css = expandAttribute(tup5._5)
563                                 }else{
564                                         css = decideClassOrStyle(doc,m.get.group(4))
565                                 }
566                                 TAG match {
567                                         case "a" =>
568                                                 return referenceExpander(m.get.group(1), regex, TAG) +
569                                                 s"""<$TAG href=\"${tup5._1}\" ${expandAttribute(tup5._2)}$css>""" +
570                                                 m.get.group(2) + s"""</$TAG>""" + referenceExpander(m.get.group(5), regex, TAG)
571                                         case "img" =>
572                                                 return referenceExpander(m.get.group(1), regex, TAG) +
573                                                 s"""<$TAG src=\"${tup5._1}\" alt=\"${m.get.group(2)}\" ${expandAttribute(tup5._2)}${expandAttribute(tup5._3)}${expandAttribute(tup5._4)} $css>""" +
574                                                 referenceExpander(m.get.group(5), regex, TAG)
575                                         case _ =>
576                                                 log error "Unknown Expand TAG from Reference"
577                                                 exit(-1)
578                                 }
579                         }else{
580                           log warn "Link definition was not found : " + key
581                           doc
582                         }
583                 }else{
584                   doc
585                 }
586
587         }
588
589         private def urlDefinitions(doc:String):String = {
590                 def _urlDefinitions(text:String):String = {
591                     var bef = ""
592                     var fol = ""
593                         if(text == ""){return text}
594
595                     log debug "doc ==>" + text
596                     val p = new Regex("""^(.*?)?(((\[([\w\d\.\_\+\-\:\/)]+?)\]:([\w\d\.\_\+\-\:\/]+?)(?:\s+\"(.+?)\")?(?:\s+(\d+%?x\d+%?))?(?:\s+\{(.+?)\})?)\s*\\,)+?)(?:\\,|\z)(.*)?$$""",
597                                 "before","seq","elem1","elem2","landMark","link","Title","Res","Css", "following")
598                     val m = p findFirstMatchIn(text)
599
600                         if(m != None){
601                                 if(m.get.group("before") != None){bef = m.get.group("before")}
602                                 if(m.get.group("following") != None){fol = m.get.group("following")}
603
604                                 log debug "bef=>" + bef
605                                 log debug "seq=>" + m.get.group("seq")
606                                 log debug "fol=>" + fol
607                                 if(m.get.group("seq") != None){
608                                         val seq = m.get.group("seq")
609                                         val mat =
610                                         """\[([\w\d\.\_\+\-\:\/]+?)\]:([\w\d\.\_\+\-\:\/]+)(\s+\"(.+?)\")?(?:\s+(\d+%?x\d+%?))?(?:\s+\{(.+?)\})?(?:\s*\\,)?""".r.findAllMatchIn(seq)
611                                         for(e <- mat){
612                                                 val link = e.group(2)
613                                                 log debug ">>" + link
614                                                 val landMark = e.group(1)
615                                                 log debug ">>>" + landMark
616                                                 var title = ""
617                                                 if(e.group(4) != null){title = s"""title=\"${e.group(4)}\" """}
618                                                 var resX = ""
619                                                 var resY = ""
620                                                 if(Option(e.group(5)) != None){
621                                                         val matResolution = """(\d+%?)x(\d+%?)""".r.findFirstMatchIn(e.group(5))
622                                                         resX = getResolutionX(matResolution.get.group(1))
623                                                         resY = getResolutionY(matResolution.get.group(2))
624                                                 }
625                                                 var css = ""
626                                                 if(e.group(6) != null){
627                                                         css = decideClassOrStyle(text,e.group(6))
628                                                 }
629                                                 urlDefMap += (landMark.toLowerCase->(link,title,resX,resY,css))
630                                         }
631                                 }
632                                 _urlDefinitions(bef) + _urlDefinitions(fol)
633                         }else{
634                           log debug "m was None!"
635                           text}
636
637                 }
638                 _urlDefinitions(doc)
639         }
640
641         private def decideClassOrStyle(doc:String,className:String):String = {
642                 if(className == "" || className == null){
643                         return ""
644                 }
645
646                 if(!searchCSSClassName(doc,className)){
647                         if(className.contains(":")){
648                                 return "style=\"" + className + "\""
649                         }else if(className.startsWith("#")){
650                                 return "id=\"" + className + "\""
651                         }
652                         log warn s"$className not found..."
653                         return "class=\"" + className +"\""
654                 }else{
655                         return "class=\"" + className +"\""
656                 }
657         }
658
659         private def getTitleName(title:String):String = {
660                 if(title == ""| title == null){
661                         return ""
662                 }
663
664                 return s"""title=\"$title\" """
665         }
666
667         private def surroundaHrefTAG(doc:String,regex:String,TAG:String):String = {
668                 val p = new Regex(regex,"before","inTag","link","title","css","following")
669                 val m = p findFirstMatchIn(doc)
670
671                 var bef = ""
672                 var fol = ""
673                 if(m != None){
674                         if(m.get.group("before") != None){
675                           bef = m.get.group("before")
676                         }
677                         if(m.get.group("following") != None){fol = m.get.group("following")}
678
679                         val link = m.get.group("link")
680                         val label = m.get.group("inTag")
681                         return surroundaHrefTAG(bef, regex, TAG) +
682                                         s"""<$TAG href=\"$link\" ${getTitleName(m.get.group("title"))}""" +
683                                         s"""${decideClassOrStyle(doc,m.get.group("css"))}>$label</$TAG>""" +
684                                         surroundaHrefTAG(fol,regex,TAG)
685                 }
686                 doc
687         }
688
689         private def putHrTAG(doc:String, regex:String, TAG:String):String = {
690                 val p = new Regex(regex,"before","line","following")
691                 val m = p findFirstMatchIn(doc)
692
693                 log debug doc
694
695                 var bef = ""
696                 var fol = ""
697                 if(m != None){
698                         if(m.get.group("before") != None){
699                         bef = m.get.group("before")
700                         }
701                         if(m.get.group("following") != None){fol = m.get.group("following")}
702
703                         return putHrTAG(bef,regex,TAG) + s"<$TAG />" + putHrTAG(fol,regex,TAG)
704                 }
705           doc
706         }
707
708         private def surroundByPreCodeTAG(doc:String, regex:String, TAG:String):String = {
709                 val p = new Regex(regex, "before","seq","inTAG","midInTag","following")
710                 val m = p findFirstMatchIn(doc)
711                 log debug "[" + doc + "]"
712
713                 var bef = ""
714                 var fol = ""
715                 var contentStr = ""
716                 if(m != None){
717                   if(m.get.group("before") != None){
718                     bef = m.get.group("before")
719                   }
720                   if(m.get.group("following") != None){fol = m.get.group("following")}
721
722                   log debug "^^^" + m.get.group("seq")
723                   val mat = """((?:\x20{4,}|\t+)(.*?\\,))+?""".r.findAllMatchIn(m.get.group("seq"))
724                   for(elem <- mat){
725                     contentStr += elem.group(2)
726                     log debug "***" + contentStr
727                   }
728
729                   log debug contentStr
730                   return surroundByPreCodeTAG(bef,regex,TAG) +
731                                   s"<pre><$TAG>" +
732                                   ltgtExpand(contentStr) +
733                                   s"</$TAG></pre>\\," +
734                                   surroundByPreCodeTAG(fol, regex, TAG)
735                 }
736
737           doc
738         }
739
740         private def ltgtExpand(doc:String):String = {
741                 return doc.replaceAll("&","&amp").replaceAll("<","&gt;").replaceAll(">","&gt;")
742         }
743
744         private def surroundByBlockquoteTAG(doc:String, regex:String, TAG:String):String = {
745           val p = new Regex(regex, "before","inTAG","midInTag","following")
746           val m = p findFirstMatchIn(doc)
747           log debug "[" + doc + "]"
748
749           var bef = ""
750           var fol = ""
751           var contentStr = ""
752           if(m != None){
753             var mid = m.get.group("inTAG")
754             log debug "***-->" + mid
755
756             if(m.get.group("before") != None){
757               bef = m.get.group("before")
758               if(bef.startsWith(">")){
759                 mid = bef + mid
760                 bef = ""
761               }
762             }else{bef = ""}
763                 if(m.get.group("following") != None){fol = m.get.group("following")}else{fol = ""}
764
765                 log debug "=>" + mid
766                 var following = ""
767                 if(mid != null){
768                   val mat = """(.+?\\,)+?""".r.findAllMatchIn(mid)
769
770                   var inCurrentBQ = true
771                   for(mt <- mat){
772                     if(!mt.group(1).startsWith(">")||mt.group(1) == "\\\\,"){
773                         inCurrentBQ = false
774                     }
775                     if(inCurrentBQ){
776                       val m = """.*?<br />\\,$$""".r.findFirstMatchIn(mt.group(1))
777                       var break = "\\\\,"
778                       if(m == None){break = "<br />\\\\,"}
779                       contentStr += mt.group(1).tail.replaceAll("\\\\,", break)
780                     }else{
781                       log debug "(" + mt.group(1) + ")"
782                       following += mt.group(1)
783                     }
784                     log debug "^^^" + mt
785                   }
786                 }
787
788                 following += fol
789                 log debug "bef=" + bef + " mid=" + contentStr + " fol="  + following
790                 log debug "-->" + contentStr
791                 return surroundByBlockquoteTAG(bef, regex, TAG) +
792                                 s"<$TAG>\\," + surroundByBlockquoteTAG(contentStr, regex, TAG) + s"</$TAG>\\," +
793                                 surroundByBlockquoteTAG(following, regex, TAG)
794           }
795           doc
796         }
797
798         private def surroundByListTAG(doc:String, regex:String, TAG:String):String = {
799                 val p = new Regex(regex, "before","elements","element","following")
800                 val m = p findFirstMatchIn(doc)
801                 var s = ""
802                 var bef = ""
803                 var fol = ""
804
805                 if(m != None){
806                         if(m.get.group("before") != None){bef = m.get.group("before")}else{bef = ""}
807                         if(m.get.group("following") != None){fol = m.get.group("following")}else{fol = ""}
808                         s = m.get.group("elements")
809                 }else{
810                   return doc
811                 }
812
813
814                 var sign = ""
815                 var sp = ""
816                 val indentWidth = 4
817                 var styles:((Int) => String) = null
818
819                 TAG match{
820               case "ul"=>
821                 sp = TAG
822                 styles = (index:Int) => {
823                           (index/indentWidth)%3 match{
824                                 case 1 => "disc"
825                                 case 2 => "circle"
826                                 case 0 => "square"
827                                 case _ => "---"
828                           }
829                 }
830                 sign = "[\\*|\\+|\\-]"
831               case "ol"=>
832                 sp = TAG
833                 styles = (index:Int) => {
834                           (index/indentWidth)%4 match{
835                             case 1 => "decimal"
836                             case 2 => "decimal-leading-zero"
837                             case 3 => "upper-latin"
838                             case 0 => "lower-latin"
839                             case _ => "---"
840                           }
841                 }
842                 sign = "\\d+?\\."
843             }
844
845                 log debug ":::" + s
846                 var docList = List[String]()
847                 for(elem <- s"""(\\x20+?$sign\\x20.+?\\\\,)+?""".r.findAllMatchIn(s)){
848                         docList = elem.group(1)::docList
849                 }
850
851
852
853                 def _surroundByListTAG(doc:List[String],TAG:String,indent:Int):TreeNode[String] = {
854                         var tree = new TreeNode[String]("")
855                     if(doc.isEmpty){return tree}
856
857                         log debug "====>" + doc
858                         tree.add(new TreeNode("<" + sp + s""" style=\"list-style-type:${styles(indent)}\">"""))
859                         var i = indent
860                         var list = List.empty[Tuple3[String,Int,String]]
861                         for(elem <- doc){
862                           val m = s"""((\\x20+?)$sign\\x20(.+?)\\\\,)""".r.findFirstMatchIn(elem)
863                           list = (m.get.group(1),m.get.group(2).size,m.get.group(3))::list
864                         }
865
866                         var restStr = List[String]()
867                         if(list.isEmpty){return new TreeNode("")
868                         }else{for(e <- list.reverse.tail){restStr = e._1::restStr}}
869
870                         restStr = restStr.reverse
871                         for(elem <- list.reverse){
872                                 if(elem._2 > i){
873                                         tree.add(new TreeNode("<" + sp + s""" style=\"list-style-type:${styles(elem._2)}\">"""))
874                                 }else if(elem._2 < i){
875                                         tree.add(new TreeNode[String](s"</$sp>"*((i - elem._2)/indentWidth)))
876                                 }
877                                 tree.add(new TreeNode[String](s"<$TAG>" + elem._3 + s"</$TAG>\\,"))
878
879
880                                 if(restStr.isEmpty){
881                                         restStr = List[String]("")
882                                 }else{
883                                         restStr = restStr.tail
884                                 }
885                                 i = elem._2
886                         }
887                 tree.add(new TreeNode(s"</$sp>"*((i - indent)/indentWidth + 1)))
888                 return tree
889           }
890
891                 log debug "->" + docList
892                 val r1 = s"""(\\x20*)${sign}.*?\\\\,""".r
893                 val wS1 = r1.findFirstMatchIn(s)
894                 var str = ""
895                 val r2 = s"""(\\x20*)${sign}.*(\\\\,<br />.*?</blockquote>\\\\,)""".r
896                 val wS2 = r2.findFirstMatchIn(s)
897
898                 var wS:Option[Regex.Match] = null
899                 if(wS2 != None){wS = wS2}else if(wS1 != None){wS = wS1}
900                 if(wS != None){
901                         for(e <- _surroundByListTAG(docList.reverse,"li",wS.get.group(1).size)){
902                           str += e.toString()
903                         }
904                   if(wS == wS2){str += wS.get.group(2)}
905
906                   log debug "!---->" + str
907                   surroundByListTAG(bef,regex,TAG) + str + surroundByListTAG(fol,regex,TAG)
908                 }else{doc}
909         }
910
911         private def surroundByHeadTAGUnderlineStyle(doc:String, regex:String, TAG:String):String = {
912           if(doc == ""){return doc}
913
914           log debug "-->" + doc
915           val p = new Regex(regex, "before","inTAG","id","style","following")
916           val m = p findFirstMatchIn(doc)
917           var bef = ""
918           var fol = ""
919           var contentStr = ""
920           var headSign = TAG
921           var id = ""
922
923           if(m != None){
924             if(m.get.group("before") != None){bef = m.get.group("before")}else{bef = ""}
925             if(m.get.group("following") != None){fol = m.get.group("following")}else{fol = ""}
926             contentStr = m.get.group("inTAG")
927
928             if(m.get.group("style").contains("-")){
929               headSign += "2"
930             }else{
931               headSign += "1"
932             }
933
934             if(Option(m.get.group("id")) != None){
935                 id = s"""id="${m.get.group("id")}" """
936             }
937
938             return surroundByHeadTAGUnderlineStyle(bef, regex, TAG) +
939                           s"<$headSign $id>$contentStr</$headSign>\\," + surroundByHeadTAGUnderlineStyle(fol, regex, TAG)
940           }
941           doc
942         }
943
944         private def surroundByHeadTAG(doc:String, regex:String, TAG:String):String = {
945                 if(doc == ""){return doc}
946
947                 log debug "--> " + doc
948                 val p = new Regex(regex, "before","startHead","inTAG","endHead","id","following")
949                 val m = p findFirstMatchIn(doc)
950                 var contentStr = ""
951
952                 if(m != None){
953               var bef = ""
954               var fol = ""
955               var id = ""
956
957                   if(m.get.group("before") != None){bef = m.get.group("before")}else{bef = ""}
958               if(m.get.group("following") != None){fol = m.get.group("following")}else{fol = ""}
959               contentStr = m.get.group("inTAG")
960
961                   val headSize = m.get.group("startHead").size
962                   val endHead = m.get.group("endHead")
963                   log debug "-->" + endHead
964                   val headTAG = TAG + headSize
965                   if(Option(m.get.group("id")) != None){val hashCode = m.get.group("id");id = s"""id="$hashCode" """}
966                   if(Option(endHead) != None){
967                     val m2 = """^\x20(#+?)$$""".r("wantSize").findFirstMatchIn(endHead)
968                     if(m2 != None){
969                       val size = m2.get.group("wantSize")
970                       if(size.size != headSize){
971                         contentStr += " " + size
972                         log warn "Curious header expression was found. " +
973                         s" BlackQuill represents this <$headTAG>$contentStr</$headTAG> ."
974                       }
975                     }
976                   }
977
978                   return surroundByHeadTAG(bef,regex,TAG) +
979                                   s"<${headTAG} $id>$contentStr</${headTAG}>\\," +
980                                   surroundByHeadTAG(fol,regex,TAG)
981                 }
982           doc
983         }
984
985         private def surroundByGeneralTAG(doc:String, regex:String, TAG:String):String = {
986           if(doc == ""||Option(doc) == None){return ""}
987           log info doc
988           val p = new Regex(regex,"before","inTAG","following")
989           val m = p findFirstMatchIn(doc)
990           if(m != None){
991               var bef = ""
992               var fol = ""
993                   if(m.get.group("before") != None){bef = m.get.group("before")}else{bef = ""}
994               if(m.get.group("following") != None){fol = m.get.group("following")}else{fol = ""}
995                   return surroundByGeneralTAG(bef,regex,TAG) +
996                                   s"<${TAG}>" + m.get.group("inTAG") + s"</${TAG}>" +
997                           surroundByGeneralTAG(fol,regex,TAG)
998           }
999           doc
1000         }
1001
1002         private def constructHEADER(doc:String):String = {
1003           val headTAG = "head"
1004           val titleTAG = "title"
1005           var title = "NO TITLE"
1006
1007           val p = new Regex("""(?i)#{1,6}\x20(.+?)\\,""")
1008           val m = p findFirstMatchIn(doc)
1009           if(m != None){title = m.get.group(1)}
1010
1011           s"<${headTAG}>\n<${titleTAG}>${title}</${titleTAG}>\n</${headTAG}>"
1012         }
1013
1014         def preProcessors(doc:String) :String = {
1015          var text = urlDefinitions(doc)
1016          text = gatheringFootnotesDefinition(text)
1017          text
1018         }
1019
1020         def toHTML(markdown:String):String = {
1021                 val docType = "<!DOCTYPE html>"
1022                 val bodyTAG = "body"
1023                 val htmlTAG = "html"
1024                 var md = preProcessors(markdown + "\\,")
1025
1026                 for(k <- Syntax keys){
1027                  md = Syntax(k)._2(md, k, Syntax(k)._1)
1028                 }
1029
1030                 md = backslashEscape(md)
1031                 md = paragraphize(md)
1032                 log info urlDefMap
1033                 log info footnoteMap
1034                 md += insertFootnoteDefinitions(footnoteMap)
1035                 val header = constructHEADER(markdown)
1036                 s"${docType}\n${header}\n<${htmlTAG}>\n<${bodyTAG}>\n${md.replaceAll("\\\\,","\n")}\n</${bodyTAG}>\n</${htmlTAG}>"
1037         }
1038
1039         private def paragraphize(doc:String):String = {
1040                 val delimiter = """\,"""
1041                 def f(text:String):String = {
1042                         text + delimiter
1043                 }
1044                 val BlockElements = new HTMLMap().BLOCKTags
1045                 var isBlock = 0
1046                 var isOneLineBlock = false
1047                 var text = ""
1048                 var pg = ""
1049
1050                 for(l <- doc.split("\\" + delimiter)){
1051                         isOneLineBlock = false
1052                         log debug l
1053                         breakable{
1054                                 for(e <- BlockElements){
1055                                   log debug e
1056                                         if(l.contains("<" + e) &&  l.contains("</" + e + ">")){
1057                                                 isOneLineBlock = true;break;
1058                                         }else if(l.contains("<" + e)){
1059                                                 isBlock += 1;break;
1060                                         }else if(l.contains("</" + e + ">")){
1061                                                 isBlock -= 1;isOneLineBlock = true;break;
1062                                         }
1063                                 }
1064                         }
1065                         log debug ">>>>" + l + "::" + isBlock + "|" + isOneLineBlock
1066                         if(isBlock > 0 | isOneLineBlock){
1067                                 text += l + delimiter
1068                         }else{
1069                                 if(l != "" && isBlock <= 0){
1070                                         text += "<p>" + l + "</p>" + delimiter
1071                                 }
1072                                 isBlock = 0
1073                         }
1074                 }
1075                 text
1076                 //var text = "<p>" + doc.replaceAll("\\\\,\\\\,","</p>\\\\,\\\\,<p>") + "</p>"
1077                 //text.replaceAll("<p></p>","")
1078         }
1079
1080         private def backslashEscape(doc:String):String = {
1081                 val escapeCharSet = Set("\\","`","*","_","{","}","[","]","(",")","#","+","-","!",":","|")
1082                 var bef = ""
1083                 for(e <- doc){
1084                         if(bef.size > 2 && escapeCharSet.contains(e.toString) && bef.reverse.head.toString == "\\"){
1085                                 bef = bef.init + e
1086                         }else{
1087                                 bef += e
1088                         }
1089                 }
1090                 return bef
1091         }
1092
1093 }