1 16 package org.apache.cocoon.components.treeprocessor.variables; 17 18 import org.apache.avalon.framework.activity.Disposable; 19 import org.apache.avalon.framework.component.ComponentManager; 20 import org.apache.avalon.framework.configuration.ConfigurationException; 21 import org.apache.avalon.framework.service.ServiceException; 22 import org.apache.avalon.framework.service.ServiceManager; 23 import org.apache.avalon.framework.service.ServiceSelector; 24 import org.apache.avalon.framework.service.WrapperServiceManager; 25 import org.apache.avalon.framework.thread.ThreadSafe; 26 27 import org.apache.cocoon.components.modules.input.InputModule; 28 import org.apache.cocoon.components.treeprocessor.InvokeContext; 29 import org.apache.cocoon.sitemap.PatternException; 30 import org.apache.commons.lang.builder.HashCodeBuilder; 31 32 import java.util.ArrayList ; 33 import java.util.Iterator ; 34 import java.util.List ; 35 import java.util.Map ; 36 import java.util.Stack ; 37 38 44 final public class PreparedVariableResolver extends VariableResolver implements Disposable { 45 46 private ServiceManager manager; 47 private ServiceSelector selector; 48 private List tokens; 49 private boolean needsMapStack; 50 51 private static final int OPEN = -2; 52 private static final int CLOSE = -3; 53 private static final int COLON = -4; 54 private static final int TEXT = -5; 55 private static final int EXPR = -7; 56 private static final int SITEMAP_VAR = -9; 57 private static final int THREADSAFE_MODULE = -10; 58 private static final int STATEFUL_MODULE = -11; 59 private static final int ROOT_SITEMAP_VARIABLE = 0; 60 private static final int ANCHOR_VAR = -1; 61 62 private static Token COLON_TOKEN = new Token(COLON); 63 private static Token OPEN_TOKEN = new Token(OPEN); 64 private static Token CLOSE_TOKEN = new Token(CLOSE); 65 private static Token EMPTY_TOKEN = new Token(EXPR); 66 67 70 public PreparedVariableResolver(String expr, ComponentManager manager) throws PatternException { 71 this(expr, new WrapperServiceManager(manager)); 72 } 73 74 public PreparedVariableResolver(String expr, ServiceManager manager) throws PatternException { 75 super(expr); 76 this.manager = manager; 77 this.tokens = new ArrayList (); 78 79 VariableExpressionTokenizer.tokenize(expr, new VariableExpressionTokenizer.TokenReciever() { 80 public void addToken(int type, String value) throws PatternException { 81 switch (type) { 82 case VariableExpressionTokenizer.TokenReciever.COLON: 83 tokens.add(COLON_TOKEN); 84 break; 85 case VariableExpressionTokenizer.TokenReciever.OPEN: 86 tokens.add(OPEN_TOKEN); 87 break; 88 case VariableExpressionTokenizer.TokenReciever.CLOSE: 89 tokens.add(CLOSE_TOKEN); 90 break; 91 case VariableExpressionTokenizer.TokenReciever.TEXT: 92 tokens.add(new Token(value)); 93 break; 94 case VariableExpressionTokenizer.TokenReciever.MODULE: 95 Token token; 96 if (value.equals("sitemap")) { 97 needsMapStack = true; 99 token = new Token(SITEMAP_VAR); 100 } else if (value.startsWith("#")) { 101 needsMapStack = true; 103 token = new Token(ANCHOR_VAR, value.substring(1)); 104 } else { 105 token = getNewModuleToken(value); 107 } 108 tokens.add(token); 109 break; 110 case VariableExpressionTokenizer.TokenReciever.VARIABLE: 111 needsMapStack = true; 112 tokens.add(getNewVariableToken(value)); 113 break; 114 default: 115 throw new IllegalArgumentException ("Unknown token type: " + type); 116 } 117 } 118 }); 119 } 120 121 private Token getNewVariableToken(String variable) { 122 if (variable.startsWith("/")) { 123 return new Token(ROOT_SITEMAP_VARIABLE, variable.substring(1)); 124 } else { 125 int level = 1; int pos = 0; 128 while (variable.startsWith("../", pos)) { 129 level++; 130 pos += "../".length(); 131 } 132 return new Token(level, variable.substring(pos)); 133 } 134 } 135 136 137 private Token getNewModuleToken(String moduleName) throws PatternException { 138 if (this.selector == null) { 139 try { 140 this.selector = (ServiceSelector) this.manager.lookup(InputModule.ROLE + "Selector"); 142 } catch(ServiceException ce) { 143 throw new PatternException("Cannot access input modules selector", ce); 144 } 145 } 146 147 InputModule module; 149 try { 150 module = (InputModule) this.selector.select(moduleName); 151 } catch (ServiceException e) { 152 throw new PatternException("Cannot get module named '" + moduleName + 153 "' in expression '" + this.originalExpr + "'", e); 154 } 155 156 Token token; 157 if (module instanceof ThreadSafe) { 159 token = new Token(THREADSAFE_MODULE, module); 160 } else { 161 this.selector.release(module); 163 token = new Token(STATEFUL_MODULE, moduleName); 164 } 165 return token; 166 } 167 168 public final String resolve(InvokeContext context, Map objectModel) throws PatternException { 169 List mapStack = null; int stackSize = 0; 171 172 if (needsMapStack) { 173 if (context == null) { 174 throw new PatternException("Need an invoke context to resolve " + this); 175 } 176 mapStack = context.getMapStack(); 177 stackSize = mapStack.size(); 178 } 179 180 Stack stack = new Stack (); 181 182 for (Iterator i = tokens.iterator(); i.hasNext();) { 183 Token token = (Token) i.next(); 184 Token last; 185 switch (token.getType()){ 186 case TEXT: 187 if (stack.empty()) { 188 stack.push(new Token(EXPR, token.getStringValue())); 189 } else { 190 last = (Token)stack.peek(); 191 if (last.hasType(EXPR)) { 192 last.merge(token); 193 } else { 194 stack.push(new Token(EXPR, token.getStringValue())); 195 } 196 } 197 break; 198 case CLOSE: 199 Token expr = (Token)stack.pop(); 200 Token lastButOne = (Token)stack.pop(); 201 Token result; 202 if (expr.hasType(COLON)) { stack.pop(); result = processModule(lastButOne, EMPTY_TOKEN, objectModel, context, mapStack, stackSize); 205 } else if (lastButOne.hasType(COLON)) { 206 Token module = (Token)stack.pop(); 207 stack.pop(); result = processModule(module, expr, objectModel, context, mapStack, stackSize); 209 } else { 210 result = processVariable(expr, mapStack, stackSize); 211 } 212 if (stack.empty()) { 213 stack.push(result); 214 } else { 215 last = (Token)stack.peek(); 216 if (last.hasType(EXPR)) { 217 last.merge(result); 218 } else { 219 stack.push(result); 220 } 221 } 222 break; 223 case OPEN: 224 case COLON: 225 case ANCHOR_VAR: 226 case THREADSAFE_MODULE: 227 case STATEFUL_MODULE: 228 case ROOT_SITEMAP_VARIABLE: 229 default: { 230 stack.push(token); 231 break; 232 } 233 } 234 } 235 if (stack.size() !=1) { 236 throw new PatternException("Evaluation error in expression: " + originalExpr); 237 } 238 return ((Token)stack.pop()).getStringValue(); 239 } 240 241 private Token processModule(Token module, Token expr, Map objectModel, InvokeContext context, List mapStack, int stackSize) throws PatternException { 242 int type = module.getType(); 243 244 if (type == ANCHOR_VAR) { 245 Map levelResult = context.getMapByAnchor(module.getStringValue()); 246 247 if (levelResult == null) { 248 throw new PatternException("Error while evaluating '" + this.originalExpr + 249 "' : no anchor '" + String.valueOf(module.getStringValue()) + "' found in context"); 250 } 251 252 Object result = levelResult.get(expr.getStringValue()); 253 return new Token(EXPR, result==null ? "" : result.toString()); 254 } else if (type == THREADSAFE_MODULE) { 255 try { 256 InputModule im = module.getModule(); 257 Object result = im.getAttribute(expr.getStringValue(), null, objectModel); 258 return new Token(EXPR, result==null ? "" : result.toString()); 259 260 } catch(ConfigurationException confEx) { 261 throw new PatternException("Cannot get variable '" + expr.getStringValue() + 262 "' in expression '" + this.originalExpr + "'", confEx); 263 } 264 265 } else if (type == STATEFUL_MODULE) { 266 InputModule im = null; 267 String moduleName = module.getStringValue(); 268 try { 269 im = (InputModule) this.selector.select(moduleName); 270 271 Object result = im.getAttribute(expr.getStringValue(), null, objectModel); 272 return new Token(EXPR, result==null ? "" : result.toString()); 273 274 } catch(ServiceException e) { 275 throw new PatternException("Cannot get module '" + moduleName + 276 "' in expression '" + this.originalExpr + "'", e); 277 278 } catch(ConfigurationException confEx) { 279 throw new PatternException("Cannot get variable '" + expr.getStringValue() + 280 "' in expression '" + this.originalExpr + "'", confEx); 281 282 } finally { 283 this.selector.release(im); 284 } 285 } else if (type == SITEMAP_VAR) { 286 String variable = expr.getStringValue(); 288 Token token; 289 if (variable.startsWith("/")) { 290 token = new Token(ROOT_SITEMAP_VARIABLE, variable.substring(1)); 291 } else { 292 int level = 1; int pos = 0; 295 while (variable.startsWith("../", pos)) { 296 level++; 297 pos += "../".length(); 298 } 299 token = new Token(level, variable.substring(pos)); 300 } 301 return processVariable(token, mapStack, stackSize); 302 } else { 303 throw new PatternException("Unknown token type: " + expr.getType()); 304 } 305 } 306 307 private Token processVariable(Token expr, List mapStack, int stackSize) throws PatternException { 308 int type = expr.getType(); 309 String value = expr.getStringValue(); 310 if (type == ROOT_SITEMAP_VARIABLE) { 311 Object result = ((Map )mapStack.get(0)).get(value); 312 return new Token(EXPR, result==null ? "" : result.toString()); 313 } else { 314 if (type > stackSize) { 316 throw new PatternException("Error while evaluating '" + this.originalExpr + 317 "' : not so many levels"); 318 } 319 320 Object result = ((Map )mapStack.get(stackSize - type)).get(value); 321 return new Token(EXPR, result==null ? "" : result.toString()); 322 } 323 } 324 325 public final void dispose() { 326 if (this.selector != null) { 327 for (Iterator i = tokens.iterator(); i.hasNext();) { 328 Token token = (Token)i.next(); 329 if (token.hasType(THREADSAFE_MODULE)) { 330 InputModule im = token.getModule(); 331 this.selector.release(im); 332 } 333 } 334 this.manager.release(this.selector); 335 this.selector = null; 336 this.manager = null; 337 } 338 } 339 340 private static class Token { 341 342 private Object value; 343 private int type; 344 345 public Token(int type) { 346 if (type==EXPR) { 347 this.value=""; 348 } else { 349 this.value = null; 350 } 351 this.type = type; 352 } 353 354 public Token(int type, String value) { 355 this.value = value; 356 this.type = type; 357 } 358 359 public Token(int type, InputModule module) { 360 this.value = module; 361 this.type = type; 362 } 363 364 public Token(String value) { 365 this.type = TEXT; 366 this.value = value; 367 } 368 369 public int getType() { 370 return type; 371 } 372 373 public String getStringValue() { 374 if (value instanceof String ) { 375 return (String )this.value; 376 } else { 377 return null; 378 } 379 } 380 381 public boolean hasType(int type){ 382 return this.type == type; 383 } 384 385 public boolean equals(Object o) { 386 if (o instanceof Token) { 387 return ((Token)o).hasType(this.type); 388 } else { 389 return false; 390 } 391 } 392 393 public int hashCode() { 394 return new HashCodeBuilder(). 395 append(value). 396 append(this.type). 397 toHashCode(); 398 } 399 400 public void merge(Token newToken) { 401 this.value = this.value + newToken.getStringValue(); 402 } 403 404 public InputModule getModule() { 405 if (value instanceof InputModule) { 406 return (InputModule)value; 407 } else { 408 return null; 409 } 410 } 411 } 412 } 413 | Popular Tags |