View Javadoc

1   /*
2    * Copyright 2006 Outsource Cafe, Inc.
3    *
4    * Licensed under the Apache License, Version 2.0 (the 'License')
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *    http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an 'AS IS' BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package org.javagen.agile.core.util;
17  
18  import java.util.ArrayList;
19  import java.util.List;
20  import java.util.regex.Matcher;
21  import java.util.regex.Pattern;
22  
23  /***
24   * Itererates through a list of regular expressions and applies a replacement string when a match is found.
25   * <p>
26   * Currently only applies the first match found, ingnoring any remaining regex's in the list.
27   * <p>
28   * Regex and replacePatterns can be set as a single property string or added individually by calling 
29   * <code>addReplacementPattern</code>.
30   * <p>For example to add a regex to remove 'Fk' & 'Pk' suffixes from a name such as <code>'PersonPk'</code>, 
31   * you can call either:
32   * <pre>addReplacementPattern("^([a-zA-Z]+)(Fk|Pk)$", "$1")</pre>
33   * or 
34   * <pre>setRegexReplacePairs("^([a-zA-Z]+)(Fk|Pk)$>$1")</pre>
35   * <p>As a single string property the general form of the list is: regex1>replace1,regex2>replace2,...
36   * <p>The replacePattern can be any text interspersed with regex group 
37   * replacement holders of the form $n, for example: text$1text$3text
38   * 
39   * @author Richard Easterling
40   */
41  public class RegexRenamer {
42  
43      /***
44       * Defines the (property file friendly) key-value structure of regex-replacement pairs.
45       * example: regex1>replace1,regex2>replace2
46       */
47      private static Pattern keyValuePattern = Pattern.compile("([^>]+)>([^,]+),?");  
48  
49      /***
50       * Used internally for parsing replacement pattern containing regex group indexes
51       * example: text${2}text${4}text
52       */
53      private static Pattern replacePattern = Pattern.compile("//$([0-9])");
54  
55      private List<PatternHolder> patternHolders = new ArrayList<PatternHolder>();
56      
57      ////////////////////////////////////////////////////////////////////////////
58      // Generator interface:
59      ////////////////////////////////////////////////////////////////////////////
60      
61      public String rename(String name) {
62          if (patternHolders.size()>0) {
63              for(PatternHolder patternHolder : patternHolders) {
64                  String newName = patternHolder.match(name);
65                  if (newName!=null)
66                      return newName;
67              }
68          }
69          return null;
70      }
71  
72      ////////////////////////////////////////////////////////////////////////////
73      // support methods:
74      ////////////////////////////////////////////////////////////////////////////
75  
76      /***
77       * Property file friendly means of adding regex-replacePattern pairs as comma-delineated list.
78       * <p>general form of the list: regex1>replace1,regex2>replace2,...
79       * <p>regex can be any regular expression excluding the > symbol.
80       * <p>The replacePattern can be any text interspersed with regex group 
81       * replacement holders of the form $n, for example: text$1text$3text
82       * <p>For example to remove 'Fk' & 'Pk' suffixes use: ^([a-zA-Z]+)(Fk|Pk)$>$1
83       */
84      public void setRegexReplacePairs(String replacePatterns) {
85          Matcher matcher = keyValuePattern.matcher(replacePatterns);
86          while (matcher.find()) {
87              String regex = matcher.group(1).trim();
88              String replace = matcher.group(2).trim();
89              addReplacementPattern(regex, replace);
90          }
91      }
92      
93      public void addReplacementPattern(String regex, String replacementPattern) {
94          patternHolders.add( new PatternHolder(regex, replacementPattern) );
95      }
96          
97      public List<PatternHolder> getPatternHolders() {
98          return patternHolders;
99      }
100 
101     public boolean isEmpty() {
102         return patternHolders==null ? true : patternHolders.isEmpty();
103     }
104     
105     ////////////////////////////////////////////////////////////////////////////
106     // internal PatternHolder class:
107     ////////////////////////////////////////////////////////////////////////////
108 
109     private static class PatternHolder {
110         public Pattern pattern;
111         public String replace;
112         
113         public PatternHolder(String regex, String replace) {
114             pattern = Pattern.compile(regex);
115             this.replace = replace;
116         }
117         
118         public String match(String name) {
119             Matcher matcher = pattern.matcher(name);
120             if (matcher.matches()) {
121                 return replace(matcher, replace);
122             } else {
123                 return null;
124             }
125         }
126         
127         public String replace(Matcher matcher, String resultPattern) {
128             if ("${1}".equals(resultPattern) && matcher.groupCount()>0)
129                 return matcher.group(1); //optimize for most common case
130             Matcher resultMatcher = replacePattern.matcher(resultPattern);
131             StringBuilder str = new StringBuilder();
132             int pos = 0;
133             while (resultMatcher.find()) {
134                 if (resultMatcher.start()>pos) {
135                     String prefix = resultPattern.substring(pos, resultMatcher.start());
136                     str.append(prefix);
137                 }
138                 pos = resultMatcher.end();
139                 String indexStr = resultMatcher.group(1);
140                 int index = Integer.parseInt( indexStr );
141                 if (index>matcher.groupCount())
142                     throw new IndexOutOfBoundsException("group index "+index+" exceeds groups in regex "+pattern.toString());
143                 String group = matcher.group(index);
144                 str.append(group);
145             }
146             if (resultPattern.length()>pos) {
147                 String suffix = resultPattern.substring(pos);
148                 str.append(suffix);
149             }
150             if (str.length()==0) {
151                 return null;
152             } else {
153                 return str.toString();
154             }
155         }
156     }
157     
158 }