1 package org.apache.velocity.runtime.directive;
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.AbstractMap;
23 import java.util.HashMap;
24 import java.util.Map;
25 import java.util.Set;
26 import org.apache.velocity.Template;
27
28 /**
29 * This handles context scoping and metadata for directives.
30 *
31 * @author Nathan Bubna
32 * @version $Id$
33 */
34 public class Scope extends AbstractMap
35 {
36 private Map storage;
37 private Object replaced;
38 private Scope parent;
39 private Info info;
40 protected final Object owner;
41
42 public Scope(Object owner, Object previous)
43 {
44 this.owner = owner;
45 if (previous != null)
46 {
47 try
48 {
49 this.parent = (Scope)previous;
50 }
51 catch (ClassCastException cce)
52 {
53 this.replaced = previous;
54 }
55 }
56 }
57
58 private Map getStorage()
59 {
60 if (storage == null)
61 {
62 storage = new HashMap();
63 }
64 return storage;
65 }
66
67 public Set entrySet()
68 {
69 return getStorage().entrySet();
70 }
71
72 public Object get(Object key)
73 {
74 Object o = super.get(key);
75 if (o == null && parent != null && !containsKey(key))
76 {
77 return parent.get(key);
78 }
79 return o;
80 }
81
82 public Object put(Object key, Object value)
83 {
84 return getStorage().put(key, value);
85 }
86
87 /**
88 * Allows #stop to easily trigger the proper StopCommand for this scope.
89 */
90 protected void stop()
91 {
92 throw new StopCommand(owner);
93 }
94
95 /**
96 * Returns the number of control arguments of this type
97 * that are stacked up. This is the distance between this
98 * instance and the topmost instance, plus one. This value
99 * will never be negative or zero.
100 */
101 protected int getDepth()
102 {
103 if (parent == null)
104 {
105 return 1;
106 }
107 return parent.getDepth() + 1;
108 }
109
110 /**
111 * Returns the topmost parent control reference, retrieved
112 * by simple recursion on {@link #getParent}.
113 */
114 public Scope getTopmost()
115 {
116 if (parent == null)
117 {
118 return this;
119 }
120 return parent.getTopmost();
121 }
122
123 /**
124 * Returns the parent control reference overridden by the placement
125 * of this instance in the context.
126 */
127 public Scope getParent()
128 {
129 return parent;
130 }
131
132 /**
133 * Returns the user's context reference overridden by the placement
134 * of this instance in the context. If there was none (as is hoped),
135 * then this will return null. This never returns parent controls;
136 * those are returned by {@link #getParent}.
137 */
138 public Object getReplaced()
139 {
140 if (replaced == null && parent != null)
141 {
142 return parent.getReplaced();
143 }
144 return replaced;
145 }
146
147 /**
148 * Returns info about the current scope for debugging purposes.
149 */
150 public Info getInfo()
151 {
152 if (info == null)
153 {
154 info = new Info(this, owner);
155 }
156 return info;
157 }
158
159 /**
160 * Class to encapsulate and provide access to info about
161 * the current scope for debugging.
162 */
163 public static class Info
164 {
165 private Scope scope;
166 private Directive directive;
167 private Template template;
168
169 public Info(Scope scope, Object owner)
170 {
171 if (owner instanceof Directive)
172 {
173 directive = (Directive)owner;
174 }
175 if (owner instanceof Template)
176 {
177 template = (Template)owner;
178 }
179 this.scope = scope;
180 }
181
182 public String getName()
183 {
184 if (directive != null)
185 {
186 return directive.getName();
187 }
188 if (template != null)
189 {
190 return template.getName();
191 }
192 return null;
193 }
194
195 public String getType()
196 {
197 if (directive != null)
198 {
199 switch (directive.getType())
200 {
201 case Directive.BLOCK:
202 return "block";
203 case Directive.LINE:
204 return "line";
205 }
206 }
207 if (template != null)
208 {
209 return template.getEncoding();
210 }
211 return null;
212 }
213
214 public int getDepth()
215 {
216 return scope.getDepth();
217 }
218
219 public String getTemplate()
220 {
221 if (directive != null)
222 {
223 return directive.getTemplateName();
224 }
225 if (template != null)
226 {
227 return template.getName();
228 }
229 return null;
230 }
231
232 public int getLine()
233 {
234 if (directive != null)
235 {
236 return directive.getLine();
237 }
238 return 0;
239 }
240
241 public int getColumn()
242 {
243 if (directive != null)
244 {
245 return directive.getColumn();
246 }
247 return 0;
248 }
249
250 public String toString()
251 {
252 StringBuffer sb = new StringBuffer();
253 if (directive != null)
254 {
255 sb.append('#');
256 }
257 sb.append(getName());
258 sb.append("[type:").append(getType());
259 int depth = getDepth();
260 if (depth > 1)
261 {
262 sb.append(" depth:").append(depth);
263 }
264 if (template == null)
265 {
266 String vtl = getTemplate();
267 sb.append(" template:");
268 if (vtl.indexOf(" ") < 0)
269 {
270 sb.append(vtl);
271 }
272 else
273 {
274 sb.append('"').append(vtl).append('"');
275 }
276 sb.append(" line:").append(getLine());
277 sb.append(" column:").append(getColumn());
278 }
279 sb.append(']');
280 return sb.toString();
281 }
282 }
283
284 }