/* * 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; } } }