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 023package org.granite.generator.gsp; 024 025import groovy.lang.Binding; 026import groovy.lang.GroovyShell; 027import groovy.lang.Script; 028 029import java.io.BufferedReader; 030import java.io.File; 031import java.io.IOException; 032import java.io.InputStream; 033import java.io.InputStreamReader; 034import java.io.Reader; 035import java.io.Writer; 036import java.net.URI; 037import java.nio.charset.Charset; 038import java.util.List; 039import java.util.Map; 040 041import org.codehaus.groovy.control.CompilationFailedException; 042import org.codehaus.groovy.runtime.InvokerHelper; 043import org.granite.generator.Template; 044import org.granite.generator.exception.TemplateCompilationException; 045import org.granite.generator.exception.TemplateExecutionException; 046import org.granite.generator.exception.TemplateParsingException; 047import org.granite.generator.gsp.token.Token; 048import org.granite.util.URIUtil; 049 050/** 051 * @author Franck WOLFF 052 */ 053public 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}