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 org.apache.velocity.Template; 23 24 import java.util.AbstractMap; 25 import java.util.HashMap; 26 import java.util.Map; 27 import java.util.Set; 28 29 /** 30 * This handles context scoping and metadata for directives. 31 * 32 * @author Nathan Bubna 33 * @version $Id$ 34 */ 35 public class Scope extends AbstractMap<Object, Object> 36 { 37 private static final String setReturnValue = ""; 38 private Map<Object, Object> storage; 39 private Object replaced; 40 private Scope parent; 41 private Info info; 42 protected final Object owner; 43 44 /** 45 * @param owner 46 * @param previous 47 */ Scope(Object owner, Object previous)48 public Scope(Object owner, Object previous) 49 { 50 this.owner = owner; 51 if (previous != null) 52 { 53 try 54 { 55 this.parent = (Scope)previous; 56 } 57 catch (ClassCastException cce) 58 { 59 this.replaced = previous; 60 } 61 } 62 } 63 getStorage()64 private Map<Object, Object> getStorage() 65 { 66 if (storage == null) 67 { 68 storage = new HashMap<>(); 69 } 70 return storage; 71 } 72 73 /** 74 * @return entry set 75 */ 76 @Override entrySet()77 public Set<Map.Entry<Object, Object>> entrySet() 78 { 79 return getStorage().entrySet(); 80 } 81 82 /** 83 * getter 84 * @param key 85 * @return found value 86 */ 87 @Override get(Object key)88 public Object get(Object key) 89 { 90 Object o = super.get(key); 91 if (o == null && parent != null && !containsKey(key)) 92 { 93 return parent.get(key); 94 } 95 return o; 96 } 97 98 /** 99 * setter 100 * @param key 101 * @param value 102 * @return previous value 103 */ 104 @Override put(Object key, Object value)105 public Object put(Object key, Object value) 106 { 107 return getStorage().put(key, value); 108 } 109 110 /** 111 * Convenience method to call put(key,val) in a template 112 * without worrying about what is returned/rendered by the call. 113 * This should ALWAYS return an empty string. 114 * @param key 115 * @param value 116 * @return empty string 117 */ set(Object key, Object value)118 public String set(Object key, Object value) 119 { 120 put(key, value); 121 return setReturnValue; 122 } 123 124 /** 125 * Allows #stop to easily trigger the proper StopCommand for this scope. 126 */ stop()127 protected void stop() 128 { 129 throw new StopCommand(owner); 130 } 131 132 /** 133 * Returns the number of control arguments of this type 134 * that are stacked up. This is the distance between this 135 * instance and the topmost instance, plus one. This value 136 * will never be negative or zero. 137 * @return depth 138 */ getDepth()139 protected int getDepth() 140 { 141 if (parent == null) 142 { 143 return 1; 144 } 145 return parent.getDepth() + 1; 146 } 147 148 /** 149 * Returns the topmost parent control reference, retrieved 150 * by simple recursion on {@link #getParent}. 151 * @return top-most scope 152 */ getTopmost()153 public Scope getTopmost() 154 { 155 if (parent == null) 156 { 157 return this; 158 } 159 return parent.getTopmost(); 160 } 161 162 /** 163 * Returns the parent control reference overridden by the placement 164 * of this instance in the context. 165 * @return parent scope 166 */ getParent()167 public Scope getParent() 168 { 169 return parent; 170 } 171 172 /** 173 * Returns the user's context reference overridden by the placement 174 * of this instance in the context. If there was none (as is hoped), 175 * then this will return null. This never returns parent controls; 176 * those are returned by {@link #getParent}. 177 * @return replaced reference value, or null 178 */ getReplaced()179 public Object getReplaced() 180 { 181 if (replaced == null && parent != null) 182 { 183 return parent.getReplaced(); 184 } 185 return replaced; 186 } 187 188 /** 189 * Returns info about the current scope for debugging purposes. 190 * @return template debugging infos 191 */ getInfo()192 public Info getInfo() 193 { 194 if (info == null) 195 { 196 info = new Info(this, owner); 197 } 198 return info; 199 } 200 201 /** 202 * Class to encapsulate and provide access to info about 203 * the current scope for debugging. 204 */ 205 public static class Info 206 { 207 private Scope scope; 208 private Directive directive; 209 private Template template; 210 211 /** 212 * c'tor 213 * @param scope 214 * @param owner 215 */ Info(Scope scope, Object owner)216 public Info(Scope scope, Object owner) 217 { 218 if (owner instanceof Directive) 219 { 220 directive = (Directive)owner; 221 } 222 if (owner instanceof Template) 223 { 224 template = (Template)owner; 225 } 226 this.scope = scope; 227 } 228 229 /** 230 * name getter 231 * @return name 232 */ getName()233 public String getName() 234 { 235 if (directive != null) 236 { 237 return directive.getName(); 238 } 239 if (template != null) 240 { 241 return template.getName(); 242 } 243 return null; 244 } 245 246 /** 247 * type getter 248 * @return scope type 249 */ getType()250 public String getType() 251 { 252 if (directive != null) 253 { 254 switch (directive.getType()) 255 { 256 case Directive.BLOCK: 257 return "block"; 258 case Directive.LINE: 259 return "line"; 260 } 261 } 262 if (template != null) 263 { 264 return template.getEncoding(); 265 } 266 return null; 267 } 268 269 /** 270 * current depth 271 * @return depth 272 */ getDepth()273 public int getDepth() 274 { 275 return scope.getDepth(); 276 } 277 278 /** 279 * template name getter 280 * @return template name 281 */ getTemplate()282 public String getTemplate() 283 { 284 if (directive != null) 285 { 286 return directive.getTemplateName(); 287 } 288 if (template != null) 289 { 290 return template.getName(); 291 } 292 return null; 293 } 294 295 /** 296 * line getter 297 * @return line number 298 */ getLine()299 public int getLine() 300 { 301 if (directive != null) 302 { 303 return directive.getLine(); 304 } 305 return 0; 306 } 307 308 /** 309 * column getter 310 * @return column number 311 */ getColumn()312 public int getColumn() 313 { 314 if (directive != null) 315 { 316 return directive.getColumn(); 317 } 318 return 0; 319 } 320 321 /** 322 * string representation getter 323 * @return string representation 324 */ toString()325 public String toString() 326 { 327 StringBuilder sb = new StringBuilder(); 328 if (directive != null) 329 { 330 sb.append('#'); // parser characters substitution is not handled here 331 } 332 sb.append(getName()); 333 sb.append("[type:").append(getType()); 334 int depth = getDepth(); 335 if (depth > 1) 336 { 337 sb.append(" depth:").append(depth); 338 } 339 if (template == null) 340 { 341 String vtl = getTemplate(); 342 sb.append(" template:"); 343 if (!vtl.contains(" ")) 344 { 345 sb.append(vtl); 346 } 347 else 348 { 349 sb.append('"').append(vtl).append('"'); 350 } 351 sb.append(" line:").append(getLine()); 352 sb.append(" column:").append(getColumn()); 353 } 354 sb.append(']'); 355 return sb.toString(); 356 } 357 } 358 359 } 360