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.selection;
018:
019: import java.util.HashMap;
020: import java.util.Map;
021:
022: import org.apache.avalon.framework.configuration.Configurable;
023: import org.apache.avalon.framework.configuration.Configuration;
024: import org.apache.avalon.framework.configuration.ConfigurationException;
025: import org.apache.regexp.RE;
026: import org.apache.regexp.RECompiler;
027: import org.apache.regexp.REProgram;
028: import org.apache.regexp.RESyntaxException;
029:
030: /**
031: * <p>The {@link AbstractRegexpSelector} abstract class defines a simple selector
032: * operating over configured regular-expression patterns.</p>
033: *
034: * <p>Configuration of an {@link AbstractRegexpSelector} is quite simple: first of
035: * all the patterns used for selections must be configured:</p>
036: *
037: * <pre>
038: * <map:components>
039: * ...
040: * <map:selectors default="...">
041: * <map:selector name="..." src="org.apache.cocoon.selection....">
042: * <pattern name="empty">^$</pattern>
043: * <pattern name="number">^[0-9]+$</pattern>
044: * <pattern name="string">^.+$</pattern>
045: * </map:selector>
046: * </map:selectors>
047: * </map:components>
048: * </pre>
049: *
050: * <p>Then, each configured pattern can be referenced in the pipelines section of
051: * the sitemap:</p>
052: *
053: * <pre>
054: * <map:pipelines>
055: * ...
056: * <map:match ...>
057: * ...
058: * <map:select type="browser">
059: * <map:when test="empty">...</map:when>
060: * <map:when test="number">...</map:when>
061: * <map:when test="string">...</map:when>
062: * <map:otherwise>...</map:otherwise>
063: * </map:select>
064: * ...
065: * </map:match>
066: * ...
067: * </map:pipelines>
068: * </pre>
069: *
070: * @author <a href="mailto:pier@apache.org">Pier Fumagalli</a>
071: * @version CVS $Id: AbstractRegexpSelector.java 30941 2004-07-29 19:56:58Z vgritsenko $
072: */
073: public abstract class AbstractRegexpSelector extends
074: AbstractSwitchSelector implements Configurable {
075:
076: /** <p>A {@link Map} of regular expression programs by name.</p> */
077: protected Map patterns = new HashMap();
078:
079: /**
080: * <p>Create a new {@link AbstractRegexpSelector} instance.</p>
081: */
082: protected AbstractRegexpSelector() {
083: super ();
084: }
085:
086: /**
087: * <p>Select a pipeline fragment based on a previously configured pattern.</p>
088: *
089: * @param patternName the name of the configured pattern.
090: * @param selectorContext the string to be matched by the named pattern.
091: * @return <b>true</b> if the contexts is matched by the configured pattern.
092: */
093: public boolean select(String patternName, Object selectorContext) {
094:
095: /* Check that the context selection returned something */
096: if (selectorContext == null)
097: return (false);
098:
099: /* Check that we actually have a configured pattern */
100: REProgram pattern = (REProgram) this .patterns.get(patternName);
101: if (pattern == null) {
102: if (this .getLogger().isWarnEnabled()) {
103: this
104: .getLogger()
105: .warn(
106: "The specified pattern name \""
107: + patternName
108: + "\" was not configured in this instance");
109: }
110: return (false);
111: }
112:
113: /* Pattern matching */
114: return (new RE(pattern).match(selectorContext.toString()));
115: }
116:
117: /**
118: * <p>Configure this instance parsing all regular expression patterns.</p>
119: *
120: * @param configuration the {@link Configuration} instance where configured
121: * patterns are defined.
122: * @throws ConfigurationException if one of the regular-expression to configure
123: * could not be compiled.
124: */
125: public void configure(Configuration configuration)
126: throws ConfigurationException {
127: Configuration patterns[] = configuration.getChildren("pattern");
128: for (int x = 0; x < patterns.length; x++) {
129: String name = patterns[x].getAttribute("name");
130: String pattern = patterns[x].getValue();
131: this .patterns.put(name, this .compile(pattern));
132: }
133: }
134:
135: /**
136: * <p>Compile the pattern in a {@link REProgram}.</p>
137: *
138: * @param pattern the regular expression pattern in a textual format.
139: * @return a compiled regular expression pattern.
140: * @throws ConfigurationException in the pattern could not be compiled.
141: */
142: protected REProgram compile(String pattern)
143: throws ConfigurationException {
144: if (pattern == null) {
145: throw new ConfigurationException("Null pattern");
146: }
147:
148: if (pattern.length() == 0) {
149: pattern = "^$";
150: if (this .getLogger().isWarnEnabled()) {
151: this
152: .getLogger()
153: .warn(
154: "The empty pattern string was rewritten to "
155: + "'^$' to match for empty strings. If you "
156: + "intended to match all strings, please "
157: + "change your pattern to '.*'");
158: }
159: }
160:
161: try {
162: RECompiler compiler = new RECompiler();
163: REProgram program = compiler.compile(pattern);
164: return program;
165: } catch (RESyntaxException rse) {
166: getLogger().debug(
167: "Failed to compile the pattern '" + pattern + "'",
168: rse);
169: throw new ConfigurationException(rse.getMessage(), rse);
170: }
171: }
172: }
|