001// Copyright 2004, 2005 The Apache Software Foundation
002//
003// Licensed under the Apache License, Version 2.0 (the "License");
004// you may not use this file except in compliance with the License.
005// You may obtain a copy of the License at
006//
007//     http://www.apache.org/licenses/LICENSE-2.0
008//
009// Unless required by applicable law or agreed to in writing, software
010// distributed under the License is distributed on an "AS IS" BASIS,
011// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
012// See the License for the specific language governing permissions and
013// limitations under the License.
014
015package org.apache.tapestry.util;
016
017import java.util.ArrayList;
018import java.util.HashMap;
019import java.util.List;
020import java.util.Map;
021
022import org.apache.hivemind.ApplicationRuntimeException;
023import org.apache.oro.text.regex.MalformedPatternException;
024import org.apache.oro.text.regex.MatchResult;
025import org.apache.oro.text.regex.Pattern;
026import org.apache.oro.text.regex.PatternCompiler;
027import org.apache.oro.text.regex.PatternMatcher;
028import org.apache.oro.text.regex.PatternMatcherInput;
029import org.apache.oro.text.regex.Perl5Compiler;
030import org.apache.oro.text.regex.Perl5Matcher;
031
032/**
033 * Streamlines the interface to ORO by implicitly constructing the necessary compilers and matchers,
034 * and by caching compiled patterns.
035 * 
036 * @author Howard Lewis Ship
037 * @since 3.0
038 */
039
040public class RegexpMatcher
041{
042    private PatternCompiler _patternCompiler;
043
044    private PatternMatcher _matcher;
045
046    private Map _compiledPatterns = new HashMap();
047
048    private Map _escapedPatternStrings = new HashMap();
049
050    protected Pattern compilePattern(String pattern)
051    {
052        if (_patternCompiler == null)
053            _patternCompiler = new Perl5Compiler();
054
055        try
056        {
057            return _patternCompiler.compile(pattern, Perl5Compiler.SINGLELINE_MASK | Perl5Compiler.READ_ONLY_MASK);
058        }
059        catch (MalformedPatternException ex)
060        {
061            throw new ApplicationRuntimeException(ex);
062        }
063    }
064
065    protected Pattern getCompiledPattern(String pattern)
066    {
067        Pattern result = (Pattern) _compiledPatterns.get(pattern);
068
069        if (result == null)
070        {
071            result = compilePattern(pattern);
072            _compiledPatterns.put(pattern, result);
073        }
074
075        return result;
076    }
077
078    /**
079     * Clears any previously compiled patterns.
080     */
081
082    public void clear()
083    {
084        _compiledPatterns.clear();
085    }
086
087    protected PatternMatcher getPatternMatcher()
088    {
089        if (_matcher == null)
090            _matcher = new Perl5Matcher();
091
092        return _matcher;
093    }
094
095    public boolean matches(String pattern, String input)
096    {
097        Pattern compiledPattern = getCompiledPattern(pattern);
098
099        return getPatternMatcher().matches(input, compiledPattern);
100    }
101
102    public boolean contains(String pattern, String input)
103    {
104        Pattern compiledPattern = getCompiledPattern(pattern);
105
106        return getPatternMatcher().contains(input, compiledPattern);
107    }
108
109    public String getEscapedPatternString(String pattern)
110    {
111        String result = (String) _escapedPatternStrings.get(pattern);
112
113        if (result == null)
114        {
115            result = Perl5Compiler.quotemeta(pattern);
116
117            _escapedPatternStrings.put(pattern, result);
118        }
119
120        return result;
121    }
122
123    /**
124     * Given an input string, finds all matches in an input string for the pattern.
125     * 
126     * @param pattern
127     *            the regexp pattern for matching
128     * @param input
129     *            the string to search for matches within
130     * @return array (possibly empty) of matches
131     * @since 4.0
132     */
133    public RegexpMatch[] getMatches(String pattern, String input)
134    {
135        Pattern compiledPattern = getCompiledPattern(pattern);
136
137        PatternMatcher matcher = getPatternMatcher();
138        PatternMatcherInput matcherInput = new PatternMatcherInput(input);
139
140        List matches = new ArrayList();
141
142        while (matcher.contains(matcherInput, compiledPattern))
143        {
144            MatchResult match = matcher.getMatch();
145
146            matches.add(new RegexpMatch(match));
147        }
148
149        return (RegexpMatch[]) matches.toArray(new RegexpMatch[matches.size()]);
150    }
151
152    /**
153     * Given an input string, finds all matches in an input string for the pattern.
154     * 
155     * @param pattern
156     *            the regexp pattern for matching
157     * @param input
158     *            the string to search for matches within
159     * @param subgroup
160     *            the group (sub-expression) within the pattern to return as a match
161     * @return array (possibly empty) of matching strings
162     */
163    public String[] getMatches(String pattern, String input, int subgroup)
164    {
165        Pattern compiledPattern = getCompiledPattern(pattern);
166
167        PatternMatcher matcher = getPatternMatcher();
168        PatternMatcherInput matcherInput = new PatternMatcherInput(input);
169
170        List matches = new ArrayList();
171
172        while (matcher.contains(matcherInput, compiledPattern))
173        {
174            MatchResult match = matcher.getMatch();
175
176            String matchedInput = match.group(subgroup);
177
178            matches.add(matchedInput);
179        }
180
181        return (String[]) matches.toArray(new String[matches.size()]);
182    }
183
184}