Home | History | Annotate | Download | only in tool
      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