Permalink
Browse files

preparing release

  • Loading branch information...
0 parents commit 75e77f6a93b4b1ff7ca2f9650960d2d68731297e @naltatis naltatis committed Apr 17, 2012
Showing with 8,039 additions and 0 deletions.
  1. +7 −0 LICENSE
  2. +245 −0 README.md
  3. +140 −0 pom.xml
  4. +52 −0 src/main/java/de/neuland/jade4j/Jade4J.java
  5. +138 −0 src/main/java/de/neuland/jade4j/JadeConfiguration.java
  6. +40 −0 src/main/java/de/neuland/jade4j/compiler/Compiler.java
  7. +12 −0 src/main/java/de/neuland/jade4j/compiler/CompilerErrorException.java
  8. +57 −0 src/main/java/de/neuland/jade4j/compiler/IndentWriter.java
  9. +88 −0 src/main/java/de/neuland/jade4j/compiler/Utils.java
  10. +17 −0 src/main/java/de/neuland/jade4j/exceptions/ExpressionException.java
  11. +21 −0 src/main/java/de/neuland/jade4j/exceptions/JadeCompilerException.java
  12. +37 −0 src/main/java/de/neuland/jade4j/exceptions/JadeException.java
  13. +19 −0 src/main/java/de/neuland/jade4j/exceptions/JadeLexerException.java
  14. +28 −0 src/main/java/de/neuland/jade4j/exceptions/JadeParserException.java
  15. +12 −0 src/main/java/de/neuland/jade4j/filter/CDATAFilter.java
  16. +29 −0 src/main/java/de/neuland/jade4j/filter/CachingFilter.java
  17. +8 −0 src/main/java/de/neuland/jade4j/filter/Filter.java
  18. +16 −0 src/main/java/de/neuland/jade4j/filter/MarkdownFilter.java
  19. +12 −0 src/main/java/de/neuland/jade4j/filter/PlainFilter.java
  20. +11 −0 src/main/java/de/neuland/jade4j/lexer/Assignment.java
  21. +201 −0 src/main/java/de/neuland/jade4j/lexer/AttributeLexer.java
  22. +29 −0 src/main/java/de/neuland/jade4j/lexer/Each.java
  23. +656 −0 src/main/java/de/neuland/jade4j/lexer/Lexer.java
  24. +102 −0 src/main/java/de/neuland/jade4j/lexer/Scanner.java
  25. +38 −0 src/main/java/de/neuland/jade4j/lexer/token/Attribute.java
  26. +9 −0 src/main/java/de/neuland/jade4j/lexer/token/Block.java
  27. +9 −0 src/main/java/de/neuland/jade4j/lexer/token/BufferedComment.java
  28. +10 −0 src/main/java/de/neuland/jade4j/lexer/token/CaseToken.java
  29. +10 −0 src/main/java/de/neuland/jade4j/lexer/token/Colon.java
  30. +14 −0 src/main/java/de/neuland/jade4j/lexer/token/Comment.java
  31. +39 −0 src/main/java/de/neuland/jade4j/lexer/token/Conditional.java
  32. +9 −0 src/main/java/de/neuland/jade4j/lexer/token/CssClass.java
  33. +9 −0 src/main/java/de/neuland/jade4j/lexer/token/CssId.java
  34. +10 −0 src/main/java/de/neuland/jade4j/lexer/token/Default.java
  35. +9 −0 src/main/java/de/neuland/jade4j/lexer/token/Deferred.java
  36. +9 −0 src/main/java/de/neuland/jade4j/lexer/token/Doctype.java
  37. +24 −0 src/main/java/de/neuland/jade4j/lexer/token/Doctypes.java
  38. +10 −0 src/main/java/de/neuland/jade4j/lexer/token/Dot.java
  39. +10 −0 src/main/java/de/neuland/jade4j/lexer/token/Eos.java
  40. +20 −0 src/main/java/de/neuland/jade4j/lexer/token/Expression.java
  41. +10 −0 src/main/java/de/neuland/jade4j/lexer/token/ExtendsToken.java
  42. +9 −0 src/main/java/de/neuland/jade4j/lexer/token/Filter.java
  43. +9 −0 src/main/java/de/neuland/jade4j/lexer/token/ForTag.java
  44. +10 −0 src/main/java/de/neuland/jade4j/lexer/token/Include.java
  45. +9 −0 src/main/java/de/neuland/jade4j/lexer/token/Indent.java
  46. +20 −0 src/main/java/de/neuland/jade4j/lexer/token/Mixin.java
  47. +10 −0 src/main/java/de/neuland/jade4j/lexer/token/Newline.java
  48. +10 −0 src/main/java/de/neuland/jade4j/lexer/token/Outdent.java
  49. +10 −0 src/main/java/de/neuland/jade4j/lexer/token/Tag.java
  50. +10 −0 src/main/java/de/neuland/jade4j/lexer/token/Text.java
  51. +71 −0 src/main/java/de/neuland/jade4j/lexer/token/Token.java
  52. +9 −0 src/main/java/de/neuland/jade4j/lexer/token/UnbufferedComment.java
  53. +10 −0 src/main/java/de/neuland/jade4j/lexer/token/When.java
  54. +7 −0 src/main/java/de/neuland/jade4j/lexer/token/While.java
  55. +9 −0 src/main/java/de/neuland/jade4j/lexer/token/Yield.java
  56. +168 −0 src/main/java/de/neuland/jade4j/model/JadeModel.java
  57. +57 −0 src/main/java/de/neuland/jade4j/ognl/OGNLRootProxyFactory.java
  58. +27 −0 src/main/java/de/neuland/jade4j/parser/BlockCommentNode.java
  59. +32 −0 src/main/java/de/neuland/jade4j/parser/CommentNode.java
  60. +15 −0 src/main/java/de/neuland/jade4j/parser/FileNameBuilder.java
  61. +685 −0 src/main/java/de/neuland/jade4j/parser/Parser.java
  62. +57 −0 src/main/java/de/neuland/jade4j/parser/expression/ExpressionHandler.java
  63. +27 −0 src/main/java/de/neuland/jade4j/parser/node/AssigmentNode.java
  64. +65 −0 src/main/java/de/neuland/jade4j/parser/node/BlockNode.java
  65. +24 −0 src/main/java/de/neuland/jade4j/parser/node/CaseConditionNode.java
  66. +38 −0 src/main/java/de/neuland/jade4j/parser/node/CaseNode.java
  67. +69 −0 src/main/java/de/neuland/jade4j/parser/node/ConditionalNode.java
  68. +16 −0 src/main/java/de/neuland/jade4j/parser/node/CssClassNode.java
  69. +16 −0 src/main/java/de/neuland/jade4j/parser/node/CssIdNode.java
  70. +33 −0 src/main/java/de/neuland/jade4j/parser/node/DoctypeNode.java
  71. +90 −0 src/main/java/de/neuland/jade4j/parser/node/EachNode.java
  72. +41 −0 src/main/java/de/neuland/jade4j/parser/node/ErrorNode.java
  73. +46 −0 src/main/java/de/neuland/jade4j/parser/node/ExpressionNode.java
  74. +26 −0 src/main/java/de/neuland/jade4j/parser/node/ExpressionString.java
  75. +51 −0 src/main/java/de/neuland/jade4j/parser/node/FilterNode.java
  76. +16 −0 src/main/java/de/neuland/jade4j/parser/node/IndentNode.java
  77. +15 −0 src/main/java/de/neuland/jade4j/parser/node/LiteralNode.java
  78. +68 −0 src/main/java/de/neuland/jade4j/parser/node/MixinNode.java
  79. +87 −0 src/main/java/de/neuland/jade4j/parser/node/Node.java
  80. +207 −0 src/main/java/de/neuland/jade4j/parser/node/TagNode.java
  81. +53 −0 src/main/java/de/neuland/jade4j/parser/node/TextNode.java
  82. +24 −0 src/main/java/de/neuland/jade4j/parser/node/WhileNode.java
  83. +16 −0 src/main/java/de/neuland/jade4j/parser/node/YieldNode.java
  84. +39 −0 src/main/java/de/neuland/jade4j/template/FileTemplateLoader.java
  85. +64 −0 src/main/java/de/neuland/jade4j/template/JadeTemplate.java
  86. +10 −0 src/main/java/de/neuland/jade4j/template/TemplateLoader.java
  87. +161 −0 src/main/resources/log4j.dtd
  88. +26 −0 src/main/resources/log4j.xml
  89. +65 −0 src/test/java/de/neuland/jade4j/TestFileHelper.java
  90. +89 −0 src/test/java/de/neuland/jade4j/compiler/CompilerIndentationErrorTest.java
  91. +88 −0 src/test/java/de/neuland/jade4j/compiler/CompilerTagErrorTest.java
  92. +304 −0 src/test/java/de/neuland/jade4j/compiler/CompilerTest.java
  93. +68 −0 src/test/java/de/neuland/jade4j/compiler/OriginalJadeTest.java
  94. +13 −0 src/test/java/de/neuland/jade4j/helper/beans/Level2TestBean.java
  95. +15 −0 src/test/java/de/neuland/jade4j/helper/beans/TestBean.java
  96. +69 −0 src/test/java/de/neuland/jade4j/lexer/JadeLexerTest.java
  97. +24 −0 src/test/java/de/neuland/jade4j/lexer/LexerTest.java
  98. +16 −0 src/test/java/de/neuland/jade4j/lexer/WorkTest.java
  99. +65 −0 src/test/java/de/neuland/jade4j/lexer/token/AttributeTest.java
  100. +71 −0 src/test/java/de/neuland/jade4j/lexer/token/CommentTest.java
  101. +69 −0 src/test/java/de/neuland/jade4j/lexer/token/CssClassTest.java
  102. +27 −0 src/test/java/de/neuland/jade4j/lexer/token/DoctypeTest.java
  103. +22 −0 src/test/java/de/neuland/jade4j/lexer/token/DotTextTest.java
  104. +13 −0 src/test/java/de/neuland/jade4j/lexer/token/ForTagTest.java
  105. +54 −0 src/test/java/de/neuland/jade4j/lexer/token/IdTest.java
  106. +102 −0 src/test/java/de/neuland/jade4j/lexer/token/IndentTest.java
  107. +44 −0 src/test/java/de/neuland/jade4j/lexer/token/OutdentTest.java
  108. +31 −0 src/test/java/de/neuland/jade4j/lexer/token/PipelessTextTest.java
  109. +24 −0 src/test/java/de/neuland/jade4j/lexer/token/TagTest.java
  110. +50 −0 src/test/java/de/neuland/jade4j/lexer/token/TextTest.java
  111. +44 −0 src/test/java/de/neuland/jade4j/lexer/token/TokenTest.java
  112. +33 −0 src/test/java/de/neuland/jade4j/model/JadeModelTest.java
  113. +181 −0 src/test/java/de/neuland/jade4j/ognl/OGNLExpressionTest.java
  114. +32 −0 src/test/java/de/neuland/jade4j/parser/AssignmentParserTest.java
  115. +133 −0 src/test/java/de/neuland/jade4j/parser/ComplexIndentOutdentParserTest.java
  116. +61 −0 src/test/java/de/neuland/jade4j/parser/CssClassAndIdParserTest.java
  117. +27 −0 src/test/java/de/neuland/jade4j/parser/DoctypeParserTest.java
  118. +17 −0 src/test/java/de/neuland/jade4j/parser/FileNameBuilderTest.java
  119. +52 −0 src/test/java/de/neuland/jade4j/parser/IncludeParserTest.java
  120. +55 −0 src/test/java/de/neuland/jade4j/parser/JadeParserTest.java
  121. +49 −0 src/test/java/de/neuland/jade4j/parser/LargeBodyTextParserTest.java
  122. +58 −0 src/test/java/de/neuland/jade4j/parser/LargeBodyTextWithoutPipesParserTest.java
  123. +28 −0 src/test/java/de/neuland/jade4j/parser/ParserTest.java
  124. +67 −0 src/test/java/de/neuland/jade4j/parser/TagsWithAttributesParserTest.java
  125. +57 −0 src/test/java/de/neuland/jade4j/parser/TextParserTest.java
  126. +79 −0 src/test/java/de/neuland/jade4j/parser/node/TagNodeTest.java
  127. +80 −0 src/test/java/de/neuland/jade4j/template/JadeConfigurationTest.java
  128. +52 −0 src/test/java/de/neuland/jade4j/template/JadeRunFullTemplateTest.java
  129. +1 −0 src/test/resources/compiler/assignment.html
  130. +2 −0 src/test/resources/compiler/assignment.jade
  131. +1 −0 src/test/resources/compiler/attribute.html
  132. +7 −0 src/test/resources/compiler/attribute.jade
  133. +1 −0 src/test/resources/compiler/attribute.json
  134. +1 −0 src/test/resources/compiler/bean_property_condition.html
  135. +14 −0 src/test/resources/compiler/bean_property_condition.jade
  136. +1 −0 src/test/resources/compiler/block_expansion.html
  137. +1 −0 src/test/resources/compiler/block_expansion.jade
  138. +1 −0 src/test/resources/compiler/block_expansion_shorthands.html
  139. +2 −0 src/test/resources/compiler/block_expansion_shorthands.jade
  140. +1 −0 src/test/resources/compiler/case.html
  141. +13 −0 src/test/resources/compiler/case.jade
  142. +1 −0 src/test/resources/compiler/comment.html
  143. +6 −0 src/test/resources/compiler/comment.jade
  144. +1 −0 src/test/resources/compiler/complex_condition.html
  145. +20 −0 src/test/resources/compiler/complex_condition.jade
  146. +4 −0 src/test/resources/compiler/complex_condition.json
  147. +1 −0 src/test/resources/compiler/complex_indent_outdent_file.html
  148. +37 −0 src/test/resources/compiler/complex_indent_outdent_file.jade
  149. +1 −0 src/test/resources/compiler/condition.html
  150. +5 −0 src/test/resources/compiler/condition.jade
  151. +1 −0 src/test/resources/compiler/condition.json
  152. +1 −0 src/test/resources/compiler/conditional_comment.html
  153. +3 −0 src/test/resources/compiler/conditional_comment.jade
  154. +1 −0 src/test/resources/compiler/css_class_and_id.html
  155. +7 −0 src/test/resources/compiler/css_class_and_id.jade
  156. +1 −0 src/test/resources/compiler/doctype.html
  157. +1 −0 src/test/resources/compiler/doctype.jade
  158. +1 −0 src/test/resources/compiler/each.html
  159. +5 −0 src/test/resources/compiler/each.jade
  160. +4 −0 src/test/resources/compiler/each.json
  161. +1 −0 src/test/resources/compiler/errors/indentation_errors.html
  162. +3 −0 src/test/resources/compiler/errors/indentation_errors.jade
  163. +1 −0 src/test/resources/compiler/errors/tags_with_errors.html
  164. +1 −0 src/test/resources/compiler/errors/tags_with_errors.jade
  165. +1 −0 src/test/resources/compiler/extends.html
  166. +18 −0 src/test/resources/compiler/extends.jade
  167. +1 −0 src/test/resources/compiler/filter_markdown.html
  168. +3 −0 src/test/resources/compiler/filter_markdown.jade
  169. +1 −0 src/test/resources/compiler/filter_plain.html
  170. +3 −0 src/test/resources/compiler/filter_plain.jade
  171. +1 −0 src/test/resources/compiler/fuzzy_boolean_condition.html
  172. +20 −0 src/test/resources/compiler/fuzzy_boolean_condition.jade
  173. +11 −0 src/test/resources/compiler/fuzzy_boolean_condition.json
  174. +2 −0 src/test/resources/compiler/include_1.html
  175. +3 −0 src/test/resources/compiler/include_1.jade
  176. +1 −0 src/test/resources/compiler/include_2.html
  177. +1 −0 src/test/resources/compiler/include_2.jade
  178. +1 −0 src/test/resources/compiler/includes/c.jade
  179. +1 −0 src/test/resources/compiler/includes/d.jade
  180. +4 −0 src/test/resources/compiler/includes/partial1.jade
  181. +5 −0 src/test/resources/compiler/includes/sub/a.jade
  182. +1 −0 src/test/resources/compiler/includes/sub/b.jade
  183. +1 −0 src/test/resources/compiler/includes/sub/e.jade
  184. +1 −0 src/test/resources/compiler/interpolation.html
  185. +7 −0 src/test/resources/compiler/interpolation.jade
  186. +4 −0 src/test/resources/compiler/interpolation.json
  187. +1 −0 src/test/resources/compiler/large_body_text_with_pipes.html
  188. +3 −0 src/test/resources/compiler/large_body_text_with_pipes.jade
  189. +4 −0 src/test/resources/compiler/large_body_text_without_pipes.html
  190. +10 −0 src/test/resources/compiler/large_body_text_without_pipes.jade
  191. +24 −0 src/test/resources/compiler/layout.jade
  192. +1 −0 src/test/resources/compiler/mixin.html
  193. +9 −0 src/test/resources/compiler/mixin.jade
  194. +1 −0 src/test/resources/compiler/nested_tags.html
  195. +3 −0 src/test/resources/compiler/nested_tags.jade
  196. +1 −0 src/test/resources/compiler/not_terse_doctype.html
  197. +2 −0 src/test/resources/compiler/not_terse_doctype.jade
  198. +1 −0 src/test/resources/compiler/one_tag.html
  199. +1 −0 src/test/resources/compiler/one_tag.jade
  200. +4 −0 src/test/resources/compiler/prettyprint.html
  201. +3 −0 src/test/resources/compiler/prettyprint.jade
  202. +1 −0 src/test/resources/compiler/scope.html
  203. +4 −0 src/test/resources/compiler/scope.jade
  204. +4 −0 src/test/resources/compiler/script_tag.html
  205. +5 −0 src/test/resources/compiler/script_tag.jade
  206. +1 −0 src/test/resources/compiler/tags_with_attributes.html
  207. +7 −0 src/test/resources/compiler/tags_with_attributes.jade
  208. +1 −0 src/test/resources/compiler/tags_with_text.html
  209. +6 −0 src/test/resources/compiler/tags_with_text.jade
  210. +1 −0 src/test/resources/compiler/terse_doctype.html
  211. +2 −0 src/test/resources/compiler/terse_doctype.jade
  212. +1 −0 src/test/resources/compiler/variable.html
  213. +2 −0 src/test/resources/compiler/variable.jade
  214. +1 −0 src/test/resources/compiler/variable.json
  215. +1 −0 src/test/resources/compiler/while.html
  216. +5 −0 src/test/resources/compiler/while.jade
  217. +2 −0 src/test/resources/compiler/xml_doctype.jade
  218. +1 −0 src/test/resources/compiler/xml_doctype.xml
  219. +2 −0 src/test/resources/grammarTest/doctype/doctype_1.jade
  220. +2 −0 src/test/resources/grammarTest/doctype/doctype_error_1.jade
  221. +66 −0 src/test/resources/grammarTest/integration/integration_01.jade
  222. +3 −0 src/test/resources/grammarTest/text/text_1.jade
  223. +3 −0 src/test/resources/grammarTest/text/text_error_1.jade
  224. +1 −0 src/test/resources/lexer/attribute_1.jade
  225. +16 −0 src/test/resources/lexer/attribute_2.jade
  226. +8 −0 src/test/resources/lexer/buffered_block_comment.jade
  227. +8 −0 src/test/resources/lexer/buffered_comment.jade
  228. +1 −0 src/test/resources/lexer/doctype.jade
  229. 0 src/test/resources/lexer/empty_file.jade
  230. +3 −0 src/test/resources/lexer/for_each.jade
  231. +8 −0 src/test/resources/lexer/indent_1.jade
  232. +16 −0 src/test/resources/lexer/indent_2.jade
  233. +8 −0 src/test/resources/lexer/indent_error_1.jade
  234. +8 −0 src/test/resources/lexer/indent_error_2.jade
  235. +3 −0 src/test/resources/lexer/large_body_text_without_pipes.jade
  236. +8 −0 src/test/resources/lexer/outdent1.jade
  237. +3 −0 src/test/resources/lexer/pipeless_text.jade
  238. +3 −0 src/test/resources/lexer/tag.jade
  239. +17 −0 src/test/resources/lexer/tag_with_css_class.jade
  240. +17 −0 src/test/resources/lexer/tag_with_id.jade
  241. +2 −0 src/test/resources/lexer/test_file1.jade
  242. +3 −0 src/test/resources/lexer/test_file2.jade
  243. +17 −0 src/test/resources/lexer/text.jade
  244. +8 −0 src/test/resources/lexer/unbuffered_comment.jade
  245. +1 −0 src/test/resources/originalTests/attrs.html
  246. +1 −0 src/test/resources/originalTests/attrs.interpolation.html
  247. +2 −0 src/test/resources/originalTests/attrs.interpolation.jade
  248. +8 −0 src/test/resources/originalTests/attrs.jade
  249. +1 −0 src/test/resources/originalTests/attrs.js.html
  250. +3 −0 src/test/resources/originalTests/attrs.js.jade
  251. +6 −0 src/test/resources/originalTests/auxiliary/dialog.jade
  252. +3 −0 src/test/resources/originalTests/auxiliary/escapes.html
  253. +6 −0 src/test/resources/originalTests/auxiliary/layout.jade
  254. +3 −0 src/test/resources/originalTests/auxiliary/mixins.jade
  255. +3 −0 src/test/resources/originalTests/auxiliary/pet.jade
  256. +1 −0 src/test/resources/originalTests/auxiliary/smile.html
  257. +4 −0 src/test/resources/originalTests/auxiliary/window.jade
  258. +1 −0 src/test/resources/originalTests/basic.html
  259. +3 −0 src/test/resources/originalTests/basic.jade
  260. +1 −0 src/test/resources/originalTests/blanks.html
  261. +8 −0 src/test/resources/originalTests/blanks.jade
  262. +1 −0 src/test/resources/originalTests/block-expansion.html
  263. +5 −0 src/test/resources/originalTests/block-expansion.jade
  264. +1 −0 src/test/resources/originalTests/block-expansion.shorthands.html
  265. +2 −0 src/test/resources/originalTests/block-expansion.shorthands.jade
  266. +1 −0 src/test/resources/originalTests/case-blocks.html
  267. +10 −0 src/test/resources/originalTests/case-blocks.jade
  268. +1 −0 src/test/resources/originalTests/case.html
  269. +7 −0 src/test/resources/originalTests/case.jade
  270. +1 −0 src/test/resources/originalTests/classes-empty.html
  271. +3 −0 src/test/resources/originalTests/classes-empty.jade
  272. +1 −0 src/test/resources/originalTests/classes.html
  273. +9 −0 src/test/resources/originalTests/classes.jade
  274. +1 −0 src/test/resources/originalTests/code.conditionals.html
  275. +26 −0 src/test/resources/originalTests/code.conditionals.jade
  276. +1 −0 src/test/resources/originalTests/code.escape.html
  277. +2 −0 src/test/resources/originalTests/code.escape.jade
  278. +1 −0 src/test/resources/originalTests/code.html
  279. +1 −0 src/test/resources/originalTests/code.iteration.html
  280. +26 −0 src/test/resources/originalTests/code.iteration.jade
  281. +10 −0 src/test/resources/originalTests/code.jade
  282. +1 −0 src/test/resources/originalTests/comments.conditional.html
  283. +3 −0 src/test/resources/originalTests/comments.conditional.jade
  284. +1 −0 src/test/resources/originalTests/comments.html
  285. +12 −0 src/test/resources/originalTests/comments.jade
  286. +1 −0 src/test/resources/originalTests/doctype.custom.html
  287. +1 −0 src/test/resources/originalTests/doctype.custom.jade
  288. +1 −0 src/test/resources/originalTests/doctype.default.html
  289. +4 −0 src/test/resources/originalTests/doctype.default.jade
  290. +1 −0 src/test/resources/originalTests/doctype.keyword.html
  291. +1 −0 src/test/resources/originalTests/doctype.keyword.jade
  292. +2 −0 src/test/resources/originalTests/escape-chars.html
  293. +2 −0 src/test/resources/originalTests/escape-chars.jade
  294. +3 −0 src/test/resources/originalTests/filters.cdata.html
  295. +7 −0 src/test/resources/originalTests/filters.cdata.jade
  296. +1 −0 src/test/resources/originalTests/filters.coffeescript.html
  297. +7 −0 src/test/resources/originalTests/filters.coffeescript.jade
  298. +1 −0 src/test/resources/originalTests/filters.less.html
  299. +7 −0 src/test/resources/originalTests/filters.less.jade
  300. +1 −0 src/test/resources/originalTests/filters.markdown.html
