1 package org.unicode.cldr.draft; 2 3 import java.util.ArrayList; 4 import java.util.List; 5 import java.util.regex.Matcher; 6 import java.util.regex.Pattern; 7 8 public class Rule { 9 private final Pattern prematch; 10 private final boolean prematchFindAtEnd; 11 private final Pattern postmatch; 12 private final List<Item> results = new ArrayList<>(); 13 /** 14 * if negative, is position before start position; otherwise is position relative to end of replacement. 15 * To put *at* the start position, put a CURSOR at the start of the results. 16 * Default is zero, meaning at the end of the replacement unless there is a CURSOR item. 17 */ 18 private int outOfBoundsCursor = 0; 19 20 /** 21 * contains a replacement text with a set of replacements (up to 10) plus a new cursor point 22 * 23 * @author markdavis 24 */ 25 // TODO add function calls &foo(....) 26 27 public interface Item { compose(Matcher prematcher, Matcher postmatcher)28 String compose(Matcher prematcher, Matcher postmatcher); 29 } 30 31 public static class StringItem implements Item { 32 String replacement; 33 StringItem(String s)34 public StringItem(String s) { 35 replacement = s; 36 } 37 38 @Override compose(Matcher prematcher, Matcher postmatcher)39 public String compose(Matcher prematcher, Matcher postmatcher) { 40 return replacement; 41 } 42 43 @Override toString()44 public String toString() { 45 return replacement; 46 } 47 } 48 49 public static class NumberedItem implements Item { 50 boolean post; 51 int number; 52 NumberedItem(int parseInt, boolean b)53 public NumberedItem(int parseInt, boolean b) { 54 number = parseInt; 55 post = b; 56 } 57 58 @Override compose(Matcher prematcher, Matcher postmatcher)59 public String compose(Matcher prematcher, Matcher postmatcher) { 60 return post ? postmatcher.group(number) : prematcher.group(number); 61 } 62 63 @Override toString()64 public String toString() { 65 return "$" + number; 66 } 67 } 68 69 public static class CursorItem implements Item { 70 static Item CURSOR = new CursorItem(); 71 72 @Override compose(Matcher prematcher, Matcher postmatcher)73 public String compose(Matcher prematcher, Matcher postmatcher) { 74 throw new UnsupportedOperationException(); 75 } 76 77 @Override toString()78 public String toString() { 79 return "|"; 80 } 81 } 82 Rule(String pre, String post, List<String> results2)83 public Rule(String pre, String post, List<String> results2) { 84 Pattern tempPrematch; 85 boolean tempPrematchFindAtEnd = false; 86 if (pre.length() == 0) { 87 tempPrematch = null; 88 } else { 89 try { 90 tempPrematch = Pattern.compile("(?<=" + pre + ")", Pattern.COMMENTS + Pattern.DOTALL); 91 tempPrematchFindAtEnd = true; 92 } catch (Exception e) { 93 tempPrematch = Pattern.compile(pre + "$", Pattern.COMMENTS + Pattern.DOTALL); 94 } 95 } 96 prematch = tempPrematch; 97 prematchFindAtEnd = tempPrematchFindAtEnd; 98 postmatch = Pattern.compile(post, Pattern.COMMENTS + Pattern.DOTALL); 99 for (String s : results2) { 100 if (s.startsWith("$")) { 101 results.add(new NumberedItem(Integer.parseInt(s.substring(1)), true)); 102 } else if (s.length() > 0) { 103 results.add(new StringItem(s)); 104 } 105 } 106 } 107 108 /** 109 * Modifies result, returns new cursor 110 * 111 * @param result 112 * @param offset 113 * @param matcher 114 * @return 115 */ append(StringBuilder result, Matcher prematcher, Matcher postmatcher)116 int append(StringBuilder result, Matcher prematcher, Matcher postmatcher) { 117 int startPosition = result.length(); 118 int cursor = -1; 119 for (Item item : results) { 120 if (item == CursorItem.CURSOR) { 121 cursor = result.length(); 122 } else { 123 final String insertion = item.compose(prematcher, postmatcher); 124 result.append(insertion); 125 } 126 } 127 return cursor >= 0 ? cursor 128 : outOfBoundsCursor < 0 ? startPosition + outOfBoundsCursor 129 : result.length() + outOfBoundsCursor; 130 } 131 132 @Override toString()133 public String toString() { 134 String main = postmatch.toString(); 135 return (prematch == null ? "" : prematch.toString()) 136 + main + " > " + results + " ; "; 137 } 138 getPrematcher(CharSequence processedAlready)139 public Matcher getPrematcher(CharSequence processedAlready) { 140 return prematch == null ? null 141 : prematch.matcher(processedAlready); 142 } 143 getPostmatcher(CharSequence toBeProcessed)144 public Matcher getPostmatcher(CharSequence toBeProcessed) { 145 return postmatch.matcher(toBeProcessed); 146 } 147 prematch(Matcher prematcher, CharSequence processedAlready)148 public boolean prematch(Matcher prematcher, CharSequence processedAlready) { 149 return prematchFindAtEnd 150 ? prematcher.find(processedAlready.length()) 151 : prematcher.find(); 152 } 153 } 154