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    
015    package org.apache.hivemind.methodmatch;
016    
017    import java.util.ArrayList;
018    import java.util.List;
019    
020    import org.apache.hivemind.ApplicationRuntimeException;
021    import 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    
032    public 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    }