1 package org.unicode.cldr.tool; 2 3 import java.io.BufferedReader; 4 import java.io.File; 5 import java.io.PrintWriter; 6 import java.lang.reflect.Modifier; 7 import java.util.Locale; 8 import java.util.Map; 9 import java.util.Set; 10 import java.util.regex.Matcher; 11 import java.util.regex.Pattern; 12 13 import org.unicode.cldr.draft.FileUtilities; 14 import org.unicode.cldr.tool.Option.Options; 15 import org.unicode.cldr.tool.Option.Params; 16 import org.unicode.cldr.util.CLDRPaths; 17 import org.unicode.cldr.util.RegexUtilities; 18 19 import com.google.common.base.Function; 20 import com.google.common.collect.ImmutableMap; 21 22 public class RegexModify { 23 24 enum MyOptions { 25 verbose(new Params() 26 .setHelp("verbose debugging messages")), sourceDirectory(new Params() 27 .setHelp("sourceDirectory") 28 .setDefault(CLDRPaths.COMMON_DIRECTORY) 29 .setMatch(".+")), targetDirectory(new Params() 30 .setHelp("targetDirectory") 31 .setDefault(CLDRPaths.GEN_DIRECTORY + "xmlModify") 32 .setMatch(".+")), fileRegex(new Params() 33 .setHelp("filename regex") 34 .setMatch(".*") 35 .setDefault(".*\\.xml")), lineRegex(new Params() 36 .setHelp("line regex") 37 .setMatch(".*")), applyFunction(new Params() 38 .setHelp("function name to apply") 39 .setMatch(".*")), 40 ; 41 42 // BOILERPLATE TO COPY 43 final Option option; 44 45 private MyOptions(Params params) { 46 option = new Option(this, params); 47 } 48 49 private static Options myOptions = new Options(); 50 static { 51 for (MyOptions option : MyOptions.values()) { 52 myOptions.add(option, option.option); 53 } 54 } 55 56 private static Set<String> parse(String[] args, boolean showArguments) { 57 return myOptions.parse(MyOptions.values()[0], args, true); 58 } 59 } 60 61 public static void main(String[] args) throws Exception { 62 MyOptions.parse(args, true); 63 File sourceDirectory = new File(MyOptions.sourceDirectory.option.getValue()); 64 File targetDirectory = new File(MyOptions.targetDirectory.option.getValue()); 65 Matcher fileMatcher = Pattern.compile(MyOptions.fileRegex.option.getValue()).matcher(""); 66 String functionName = MyOptions.applyFunction.option.getValue(); 67 RegexFunction f = getFunction(RegexModify.class, functionName); 68 69 for (String file : sourceDirectory.list()) { 70 if (!fileMatcher.reset(file).matches()) { 71 continue; 72 } 73 try ( 74 BufferedReader in = FileUtilities.openUTF8Reader(sourceDirectory.toString(), file); 75 PrintWriter out = FileUtilities.openUTF8Writer(targetDirectory.toString(), file)) { 76 f.clear(); 77 for (String line : FileUtilities.in(in)) { 78 String newLine = f.apply(line); 79 if (newLine != null) { 80 out.println(newLine); 81 } 82 } 83 System.out.println(f.getChangedCount() + " changed lines in " + file); 84 } 85 } 86 } 87 88 @SuppressWarnings({ "rawtypes", "unchecked" }) 89 private static <T> T getFunction(Class class1, String applyFunction) { 90 Map<String, Class<Function>> methods = getMethods(class1); 91 Class result = methods.get(applyFunction); 92 try { 93 return (T) result.newInstance(); 94 } catch (Exception e) { 95 throw new IllegalArgumentException("-a value must be in " + methods.keySet() 96 + " but is " + applyFunction + ""); 97 } 98 } 99 100 @SuppressWarnings("rawtypes") 101 private static Map<String, Class<Function>> getMethods(Class class1) { 102 ImmutableMap.Builder<String, Class<Function>> result = ImmutableMap.builder(); 103 //Set<Class<Function>> skipSet = new HashSet<>(Arrays.asList(skip)); 104 for (Class classMember : class1.getClasses()) { 105 if ((Modifier.ABSTRACT & classMember.getModifiers()) != 0 106 || !Function.class.isAssignableFrom(classMember)) { 107 continue; 108 } 109 110 String name = classMember.getName(); 111 result.put(name.substring(name.indexOf('$') + 1), classMember); 112 } 113 return result.build(); 114 } 115 116 public static abstract class RegexFunction implements Function<String, String> { 117 protected Matcher lineMatcher; 118 private int count; 119 120 public RegexFunction() { 121 lineMatcher = Pattern.compile(getPattern()).matcher(""); 122 } 123 124 public void clear() { 125 count = 0; 126 } 127 128 public int getChangedCount() { 129 return count; 130 } 131 132 public String apply(String line) { 133 if (lineMatcher.reset(line).matches()) { 134 String oldLine = line; 135 line = fixLine(); 136 if (!line.equals(oldLine)) { 137 ++count; 138 } 139 } else if (getCheckOnPattern() != null && line.contains(getCheckOnPattern())) { 140 System.out.println(RegexUtilities.showMismatch(lineMatcher, line)); 141 } 142 return line; 143 } 144 145 public abstract String getPattern(); 146 147 public abstract String getCheckOnPattern(); // for debugging the regex 148 149 public abstract String fixLine(); 150 } 151 152 public static class Subdivision extends RegexFunction { 153 @Override 154 public String getCheckOnPattern() { 155 return "subdivision"; 156 } 157 158 @Override 159 public String getPattern() { 160 //return "(.*<subdivision(?:Alias)? type=\")([^\"]+)(\".*)"; 161 return "(.*<subdivision(?:Alias)? type=\")([^\"]+)(\" replacement=\")([^\"]+)(\".*)"; 162 } 163 164 @Override 165 public String fixLine() { 166 String value = convertToCldr(lineMatcher.group(2)); 167 String value2 = convertToCldr(lineMatcher.group(4)); 168 //return lineMatcher.replaceAll("$1"+value+"$3"); // TODO modify to be cleaner 169 return lineMatcher.replaceAll("$1" + value + "$3" + value2 + "$5"); // TODO modify to be cleaner 170 } 171 172 private static boolean isRegionCode(String s) { 173 return s.length() == 2 || (s.length() == 3 && s.compareTo("A") < 0); 174 } 175 176 private static String convertToCldr(String regionOrSubdivision) { 177 return isRegionCode(regionOrSubdivision) ? regionOrSubdivision.toUpperCase(Locale.ROOT) 178 : regionOrSubdivision.replace("-", "").toLowerCase(Locale.ROOT); 179 } 180 181 } 182 } 183