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