001/*
002 * Stallion Core: A Modern Web Framework
003 *
004 * Copyright (C) 2015 - 2016 Stallion Software LLC.
005 *
006 * This program is free software: you can redistribute it and/or modify it under the terms of the
007 * GNU General Public License as published by the Free Software Foundation, either version 2 of
008 * the License, or (at your option) any later version. This program is distributed in the hope that
009 * it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
010 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public
011 * License for more details. You should have received a copy of the GNU General Public License
012 * along with this program.  If not, see <http://www.gnu.org/licenses/gpl-2.0.html>.
013 *
014 *
015 *
016 */
017
018package io.stallion.templating;
019
020import com.hubspot.jinjava.Jinjava;
021import com.hubspot.jinjava.JinjavaConfig;
022import com.hubspot.jinjava.interpret.InterpretException;
023import com.hubspot.jinjava.interpret.RenderResult;
024import com.hubspot.jinjava.interpret.TemplateError;
025import com.hubspot.jinjava.lib.filter.Filter;
026import com.hubspot.jinjava.lib.tag.Tag;
027import com.hubspot.jinjava.loader.ResourceNotFoundException;
028import io.stallion.exceptions.UsageException;
029import io.stallion.exceptions.WebException;
030import io.stallion.services.Log;
031import io.stallion.settings.Settings;
032import org.apache.commons.lang3.StringUtils;
033import org.apache.commons.lang3.exception.ExceptionUtils;
034
035import java.io.IOException;
036import java.nio.charset.Charset;
037import java.util.Iterator;
038import java.util.List;
039import java.util.Map;
040import java.util.stream.Collectors;
041
042import static io.stallion.utils.Literals.*;
043
044public class JinjaTemplating implements Templating {
045    private static String targetFolder = "";
046    private static Jinjava jinjava;
047    private boolean devMode = false;
048
049    public JinjaTemplating(String folder, boolean devMode) {
050        this.devMode = devMode;
051        init(folder);
052    }
053
054    @Override
055    public void init(String folder) {
056        this.targetFolder = folder;
057        reset();
058    }
059
060    private static List<Tag> tags;
061    private static List<Filter> filters;
062
063    @Override
064    public void reset() {
065
066        //JinjavaConfig config = new JinjavaConfig();
067        //config.setResourceLocator(new MyCustomResourceLocator());
068
069        if (tags == null) {
070            tags = list();
071        }
072        if (filters == null) {
073            filters = list(new MarkdownFilter());
074        }
075
076
077        //JinjavaConfig config = JinjavaConfig.newBuilder().withMaxRenderDepth(0).build();
078        //jinjava = new Jinjava(config);
079        jinjava = new Jinjava();
080
081        jinjava.setResourceLocator(new JinjaResourceLocator(targetFolder, devMode));
082        for (Filter filter: filters) {
083            jinjava.getGlobalContext().registerFilter(filter);
084        }
085        for (Tag tag: tags) {
086            jinjava.getGlobalContext().registerTag(tag);
087        }
088
089        JinjaResourceLocator.clearCache();
090    }
091
092    @Override
093    public Boolean templateExists(String path) {
094        if (path.contains("\n")) {
095            return true;
096        }
097
098        String result = null;
099        try {
100            result = jinjava.getResourceLocator().getString(path, Charset.forName("UTF-8"), null);
101        } catch (ResourceNotFoundException e) {
102            return false;
103        } catch (IOException e) {
104            throw new RuntimeException(e);
105        }
106
107        if (StringUtils.isEmpty(result)) {
108            return false;
109        } else {
110            return true;
111        }
112    }
113
114    @Override
115    public String renderTemplate(String template, Map<String, Object> context) {
116        String content = "";
117        try {
118            String templateString = "";
119            if (template.contains("\n")) {
120                templateString = template;
121            } else {
122                templateString = jinjava.getResourceLocator().getString(template, Charset.forName("UTF-8"), null);
123            }
124            if (templateString == null && Settings.instance().isStrict()) {
125                throw new UsageException("Could not find the template: " + template);
126            } else if (templateString == null) {
127                templateString = "";
128            }
129            RenderResult result = jinjava.renderForResult(templateString, context);
130            List fatalErrors = (List)result.getErrors().stream().filter((error) -> {
131                return error.getSeverity() == TemplateError.ErrorType.FATAL;
132            }).collect(Collectors.toList());
133            if(!fatalErrors.isEmpty()) {
134                Log.warn("Exception when rendering template: {0}", template);
135                throw new FatalTemplateErrorsException(template, fatalErrors);
136            } else {
137                return result.getOutput();
138            }
139        } catch (IOException e) {
140            throw new RuntimeException(e);
141        }
142    }
143
144    public static class FatalTemplateErrorsException extends InterpretException {
145        private final String templatePath;
146        private final Iterable<TemplateError> errors;
147
148        public FatalTemplateErrorsException(String templatePath, Iterable<TemplateError> errors) {
149            super("Fatal error rendering template \"" + templatePath + "\".\n" + generateMessage(errors));
150            this.templatePath = templatePath;
151            this.errors = errors;
152        }
153
154        private static String generateMessage(Iterable<TemplateError> errors) {
155            StringBuilder msg = new StringBuilder();
156            Iterator var2 = errors.iterator();
157
158            while(var2.hasNext()) {
159                TemplateError error = (TemplateError)var2.next();
160                msg.append(error.toString()).append('\n');
161                if(error.getException() != null) {
162                    msg.append(ExceptionUtils.getStackTrace(error.getException())).append('\n');
163                }
164            }
165
166            return msg.toString();
167        }
168
169        public String getTemplatePath() {
170            return this.templatePath;
171        }
172
173        public Iterable<TemplateError> getErrors() {
174            return this.errors;
175        }
176    }
177
178
179
180    public void registerTag(Tag tag) {
181        tags.add(tag);
182        jinjava.getGlobalContext().registerTag(tag);
183
184    }
185
186    public void registerFilter(Filter filter) {
187        filters.add(filter);
188        jinjava.getGlobalContext().registerFilter(filter);
189
190    }
191
192}