View Javadoc

1   package org.apache.velocity.context;
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.util.HashSet;
23  import java.util.Set;
24  
25  import org.apache.velocity.VelocityContext;
26  import org.apache.velocity.runtime.RuntimeConstants;
27  import org.apache.velocity.runtime.RuntimeServices;
28  import org.apache.velocity.util.ClassUtils;
29  
30  /**
31   *  This is a special, internal-use-only context implementation to be
32   *  used for the #evaluate directive.
33   *
34   *  We use this context to chain the existing context, preventing any changes
35   *  from impacting the parent context.  By separating this context into a 
36   *  separate class it also allows for the future possibility of changing
37   *  the context behavior for the #evaluate directive.
38   *  
39   *  Note that the context used to store values local to #evaluate()
40   *  is user defined but defaults to {@link VelocityContext}.
41   *
42   *  @author <a href="mailto:wglass@forio.com">Will Glass-Husain</a>
43   *  @version $Id: EvaluateContext.java 898032 2010-01-11 19:51:03Z nbubna $
44   *  @since 1.6
45   *  @deprecated Will be removed in 2.0
46   */
47  public class EvaluateContext extends ChainedInternalContextAdapter
48  {
49      /** container for any local items */
50      Context localContext;
51      
52       /**
53       *  CTOR, wraps an ICA
54       * @param inner context for parent template
55       * @param rsvc 
56       */
57      public EvaluateContext( InternalContextAdapter  inner, RuntimeServices rsvc )
58      {
59          super(inner);
60          initContext(rsvc);
61      }
62  
63      /**
64       * Initialize the context based on user-configured class 
65       * @param rsvc
66       */
67      private void initContext( RuntimeServices rsvc )
68      {
69          String contextClass = rsvc.getString(RuntimeConstants.EVALUATE_CONTEXT_CLASS);
70  
71          if (contextClass != null && contextClass.length() > 0)
72          {
73              rsvc.getLog().warn("The "+RuntimeConstants.EVALUATE_CONTEXT_CLASS+
74                  " property has been deprecated. It will be removed in Velocity 2.0. "+
75                  " Instead, please use the automatically provided $evaluate"+
76                  " namespace to get and set local references"+
77                  " (e.g. #set($evaluate.foo = 'bar') and $evaluate.foo).");
78              
79              Object o = null;
80  
81              try
82              {
83                  o = ClassUtils.getNewInstance( contextClass );
84              }
85              catch (ClassNotFoundException cnfe)
86              {
87                  String err = "The specified class for #evaluate() context (" + contextClass
88                  + ") does not exist or is not accessible to the current classloader.";
89                  rsvc.getLog().error(err);
90                  throw new RuntimeException(err,cnfe);
91              }
92              catch (Exception e)
93              {
94                  String err = "The specified class for #evaluate() context (" + contextClass
95                  + ") can not be loaded.";
96                  rsvc.getLog().error(err,e);
97                  throw new RuntimeException(err);
98              }
99  
100             if (!(o instanceof Context))
101             {                
102                 String err = "The specified class for #evaluate() context (" + contextClass
103                 + ") does not implement " + Context.class.getName() + ".";
104                 rsvc.getLog().error(err);
105                 throw new RuntimeException(err);
106             }
107             
108             localContext = (Context) o; 
109 
110         }
111         else
112         {
113             if (rsvc.getLog().isDebugEnabled())
114             {
115                 rsvc.getLog().debug("No class specified for #evaluate() context, "+
116                     "so #set calls will now alter the global context and no longer be local.  "+
117                     "This is a change from earlier versions due to VELOCITY-704.  "+
118                     "If you need references within #evaluate to stay local, "+
119                     "please use the automatically provided $evaluate namespace instead "+
120                     "(e.g. #set($evaluate.foo = 'bar') and $evaluate.foo).");
121             }
122         }
123         
124     }
125 
126     /**
127      *  Put method also stores values in local scope 
128      *
129      *  @param key name of item to set
130      *  @param value object to set to key
131      *  @return old stored object
132      */
133     public Object put(String key, Object value)
134     {
135         if (localContext != null)
136         {
137             return localContext.put(key, value);
138         }
139         return super.put(key, value);
140     }
141 
142     /**
143      *  Retrieves from local or global context.
144      *
145      *  @param key name of item to get
146      *  @return  stored object or null
147      */
148     public Object get( String key )
149     {
150         /*
151          *  always try the local context then innerContext
152          */
153         Object o = null;
154         if (localContext != null)
155         {
156             o = localContext.get(key);
157         }
158         if (o == null)
159         {
160             o = super.get( key );
161         }
162         return o;
163     }
164 
165     /**
166      * @see org.apache.velocity.context.Context#containsKey(java.lang.Object)
167      */
168     public boolean containsKey(Object key)
169     {
170         return (localContext != null && localContext.containsKey(key)) ||
171                super.containsKey(key);
172     }
173 
174     /**
175      * @see org.apache.velocity.context.Context#getKeys()
176      */
177     public Object[] getKeys()
178     {
179         if (localContext != null)
180         {
181             Set keys = new HashSet();
182             Object[] localKeys = localContext.getKeys();
183             for (int i=0; i < localKeys.length; i++)
184             {
185                 keys.add(localKeys[i]);
186             }
187 
188             Object[] innerKeys = super.getKeys();
189             for (int i=0; i < innerKeys.length; i++)
190             {
191                 keys.add(innerKeys[i]);
192             }
193             return keys.toArray();
194         }
195         return super.getKeys();
196     }
197 
198     /**
199      * @see org.apache.velocity.context.Context#remove(java.lang.Object)
200      */
201     public Object remove(Object key)
202     {
203         if (localContext != null)
204         {
205             return localContext.remove(key);
206         }
207         return super.remove(key);
208     }
209 
210     /**
211      * Allows callers to explicitly put objects in the local context.
212      * Objects added to the context through this method always end up
213      * in the top-level context of possible wrapped contexts.
214      *
215      *  @param key name of item to set.
216      *  @param value object to set to key.
217      *  @return old stored object
218      */
219     public Object localPut(final String key, final Object value)
220     {
221         if (localContext != null)
222         {
223             return localContext.put(key, value);
224         }
225         return super.localPut(key, value);
226     }
227 
228 }