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.hivemind.methodmatch;
016
017import java.util.ArrayList;
018import java.util.List;
019
020import org.apache.hivemind.ApplicationRuntimeException;
021import org.apache.hivemind.util.StringUtils;
022
023/**
024 * Parses a method pattern (consisting of a name pattern, followed by an optional parameters
025 * pattern) into a {@link org.apache.hivemind.methodmatch.MethodFilter}. In most cases, the
026 * patterns will require several checks (i.e., match against name, match against parameters) in
027 * which case a {@link org.apache.hivemind.methodmatch.CompositeFilter} is returned.
028 * 
029 * @author Howard Lewis Ship
030 */
031
032public class MethodPatternParser
033{
034    private List _filters;
035
036    public MethodFilter parseMethodPattern(String pattern)
037    {
038        _filters = new ArrayList();
039
040        int parenx = pattern.indexOf('(');
041
042        String namePattern = parenx < 0 ? pattern : pattern.substring(0, parenx);
043
044        parseNamePattern(pattern, namePattern);
045
046        if (parenx >= 0)
047            parseParametersPattern(pattern, pattern.substring(parenx));
048
049        switch (_filters.size())
050        {
051            case 0:
052                return new MatchAllFilter();
053
054            case 1:
055
056                return (MethodFilter) _filters.get(0);
057
058            default:
059                return new CompositeFilter(_filters);
060        }
061    }
062
063    private void parseNamePattern(String methodPattern, String namePattern)
064    {
065        if (namePattern.equals("*"))
066            return;
067
068        if (namePattern.length() == 0)
069            throw new ApplicationRuntimeException(MethodMatchMessages
070                    .missingNamePattern(methodPattern));
071
072        if (namePattern.startsWith("*") && namePattern.endsWith("*"))
073        {
074            String substring = namePattern.substring(1, namePattern.length() - 1);
075
076            validateNamePattern(methodPattern, substring);
077
078            _filters.add(new InfixNameFilter(substring));
079            return;
080        }
081
082        if (namePattern.startsWith("*"))
083        {
084            String suffix = namePattern.substring(1);
085
086            validateNamePattern(methodPattern, suffix);
087
088            _filters.add(new NameSuffixFilter(suffix));
089            return;
090        }
091
092        if (namePattern.endsWith("*"))
093        {
094            String prefix = namePattern.substring(0, namePattern.length() - 1);
095
096            validateNamePattern(methodPattern, prefix);
097
098            _filters.add(new NamePrefixFilter(prefix));
099            return;
100        }
101
102        validateNamePattern(methodPattern, namePattern);
103
104        _filters.add(new ExactNameFilter(namePattern));
105    }
106
107    private void parseParametersPattern(String methodPattern, String pattern)
108    {
109        if (pattern.equals("()"))
110        {
111            addParameterCountFilter(0);
112            return;
113        }
114
115        if (!pattern.endsWith(")"))
116            throw new ApplicationRuntimeException(MethodMatchMessages
117                    .invalidParametersPattern(methodPattern));
118
119        // Trim off leading and trailing parens.
120
121        pattern = pattern.substring(1, pattern.length() - 1);
122
123        char ch = pattern.charAt(0);
124
125        if (Character.isDigit(ch))
126        {
127            addParameterCountFilter(methodPattern, pattern);
128            return;
129        }
130
131        String[] names = StringUtils.split(pattern);
132
133        // Would be nice to do some kind of validation here, to prove
134        // that the provided class names exist, and that
135        // primitive types names are valid.
136
137        addParameterCountFilter(names.length);
138        for (int i = 0; i < names.length; i++)
139            _filters.add(new ParameterFilter(i, names[i].trim()));
140
141    }
142
143    private void addParameterCountFilter(String methodPattern, String pattern)
144    {
145        try
146        {
147            int count = Integer.parseInt(pattern);
148            addParameterCountFilter(count);
149        }
150        catch (NumberFormatException ex)
151        {
152            throw new ApplicationRuntimeException(MethodMatchMessages
153                    .invalidParametersPattern(methodPattern));
154        }
155    }
156
157    private void addParameterCountFilter(int count)
158    {
159        // Add the count filter first, since it is always the least expensive test.
160        _filters.add(0, new ParameterCountFilter(count));
161    }
162
163    private void validateNamePattern(String methodPattern, String nameSubstring)
164    {
165        if (nameSubstring.indexOf('*') >= 0)
166            throw new ApplicationRuntimeException(MethodMatchMessages
167                    .invalidNamePattern(methodPattern));
168    }
169}