001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017: package org.apache.cocoon.components.variables;
018:
019: import java.util.ArrayList;
020: import java.util.List;
021:
022: import org.apache.avalon.framework.activity.Disposable;
023: import org.apache.avalon.framework.configuration.ConfigurationException;
024: import org.apache.avalon.framework.context.Context;
025: import org.apache.avalon.framework.service.ServiceException;
026: import org.apache.avalon.framework.service.ServiceManager;
027: import org.apache.avalon.framework.service.ServiceSelector;
028: import org.apache.avalon.framework.thread.ThreadSafe;
029: import org.apache.cocoon.components.ContextHelper;
030: import org.apache.cocoon.components.modules.input.InputModule;
031: import org.apache.cocoon.sitemap.PatternException;
032:
033: /**
034: * Prepared implementation of {@link VariableResolver} for fast evaluation.
035: *
036: * @author <a href="mailto:sylvain@apache.org">Sylvain Wallez</a>
037: * @author <a href="mailto:tcurdt@apache.org">Torsten Curdt</a>
038: * @author <a href="mailto:cziegeler@apache.org">Carsten Ziegeler</a>
039: * @version CVS $Id: PreparedVariableResolver.java 433543 2006-08-22 06:22:54Z crossley $
040: */
041: public class PreparedVariableResolver extends NOPVariableResolver
042: implements Disposable {
043:
044: protected ServiceManager manager;
045: protected ServiceSelector selector;
046: protected Context context;
047:
048: protected List items = new ArrayList();
049:
050: // Special constants used for levels
051: static final int LITERAL = -2;
052: static final int THREADSAFE_MODULE = -3;
053: static final int STATEFUL_MODULE = -4;
054:
055: private static final Integer LITERAL_OBJ = new Integer(LITERAL);
056: private static final Integer THREADSAFE_MODULE_OBJ = new Integer(
057: THREADSAFE_MODULE);
058: private static final Integer STATEFUL_MODULE_OBJ = new Integer(
059: STATEFUL_MODULE);
060:
061: public PreparedVariableResolver(String expr,
062: ServiceManager manager, Context context)
063: throws PatternException {
064:
065: super (null);
066: this .expression = expr;
067: this .manager = manager;
068: this .context = context;
069:
070: int length = expr.length();
071: int prev = 0; // position after last closing brace
072:
073: compile: while (prev < length) {
074: // find next unescaped '{'
075: int pos = prev;
076: while (pos < length && (pos = expr.indexOf('{', pos)) != -1
077: && (pos != 0 && expr.charAt(pos - 1) == '\\')) {
078: pos++;
079: }
080:
081: if (pos >= length || pos == -1) {
082: // no more braces : add ending literal
083: if (prev < length) {
084: addLiteral(expr.substring(prev));
085: }
086: break compile;
087: }
088:
089: // Pass closing brace
090: pos++;
091:
092: // Add litteral strings between closing and next opening brace
093: if (prev < pos - 1) {
094: addLiteral(expr.substring(prev, pos - 1));
095: }
096:
097: int end = expr.indexOf('}', pos);
098: if (end == -1) {
099: throw new PatternException("Unmatched '{' in " + expr);
100: }
101:
102: int colon = expr.indexOf(':', pos);
103: if (colon != -1 && colon < end) {
104:
105: String module = expr.substring(pos, colon);
106: String variable = expr.substring(colon + 1, end);
107:
108: // Module used
109: addModuleVariable(module, variable);
110: } else {
111: throw new PatternException("Unknown variable format "
112: + expr.substring(pos, end));
113: }
114:
115: prev = end + 1;
116: }
117: }
118:
119: protected void addLiteral(String litteral) {
120: this .items.add(LITERAL_OBJ);
121: this .items.add(litteral);
122: }
123:
124: protected void addModuleVariable(String moduleName, String variable)
125: throws PatternException {
126: if (this .selector == null) {
127: try {
128: // First access to a module : lookup selector
129: this .selector = (ServiceSelector) this .manager
130: .lookup(InputModule.ROLE + "Selector");
131: } catch (ServiceException ce) {
132: throw new PatternException(
133: "Cannot access input modules selector", ce);
134: }
135: }
136:
137: // Get the module
138: InputModule module;
139: try {
140: module = (InputModule) this .selector.select(moduleName);
141: } catch (ServiceException ce) {
142: throw new PatternException("Cannot get InputModule named '"
143: + moduleName + "' in expression '"
144: + this .expression + "'", ce);
145: }
146:
147: // Is this module threadsafe ?
148: if (module instanceof ThreadSafe) {
149: this .items.add(THREADSAFE_MODULE_OBJ);
150: this .items.add(module);
151: this .items.add(variable);
152: } else {
153: // Statefull module : release it
154: this .selector.release(module);
155: this .items.add(STATEFUL_MODULE_OBJ);
156: this .items.add(moduleName);
157: this .items.add(variable);
158: }
159: }
160:
161: public String resolve() throws PatternException {
162:
163: StringBuffer result = new StringBuffer();
164:
165: for (int i = 0; i < this .items.size(); i++) {
166: int type = ((Integer) this .items.get(i)).intValue();
167:
168: switch (type) {
169: case LITERAL:
170: result.append(items.get(++i));
171: break;
172:
173: case THREADSAFE_MODULE: {
174: InputModule module = (InputModule) items.get(++i);
175: String variable = (String) items.get(++i);
176:
177: try {
178: Object value = module.getAttribute(variable, null,
179: ContextHelper.getObjectModel(this .context));
180:
181: if (value != null) {
182: result.append(value);
183: }
184:
185: } catch (ConfigurationException confEx) {
186: throw new PatternException("Cannot get variable '"
187: + variable + "' in expression '"
188: + this .expression + "'", confEx);
189: }
190: }
191: break;
192:
193: case STATEFUL_MODULE: {
194: InputModule module = null;
195: String moduleName = (String) items.get(++i);
196: String variableName = (String) items.get(++i);
197: try {
198: module = (InputModule) this .selector
199: .select(moduleName);
200:
201: Object value = module.getAttribute(variableName,
202: null, ContextHelper
203: .getObjectModel(this .context));
204:
205: if (value != null) {
206: result.append(value);
207: }
208:
209: } catch (ServiceException compEx) {
210: throw new PatternException("Cannot get module '"
211: + moduleName + "' in expression '"
212: + this .expression + "'", compEx);
213:
214: } catch (ConfigurationException confEx) {
215: throw new PatternException("Cannot get variable '"
216: + variableName + "' in expression '"
217: + this .expression + "'", confEx);
218:
219: } finally {
220: this .selector.release(module);
221: }
222: }
223: break;
224: }
225: }
226:
227: return result.toString();
228:
229: }
230:
231: /* (non-Javadoc)
232: * @see org.apache.avalon.framework.activity.Disposable#dispose()
233: */
234: public void dispose() {
235: if (this .selector != null) {
236: for (int i = 0; i < this .items.size(); i++) {
237: int type = ((Integer) this .items.get(i)).intValue();
238:
239: switch (type) {
240: case LITERAL:
241: i++; // literal string
242: break;
243:
244: case THREADSAFE_MODULE:
245: i++; // module
246: this .selector.release(this .items.get(i));
247: i++; // variable
248: break;
249:
250: case STATEFUL_MODULE:
251: i += 2; // module name, variable
252: break;
253:
254: default:
255: }
256: }
257: this.manager.release(this.selector);
258: this.selector = null;
259: this.manager = null;
260: }
261: }
262: }
|