1 package org.apache.velocity.runtime.parser.node;
2
3 /*
4 * Licensed to the Apache Software Foundation (ASF) under one
5 * or more contributor license agreements. See the NOTICE file
6 * distributed with this work for additional information
7 * regarding copyright ownership. The ASF licenses this file
8 * to you under the Apache License, Version 2.0 (the
9 * "License"); you may not use this file except in compliance
10 * with the License. You may obtain a copy of the License at
11 *
12 * http://www.apache.org/licenses/LICENSE-2.0
13 *
14 * Unless required by applicable law or agreed to in writing,
15 * software distributed under the License is distributed on an
16 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17 * KIND, either express or implied. See the License for the
18 * specific language governing permissions and limitations
19 * under the License.
20 */
21
22 import java.io.IOException;
23 import java.io.Writer;
24
25 import org.apache.velocity.app.event.EventHandlerUtil;
26 import org.apache.velocity.context.InternalContextAdapter;
27 import org.apache.velocity.exception.MethodInvocationException;
28 import org.apache.velocity.exception.TemplateInitException;
29 import org.apache.velocity.runtime.RuntimeConstants;
30 import org.apache.velocity.runtime.log.Log;
31 import org.apache.velocity.runtime.parser.Parser;
32 import org.apache.velocity.util.introspection.Info;
33
34 /**
35 * Node for the #set directive
36 *
37 * @author <a href="mailto:jvanzyl@apache.org">Jason van Zyl</a>
38 * @author <a href="mailto:geirm@optonline.net">Geir Magnusson Jr.</a>
39 * @version $Id: ASTSetDirective.java 720228 2008-11-24 16:58:33Z nbubna $
40 */
41 public class ASTSetDirective extends SimpleNode
42 {
43 private String leftReference = "";
44 private Node right = null;
45 private ASTReference left = null;
46 boolean logOnNull = false;
47 private boolean allowNull = false;
48 private boolean isInitialized;
49
50 /**
51 * This is really immutable after the init, so keep one for this node
52 */
53 protected Info uberInfo;
54
55 /**
56 * Indicates if we are running in strict reference mode.
57 */
58 protected boolean strictRef = false;
59
60 /**
61 * @param id
62 */
63 public ASTSetDirective(int id)
64 {
65 super(id);
66 }
67
68 /**
69 * @param p
70 * @param id
71 */
72 public ASTSetDirective(Parser p, int id)
73 {
74 super(p, id);
75 }
76
77 /**
78 * @see org.apache.velocity.runtime.parser.node.SimpleNode#jjtAccept(org.apache.velocity.runtime.parser.node.ParserVisitor, java.lang.Object)
79 */
80 public Object jjtAccept(ParserVisitor visitor, Object data)
81 {
82 return visitor.visit(this, data);
83 }
84
85 /**
86 * simple init. We can get the RHS and LHS as the the tree structure is static
87 * @param context
88 * @param data
89 * @return Init result.
90 * @throws TemplateInitException
91 */
92 public synchronized Object init(InternalContextAdapter context, Object data)
93 throws TemplateInitException
94 {
95 /** This method is synchronized to prevent double initialization or initialization while rendering **/
96
97 if (!isInitialized)
98 {
99 /*
100 * init the tree correctly
101 */
102
103 super.init( context, data );
104
105 uberInfo = new Info(getTemplateName(),
106 getLine(), getColumn());
107
108 right = getRightHandSide();
109 left = getLeftHandSide();
110
111 logOnNull = rsvc.getBoolean(RuntimeConstants.RUNTIME_LOG_REFERENCE_LOG_INVALID, true);
112 allowNull = rsvc.getBoolean(RuntimeConstants.SET_NULL_ALLOWED, false);
113 strictRef = rsvc.getBoolean(RuntimeConstants.RUNTIME_REFERENCES_STRICT, false);
114 if (strictRef) allowNull = true; // strictRef implies allowNull
115
116 /*
117 * grab this now. No need to redo each time
118 */
119 leftReference = left.getFirstToken().image.substring(1);
120
121 isInitialized = true;
122 }
123
124 return data;
125 }
126
127 /**
128 * puts the value of the RHS into the context under the key of the LHS
129 * @param context
130 * @param writer
131 * @return True if rendering was sucessful.
132 * @throws IOException
133 * @throws MethodInvocationException
134 */
135 public boolean render( InternalContextAdapter context, Writer writer)
136 throws IOException, MethodInvocationException
137 {
138 /*
139 * get the RHS node, and its value
140 */
141
142 Object value = right.value(context);
143
144 /*
145 * it's an error if we don't have a value of some sort AND
146 * it is not allowed by configuration
147 */
148
149 if( !allowNull )
150 {
151 if ( value == null )
152 {
153 /*
154 * first, are we supposed to say anything anyway?
155 */
156 if(logOnNull)
157 {
158 boolean doit = EventHandlerUtil.shouldLogOnNullSet( rsvc, context, left.literal(), right.literal() );
159
160 if (doit && rsvc.getLog().isDebugEnabled())
161 {
162 rsvc.getLog().debug("RHS of #set statement is null. Context will not be modified. "
163 + Log.formatFileString(this));
164 }
165 }
166
167 String rightReference = null;
168 if (right instanceof ASTExpression)
169 {
170 rightReference = ((ASTExpression) right).getLastToken().image;
171 }
172 EventHandlerUtil.invalidSetMethod(rsvc, context, leftReference, rightReference, uberInfo);
173
174 return false;
175 }
176 }
177
178 if ( value == null && !strictRef)
179 {
180 String rightReference = null;
181 if (right instanceof ASTExpression)
182 {
183 rightReference = ((ASTExpression) right).getLastToken().image;
184 }
185 EventHandlerUtil.invalidSetMethod(rsvc, context, leftReference, rightReference, uberInfo);
186
187 /*
188 * if RHS is null, remove simple LHS from context
189 * or call setValue() with a null value for complex LHS
190 */
191 if (left.jjtGetNumChildren() == 0)
192 {
193 context.remove( leftReference );
194 }
195 else
196 {
197 left.setValue(context, null);
198 }
199
200 return false;
201
202 }
203 else
204 {
205 /*
206 * if the LHS is simple, just punch the value into the context
207 * otherwise, use the setValue() method do to it.
208 * Maybe we should always use setValue()
209 */
210
211 if (left.jjtGetNumChildren() == 0)
212 {
213 context.put( leftReference, value);
214 }
215 else
216 {
217 left.setValue(context, value);
218 }
219 }
220
221 return true;
222 }
223
224 /**
225 * returns the ASTReference that is the LHS of the set statememt
226 *
227 * @return left hand side of #set statement
228 */
229 private ASTReference getLeftHandSide()
230 {
231 return (ASTReference) jjtGetChild(0);
232 }
233
234 /**
235 * returns the RHS Node of the set statement
236 *
237 * @return right hand side of #set statement
238 */
239 private Node getRightHandSide()
240 {
241 return jjtGetChild(1);
242 }
243 }