blob: c5a98d3f27a8177939eb01ba348e4e1a93ebba31 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.variables;
import java.util.ArrayList;
import java.util.List;
import org.apache.avalon.framework.activity.Disposable;
import org.apache.avalon.framework.configuration.ConfigurationException;
import org.apache.avalon.framework.context.Context;
import org.apache.avalon.framework.service.ServiceException;
import org.apache.avalon.framework.service.ServiceManager;
import org.apache.avalon.framework.service.ServiceSelector;
import org.apache.avalon.framework.thread.ThreadSafe;
import org.apache.cocoon.components.ContextHelper;
import org.apache.cocoon.components.modules.input.InputModule;
import org.apache.cocoon.sitemap.PatternException;
/**
* 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>
* @author <a href="mailto:cziegeler@apache.org">Carsten Ziegeler</a>
* @version CVS $Id$
*/
public class PreparedVariableResolver
extends NOPVariableResolver
implements Disposable {
protected ServiceManager manager;
protected ServiceSelector selector;
protected Context context;
protected List items = new ArrayList();
// Special constants used for levels
static final int LITERAL = -2;
static final int THREADSAFE_MODULE = -3;
static final int STATEFUL_MODULE = -4;
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);
public PreparedVariableResolver(String expr, ServiceManager manager, Context context)
throws PatternException {
super(null);
this.expression = expr;
this.manager = manager;
this.context = context;
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) {
String module = expr.substring(pos, colon);
String variable = expr.substring(colon + 1, end);
// Module used
addModuleVariable(module, variable);
} else {
throw new PatternException("Unknown variable format " + expr.substring(pos, end));
}
prev = end + 1;
}
}
protected void addLiteral(String litteral) {
this.items.add(LITERAL_OBJ);
this.items.add(litteral);
}
protected void addModuleVariable(String moduleName, String variable) throws PatternException {
if (this.selector == null) {
try {
// First access to a module : lookup selector
this.selector = (ServiceSelector)this.manager.lookup(InputModule.ROLE + "Selector");
} catch(ServiceException ce) {
throw new PatternException("Cannot access input modules selector", ce);
}
}
// Get the module
InputModule module;
try {
module = (InputModule)this.selector.select(moduleName);
} catch(ServiceException ce) {
throw new PatternException("Cannot get InputModule named '" + moduleName +
"' in expression '" + this.expression + "'", 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 String resolve()
throws PatternException {
StringBuffer result = new StringBuffer();
for (int i = 0; i < this.items.size(); i++) {
int type = ((Integer)this.items.get(i)).intValue();
switch(type) {
case LITERAL :
result.append(items.get(++i));
break;
case THREADSAFE_MODULE :
{
InputModule module = (InputModule)items.get(++i);
String variable = (String)items.get(++i);
try {
Object value = module.getAttribute(variable, null, ContextHelper.getObjectModel(this.context));
if (value != null) {
result.append(value);
}
} catch(ConfigurationException confEx) {
throw new PatternException("Cannot get variable '" + variable +
"' in expression '" + this.expression + "'", 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, ContextHelper.getObjectModel(this.context));
if (value != null) {
result.append(value);
}
} catch(ServiceException compEx) {
throw new PatternException("Cannot get module '" + moduleName +
"' in expression '" + this.expression + "'", compEx);
} catch(ConfigurationException confEx) {
throw new PatternException("Cannot get variable '" + variableName +
"' in expression '" + this.expression + "'", confEx);
} finally {
this.selector.release(module);
}
}
break;
}
}
return result.toString();
}
/* (non-Javadoc)
* @see org.apache.avalon.framework.activity.Disposable#dispose()
*/
public 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 LITERAL:
i++; // literal string
break;
case THREADSAFE_MODULE:
i++; // module
this.selector.release(this.items.get(i));
i++; // variable
break;
case STATEFUL_MODULE:
i += 2; // module name, variable
break;
default:
}
}
this.manager.release(this.selector);
this.selector = null;
this.manager = null;
}
}
}