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