blob: 2f7abdcf2734f85080bd3145dd7c4540f2e2b646 [file] [log] [blame]
/*
* Copyright 1999-2004 The Apache Software Foundation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.cocoon.components.treeprocessor.variables;
import org.apache.avalon.framework.activity.Disposable;
import org.apache.avalon.framework.component.ComponentException;
import org.apache.avalon.framework.component.ComponentManager;
import org.apache.avalon.framework.component.ComponentSelector;
import org.apache.avalon.framework.configuration.ConfigurationException;
import org.apache.avalon.framework.thread.ThreadSafe;
import org.apache.cocoon.components.modules.input.InputModule;
import org.apache.cocoon.components.treeprocessor.InvokeContext;
import org.apache.cocoon.sitemap.PatternException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
/**
* Prepared implementation of {@link VariableResolver} for fast evaluation.
*
* @author <a href="mailto:sylvain@apache.org">Sylvain Wallez</a>
* @author <a href="mailto:tcurdt@apache.org">Torsten Curdt</a>
* @version CVS $Id: PreparedVariableResolver.java,v 1.3 2004/03/05 13:02:53 bdelacretaz Exp $
*/
final public class PreparedVariableResolver extends VariableResolver implements Disposable {
private ComponentManager manager;
private ComponentSelector selector;
final private List items = new ArrayList();
// Special constants used for levels
// ROOT and ANCHOR are placed first as they need a context to be resolved (see resolve())
static final int ROOT = 0;
static final int ANCHOR = -1;
static final int LITERAL = -2;
static final int THREADSAFE_MODULE = -3;
static final int STATEFUL_MODULE = -4;
private static final Integer ROOT_OBJ = new Integer(ROOT);
private static final Integer LITERAL_OBJ = new Integer(LITERAL);
private static final Integer THREADSAFE_MODULE_OBJ = new Integer(THREADSAFE_MODULE);
private static final Integer STATEFUL_MODULE_OBJ = new Integer(STATEFUL_MODULE);
private static final Integer ANCHOR_OBJ = new Integer(ANCHOR);
public PreparedVariableResolver(String expr, ComponentManager manager) throws PatternException {
super(expr);
this.manager = manager;
int length = expr.length();
int prev = 0; // position after last closing brace
compile : while(prev < length) {
// find next unescaped '{'
int pos = prev;
while(pos < length &&
(pos = expr.indexOf('{', pos)) != -1 &&
(pos != 0 && expr.charAt(pos - 1) == '\\')) {
pos++;
}
if (pos >= length || pos == -1) {
// no more braces : add ending literal
if (prev < length) {
addLiteral(expr.substring(prev));
}
break compile;
}
// Pass closing brace
pos++;
// Add litteral strings between closing and next opening brace
if (prev < pos-1) {
addLiteral(expr.substring(prev, pos - 1));
}
int end = expr.indexOf('}', pos);
if (end == -1) {
throw new PatternException("Unmatched '{' in " + expr);
}
int colon = expr.indexOf(':', pos);
if (colon != -1 && colon < end) {
if (expr.startsWith("sitemap:", pos)) {
// Explicit prefix for sitemap variable
String variable = expr.substring(pos + "sitemap:".length(), end);
addSitemapVariable(variable);
} else {
String module = expr.substring(pos, colon);
String variable = expr.substring(colon + 1, end);
if (module.startsWith("#")) {
// anchor syntax refering to a name result level
addAnchorVariable(module.substring(1), variable);
}
else {
// Module used
addModuleVariable(module, variable);
}
}
} else {
// Unprefixed name : sitemap variable
addSitemapVariable(expr.substring(pos, end));
}
prev = end + 1;
}
}
private void addLiteral(String litteral) {
this.items.add(LITERAL_OBJ);
this.items.add(litteral);
}
private void addSitemapVariable(String variable) {
if (variable.startsWith("/")) {
this.items.add(ROOT_OBJ);
this.items.add(variable.substring(1));
}
else {
// Find level
int level = 1; // Start at 1 since it will be substracted from list.size()
int pos = 0;
while (variable.startsWith("../", pos)) {
level++;
pos += "../".length();
}
this.items.add(new Integer(level));
this.items.add(variable.substring(pos));
}
}
private void addAnchorVariable(String anchor, String variable) throws PatternException {
this.items.add(ANCHOR_OBJ);
this.items.add(anchor);
this.items.add(variable);
}
private void addModuleVariable(String moduleName, String variable) throws PatternException {
if (this.selector == null) {
try {
// First access to a module : lookup selector
this.selector = (ComponentSelector)this.manager.lookup(InputModule.ROLE + "Selector");
} catch(ComponentException ce) {
throw new PatternException("Cannot access input modules selector", ce);
}
}
// Get the module
InputModule module;
try {
module = (InputModule)this.selector.select(moduleName);
} catch(ComponentException ce) {
throw new PatternException("Cannot get InputModule named '" + moduleName +
"' in expression '" + this.originalExpr + "'", ce);
}
// Is this module threadsafe ?
if (module instanceof ThreadSafe) {
this.items.add(THREADSAFE_MODULE_OBJ);
this.items.add(module);
this.items.add(variable);
} else {
// Statefull module : release it
this.selector.release(module);
this.items.add(STATEFUL_MODULE_OBJ);
this.items.add(moduleName);
this.items.add(variable);
}
}
public final String resolve(InvokeContext context, Map objectModel) throws PatternException {
List mapStack = null; // get the stack only when necessary - lazy inside the loop
int stackSize = 0;
StringBuffer result = new StringBuffer();
for (int i = 0; i < this.items.size(); i++) {
int type = ((Integer)this.items.get(i)).intValue();
if (type >= ANCHOR && mapStack == null) {
if (context == null) {
throw new PatternException("Need an invoke context to resolve " + this);
}
mapStack = context.getMapStack();
stackSize = mapStack.size();
}
if (type > 0) {
// relative sitemap variable
if (type > stackSize) {
throw new PatternException("Error while evaluating '" + this.originalExpr +
"' : not so many levels");
}
Object key = this.items.get(++i);
Object value = ((Map)mapStack.get(stackSize - type)).get(key);
if (value != null) {
result.append(value);
}
} else {
// other variable types
switch(type) {
case LITERAL :
result.append(items.get(++i));
break;
case ROOT :
{
Object key = this.items.get(++i);
Object value = ((Map)mapStack.get(0)).get(key);
if (value != null) {
result.append(value);
}
}
break;
case ANCHOR:
{
String name = (String) this.items.get(++i);
Object variable = this.items.get(++i);
Map levelResult = context.getMapByAnchor(name);
if (levelResult == null) {
throw new PatternException("Error while evaluating '" + this.originalExpr +
"' : no anchor '" + String.valueOf(name) + "' found in context");
}
Object value = levelResult.get(variable);
if (value != null) {
result.append(value);
}
}
break;
case THREADSAFE_MODULE :
{
InputModule module = (InputModule)items.get(++i);
String variable = (String)items.get(++i);
try {
Object value = module.getAttribute(variable, null, objectModel);
if (value != null) {
result.append(value);
}
} catch(ConfigurationException confEx) {
throw new PatternException("Cannot get variable '" + variable +
"' in expression '" + this.originalExpr + "'", confEx);
}
}
break;
case STATEFUL_MODULE :
{
InputModule module = null;
String moduleName = (String)items.get(++i);
String variableName = (String)items.get(++i);
try {
module = (InputModule)this.selector.select(moduleName);
Object value = module.getAttribute(variableName, null, objectModel);
if (value != null) {
result.append(value);
}
} catch(ComponentException compEx) {
throw new PatternException("Cannot get module '" + moduleName +
"' in expression '" + this.originalExpr + "'", compEx);
} catch(ConfigurationException confEx) {
throw new PatternException("Cannot get variable '" + variableName +
"' in expression '" + this.originalExpr + "'", confEx);
} finally {
this.selector.release(module);
}
}
break;
}
}
}
return result.toString();
}
public final void dispose() {
if (this.selector != null) {
for (int i = 0; i < this.items.size(); i++) {
int type = ((Integer) this.items.get(i)).intValue();
switch (type) {
case ROOT:
i++; // variable
break;
case LITERAL:
i++; // literal string
break;
case ANCHOR:
i += 2; // anchor name, variable
break;
case THREADSAFE_MODULE:
i++; // module
this.selector.release((InputModule) this.items.get(i));
i++; // variable
break;
case STATEFUL_MODULE:
i += 2; // module name, variable
break;
default:
// relative sitemap variable
i++; // variable
}
}
this.manager.release(this.selector);
this.selector = null;
this.manager = null;
}
}
}