1 package org.apache.velocity.tools.generic;
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.Arrays;
23 import java.util.HashSet;
24 import java.util.Iterator;
25 import java.util.Map;
26 import java.util.Set;
27 import org.apache.velocity.context.AbstractContext;
28 import org.apache.velocity.context.Context;
29 import org.apache.velocity.tools.Scope;
30 import org.apache.velocity.tools.ToolContext;
31 import org.apache.velocity.tools.config.DefaultKey;
32 import org.apache.velocity.tools.config.InvalidScope;
33 import org.apache.velocity.tools.generic.ValueParser;
34
35 /**
36 * <p>Tool for convenient access to {@link Context} data and
37 * meta-data.</p>
38 * <p><pre>
39 * Template example(s):
40 * #foreach( $key in $context.keys )
41 * $key = $context.get($key)
42 * #end
43 *
44 * Toolbox configuration:
45 * <tools>
46 * <toolbox scope="request">
47 * <tool class="org.apache.velocity.tools.generic.ContextTool"/>
48 * </toolbox>
49 * </tools>
50 * </pre></p>
51 *
52 * <p>This class is only designed for use as a request-scope tool.</p>
53 *
54 * @author Nathan Bubna
55 * @since VelocityTools 2.0
56 * @version $Id: ContextTool.java 385122 2006-03-11 18:37:42Z nbubna $
57 */
58 @DefaultKey("context")
59 @InvalidScope({Scope.APPLICATION,Scope.SESSION})
60 public class ContextTool extends SafeConfig
61 {
62 protected Context context;
63 protected Map<String,Object> toolbox;
64
65 /**
66 * Initializes this instance for the current request.
67 * Also looks for a safe-mode configuration setting. By default,
68 * safeMode is true and thus keys with '.' in them are hidden.
69 */
70 protected void configure(ValueParser parser)
71 {
72 this.context = (Context)parser.getValue(ToolContext.CONTEXT_KEY);
73 }
74
75
76 /**
77 * Returns the context being analyzed by this tool.
78 */
79 public Context getThis()
80 {
81 return this.context;
82 }
83
84 /**
85 * <p>Returns a read-only view of the toolbox {@link Map}
86 * for this context.</p>
87 * @return a map of all available tools for this request
88 * or {@code null} if such a map is not available
89 */
90 public Map<String,Object> getToolbox()
91 {
92 if (this.toolbox == null && this.context instanceof ToolContext)
93 {
94 this.toolbox = ((ToolContext)context).getToolbox();
95 }
96 return this.toolbox;
97 }
98
99 /**
100 * <p>Return a {@link Set} of the available reference keys in the current
101 * context.</p>
102 */
103 public Set getKeys()
104 {
105 Set keys = new HashSet();
106
107 // fill the keyset in extendable method
108 fillKeyset(keys);
109
110 // if we're in safe mode, remove keys that contain '.'
111 if (isSafeMode())
112 {
113 for (Iterator i = keys.iterator(); i.hasNext(); )
114 {
115 String key = String.valueOf(i.next());
116 if (key.indexOf('.') >= 0)
117 {
118 i.remove();
119 }
120 }
121 }
122 // return the key set
123 return keys;
124 }
125
126
127 /**
128 * Actually do the work of filling in the set of keys
129 * for {@link #getKeys} here so subclasses can add keys too.
130 */
131 protected void fillKeyset(Set keys)
132 {
133 //NOTE: we don't need to manually add the toolbox keys here
134 // because retrieval of those depends on the context being
135 // a ToolContext which would already give tool keys below
136
137 // recurse down the velocity context collecting keys
138 Context velctx = this.context;
139 while (velctx != null)
140 {
141 Object[] ctxKeys = velctx.getKeys();
142 keys.addAll(Arrays.asList(ctxKeys));
143 if (velctx instanceof AbstractContext)
144 {
145 velctx = ((AbstractContext)velctx).getChainedContext();
146 }
147 else
148 {
149 velctx = null;
150 }
151 }
152 }
153
154 /**
155 * <p>Return a {@link Set} of the available values in the current
156 * context.</p>
157 */
158 public Set getValues()
159 {
160 //TODO: this could be a lot more efficient
161 Set keys = getKeys();
162 Set values = new HashSet(keys.size());
163 for (Iterator i = keys.iterator(); i.hasNext(); )
164 {
165 String key = String.valueOf(i.next());
166 values.add(this.context.get(key));
167 }
168 return values;
169 }
170
171
172 /**
173 * <p>Returns {@code true} if the context contains a value for the specified
174 * reference name (aka context key).</p>
175 */
176 public boolean contains(Object refName)
177 {
178 return (get(refName) != null);
179 }
180
181 /**
182 * Retrieves the value for the specified reference name (aka context key).
183 */
184 public Object get(Object refName)
185 {
186 String key = String.valueOf(refName);
187 if (isSafeMode() && key.indexOf('.') >= 0)
188 {
189 return null;
190 }
191 return this.context.get(key);
192 }
193
194 }