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 */
017package org.apache.activemq.filter.function;
018
019import java.util.regex.Matcher;
020import java.util.regex.Pattern;
021import org.apache.activemq.filter.FunctionCallExpression;
022import org.apache.activemq.filter.MessageEvaluationContext;
023import org.apache.activemq.util.LRUCache;
024
025/**
026 * Filter function that matches a value against a regular expression.
027 * <p/>
028 * <p style="margin-left: 4em">
029 * REGEX( 'A.B', 'A-B' )
030 * </p>
031 * <p/>
032 * Note that the regular expression is not anchored; use the anchor characters, ^ and $, as-needed.  For example,
033 * REGEX( 'AA', 'XAAX' ) evaluates to true while REGEX( '^AA$' , 'XAAX' ) evaluates to false.
034 */
035
036public class regexMatchFunction implements FilterFunction {
037    protected static final LRUCache<String, Pattern> compiledExprCache = new LRUCache(100);
038
039    /**
040     * Check whether the given expression is a valid call of this function.  Two arguments are required.  When
041     * evaluated, the arguments are converted to strings if they are not already strings.
042     *
043     * @param    expr - the expression consisting of a call to this function.
044     * @return true - if the expression is valid; false - otherwise.
045     */
046
047    public boolean isValid(FunctionCallExpression expr) {
048        if (expr.getNumArguments() == 2)
049            return true;
050
051        return false;
052    }
053
054
055    /**
056     * Indicate that this Filter Function evaluates to a Boolean result.
057     *
058     * @param    expr - the expression consisting of a call to this function.
059     * @return true - this function always evaluates to a Boolean result.
060     */
061
062    public boolean returnsBoolean(FunctionCallExpression expr) {
063        return true;
064    }
065
066    /**
067     * Evalutate the given expression, which consists of a call to this function, in the context given.  Returns
068     * an indication of whether the second argument matches the regular expression in the first argument.
069     *
070     * @param    expr - the expression consisting of a call to this function.
071     * @param    message_ctx - the context in which the call is being evaluated.
072     * @return true - if the value matches the regular expression; false - otherwise.
073     */
074
075    public Object evaluate(FunctionCallExpression expr, MessageEvaluationContext message)
076            throws javax.jms.JMSException {
077        Object reg;
078        Object cand;
079        String reg_str;
080        String cand_str;
081        Pattern pat;
082        Matcher match_eng;
083
084        //
085        // Evaluate the first argument (the regular expression).
086        //
087        reg = expr.getArgument(0).evaluate(message);
088
089        if (reg != null) {
090            // Convert to a string, if it's not already a string.
091            if (reg instanceof String)
092                reg_str = (String) reg;
093            else
094                reg_str = reg.toString();
095
096
097            //
098            // Evaluate the second argument (the candidate to match against the regular
099            //  expression).
100            //
101            cand = expr.getArgument(1).evaluate(message);
102
103            if (cand != null) {
104                // Convert to a string, if it's not already a string.
105                if (cand instanceof String)
106                    cand_str = (String) cand;
107                else
108                    cand_str = cand.toString();
109
110
111                //
112                // Obtain the compiled regular expression and match it.
113                //
114
115                pat = getCompiledPattern(reg_str);
116                match_eng = pat.matcher(cand_str);
117
118
119                //
120                // Return an indication of whether the regular expression matches at any
121                //  point in the candidate (see Matcher#find()).
122                //
123
124                return Boolean.valueOf(match_eng.find());
125            }
126        }
127
128        return Boolean.FALSE;
129    }
130
131
132    /**
133     * Retrieve a compiled pattern for the given pattern string.  A cache of recently used strings is maintained to
134     * improve performance.
135     *
136     * @param    reg_ex_str - the string specifying the regular expression.
137     * @return Pattern - compiled form of the regular expression.
138     */
139
140    protected Pattern getCompiledPattern(String reg_ex_str) {
141        Pattern result;
142
143        //
144        // Look for the compiled pattern in the cache.
145        //
146
147        synchronized (compiledExprCache) {
148            result = compiledExprCache.get(reg_ex_str);
149        }
150
151
152        //
153        // If it was not found, compile it and add it to the cache.
154        //
155
156        if (result == null) {
157            result = Pattern.compile(reg_ex_str);
158
159            synchronized (compiledExprCache) {
160                compiledExprCache.put(reg_ex_str, result);
161            }
162        }
163
164        return result;
165    }
166}