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 }