Sorry, we could not display the entire diff because too many files (381) changed.
7 LICENSE
@@ -0,0 +1,7 @@
+Copyright (C) 2011-2012 neuland Büro für Informatik, Bremen, Germany
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
245 README.md
@@ -0,0 +1,245 @@
+# jade4j - a jade implementation written in Java
+jade4j's intention is to be able to process jade templates in Java without the need of a JavaScript environment, while being **fully compatible** with the original jade syntax.
+
+## Example
+
+index.jade
+
+```
+!!! 5
+html
+ head
+ title= pageName
+ body
+ ol#books
+ for book in books
+ if book.available
+ li #{book.name} for #{book.price} €
+```
+
+Java model
+
+```java
+List<Book> books = new ArrayList<Book>();
+books.add(new Book("The Hitchhiker's Guide to the Galaxy", 5.70, true));
+books.add(new Book("Life, the Universe and Everything", 5,60, false));
+books.add(new Book("The Restaurant at the End of the Universe", 5.40, true));
+
+Map<String, Object> model = new HashMap<String, Object>();
+model.put("books", books);
+model.put("pageName", "My Bookshelf")
+```
+
+running the above code through `String html = Jade4J.render("./index.jade", model)` will result in the following output:
+
+```html
+<!DOCTYPE html>
+<html>
+ <head>
+ <title>My Bookshelf</title>
+ </head>
+ <body>
+ <ol id="books">
+ <li>The Hitchhiker's Guide to the Galaxy for 5,70 €</li>
+ <li>The Restaurant at the End of the Universe for 5,40 €</li>
+ </ol>
+ </body>
+</html>
+```
+
+## Syntax
+
+see the original [visionmedia/jade documentation](https://github.com/visionmedia/jade#a6).
+
+## Usage
+
+clone this repository
+
+```bash
+git clone https://github.com/neuland/jade4j.git
+```
+
+build it using `maven`
+
+```bash
+cd jade4j
+mvn install
+```
+
+add the dependency to your `pom.xml`
+
+```xml
+<dependency>
+ <groupId>de.neuland</groupId>
+ <artifactId>jade4j</artifactId>
+ <version>0.2.0</version>
+</dependency>
+```
+
+or directly use the `jade4j-0.2.0.jar` located in your target directory.
+
+## Simple static API
+
+Parsing template and and generating template in one step.
+
+```java
+String html = Jade4J.render("./index.jade", model);
+```
+
+If you use this in production you would probalby do the template parsing only once per template and call the render method with different models.
+
+```java
+JadeTemplate template = Jade4J.getTemplate("./index.jade");
+String html = Jade4J.render(template, model);
+```
+
+Streaming output using a `java.io.Writer`
+
+```java
+Jade4J.render(template, model, writer);
+```
+
+## Flexible and convenient API
+
+If you need more control you can instanciate a `JadeConfiguration` object.
+
+```java
+JadeConfiguration config = new JadeConfiguration();
+
+JadeTemplate template = config.getTemplate("index");
+
+Map<String, Object> model = new HashMap<String, Object>();
+model.put("company", "neuland");
+
+config.renderTemplate(template, model);
+```
+
+### caching
+
+The `JadeConfiguration` handles template caching for you. If you request the same unmodified template twice you'll get the same instance and avoid unnecesarry parsing.
+
+```java
+JadeTemplate t1 = config.getTemplate("index.jade");
+JadeTemplate t2 = config.getTemplate("index.jade");
+t1.equals(t2) // true
+```
+
+### output formatting
+
+By default Jade4J produces compressed HTML without unneeded whitespace. You can change this behaviour by enabling PrettyPrint.
+
+```java
+config.setPrettyPrint(true);
+```
+
+### HTML vs. XHTML vs. XML
+
+Jade detects if it has to generate HTML or XML code by your specified [doctype](https://github.com/visionmedia/jade#a6-11).
+
+If you are rendering partial templates that don't include a doctype you can set the generation mode manually.
+
+ input(checked=true)
+
+**HTML5** (default)
+
+ config.setTerse(true);
+ config.setXML(false);
+ // <input checked>
+
+**XHTML**
+
+ config.setTerse(false);
+ config.setXML(false);
+ // <input checked="true" />
+
+**XML**
+
+ config.setTerse(false);
+ config.setXML(true);
+ // <input checked="true"></input>
+
+### adding filters
+
+Filters allow embedding content like `markdown` or `coffeescript` into your jade template.
+
+ script
+ :coffeescript
+ sayHello -> alert "hello world"
+
+will generate
+
+ <script>
+ sayHello(function() {
+ return alert("hello world");
+ });
+ </script>
+
+jade4j comes with a `plain` and `cdata` filter. `plain` takes your input to pass it directly through, `cdata` wraps your content in `<![CDATA[...]]>`. You can add your custom filters to your configuration.
+
+ config.setFilter("coffeescript", new CoffeeScriptFilter());
+
+To implement your own filter you have to implement the `Filter` Interface. If your filter doesn't use any data from the model you can inherit from the abstract `CachingFilter` and also get caching for free. See the [neuland/jade4j-coffeescript-filter](https://github.com/neuland/jade4j-coffeescript-filter) project as an example.
+
+### define model defaults
+
+If you are using multiple templates, you might have the need for a set of default objects that are available in all templates.
+
+```java
+Map<String, Object> defaults = new HashMap<String, Object>();
+defaults.put("city", "Bremen");
+defaults.put("country", "Germany");
+defaults.put("url", new MyUrlHelper());
+config.setSharedVariables(defaults);
+```
+
+### bring your own template loader
+
+By default jade4j searches for template files in your work directory. By specifying your own `FileTemplateLoader` you can alter that behaviour. You can also implement the `TemplateLoader` interface to create your own.
+
+```java
+TemplateLoader loader = new FileTemplateLoader("/templates/", "UTF-8");
+config.setTemplateLoader(loader);
+```
+
+## Differences
+
+The original jade implementation uses JavaScript for expression handling in `if`, `unless`, `for`, `case` commands, like this
+
+ if book.price < 5.50 && book.available
+ p.sale special offer
+
+We decided to use OGNL instead of a JavaScript interpreter like Rhino. OGNL's syntax and handling of `falsy` values is very similar to JavaScript. There are although some differences when it comes to `array` and `object/map` creation in the template:
+
+in original jade:
+
+ names = ["artur", "stefan"]
+ book = {name: "My Diary", pages: 10}
+
+in jade4j:
+
+ names = {"artur", "stefan"}
+ book = #{"name": "My Diary", "pages": "10"}
+
+However, in most cases you would not create new objects in templates anyway.
+
+## Framework Integrations
+
+If you want to use jade4j with Spring check out our [neuland/spring-jade4j](https://github.com/neuland/spring-jade4j) project.
+
+## Authors
+
+- Artur Tomas / [atomiccoder](https://github.com/atomiccoder)
+- Stefan Kuper / [planetk](https://github.com/planetk)
+- Michael Geers / [naltatis](https://github.com/naltatis)
+
+## License
+
+(The MIT License)
+
+Copyright (C) 2011-2012 [neuland Büro für Informatik](http://www.neuland-bfi.de/), Bremen, Germany
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
140 pom.xml
@@ -0,0 +1,140 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <groupId>de.neuland</groupId>
+ <artifactId>jade4j</artifactId>
+ <version>0.2.0</version>
+ <name>jade templating engine for Java VM</name>
+ <issueManagement>
+ <url>https://github.com/neuland/jade4j/issues</url>
+ <system>GitHub Issues</system>
+ </issueManagement>
+
+ <licenses>
+ <license>
+ <name>MIT License</name>
+ <url>http://www.opensource.org/licenses/mit-license.php</url>
+ <distribution>repo</distribution>
+ </license>
+ </licenses>
+
+ <scm>
+ <url>https://github.com/neuland/jade4j</url>
+ <connection>scm:git:git://github.com/neuland/jade4j.git</connection>
+ <developerConnection>scm:git:git@github.com:neuland/jade4j.git</developerConnection>
+ </scm>
+
+ <developers>
+ <developer>
+ <name>Artur Tomas</name>
+ <url>https://github.com/atomiccoder</url>
+ <id>atomiccoder</id>
+ </developer>
+ <developer>
+ <name>Stefan Kuper</name>
+ <url>https://github.com/planetk</url>
+ <id>planetk</id>
+ </developer>
+ <developer>
+ <name>Michael Geers</name>
+ <url>https://github.com/naltatis</url>
+ <id>naltatis</id>
+ </developer>
+ </developers>
+ <properties>
+ <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
+ </properties>
+ <build>
+ <plugins>
+ <plugin>
+ <artifactId>maven-compiler-plugin</artifactId>
+ <version>2.3.2</version>
+ <configuration>
+ <source>1.6</source>
+ <target>1.6</target>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-surefire-plugin</artifactId>
+ <version>2.12</version>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-source-plugin</artifactId>
+ <executions>
+ <execution>
+ <goals>
+ <goal>jar</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+ <dependencies>
+ <dependency>
+ <groupId>ognl</groupId>
+ <artifactId>ognl</artifactId>
+ <version>3.0.3</version>
+ </dependency>
+
+ <dependency>
+ <groupId>org.springframework</groupId>
+ <artifactId>spring-expression</artifactId>
+ <version>3.0.5.RELEASE</version>
+ </dependency>
+ <dependency>
+ <groupId>commons-collections</groupId>
+ <artifactId>commons-collections</artifactId>
+ <version>3.2.1</version>
+ </dependency>
+ <dependency>
+ <groupId>commons-io</groupId>
+ <artifactId>commons-io</artifactId>
+ <version>2.1</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.commons</groupId>
+ <artifactId>commons-lang3</artifactId>
+ <version>3.1</version>
+ </dependency>
+ <dependency>
+ <groupId>com.google.code.gson</groupId>
+ <artifactId>gson</artifactId>
+ <version>2.1</version>
+ </dependency>
+
+ <!-- Other dependencies -->
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-api</artifactId>
+ <version>1.6.0</version>
+ </dependency>
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-log4j12</artifactId>
+ <version>1.6.0</version>
+ </dependency>
+
+ <!-- Testing -->
+ <dependency>
+ <groupId>org.hamcrest</groupId>
+ <artifactId>hamcrest-all</artifactId>
+ <version>1.1</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <version>4.10</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.pegdown</groupId>
+ <artifactId>pegdown</artifactId>
+ <version>1.1.0</version>
+ </dependency>
+ </dependencies>
+</project>
52 src/main/java/de/neuland/jade4j/Jade4J.java
@@ -0,0 +1,52 @@
+package de.neuland.jade4j;
+
+import java.io.IOException;
+import java.io.StringWriter;
+import java.io.Writer;
+import java.util.Map;
+
+import de.neuland.jade4j.exceptions.JadeCompilerException;
+import de.neuland.jade4j.model.JadeModel;
+import de.neuland.jade4j.parser.Parser;
+import de.neuland.jade4j.parser.node.Node;
+import de.neuland.jade4j.template.FileTemplateLoader;
+import de.neuland.jade4j.template.JadeTemplate;
+import de.neuland.jade4j.template.TemplateLoader;
+
+public class Jade4J {
+
+ public static String render(String filename, Map<String, Object> model) throws IOException, JadeCompilerException {
+ return templateToString(getTemplate(filename), model);
+ }
+
+ public static void render(String filename, Map<String, Object> model, Writer writer) throws IOException, JadeCompilerException {
+ getTemplate(filename).process(new JadeModel(model), writer);
+ }
+
+ public static String render(JadeTemplate template, Map<String, Object> model) throws JadeCompilerException {
+ return templateToString(template, model);
+ }
+
+ public static void render(JadeTemplate template, Map<String, Object> model, Writer writer) throws JadeCompilerException {
+ template.process(new JadeModel(model), writer);
+ }
+
+ public static JadeTemplate getTemplate(String filename) throws IOException {
+ TemplateLoader loader = new FileTemplateLoader("", "UTF-8");
+
+ Parser parser = new Parser(filename, loader);
+ Node root = parser.parse();
+ JadeTemplate template = new JadeTemplate();
+ template.setRootNode(root);
+ return template;
+ }
+
+ private static String templateToString(JadeTemplate template, Map<String, Object> model) throws JadeCompilerException {
+ JadeModel jadeModel = new JadeModel(model);
+ StringWriter writer = new StringWriter();
+
+ template.process(jadeModel, writer);
+ return writer.toString();
+ }
+
+}
138 src/main/java/de/neuland/jade4j/JadeConfiguration.java
@@ -0,0 +1,138 @@
+package de.neuland.jade4j;
+
+import java.io.IOException;
+import java.io.StringWriter;
+import java.io.Writer;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import de.neuland.jade4j.exceptions.JadeCompilerException;
+import de.neuland.jade4j.filter.CDATAFilter;
+import de.neuland.jade4j.filter.Filter;
+import de.neuland.jade4j.filter.PlainFilter;
+import de.neuland.jade4j.model.JadeModel;
+import de.neuland.jade4j.parser.Parser;
+import de.neuland.jade4j.parser.node.Node;
+import de.neuland.jade4j.template.FileTemplateLoader;
+import de.neuland.jade4j.template.JadeTemplate;
+import de.neuland.jade4j.template.TemplateLoader;
+
+public class JadeConfiguration {
+
+ @SuppressWarnings("unused")
+ private static Logger logger = LoggerFactory.getLogger(JadeConfiguration.class);
+
+ private boolean prettyPrint = false;
+ private boolean terse = true;
+ private boolean xml = false;
+
+ private Map<String, Filter> filters = new HashMap<String, Filter>();
+ private Map<String, Object> sharedVariables = new HashMap<String, Object>();
+ private TemplateLoader templateLoader = new FileTemplateLoader("", "UTF-8");
+ protected static final int MAX_ENTRIES = 1000;
+
+ public JadeConfiguration() {
+ setFilter("plain", new PlainFilter());
+ setFilter("cdata", new CDATAFilter());
+ }
+
+ private Map<String, JadeTemplate> cache = new LinkedHashMap<String, JadeTemplate>(MAX_ENTRIES + 1, .75F, true) {
+ private static final long serialVersionUID = -2234660416692828706L;
+
+ public boolean removeEldestEntry(Map.Entry<String, JadeTemplate> eldest) {
+ return size() > MAX_ENTRIES;
+ }
+ };
+
+ public JadeTemplate getTemplate(String name) throws IOException {
+
+ long lastModified = templateLoader.getLastModified(name);
+
+ String key = name + "-" + lastModified;
+ if (!cache.containsKey(key)) {
+ cache.put(key, createTemplate(name, lastModified));
+ }
+ return cache.get(key);
+ }
+
+ public void renderTemplate(JadeTemplate template, Map<String, Object> model, Writer writer) throws JadeCompilerException {
+ JadeModel jadeModel = new JadeModel(sharedVariables);
+ for (String filterName : filters.keySet()) {
+ jadeModel.addFilter(filterName, filters.get(filterName));
+ }
+ jadeModel.putAll(model);
+ template.process(jadeModel, writer);
+ }
+
+ public String renderTemplate(JadeTemplate template, Map<String, Object> model) {
+ StringWriter writer = new StringWriter();
+ renderTemplate(template, model, writer);
+ return writer.toString();
+ }
+
+ private JadeTemplate createTemplate(String name, long lastModified) throws IOException {
+ JadeTemplate template = new JadeTemplate();
+ template.setLastmodified(lastModified);
+
+ Parser parser = new Parser(name, templateLoader);
+ Node root = parser.parse();
+ template.setRootNode(root);
+ template.setPrettyPrint(prettyPrint);
+ template.setTerse(terse);
+ template.setXml(xml);
+ return template;
+ }
+
+ public boolean isPrettyPrint() {
+ return prettyPrint;
+ }
+
+ public void setPrettyPrint(boolean prettyPrint) {
+ this.prettyPrint = prettyPrint;
+ }
+
+ public void setFilter(String name, Filter filter) {
+ filters.put(name, filter);
+ }
+
+ public void removeFilter(String name) {
+ filters.remove(name);
+ }
+
+ public Map<String, Object> getSharedVariables() {
+ return sharedVariables;
+ }
+
+ public void setSharedVariables(Map<String, Object> sharedVariables) {
+ this.sharedVariables = sharedVariables;
+ }
+
+ public TemplateLoader getTemplateLoader() {
+ return templateLoader;
+ }
+
+ public void setTemplateLoader(TemplateLoader templateLoader) {
+ this.templateLoader = templateLoader;
+ }
+
+ public boolean isTerse() {
+ return terse;
+ }
+
+ public void setTerse(boolean terse) {
+ this.terse = terse;
+ }
+
+ public boolean isXml() {
+ return xml;
+ }
+
+ public void setXml(boolean xml) {
+ this.xml = xml;
+ }
+
+}
40 src/main/java/de/neuland/jade4j/compiler/Compiler.java
@@ -0,0 +1,40 @@
+package de.neuland.jade4j.compiler;
+
+import java.io.StringWriter;
+import java.io.Writer;
+
+import de.neuland.jade4j.exceptions.JadeCompilerException;
+import de.neuland.jade4j.model.JadeModel;
+import de.neuland.jade4j.parser.node.Node;
+import de.neuland.jade4j.template.JadeTemplate;
+
+public class Compiler {
+
+ private final Node rootNode;
+ private boolean prettyPrint;
+ private JadeTemplate template = new JadeTemplate();
+
+ public Compiler(Node rootNode) {
+ this.rootNode = rootNode;
+ }
+
+ public String compileToString(JadeModel model) throws JadeCompilerException {
+ StringWriter writer = new StringWriter();
+ compile(model, writer);
+ return writer.toString();
+ }
+
+ public void compile(JadeModel model, Writer w) throws JadeCompilerException {
+ IndentWriter writer = new IndentWriter(w);
+ writer.setUseIndent(prettyPrint);
+ rootNode.execute(writer, model, template);
+ }
+
+ public void setPrettyPrint(boolean prettyPrint) {
+ this.prettyPrint = prettyPrint;
+ }
+
+ public void setTemplate(JadeTemplate jadeTemplate) {
+ this.template = jadeTemplate;
+ }
+}
12 src/main/java/de/neuland/jade4j/compiler/CompilerErrorException.java
@@ -0,0 +1,12 @@
+package de.neuland.jade4j.compiler;
+
+import de.neuland.jade4j.parser.node.ErrorNode;
+
+public class CompilerErrorException extends Exception {
+
+ private static final long serialVersionUID = -4649766458564259961L;
+
+ public CompilerErrorException(ErrorNode errorNode) {
+ }
+
+}
57 src/main/java/de/neuland/jade4j/compiler/IndentWriter.java
@@ -0,0 +1,57 @@
+package de.neuland.jade4j.compiler;
+
+import java.io.IOException;
+import java.io.Writer;
+
+import org.apache.commons.lang3.StringUtils;
+
+public class IndentWriter {
+ private int indent = 0;
+ private boolean useIndent = false;
+ private boolean empty = true;
+ private Writer writer;
+
+ public IndentWriter(Writer writer) {
+ this.writer = writer;
+ }
+
+ public IndentWriter add(String string) {
+ return append(string);
+ }
+
+ public IndentWriter append(String string) {
+ write(string);
+ return this;
+ }
+
+ public void increment() {
+ indent++;
+ }
+
+ public void decrement() {
+ indent--;
+ }
+
+ private void write(String string) {
+ try {
+ writer.write(string);
+ empty = false;
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+
+ public String toString() {
+ return writer.toString();
+ }
+
+ public void newline() {
+ if (useIndent && !empty) {
+ write("\n" + StringUtils.repeat(" ", indent));
+ }
+ }
+
+ public void setUseIndent(boolean useIndent) {
+ this.useIndent = useIndent;
+ }
+}
88 src/main/java/de/neuland/jade4j/compiler/Utils.java
@@ -0,0 +1,88 @@
+package de.neuland.jade4j.compiler;
+
+import java.util.LinkedList;
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.apache.commons.lang3.StringEscapeUtils;
+
+import de.neuland.jade4j.exceptions.ExpressionException;
+import de.neuland.jade4j.model.JadeModel;
+import de.neuland.jade4j.parser.expression.ExpressionHandler;
+import de.neuland.jade4j.parser.node.ExpressionString;
+
+public class Utils {
+ public static Pattern interpolationPattern = Pattern.compile("(\\\\)?([#!])\\{(.*?)\\}");
+
+ public static List<Object> prepareInterpolate(String string, boolean xmlEscape) {
+ List<Object> result = new LinkedList<Object>();
+
+ Matcher matcher = interpolationPattern.matcher(string);
+ int start = 0;
+ while (matcher.find()) {
+ String before = string.substring(start, matcher.start(0));
+ if (xmlEscape) {
+ before = escapeHTML(before);
+ }
+ result.add(before);
+
+ boolean escape = matcher.group(1) != null;
+ String flag = matcher.group(2);
+ String code = matcher.group(3);
+
+ if (escape) {
+ String escapedExpression = matcher.group(0).substring(1);
+ if (xmlEscape) {
+ escapedExpression = escapeHTML(escapedExpression);
+ }
+ result.add(escapedExpression);
+ } else {
+ ExpressionString expression = new ExpressionString(code);
+ if (flag.equals("#")) {
+ expression.setEscape(true);
+ }
+ result.add(expression);
+ }
+ start = matcher.end(0);
+ }
+ String last = string.substring(start);
+ if (xmlEscape) {
+ last = escapeHTML(last);
+ }
+ result.add(last);
+
+ return result;
+ }
+
+ public static String interpolate(List<Object> prepared, JadeModel model) throws ExpressionException {
+ StringBuffer result = new StringBuffer();
+
+ for (Object entry : prepared) {
+ if (entry instanceof String) {
+ result.append(entry);
+ } else if (entry instanceof ExpressionString) {
+ ExpressionString expression = (ExpressionString) entry;
+ Object value = ExpressionHandler.evaluateExpression(expression.getValue(), model);
+ if (value == null) {
+ throw new ExpressionException("expression [" + expression.getValue() + "] returned null");
+ }
+ String stringValue = value.toString();
+ if (expression.isEscape()) {
+ stringValue = escapeHTML(stringValue);
+ }
+ result.append(stringValue);
+ }
+ }
+
+ return result.toString();
+ }
+
+ private static String escapeHTML(String string) {
+ return StringEscapeUtils.escapeHtml4(string);
+ }
+
+ public static String interpolate(String string, JadeModel model, boolean escape) throws ExpressionException {
+ return interpolate(prepareInterpolate(string, escape), model);
+ }
+}
17 src/main/java/de/neuland/jade4j/exceptions/ExpressionException.java
@@ -0,0 +1,17 @@
+package de.neuland.jade4j.exceptions;
+
+import ognl.OgnlException;
+
+public class ExpressionException extends Exception {
+
+ private static final long serialVersionUID = 1201110801125266239L;
+
+ public ExpressionException(OgnlException e) {
+ super(e.getMessage(), e);
+ }
+
+ public ExpressionException(String message) {
+ super(message);
+ }
+
+}
21 src/main/java/de/neuland/jade4j/exceptions/JadeCompilerException.java
@@ -0,0 +1,21 @@
+package de.neuland.jade4j.exceptions;
+
+import de.neuland.jade4j.parser.node.Node;
+
+public class JadeCompilerException extends JadeException {
+
+ private static final long serialVersionUID = -126617495230190225L;
+
+ public JadeCompilerException(Node node, Throwable e) {
+ super(e.getMessage(), e);
+ setFilename(node.getFileName());
+ setLineNumber(node.getLineNumber());
+ }
+
+ public JadeCompilerException(Node node, String message) {
+ super(message);
+ setFilename(node.getFileName());
+ setLineNumber(node.getLineNumber());
+ }
+
+}
37 src/main/java/de/neuland/jade4j/exceptions/JadeException.java
@@ -0,0 +1,37 @@
+package de.neuland.jade4j.exceptions;
+
+public abstract class JadeException extends RuntimeException {
+
+ private static final long serialVersionUID = -8189536050437574552L;
+ private String filename;
+ private int lineNumber;
+
+ public JadeException(String message, Throwable e) {
+ super(message, e);
+ }
+
+ public JadeException(String message) {
+ super(message);
+ }
+
+ public void setFilename(String filename) {
+ this.filename = filename;
+ }
+
+ public String getFilename() {
+ return filename;
+ }
+
+ public void setLineNumber(int lineNumber) {
+ this.lineNumber = lineNumber;
+ }
+
+ public int getLineNumber() {
+ return lineNumber;
+ }
+
+ @Override
+ public String toString() {
+ return getClass() + " " + getFilename() + ":" + getLineNumber() + "\n" + getMessage();
+ }
+}
19 src/main/java/de/neuland/jade4j/exceptions/JadeLexerException.java
@@ -0,0 +1,19 @@
+package de.neuland.jade4j.exceptions;
+
+
+public class JadeLexerException extends JadeException {
+
+ private static final long serialVersionUID = -4390591022593362563L;
+ private final String input;
+
+ public JadeLexerException(String message, String filename, int lineNumber, String input) {
+ super(message + "\n" + input);
+ this.input = input;
+ setFilename(filename);
+ setLineNumber(lineNumber);
+ }
+
+ public String getInput() {
+ return input;
+ }
+}
28 src/main/java/de/neuland/jade4j/exceptions/JadeParserException.java
@@ -0,0 +1,28 @@
+package de.neuland.jade4j.exceptions;
+
+import de.neuland.jade4j.lexer.token.Token;
+
+public class JadeParserException extends JadeException {
+
+ private static final long serialVersionUID = 2022663314591205451L;
+
+ @SuppressWarnings("rawtypes")
+ public JadeParserException(String filename, int lineNumber, Class expected, Class got) {
+ super("expected " + expected + " but got " + got);
+ setFilename(filename);
+ setLineNumber(lineNumber);
+ }
+
+ public JadeParserException(String filename, int lineNumber, Token token) {
+ super("unknown token " + token);
+ setFilename(filename);
+ setLineNumber(lineNumber);
+ }
+
+ public JadeParserException(String filename, int lineNumber, String message) {
+ super(message);
+ setFilename(filename);
+ setLineNumber(lineNumber);
+ }
+
+}
12 src/main/java/de/neuland/jade4j/filter/CDATAFilter.java
@@ -0,0 +1,12 @@
+package de.neuland.jade4j.filter;
+
+import java.util.Map;
+
+public class CDATAFilter implements Filter {
+
+ @Override
+ public String convert(String source, Map<String, Object> attributes, Map<String, Object> model) {
+ return "<![CDATA[\n" + source + "\n]]>";
+ }
+
+}
29 src/main/java/de/neuland/jade4j/filter/CachingFilter.java
@@ -0,0 +1,29 @@
+package de.neuland.jade4j.filter;
+
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+public abstract class CachingFilter implements Filter {
+
+ private static final int MAX_ENTRIES = 1000;
+
+ private static Map<String, String> cache = new LinkedHashMap<String, String>(MAX_ENTRIES + 1, .75F, true) {
+ private static final long serialVersionUID = 618942552777647107L;
+
+ public boolean removeEldestEntry(Map.Entry<String, String> eldest) {
+ return size() > MAX_ENTRIES;
+ }
+ };
+
+ @Override
+ public String convert(String source, Map<String, Object> attributes, Map<String, Object> model) {
+ String key = source.hashCode() + "-" + attributes.hashCode();
+ if (!cache.containsKey(key)) {
+ cache.put(key, convert(source, attributes));
+ }
+ return cache.get(key);
+ }
+
+ abstract protected String convert(String source, Map<String, Object> attributes);
+
+}
8 src/main/java/de/neuland/jade4j/filter/Filter.java
@@ -0,0 +1,8 @@
+package de.neuland.jade4j.filter;
+
+import java.util.Map;
+
+
+public interface Filter {
+ public String convert(String source, Map<String, Object> attributes, Map<String, Object> model);
+}
16 src/main/java/de/neuland/jade4j/filter/MarkdownFilter.java
@@ -0,0 +1,16 @@
+package de.neuland.jade4j.filter;
+
+import java.util.Map;
+
+import org.pegdown.PegDownProcessor;
+
+public class MarkdownFilter extends CachingFilter {
+
+ private PegDownProcessor pegdown = new PegDownProcessor();
+
+ @Override
+ protected String convert(String source, Map<String, Object> attributes) {
+ return pegdown.markdownToHtml(source);
+ }
+
+}
12 src/main/java/de/neuland/jade4j/filter/PlainFilter.java
@@ -0,0 +1,12 @@
+package de.neuland.jade4j.filter;
+
+import java.util.Map;
+
+public class PlainFilter implements Filter {
+
+ @Override
+ public String convert(String source, Map<String, Object> attributes, Map<String, Object> model) {
+ return source;
+ }
+
+}
11 src/main/java/de/neuland/jade4j/lexer/Assignment.java
@@ -0,0 +1,11 @@
+package de.neuland.jade4j.lexer;
+
+import de.neuland.jade4j.lexer.token.Token;
+
+public class Assignment extends Token {
+
+ public Assignment(String value, int lineNumber) {
+ super(value, lineNumber);
+ }
+
+}
201 src/main/java/de/neuland/jade4j/lexer/AttributeLexer.java
@@ -0,0 +1,201 @@
+package de.neuland.jade4j.lexer;
+
+import java.util.Deque;
+import java.util.LinkedList;
+
+import de.neuland.jade4j.lexer.token.Attribute;
+
+public class AttributeLexer {
+
+ public enum State {
+ KEY, KEY_CHAR, VALUE, EXPRESSION, ARRAY, STRING, OBJECT
+ }
+
+ /*
+ * len = str.length , colons = this.colons , states = ['key'] , key = '' ,
+ * val = '' , quote , c;
+ */
+
+ private String key = "";
+ private String value = "";
+ private Attribute token;
+ private Deque<State> states = new LinkedList<State>();
+ private char quote = ' ';
+
+ public AttributeLexer() {
+ states.add(State.KEY);
+ }
+
+ public Attribute getToken(String input, int lineno) {
+ token = new Attribute(input, lineno);
+ for (int i = 0; i < input.length(); i++) {
+ parse(input.charAt(i));
+ }
+ parse(',');
+ return token;
+ }
+
+ private State state() {
+ return states.getFirst();
+ }
+
+ private void parse(char c) {
+ char real = c;
+ switch (c) {
+ case ',':
+ case '\n':
+ switch (state()) {
+ case EXPRESSION:
+ case ARRAY:
+ case STRING:
+ case OBJECT:
+ value += c;
+ break;
+ default:
+ states.push(State.KEY);
+ value = value.trim();
+ key = key.trim();
+ if ("".equals(key)) {
+ return;
+ }
+ String name = key.replaceAll("^['\"]|['\"]$", "");
+ String cleanValue = value.replaceAll("^['\"]|['\"]$", "");
+
+ if ("".equals(cleanValue) && quote == ' ') {
+ token.addBooleanAttribute(name, Boolean.TRUE);
+ } else if (value.matches("^\".*\"$") || value.matches("^'.*'$")) {
+ token.addAttribute(name, cleanValue);
+ } else {
+ token.addExpressionAttribute(name, value);
+ }
+ key = "";
+ value = "";
+ quote = ' ';
+ break;
+ }
+ break;
+ case '=':
+ parseAssign(real);
+ break;
+ case '(':
+ parseExpressionStart(c);
+ break;
+ case ')':
+ parseExpressionEnd(c);
+ break;
+ case '{':
+ parseObjectStart(c);
+ break;
+ case '}':
+ parseObjectEnd(c);
+ break;
+ case '[':
+ parseArrayStart(c);
+ break;
+ case ']':
+ parseArrayEnd(c);
+ break;
+ case '"':
+ case '\'':
+ parseQuotes(c);
+ break;
+ default:
+ parseDefaults(c);
+ break;
+ }
+ }
+
+ private void parseAssign(char real) {
+ switch (state()) {
+ case KEY_CHAR:
+ key += real;
+ break;
+ case VALUE:
+ case EXPRESSION:
+ case ARRAY:
+ case STRING:
+ case OBJECT:
+ value += real;
+ break;
+ default:
+ states.push(State.VALUE);
+ break;
+ }
+ }
+
+ private void parseExpressionStart(char c) {
+ if (state() == State.VALUE || state() == State.EXPRESSION) {
+ states.push(State.EXPRESSION);
+ }
+ value += c;
+ }
+
+ private void parseExpressionEnd(char c) {
+ if (state() == State.VALUE || state() == State.EXPRESSION) {
+ states.pop();
+ }
+ value += c;
+ }
+
+ private void parseObjectStart(char c) {
+ if (state() == State.VALUE) {
+ states.push(State.OBJECT);
+ }
+ value += c;
+ }
+
+ private void parseObjectEnd(char c) {
+ if (state() == State.OBJECT) {
+ states.pop();
+ }
+ value += c;
+ }
+
+ private void parseArrayStart(char c) {
+ if (state() == State.VALUE) {
+ states.push(State.ARRAY);
+ }
+ value += c;
+ }
+
+ private void parseArrayEnd(char c) {
+ if (state() == State.ARRAY) {
+ states.pop();
+ }
+ value += c;
+ }
+
+ private void parseQuotes(char c) {
+ switch (state()) {
+ case KEY:
+ states.push(State.KEY_CHAR);
+ break;
+ case KEY_CHAR:
+ states.pop();
+ break;
+ case STRING:
+ if (c == quote) {
+ states.pop();
+ }
+ value += c;
+ break;
+ default:
+ states.push(State.STRING);
+ value += c;
+ quote = c;
+ break;
+ }
+ }
+
+ private void parseDefaults(char c) {
+ switch (state()) {
+ case KEY:
+ case KEY_CHAR:
+ key += c;
+ break;
+ default:
+ value += c;
+ break;
+ }
+ }
+}
29 src/main/java/de/neuland/jade4j/lexer/Each.java
@@ -0,0 +1,29 @@
+package de.neuland.jade4j.lexer;
+
+import de.neuland.jade4j.lexer.token.Token;
+
+public class Each extends Token {
+
+ private String code;
+ private String key;
+
+ public Each(String value, int lineNumber) {
+ super(value, lineNumber);
+ }
+
+ public void setCode(String code) {
+ this.code = code;
+ }
+
+ public String getCode() {
+ return code;
+ }
+
+ public String getKey() {
+ return key;
+ }
+
+ public void setKey(String key) {
+ this.key = key;
+ }
+}
656 src/main/java/de/neuland/jade4j/lexer/Lexer.java
@@ -0,0 +1,656 @@
+package de.neuland.jade4j.lexer;
+
+import java.io.IOException;
+import java.io.Reader;
+import java.util.LinkedList;
+import java.util.regex.Matcher;
+
+import org.apache.commons.lang3.StringUtils;
+
+import de.neuland.jade4j.exceptions.JadeLexerException;
+import de.neuland.jade4j.lexer.token.Block;
+import de.neuland.jade4j.lexer.token.CaseToken;
+import de.neuland.jade4j.lexer.token.Colon;
+import de.neuland.jade4j.lexer.token.Comment;
+import de.neuland.jade4j.lexer.token.Conditional;
+import de.neuland.jade4j.lexer.token.CssClass;
+import de.neuland.jade4j.lexer.token.CssId;
+import de.neuland.jade4j.lexer.token.Default;
+import de.neuland.jade4j.lexer.token.Doctype;
+import de.neuland.jade4j.lexer.token.Dot;
+import de.neuland.jade4j.lexer.token.Eos;
+import de.neuland.jade4j.lexer.token.Expression;
+import de.neuland.jade4j.lexer.token.ExtendsToken;
+import de.neuland.jade4j.lexer.token.Filter;
+import de.neuland.jade4j.lexer.token.Include;
+import de.neuland.jade4j.lexer.token.Indent;
+import de.neuland.jade4j.lexer.token.Mixin;
+import de.neuland.jade4j.lexer.token.Newline;
+import de.neuland.jade4j.lexer.token.Outdent;
+import de.neuland.jade4j.lexer.token.Tag;
+import de.neuland.jade4j.lexer.token.Text;
+import de.neuland.jade4j.lexer.token.Token;
+import de.neuland.jade4j.lexer.token.When;
+import de.neuland.jade4j.lexer.token.While;
+import de.neuland.jade4j.lexer.token.Yield;
+import de.neuland.jade4j.template.TemplateLoader;
+
+public class Lexer {
+ @SuppressWarnings("unused")
+ private LinkedList<String> options;
+ private Scanner scanner;
+ private LinkedList<Token> deferredTokens;
+ private int lastIndents = -1;
+ private int lineno;
+ private LinkedList<Token> stash;
+ private LinkedList<Integer> indentStack;
+ private String indentRe = null;
+ private boolean pipeless = false;
+ @SuppressWarnings("unused")
+ private boolean attributeMode;
+ private Reader reader;
+ private final String filename;
+
+ public Lexer(String filename, TemplateLoader templateLoader) throws IOException {
+ this.filename = filename;
+ reader = templateLoader.getReader(filename);
+ options = new LinkedList<String>();
+ scanner = new Scanner(reader);
+ deferredTokens = new LinkedList<Token>();
+ stash = new LinkedList<Token>();
+ indentStack = new LinkedList<Integer>();
+ lastIndents = 0;
+ lineno = 1;
+ }
+
+ public Token next() {
+ Token token = null;
+ if (token == null) {
+ token = deferred();
+ }
+ if (token == null) {
+ token = eos();
+ }
+ if (token == null) {
+ token = pipelessText();
+ }
+ if (token == null) {
+ token = yield();
+ }
+ if (token == null) {
+ token = doctype();
+ }
+ if (token == null) {
+ token = caseToken();
+ }
+ if (token == null) {
+ token = when();
+ }
+ if (token == null) {
+ token = defaultToken();
+ }
+ if (token == null) {
+ token = extendsToken();
+ }
+ if (token == null) {
+ token = append();
+ }
+ if (token == null) {
+ token = prepend();
+ }
+ if (token == null) {
+ token = block();
+ }
+ if (token == null) {
+ token = include();
+ }
+ if (token == null) {
+ token = mixin();
+ }
+ if (token == null) {
+ token = conditional();
+ }
+ if (token == null) {
+ token = each();
+ }
+ if (token == null) {
+ token = whileToken();
+ }
+ if (token == null) {
+ token = assignment();
+ }
+ if (token == null) {
+ token = tag();
+ }
+ if (token == null) {
+ token = filter();
+ }
+ if (token == null) {
+ token = code();
+ }
+ if (token == null) {
+ token = id();
+ }
+ if (token == null) {
+ token = className();
+ }
+ if (token == null) {
+ token = attributes();
+ }
+ if (token == null) {
+ token = indent();
+ }
+ if (token == null) {
+ token = comment();
+ }
+ if (token == null) {
+ token = colon();
+ }
+ if (token == null) {
+ token = text();
+ }
+ if (token == null) {
+ token = dot();
+ }
+ if (token == null) {
+ throw new JadeLexerException("token not recognized", filename, getLineno(), scanner.getInput());
+ }
+
+ return token;
+ }
+
+ public void consume(int len) {
+ scanner.consume(len);
+ }
+
+ public void defer(Token tok) {
+ deferredTokens.add(tok);
+ }
+
+ public Token lookahead(int n) {
+ int fetch = n - stash.size();
+ while (fetch > 0) {
+ stash.add(next());
+ fetch = fetch - 1;
+ }
+ n = n - 1;
+ return this.stash.get(n);
+ }
+
+ public int getLineno() {
+ return lineno;
+ }
+
+ public void setPipeless(boolean pipeless) {
+ this.pipeless = pipeless;
+ }
+
+ public Token advance() {
+ Token t = this.stashed();
+ return t != null ? t : next();
+ }
+
+ // TODO: use multiscan?!
+ private String scan(String regexp) {
+ String result = null;
+ Matcher matcher = scanner.getMatcherForPattern(regexp);
+ if (matcher.find(0) && matcher.groupCount() > 0) {
+ result = matcher.group(1);
+ int end = matcher.end();
+ consume(end);
+ }
+ return result;
+ }
+
+ // private int indexOfDelimiters(char start, char end) {
+ // String str = scanner.getInput();
+ // int nstart = 0;
+ // int nend = 0;
+ // int pos = 0;
+ // for (int i = 0, len = str.length(); i < len; ++i) {
+ // if (start == str.charAt(i)) {
+ // nstart++;
+ // } else if (end == str.charAt(i)) {
+ // nend = nend + 1;
+ // if (nend == nstart) {
+ // pos = i;
+ // break;
+ // }
+ // }
+ // }
+ // return pos;
+ // }
+
+ private Token stashed() {
+ if (stash.size() > 0) {
+ return stash.poll();
+ }
+ return null;
+ }
+
+ private Token deferred() {
+ if (deferredTokens.size() > 0) {
+ return deferredTokens.poll();
+ }
+ return null;
+ }
+
+ private Token eos() {
+ if (scanner.getInput().length() > 0) {
+ return null;
+ }
+ if (indentStack.size() > 0) {
+ indentStack.poll();
+ return new Outdent("outdent", lineno);
+ } else {
+ return new Eos("eos", lineno);
+ }
+ }
+
+ private Token comment() {
+ Matcher matcher = scanner.getMatcherForPattern("^ *\\/\\/(-)?([^\\n]*)");
+ if (matcher.find(0) && matcher.groupCount() > 1) {
+ boolean buffer = !"-".equals(matcher.group(1));
+ Comment comment = new Comment(matcher.group(2).trim(), lineno, buffer);
+ consume(matcher.end());
+ return comment;
+ }
+ return null;
+ }
+
+ private Token code() {
+ Matcher matcher = scanner.getMatcherForPattern("^(!?=|-)([^\\n]+)");
+ if (matcher.find(0) && matcher.groupCount() > 1) {
+ Expression code = new Expression(matcher.group(2), lineno);
+ String type = matcher.group(1);
+ code.setEscape(type.equals("="));
+ code.setBuffer(type.equals("=") || type.equals("!="));
+
+ consume(matcher.end());
+ return code;
+ }
+ return null;
+ }
+
+ // code: function() {
+ // var captures;
+ // if (captures = /^(!?=|-)([^\n]+)/.exec(this.input)) {
+ // this.consume(captures[0].length);
+ // var flags = captures[1];
+ // captures[1] = captures[2];
+ // var tok = this.tok('code', captures[1]);
+ // tok.escape = flags[0] === '=';
+ // tok.buffer = flags[0] === '=' || flags[1] === '=';
+ // return tok;
+ // }
+ // },
+
+ private Token tag() {
+ Matcher matcher = scanner.getMatcherForPattern("^(\\w[-:\\w]*)");
+ if (matcher.find(0) && matcher.groupCount() > 0) {
+ consume(matcher.end());
+ Tag tok;
+ String name = matcher.group(1);
+ if (':' == name.charAt(name.length() - 1)) {
+ name = name.substring(0, name.length() - 1);
+ tok = new Tag(name, lineno);
+ this.defer(new Colon(":", lineno));
+ while (' ' == scanner.getInput().charAt(0))
+ scanner.consume(1);
+ } else {
+ tok = new Tag(name, lineno);
+ }
+ return tok;
+ }
+ return null;
+ }
+
+ private Token yield() {
+ Matcher matcher = scanner.getMatcherForPattern("^yield *");
+ if (matcher.find(0)) {
+ matcher.group(0);
+ int end = matcher.end();
+ consume(end);
+ return new Yield("yield", lineno);
+ }
+ return null;
+ }
+
+ private Token filter() {
+ String val = scan("^:(\\w+)");
+ if (StringUtils.isNotBlank(val)) {
+ return new Filter(val, lineno);
+ }
+ return null;
+ }
+
+ private Token each() {
+ Matcher matcher = scanner.getMatcherForPattern("^(?:- *)?(?:each|for) +(\\w+)(?: *, *(\\w+))? * in *([^\\n]+)");
+ if (matcher.find(0) && matcher.groupCount() > 1) {
+ consume(matcher.end());
+ String value = matcher.group(1);
+ String key = matcher.group(2);
+ String code = matcher.group(3);
+ Each each = new Each(value, lineno);
+ each.setCode(code);
+ each.setKey(key);
+ return each;
+ }
+ return null;
+ /*
+ * if (captures = /^(?:- *)?(?:each|for) +(\w+)(?: *, *(\w+))? * in
+ * *([^\n]+)/.exec(this.input)) { this.consume(captures[0].length); var
+ * tok = this.tok('each', captures[1]); tok.key = captures[2] ||
+ * '$index'; tok.code = captures[3]; return tok; }
+ */
+ }
+
+ private Token whileToken() {
+ String val = scan("^while +([^\\n]+)");
+ if (StringUtils.isNotBlank(val)) {
+ return new While(val, lineno);
+ }
+ return null;
+ }
+
+ private Token conditional() {
+ Matcher matcher = scanner.getMatcherForPattern("^(if|unless|else if|else)\\b([^\\n]*)");
+ if (matcher.find(0) && matcher.groupCount() > 1) {
+ String type = matcher.group(1);
+ String condition = matcher.group(2);
+ consume(matcher.end());
+ Conditional conditional = new Conditional(condition, lineno);
+ conditional.setInverseCondition("unless".equals(type));
+ conditional.setAlternativeCondition(type.startsWith("else"));
+ conditional.setConditionActive(!"else".equals(type));
+ return conditional;
+ }
+ // TODO: Make else own Token!
+ return null;
+ }
+
+ /*
+ * private Token conditionalElse() { String val = scan("^(else)"); if
+ * (StringUtils.isNotBlank(val)) { return new Filter(val, lineno); } return
+ * null; }
+ */
+
+ private Token doctype() {
+ Matcher matcher = scanner.getMatcherForPattern("^(?:!!!|doctype) *([^\\n]+)?");
+ if (matcher.find(0) && matcher.groupCount() > 0) {
+ consume(matcher.end());
+ return new Doctype(matcher.group(1), lineno);
+ }
+ return null;
+ }
+
+ private Token id() {
+ String val = scan("^#([\\w-]+)");
+ if (StringUtils.isNotBlank(val)) {
+ return new CssId(val, lineno);
+ }
+ return null;
+ }
+
+ private Token className() {
+ String val = scan("^\\.([\\w-]+)");
+ if (StringUtils.isNotBlank(val)) {
+ return new CssClass(val, lineno);
+ }
+ return null;
+ }
+
+ private Token text() {
+ String val = scan("^(?:\\|)? ([^\\n]+)");
+ // TODO: regex differs from original implementation. this implementation
+ // requires one blank after pipe
+ if (StringUtils.isNotBlank(val)) {
+ return new Text(val, lineno);
+ }
+ return null;
+ }
+
+ private Token extendsToken() {
+ String val = scan("^extends? +([^\\n]+)");
+ if (StringUtils.isNotBlank(val)) {
+ return new ExtendsToken(val, lineno);
+ }
+ return null;
+ }
+
+ private Token prepend() {
+ String name = scan("^prepend +([^\\n]+)");
+ if (StringUtils.isNotBlank(name)) {
+ Block tok = new Block(name, lineno);
+ tok.setMode("prepend");
+ return tok;
+ }
+ return null;
+ }
+
+ private Token append() {
+ String name = scan("^append +([^\\n]+)");
+ if (StringUtils.isNotBlank(name)) {
+ Block tok = new Block(name, lineno);
+ tok.setMode("append");
+ return tok;
+ }
+ return null;
+ }
+
+ private Token block() {
+ Matcher matcher = scanner.getMatcherForPattern("^block +(?:(prepend|append) +)?([^\\n]+)");
+ if (matcher.find(0) && matcher.groupCount() > 1) {
+ String val = matcher.group(1);
+ String mode = StringUtils.isNotBlank(val) ? val : "replace";
+ String name = matcher.group(2);
+ Block tok = new Block(name, lineno);
+ tok.setMode(mode);
+ consume(matcher.end());
+ return tok;
+ }
+ return null;
+ }
+
+ private Token include() {
+ String val = scan("^include +([^\\n]+)");
+ if (StringUtils.isNotBlank(val)) {
+ return new Include(val, lineno);
+ }
+ return null;
+ }
+
+ private Token caseToken() {
+ String val = scan("^case +([^\\n]+)");
+ if (StringUtils.isNotBlank(val)) {
+ return new CaseToken(val, lineno);
+ }
+ return null;
+ }
+
+ private Token when() {
+ String val = scan("^when +([^:\\n]+)");
+ if (StringUtils.isNotBlank(val)) {
+ return new When(val, lineno);
+ }
+ return null;
+ }
+
+ private Token defaultToken() {
+ String val = scan("^(default *)");
+ if (StringUtils.isNotBlank(val)) {
+ return new Default(val, lineno);
+ }
+ return null;
+ }
+
+ private Token assignment() {
+ Matcher matcher = scanner.getMatcherForPattern("^(\\w+) += *([^;\\n]+)( *;? *)");
+ if (matcher.find(0) && matcher.groupCount() > 1) {
+ String name = matcher.group(1);
+ String val = matcher.group(2);
+ consume(matcher.end());
+ Assignment assign = new Assignment(val, lineno);
+ assign.setName(name);
+ return assign;
+ }
+ return null;
+ }
+
+ private Token dot() {
+ Matcher matcher = scanner.getMatcherForPattern("^\\.");
+ if (matcher.find(0)) {
+ Dot tok = new Dot(".", lineno);