1 | |
2 | package org.apache.velocity.runtime.directive; |
3 | |
4 | /* |
5 | * Copyright 2000-2001,2004 The Apache Software Foundation. |
6 | * |
7 | * Licensed under the Apache License, Version 2.0 (the "License"); |
8 | * you may not use this file except in compliance with the License. |
9 | * You may obtain a copy of the License at |
10 | * |
11 | * http://www.apache.org/licenses/LICENSE-2.0 |
12 | * |
13 | * Unless required by applicable law or agreed to in writing, software |
14 | * distributed under the License is distributed on an "AS IS" BASIS, |
15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
16 | * See the License for the specific language governing permissions and |
17 | * limitations under the License. |
18 | */ |
19 | |
20 | import java.io.Writer; |
21 | import java.io.IOException; |
22 | |
23 | import org.apache.velocity.context.InternalContextAdapter; |
24 | import org.apache.velocity.runtime.RuntimeServices; |
25 | import org.apache.velocity.runtime.RuntimeConstants; |
26 | import org.apache.velocity.runtime.parser.ParserTreeConstants; |
27 | import org.apache.velocity.runtime.parser.node.Node; |
28 | import org.apache.velocity.runtime.resource.Resource; |
29 | |
30 | import org.apache.velocity.exception.MethodInvocationException; |
31 | import org.apache.velocity.exception.ResourceNotFoundException; |
32 | |
33 | /** |
34 | * Pluggable directive that handles the #include() statement in VTL. |
35 | * This #include() can take multiple arguments of either |
36 | * StringLiteral or Reference. |
37 | * |
38 | * Notes: |
39 | * ----- |
40 | * 1) The included source material can only come from somewhere in |
41 | * the TemplateRoot tree for security reasons. There is no way |
42 | * around this. If you want to include content from elsewhere on |
43 | * your disk, use a link from somwhere under Template Root to that |
44 | * content. |
45 | * |
46 | * 2) By default, there is no output to the render stream in the event of |
47 | * a problem. You can override this behavior with two property values : |
48 | * include.output.errormsg.start |
49 | * include.output.errormsg.end |
50 | * If both are defined in velocity.properties, they will be used to |
51 | * in the render output to bracket the arg string that caused the |
52 | * problem. |
53 | * Ex. : if you are working in html then |
54 | * include.output.errormsg.start=<!-- #include error : |
55 | * include.output.errormsg.end= --> |
56 | * might be an excellent way to start... |
57 | * |
58 | * 3) As noted above, #include() can take multiple arguments. |
59 | * Ex : #include( "foo.vm" "bar.vm" $foo ) |
60 | * will simply include all three if valid to output w/o any |
61 | * special separator. |
62 | * |
63 | * @author <a href="mailto:geirm@optonline.net">Geir Magnusson Jr.</a> |
64 | * @author <a href="mailto:jvanzyl@apache.org">Jason van Zyl</a> |
65 | * @author <a href="mailto:kav@kav.dk">Kasper Nielsen</a> |
66 | * @version $Id: Include.java,v 1.26.4.1 2004/03/03 23:22:56 geirm Exp $ |
67 | */ |
68 | public class Include extends InputBase |
69 | { |
70 | private String outputMsgStart = ""; |
71 | private String outputMsgEnd = ""; |
72 | |
73 | /** |
74 | * Return name of this directive. |
75 | */ |
76 | public String getName() |
77 | { |
78 | return "include"; |
79 | } |
80 | |
81 | /** |
82 | * Return type of this directive. |
83 | */ |
84 | public int getType() |
85 | { |
86 | return LINE; |
87 | } |
88 | |
89 | /** |
90 | * simple init - init the tree and get the elementKey from |
91 | * the AST |
92 | */ |
93 | public void init(RuntimeServices rs, InternalContextAdapter context, |
94 | Node node) |
95 | throws Exception |
96 | { |
97 | super.init( rs, context, node ); |
98 | |
99 | /* |
100 | * get the msg, and add the space so we don't have to |
101 | * do it each time |
102 | */ |
103 | outputMsgStart = rsvc.getString(RuntimeConstants.ERRORMSG_START); |
104 | outputMsgStart = outputMsgStart + " "; |
105 | |
106 | outputMsgEnd = rsvc.getString(RuntimeConstants.ERRORMSG_END ); |
107 | outputMsgEnd = " " + outputMsgEnd; |
108 | } |
109 | |
110 | /** |
111 | * iterates through the argument list and renders every |
112 | * argument that is appropriate. Any non appropriate |
113 | * arguments are logged, but render() continues. |
114 | */ |
115 | public boolean render(InternalContextAdapter context, |
116 | Writer writer, Node node) |
117 | throws IOException, MethodInvocationException, |
118 | ResourceNotFoundException |
119 | { |
120 | /* |
121 | * get our arguments and check them |
122 | */ |
123 | |
124 | int argCount = node.jjtGetNumChildren(); |
125 | |
126 | for( int i = 0; i < argCount; i++) |
127 | { |
128 | /* |
129 | * we only handle StringLiterals and References right now |
130 | */ |
131 | |
132 | Node n = node.jjtGetChild(i); |
133 | |
134 | if ( n.getType() == ParserTreeConstants.JJTSTRINGLITERAL || |
135 | n.getType() == ParserTreeConstants.JJTREFERENCE ) |
136 | { |
137 | if (!renderOutput( n, context, writer )) |
138 | outputErrorToStream( writer, "error with arg " + i |
139 | + " please see log."); |
140 | } |
141 | else |
142 | { |
143 | rsvc.error("#include() error : invalid argument type : " |
144 | + n.toString()); |
145 | outputErrorToStream( writer, "error with arg " + i |
146 | + " please see log."); |
147 | } |
148 | } |
149 | |
150 | return true; |
151 | } |
152 | |
153 | /** |
154 | * does the actual rendering of the included file |
155 | * |
156 | * @param node AST argument of type StringLiteral or Reference |
157 | * @param context valid context so we can render References |
158 | * @param writer output Writer |
159 | * @return boolean success or failure. failures are logged |
160 | */ |
161 | private boolean renderOutput( Node node, InternalContextAdapter context, |
162 | Writer writer ) |
163 | throws IOException, MethodInvocationException, |
164 | ResourceNotFoundException |
165 | { |
166 | String arg = ""; |
167 | |
168 | if ( node == null ) |
169 | { |
170 | rsvc.error("#include() error : null argument"); |
171 | return false; |
172 | } |
173 | |
174 | /* |
175 | * does it have a value? If you have a null reference, then no. |
176 | */ |
177 | Object value = node.value( context ); |
178 | if ( value == null) |
179 | { |
180 | rsvc.error("#include() error : null argument"); |
181 | return false; |
182 | } |
183 | |
184 | /* |
185 | * get the path |
186 | */ |
187 | arg = value.toString(); |
188 | |
189 | Resource resource = null; |
190 | |
191 | try |
192 | { |
193 | resource = rsvc.getContent(arg, getInputEncoding(context)); |
194 | } |
195 | catch ( ResourceNotFoundException rnfe ) |
196 | { |
197 | /* |
198 | * the arg wasn't found. Note it and throw |
199 | */ |
200 | |
201 | rsvc.error("#include(): cannot find resource '" + arg + |
202 | "', called from template " + |
203 | context.getCurrentTemplateName() + " at (" + |
204 | getLine() + ", " + getColumn() + ")" ); |
205 | throw rnfe; |
206 | } |
207 | |
208 | catch (Exception e) |
209 | { |
210 | rsvc.error("#include(): arg = '" + arg + |
211 | "', called from template " + |
212 | context.getCurrentTemplateName() + " at (" + |
213 | getLine() + ", " + getColumn() + ") : " + e); |
214 | } |
215 | |
216 | if ( resource == null ) |
217 | return false; |
218 | |
219 | writer.write((String)resource.getData()); |
220 | return true; |
221 | } |
222 | |
223 | /** |
224 | * Puts a message to the render output stream if ERRORMSG_START / END |
225 | * are valid property strings. Mainly used for end-user template |
226 | * debugging. |
227 | */ |
228 | private void outputErrorToStream( Writer writer, String msg ) |
229 | throws IOException |
230 | { |
231 | if ( outputMsgStart != null && outputMsgEnd != null) |
232 | { |
233 | writer.write(outputMsgStart); |
234 | writer.write(msg); |
235 | writer.write(outputMsgEnd); |
236 | } |
237 | return; |
238 | } |
239 | } |