001 /*
002 GRANITE DATA SERVICES
003 Copyright (C) 2011 GRANITE DATA SERVICES S.A.S.
004
005 This file is part of Granite Data Services.
006
007 Granite Data Services is free software; you can redistribute it and/or modify
008 it under the terms of the GNU Library General Public License as published by
009 the Free Software Foundation; either version 2 of the License, or (at your
010 option) any later version.
011
012 Granite Data Services is distributed in the hope that it will be useful, but
013 WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
014 FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License
015 for more details.
016
017 You should have received a copy of the GNU Library General Public License
018 along with this library; if not, see <http://www.gnu.org/licenses/>.
019 */
020
021 package org.granite.generator.gsp;
022
023 import groovy.lang.Binding;
024 import groovy.lang.GroovyShell;
025 import groovy.lang.Script;
026
027 import java.io.BufferedReader;
028 import java.io.File;
029 import java.io.IOException;
030 import java.io.InputStream;
031 import java.io.InputStreamReader;
032 import java.io.Reader;
033 import java.io.Writer;
034 import java.net.URI;
035 import java.nio.charset.Charset;
036 import java.util.List;
037 import java.util.Map;
038
039 import org.codehaus.groovy.control.CompilationFailedException;
040 import org.codehaus.groovy.runtime.InvokerHelper;
041 import org.granite.generator.Template;
042 import org.granite.generator.exception.TemplateCompilationException;
043 import org.granite.generator.exception.TemplateExecutionException;
044 import org.granite.generator.exception.TemplateParsingException;
045 import org.granite.generator.gsp.token.Token;
046 import org.granite.util.URIUtil;
047
048 /**
049 * @author Franck WOLFF
050 */
051 public class GroovyTemplate implements Template {
052
053 private final URI uri;
054 private final boolean base;
055 private final Charset charset;
056
057 private String source = null;
058 private Script script = null;
059 private long lastModified = -1L;
060
061 GroovyTemplate(URI uri, boolean base, Charset charset) {
062 if (uri == null || charset == null)
063 throw new IllegalArgumentException("uri and charset cannot be null");
064
065 this.uri = uri;
066 this.base = base;
067 this.charset = charset;
068 }
069
070 protected void reset(boolean cleanSource) {
071 if (cleanSource)
072 source = null;
073 script = null;
074 lastModified = -1L;
075 }
076 protected void reset() {
077 reset(true);
078 }
079
080 public URI getUri() {
081 return uri;
082 }
083
084 public long getLastModified() {
085 return lastModified;
086 }
087
088 protected long readLastModified() {
089 long readLastModified = -1L;
090 if ("file".equals(uri.getScheme())) {
091 readLastModified = new File(uri).lastModified();
092 if (readLastModified == 0L)
093 readLastModified = -1L;
094 }
095 else
096 readLastModified = Long.MAX_VALUE;
097 return readLastModified;
098 }
099
100 public boolean isOutdated() {
101 long newLastModified = readLastModified();
102 return script != null && (newLastModified == -1 || lastModified < newLastModified);
103 }
104
105 public boolean isBase() {
106 return base;
107 }
108
109 public String getMarkup() {
110 try {
111 return URIUtil.getContentAsString(uri);
112 } catch (Exception e) {
113 return "Could not load uri content: " + uri + " (" + e.toString() + ")";
114 }
115 }
116
117 public String getSource() {
118 return source;
119 }
120
121 public void compile() throws IOException, TemplateParsingException, TemplateCompilationException {
122 reset();
123
124 InputStream is = null;
125 try {
126 is = URIUtil.getInputStream(uri, getClass().getClassLoader());
127
128 lastModified = readLastModified();
129
130 Reader reader = new BufferedReader(new InputStreamReader(is, charset));
131
132 Parser parser = new Parser();
133 List<Token> tokens = parser.parse(reader);
134
135 GroovyRenderer renderer = new GroovyRenderer();
136 source = renderer.renderSource(tokens);
137
138 // Warning: the classloader for javax annotations is defined in BuilderParentClassLoader
139 // in the case of the Eclipse builder
140 GroovyShell shell = new GroovyShell(Thread.currentThread().getContextClassLoader());
141 script = shell.parse(source);
142 } catch (ParseException e) {
143 throw new TemplateParsingException(this, "Could not parse template: " + uri, e);
144 } catch (CompilationFailedException e) {
145 throw new TemplateCompilationException(this, "Could not compile template: " + uri, e);
146 } finally {
147 if (script == null)
148 reset(false);
149
150 if (is != null)
151 is.close();
152 }
153 }
154
155 public void execute(Map<String, Object> bindings, Writer out)
156 throws IOException, TemplateParsingException, TemplateCompilationException, TemplateExecutionException {
157
158 if (script == null)
159 compile();
160
161 try {
162 Binding binding = (bindings == null ? new Binding() : new Binding(bindings));
163 Script scriptInstance = InvokerHelper.createScript(script.getClass(), binding);
164 scriptInstance.setProperty("out", out);
165 scriptInstance.run();
166 out.flush();
167 } catch (IOException e) {
168 throw e;
169 } catch (Exception e) {
170 throw new TemplateExecutionException(this, "Could not execute template: " + uri, e);
171 }
172 }
173
174 @Override
175 public boolean equals(Object obj) {
176 if (obj == this)
177 return true;
178 if (!(obj instanceof GroovyTemplate))
179 return false;
180 return uri.equals(((GroovyTemplate)obj).uri);
181 }
182
183 @Override
184 public int hashCode() {
185 return uri.hashCode();
186 }
187 }