0001    // The MIT License
0002    //
0003    // Copyright (c) 2015 Gwendal Roué
0004    //
0005    // Permission is hereby granted, free of charge, to any person obtaining a copy
0006    // of this software and associated documentation files (the "Software"), to deal
0007    // in the Software without restriction, including without limitation the rights
0008    // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
0009    // copies of the Software, and to permit persons to whom the Software is
0010    // furnished to do so, subject to the following conditions:
0011    //
0012    // The above copyright notice and this permission notice shall be included in
0013    // all copies or substantial portions of the Software.
0014    //
0015    // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
0016    // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
0017    // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
0018    // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
0019    // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
0020    // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
0021    // THE SOFTWARE.
0022    
0023    final class RenderingEngine
RenderingEngine.swift:71
            let renderingEngine = RenderingEngine(templateAST: templateAST, context: context)
Template.swift:81
        let renderingEngine = RenderingEngine(templateAST: templateAST, context: context)
Template.swift:278
                    let renderingEngine = RenderingEngine(
SectionTag.swift:48
        let renderingEngine = RenderingEngine(templateAST: innerTemplateAST, context: context)
{ 0024 0025 init
RenderingEngine.swift:71
            let renderingEngine = RenderingEngine(templateAST: templateAST, context: context)
Template.swift:81
        let renderingEngine = RenderingEngine(templateAST: templateAST, context: context)
Template.swift:278
                    let renderingEngine = RenderingEngine(
SectionTag.swift:48
        let renderingEngine = RenderingEngine(templateAST: innerTemplateAST, context: context)
(templateAST: TemplateAST, context: Context) { 0026 self.templateAST = templateAST 0027 self.baseContext = context 0028 buffer = "" 0029 } 0030 0031 func render
RenderingEngine.swift:72
            let rendering = try renderingEngine.render()
Template.swift:82
        return try renderingEngine.render()
Template.swift:281
                    return try renderingEngine.render()
SectionTag.swift:49
        return try renderingEngine.render()
() throws -> Rendering { 0032 buffer = "" 0033 try renderTemplateAST(templateAST, inContext: baseContext) 0034 return Rendering(buffer, templateAST.contentType) 0035 } 0036 0037 0038 // MARK: - Rendering 0039 0040 private let templateAST
RenderingEngine.swift:26
        self.templateAST = templateAST
RenderingEngine.swift:33
        try renderTemplateAST(templateAST, inContext: baseContext)
RenderingEngine.swift:34
        return Rendering(buffer, templateAST.contentType)
RenderingEngine.swift:56
        let targetContentType = self.templateAST.contentType!
RenderingEngine.swift:190
        switch (templateAST.contentType!, rendering.contentType, escapesHTML) {
: TemplateAST 0041 private let baseContext
RenderingEngine.swift:27
        self.baseContext = context
RenderingEngine.swift:33
        try renderTemplateAST(templateAST, inContext: baseContext)
: Context 0042 private var buffer
RenderingEngine.swift:28
        buffer = ""
RenderingEngine.swift:32
        buffer = ""
RenderingEngine.swift:34
        return Rendering(buffer, templateAST.contentType)
RenderingEngine.swift:75
                buffer.appendContentsOf(escapeHTML(rendering.string))
RenderingEngine.swift:77
                buffer.appendContentsOf(rendering.string)
RenderingEngine.swift:114
            buffer.appendContentsOf(text)
RenderingEngine.swift:196
        buffer.appendContentsOf(string)
: String 0043 0044 private func renderTemplateAST
RenderingEngine.swift:33
        try renderTemplateAST(templateAST, inContext: baseContext)
RenderingEngine.swift:89
            return try renderTemplateAST(resolvedBlock.innerTemplateAST, inContext: context)
RenderingEngine.swift:96
            return try renderTemplateAST(partialOverride.parentPartial.templateAST, inContext: context)
RenderingEngine.swift:102
            return try renderTemplateAST(partial.templateAST, inContext: context)
(templateAST: TemplateAST, inContext context: Context) throws { 0045 // We must take care of eventual content-type mismatch between the 0046 // currently rendered AST (defined by init), and the argument. 0047 // 0048 // For example, the partial loaded by the HTML template `{{>partial}}` 0049 // may be a text one. In this case, we must render the partial as text, 0050 // and then HTML-encode its rendering. See the "Partial containing 0051 // CONTENT_TYPE:TEXT pragma is HTML-escaped when embedded." test in 0052 // the text_rendering.json test suite. 0053 // 0054 // So let's check for a content-type mismatch: 0055 0056 let targetContentType = self.templateAST.contentType! 0057 if templateAST.contentType == targetContentType 0058 { 0059 // Content-type match 0060 0061 for node in templateAST.nodes { 0062 try renderNode(node, inContext: context) 0063 } 0064 } 0065 else 0066 { 0067 // Content-type mismatch 0068 // 0069 // Render separately, so that we can HTML-escape the rendering of 0070 // the templateAST before appending to our buffer. 0071 let renderingEngine = RenderingEngine(templateAST: templateAST, context: context) 0072 let rendering = try renderingEngine.render() 0073 switch (targetContentType, rendering.contentType) { 0074 case (.HTML, .Text): 0075 buffer.appendContentsOf(escapeHTML(rendering.string)) 0076 default: 0077 buffer.appendContentsOf(rendering.string) 0078 } 0079 } 0080 } 0081 0082 private func renderNode
RenderingEngine.swift:62
                try renderNode(node, inContext: context)
(node: TemplateASTNode, inContext context: Context) throws { 0083 switch node { 0084 case .BlockNode(let block): 0085 // {{$ name }}...{{/ name }} 0086 // 0087 // Render the inner content of the resolved block. 0088 let resolvedBlock = resolveBlock(block, inContext: context) 0089 return try renderTemplateAST(resolvedBlock.innerTemplateAST, inContext: context) 0090 0091 case .PartialOverrideNode(let partialOverride): 0092 // {{< name }}...{{/ name }} 0093 // 0094 // Extend the inheritance stack, and render the content of the parent partial 0095 let context = context.extendedContext(partialOverride: partialOverride) 0096 return try renderTemplateAST(partialOverride.parentPartial.templateAST, inContext: context) 0097 0098 case .PartialNode(let partial): 0099 // {{> name }} 0100 // 0101 // Render the content of the partial 0102 return try renderTemplateAST(partial.templateAST, inContext: context) 0103 0104 case .SectionNode(let section): 0105 // {{# name }}...{{/ name }} 0106 // {{^ name }}...{{/ name }} 0107 // 0108 // We have common rendering for sections and variable tags, yet with 0109 // a few specific flags: 0110 return try renderTag(section.tag, escapesHTML: true, inverted: section.inverted, expression: section.expression, inContext: context) 0111 0112 case .TextNode(let text): 0113 // text is the trivial case: 0114 buffer.appendContentsOf(text) 0115 0116 case .VariableNode(let variable): 0117 // {{ name }} 0118 // {{{ name }}} 0119 // {{& name }} 0120 // 0121 // We have common rendering for sections and variable tags, yet with 0122 // a few specific flags: 0123 return try renderTag(variable.tag, escapesHTML: variable.escapesHTML, inverted: false, expression: variable.expression, inContext: context) 0124 } 0125 } 0126 0127 private func renderTag
RenderingEngine.swift:110
            return try renderTag(section.tag, escapesHTML: true, inverted: section.inverted, expression: section.expression, inContext: context)
RenderingEngine.swift:123
            return try renderTag(variable.tag, escapesHTML: variable.escapesHTML, inverted: false, expression: variable.expression, inContext: context)
(tag: LocatedTag, escapesHTML: Bool, inverted: Bool, expression: Expression, inContext context: Context) throws { 0128 0129 // 1. Evaluate expression 0130 0131 var box: MustacheBox 0132 0133 do { 0134 box = try ExpressionInvocation(expression: expression).invokeWithContext(context) 0135 } catch let error as MustacheError { 0136 let newMessage: String 0137 if let oldMessage = error.message { 0138 newMessage = "Could not evaluate \(tag): \(oldMessage)" 0139 } else { 0140 newMessage = "Could not evaluate \(tag)" 0141 } 0142 throw error.errorWith(message: newMessage, templateID: tag.templateID, lineNumber: tag.lineNumber) 0143 } catch { 0144 throw MustacheError(kind: .RenderError, message: "Could not evaluate \(tag)", templateID: tag.templateID, lineNumber: tag.lineNumber, underlyingError: error) 0145 } 0146 0147 0148 // 2. Let willRender functions alter the box 0149 0150 for willRender in context.willRenderStack { 0151 box = willRender(tag: tag, box: box) 0152 } 0153 0154 0155 // 3. Render the box 0156 0157 let rendering: Rendering 0158 do { 0159 switch tag.type { 0160 case .Variable: 0161 let info = RenderingInfo(tag: tag, context: context, enumerationItem: false) 0162 rendering = try box.render(info: info) 0163 case .Section: 0164 switch (inverted, box.boolValue) { 0165 case (false, true): 0166 // {{# true }}...{{/ true }} 0167 // Only case where we trigger the RenderFunction of the Box 0168 let info = RenderingInfo(tag: tag, context: context, enumerationItem: false) 0169 rendering = try box.render(info: info) 0170 case (true, false): 0171 // {{^ false }}...{{/ false }} 0172 rendering = try tag.render(context) 0173 default: 0174 // {{^ true }}...{{/ true }} 0175 // {{# false }}...{{/ false }} 0176 rendering = Rendering("") 0177 } 0178 } 0179 } catch { 0180 for didRender in context.didRenderStack { 0181 didRender(tag: tag, box: box, string: nil) 0182 } 0183 // TODO? Inject location in error 0184 throw error 0185 } 0186 0187 // 4. Extend buffer with the rendering, HTML-escaped if needed. 0188 0189 let string: String 0190 switch (templateAST.contentType!, rendering.contentType, escapesHTML) { 0191 case (.HTML, .Text, true): 0192 string = escapeHTML(rendering.string) 0193 default: 0194 string = rendering.string 0195 } 0196 buffer.appendContentsOf(string) 0197 0198 0199 // 5. Let didRender functions do their job 0200 0201 for didRender in context.didRenderStack { 0202 didRender(tag: tag, box: box, string: string) 0203 } 0204 } 0205 0206 0207 // MARK: - Template inheritance 0208 0209 private func resolveBlock
RenderingEngine.swift:88
            let resolvedBlock = resolveBlock(block, inContext: context)
(block: TemplateASTNode.Block, inContext context: Context) -> TemplateASTNode.Block { 0210 // As we iterate partial overrides, block becomes the deepest overriden 0211 // block. context.partialOverrideStack has been built in 0212 // renderNode(node:inContext:). 0213 // 0214 // We also update an array of used parent template AST in order to 0215 // support nested partial overrides. 0216 var usedParentTemplateASTs: [TemplateAST] = [] 0217 return context.partialOverrideStack.reduce(block) { (block, partialOverride) in 0218 // Don't apply already used partial 0219 // 0220 // Relevant test: 0221 // { 0222 // "name": "com.github.mustachejava.ExtensionTest.testNested", 0223 // "template": "{{<box}}{{$box_content}}{{<main}}{{$main_content}}{{<box}}{{$box_content}}{{<tweetbox}}{{$tweetbox_classes}}tweetbox-largetweetbox-user-styled{{/tweetbox_classes}}{{$tweetbox_attrs}}data-rich-text{{/tweetbox_attrs}}{{/tweetbox}}{{/box_content}}{{/box}}{{/main_content}}{{/main}}{{/box_content}}{{/box}}", 0224 // "partials": { 0225 // "box": "<box>{{$box_content}}{{/box_content}}</box>", 0226 // "main": "<main>{{$main_content}}{{/main_content}}</main>", 0227 // "tweetbox": "<tweetbox classes=\"{{$tweetbox_classes}}{{/tweetbox_classes}}\" attrs=\"{{$tweetbox_attrs}}{{/tweetbox_attrs}}\"></tweetbox>" 0228 // }, 0229 // "expected": "<box><main><box><tweetbox classes=\"tweetbox-largetweetbox-user-styled\" attrs=\"data-rich-text\"></tweetbox></box></main></box>" 0230 // } 0231 0232 let parentTemplateAST = partialOverride.parentPartial.templateAST 0233 if (usedParentTemplateASTs.contains { $0 === parentTemplateAST }) { 0234 return block 0235 } else { 0236 let (resolvedBlock, modified) = resolveBlock(block, inChildTemplateAST: partialOverride.childTemplateAST) 0237 if modified { 0238 usedParentTemplateASTs.append(parentTemplateAST) 0239 } 0240 return resolvedBlock 0241 } 0242 } 0243 } 0244 0245 // Looks for an override for the block argument in a TemplateAST. 0246 // Returns the resolvedBlock, and a boolean that tells whether the block was 0247 // actually overriden. 0248 private func resolveBlock
RenderingEngine.swift:236
                let (resolvedBlock, modified) = resolveBlock(block, inChildTemplateAST: partialOverride.childTemplateAST)
RenderingEngine.swift:290
                let (resolvedBlock1, modified1) = resolveBlock(block, inChildTemplateAST: partialOverride.parentPartial.templateAST)
RenderingEngine.swift:291
                let (resolvedBlock2, modified2) = resolveBlock(resolvedBlock1, inChildTemplateAST: partialOverride.childTemplateAST)
RenderingEngine.swift:308
                let (resolvedBlock1, modified1) = resolveBlock(block, inChildTemplateAST: partial.templateAST)
(block: TemplateASTNode.Block, inChildTemplateAST childTemplateAST: TemplateAST) -> (TemplateASTNode.Block, Bool) 0249 { 0250 // As we iterate template AST nodes, block becomes the last inherited 0251 // block in the template AST. 0252 // 0253 // The boolean turns to true once the block has been actually overriden. 0254 return childTemplateAST.nodes.reduce((block, false)) { (step, node) in 0255 let (block, modified) = step 0256 switch node { 0257 case .BlockNode(let resolvedBlock) where resolvedBlock.name == block.name: 0258 // {{$ name }}...{{/ name }} 0259 // 0260 // A block is overriden by another block with the same name. 0261 return (resolvedBlock, true) 0262 0263 case .PartialOverrideNode(let partialOverride): 0264 // {{< partial }}...{{/ partial }} 0265 // 0266 // Partial overrides have two opprtunities to override the 0267 // block: their parent partial, and their overriding blocks. 0268 // 0269 // Relevant tests: 0270 // 0271 // { 0272 // "name": "Two levels of inheritance: parent partial with overriding content containing another parent partial", 0273 // "data": { }, 0274 // "template": "{{<partial}}{{<partial2}}{{/partial2}}{{/partial}}", 0275 // "partials": { 0276 // "partial": "{{$block}}ignored{{/block}}", 0277 // "partial2": "{{$block}}inherited{{/block}}" }, 0278 // "expected": "inherited" 0279 // }, 0280 // { 0281 // "name": "Two levels of inheritance: parent partial with overriding content containing another parent partial with overriding content containing a block", 0282 // "data": { }, 0283 // "template": "{{<partial}}{{<partial2}}{{$block}}inherited{{/block}}{{/partial2}}{{/partial}}", 0284 // "partials": { 0285 // "partial": "{{$block}}ignored{{/block}}", 0286 // "partial2": "{{$block}}ignored{{/block}}" }, 0287 // "expected": "inherited" 0288 // } 0289 0290 let (resolvedBlock1, modified1) = resolveBlock(block, inChildTemplateAST: partialOverride.parentPartial.templateAST) 0291 let (resolvedBlock2, modified2) = resolveBlock(resolvedBlock1, inChildTemplateAST: partialOverride.childTemplateAST) 0292 return (resolvedBlock2, modified || modified1 || modified2) 0293 0294 case .PartialNode(let partial): 0295 // {{> partial }} 0296 // 0297 // Relevant test: 0298 // 0299 // { 0300 // "name": "Partials in parent partials can override blocks", 0301 // "data": { }, 0302 // "template": "{{<partial2}}{{>partial1}}{{/partial2}}", 0303 // "partials": { 0304 // "partial1": "{{$block}}partial1{{/block}}", 0305 // "partial2": "{{$block}}ignored{{/block}}" }, 0306 // "expected": "partial1" 0307 // }, 0308 let (resolvedBlock1, modified1) = resolveBlock(block, inChildTemplateAST: partial.templateAST) 0309 return (resolvedBlock1, modified || modified1) 0310 0311 default: 0312 // Other nodes can't override the block. 0313 return (block, modified) 0314 } 0315 } 0316 } 0317 } 0318