View Javadoc

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 }