1 /*
2 * Copyright (c) 2003 The Visigoth Software Society. All rights
3 * reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 *
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in
14 * the documentation and/or other materials provided with the
15 * distribution.
16 *
17 * 3. The end-user documentation included with the redistribution, if
18 * any, must include the following acknowledgement:
19 * "This product includes software developed by the
20 * Visigoth Software Society (http://www.visigoths.org/)."
21 * Alternately, this acknowledgement may appear in the software itself,
22 * if and wherever such third-party acknowledgements normally appear.
23 *
24 * 4. Neither the name "FreeMarker", "Visigoth", nor any of the names of the
25 * project contributors may be used to endorse or promote products derived
26 * from this software without prior written permission. For written
27 * permission, please contact visigoths@visigoths.org.
28 *
29 * 5. Products derived from this software may not be called "FreeMarker" or "Visigoth"
30 * nor may "FreeMarker" or "Visigoth" appear in their names
31 * without prior written permission of the Visigoth Software Society.
32 *
33 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
34 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
35 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
36 * DISCLAIMED. IN NO EVENT SHALL THE VISIGOTH SOFTWARE SOCIETY OR
37 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
38 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
39 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
40 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
41 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
42 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
43 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
44 * SUCH DAMAGE.
45 * ====================================================================
46 *
47 * This software consists of voluntary contributions made by many
48 * individuals on behalf of the Visigoth Software Society. For more
49 * information on the Visigoth Software Society, please see
50 * http://www.visigoths.org/
51 */
52
53 package freemarker.core;
54
55 import java.io.IOException;
56
57 import freemarker.cache.TemplateCache;
58 import freemarker.template;
59 import freemarker.template.utility.StringUtil;
60 import freemarker.template.utility.UndeclaredThrowableException;
61
62
63 /**
64 * An instruction that gets another template
65 * and processes it within the current template.
66 */
67 final class Include extends TemplateElement {
68
69 private Expression includedTemplateName, encodingExp, parseExp;
70 private String encoding;
71 private boolean parse;
72 private final String templatePath;
73
74 /**
75 * @param template the template that this <tt>Include</tt> is a part of.
76 * @param includedTemplateName the name of the template to be included.
77 * @param encodingExp the encoding to be used or null, if it is a default.
78 * @param parseExp whether the template should be parsed (or is raw text)
79 */
80 Include(Template template,
81 Expression includedTemplateName,
82 Expression encodingExp,
83 Expression parseExp) throws ParseException
84 {
85 String templatePath1 = template.getName();
86 int lastSlash = templatePath1.lastIndexOf('/');
87 templatePath = lastSlash == -1 ? "" : templatePath1.substring(0, lastSlash + 1);
88 this.includedTemplateName = includedTemplateName;
89 if (encodingExp instanceof StringLiteral) {
90 encoding = encodingExp.toString();
91 encoding = encoding.substring(1, encoding.length() -1);
92 }
93 else {
94 this.encodingExp = encodingExp;
95 }
96 if(parseExp == null) {
97 parse = true;
98 }
99 else if(parseExp.isLiteral()) {
100 try {
101 if (parseExp instanceof StringLiteral) {
102 parse = StringUtil.getYesNo(parseExp.getStringValue(null));
103 }
104 else {
105 try {
106 parse = parseExp.isTrue(null);
107 }
108 catch(NonBooleanException e) {
109 throw new ParseException("Expected a boolean or string as the value of the parse attribute", parseExp);
110 }
111 }
112 }
113 catch(TemplateException e) {
114 // evaluation of literals must not throw a TemplateException
115 throw new UndeclaredThrowableException(e);
116 }
117 }
118 else {
119 this.parseExp = parseExp;
120 }
121 }
122
123 void accept(Environment env) throws TemplateException, IOException {
124 String templateNameString = includedTemplateName.getStringValue(env);
125 if( templateNameString == null ) {
126 String msg = "Error " + getStartLocation()
127 + "The expression " + includedTemplateName + " is undefined.";
128 throw new InvalidReferenceException(msg, env);
129 }
130 String enc = encoding;
131 if (encoding == null && encodingExp != null) {
132 enc = encodingExp.getStringValue(env);
133 }
134
135 boolean parse = this.parse;
136 if (parseExp != null) {
137 TemplateModel tm = parseExp.getAsTemplateModel(env);
138 if(tm == null) {
139 if(env.isClassicCompatible()) {
140 parse = false;
141 }
142 else {
143 assertNonNull(tm, parseExp, env);
144 }
145 }
146 if (tm instanceof TemplateScalarModel) {
147 parse = getYesNo(EvaluationUtil.getString((TemplateScalarModel)tm, parseExp, env));
148 }
149 else {
150 parse = parseExp.isTrue(env);
151 }
152 }
153
154 Template includedTemplate;
155 try {
156 templateNameString = TemplateCache.getFullTemplatePath(env, templatePath, templateNameString);
157 includedTemplate = env.getTemplateForInclusion(templateNameString, enc, parse);
158 }
159 catch (ParseException pe) {
160 String msg = "Error parsing included template "
161 + templateNameString + "\n" + pe.getMessage();
162 throw new TemplateException(msg, pe, env);
163 }
164 catch (IOException ioe) {
165 String msg = "Error reading included file "
166 + templateNameString;
167 throw new TemplateException(msg, ioe, env);
168 }
169 env.include(includedTemplate);
170 }
171
172 public String getCanonicalForm() {
173 StringBuffer buf = new StringBuffer("<#include ");
174 buf.append(includedTemplateName);
175 if (encoding != null) {
176 buf.append(" encoding=\"");
177 buf.append(encodingExp.getCanonicalForm());
178 buf.append("\"");
179 }
180 if(parseExp != null) {
181 buf.append(" parse=" + parseExp.getCanonicalForm());
182 }
183 else if (!parse) {
184 buf.append(" parse=false");
185 }
186 buf.append("/>");
187 return buf.toString();
188 }
189
190 public String getDescription() {
191 return "include " + includedTemplateName;
192 }
193
194 private boolean getYesNo(String s) throws ParseException {
195 try {
196 return StringUtil.getYesNo(s);
197 }
198 catch (IllegalArgumentException iae) {
199 throw new ParseException("Error " + getStartLocation()
200 + "\nValue of include parse parameter "
201 + "must be boolean or one of these strings: "
202 + "\"n\", \"no\", \"f\", \"false\", \"y\", \"yes\", \"t\", \"true\""
203 + "\nFound: " + parseExp, parseExp);
204 }
205 }
206
207 /*
208 boolean heedsOpeningWhitespace() {
209 return true;
210 }
211
212 boolean heedsTrailingWhitespace() {
213 return true;
214 }
215 */
216 }