1 package eu.fbk.dkm.premon.util;
2
3 import java.io.BufferedReader;
4 import java.io.IOException;
5 import java.io.Reader;
6 import java.util.Arrays;
7 import java.util.Collection;
8 import java.util.List;
9 import java.util.Map;
10 import java.util.Objects;
11 import java.util.Set;
12
13 import javax.annotation.Nullable;
14
15 import com.google.common.base.Joiner;
16 import com.google.common.collect.HashMultimap;
17 import com.google.common.collect.ImmutableList;
18 import com.google.common.collect.ImmutableMap;
19 import com.google.common.collect.ImmutableSet;
20 import com.google.common.collect.Lists;
21 import com.google.common.collect.Multimap;
22 import com.google.common.collect.Ordering;
23
24 import eu.fbk.rdfpro.util.IO;
25
26 public final class Replacer {
27
28 private final List<Rule> rules;
29
30 private final Map<String, List<Rule>> index;
31
32 public Replacer(final String location) throws IOException {
33 this(Rule.parse(location));
34 }
35
36 public Replacer(final Rule... rules) {
37 this(Arrays.asList(rules));
38 }
39
40 public Replacer(final Iterable<Rule> rules) {
41
42 this.rules = Ordering.natural().sortedCopy(rules);
43
44 final Multimap<String, Rule> multimap = HashMultimap.create();
45 for (final Rule rule : rules) {
46 multimap.put(rule.getSource(), rule);
47 }
48
49 final ImmutableMap.Builder<String, List<Rule>> builder = ImmutableMap.builder();
50 for (final Map.Entry<String, Collection<Rule>> entry : multimap.asMap().entrySet()) {
51 final String source = entry.getKey();
52 final Rule[] sortedRules = entry.getValue().toArray(new Rule[entry.getValue().size()]);
53 Arrays.sort(sortedRules);
54 builder.put(source, ImmutableList.copyOf(sortedRules));
55 }
56 this.index = builder.build();
57 }
58
59 public List<Rule> getRules() {
60 return this.rules;
61 }
62
63 public List<Rule> getRules(@Nullable final String value, final String... context) {
64 if (value != null) {
65 final List<Rule> rules = this.index.get(value);
66 if (rules != null) {
67 final List<Rule> result = Lists.newArrayList();
68 for (final Rule rule : rules) {
69 final String to = rule.apply(value, context);
70 if (to != value) {
71 result.add(rule);
72 }
73 }
74 return result;
75 }
76 }
77 return ImmutableList.of();
78 }
79
80 @Nullable
81 public String apply(@Nullable final String value, final String... context) {
82 return apply(value, Arrays.asList(context));
83 }
84
85 @Nullable
86 public String apply(@Nullable final String value, final Iterable<String> context) {
87 if (value != null) {
88 final List<Rule> rules = this.index.get(value);
89 if (rules != null) {
90 for (final Rule rule : rules) {
91 final String to = rule.apply(value, context);
92 if (to != value) {
93 return to;
94 }
95 }
96 }
97 }
98 return value;
99 }
100
101 @Override
102 public String toString() {
103 final StringBuilder builder = new StringBuilder();
104 String separator = "";
105 for (final String source : Ordering.natural().sortedCopy(this.index.keySet())) {
106 for (final Rule rule : this.index.get(source)) {
107 builder.append(separator).append(rule);
108 separator = "\n";
109 }
110 }
111 return builder.toString();
112 }
113
114 public static final class Rule implements Comparable<Rule> {
115
116 private final String source;
117
118 private final String target;
119
120 private final Set<String> context;
121
122 public Rule(final String source, final String target,
123 @Nullable final Iterable<String> context) {
124
125 this.source = Objects.requireNonNull(source);
126 this.target = Objects.requireNonNull(target);
127 this.context = context == null ? ImmutableSet.of() : ImmutableSet.copyOf(context);
128 }
129
130 public String getSource() {
131 return this.source;
132 }
133
134 public String getTarget() {
135 return this.target;
136 }
137
138 public Set<String> getContext() {
139 return this.context;
140 }
141
142 @Nullable
143 public String apply(@Nullable final String value, final String... context) {
144 return apply(value, Arrays.asList(context));
145 }
146
147 @Nullable
148 public String apply(@Nullable final String value, final Iterable<String> context) {
149 if (this.source.equals(value)) {
150 int count = 0;
151 for (final String s : context) {
152 if (this.context.contains(s)) {
153 ++count;
154 }
155 if (count == this.context.size()) {
156 return this.target;
157 }
158 }
159 }
160 return value;
161 }
162
163 @Override
164 public int compareTo(final Rule other) {
165 int result = this.source.compareTo(other.source);
166 if (result == 0) {
167 result = -this.context.size() + other.context.size();
168 if (result == 0) {
169 final Ordering<String> o = Ordering.natural();
170 result = Joiner.on('|').join(o.sortedCopy(this.context))
171 .compareTo(Joiner.on('|').join(o.sortedCopy(other.context)));
172 if (result == 0) {
173 return this.target.compareTo(other.target);
174 }
175 }
176 }
177 return result;
178 }
179
180 @Override
181 public boolean equals(final Object object) {
182 if (object == this) {
183 return true;
184 }
185 if (!(object instanceof Rule)) {
186 return false;
187 }
188 final Rule other = (Rule) object;
189 return this.source.equals(other.source) && this.target.equals(other.target)
190 && this.context.equals(other.context);
191 }
192
193 @Override
194 public int hashCode() {
195 return Objects.hash(this.source, this.target, this.context);
196 }
197
198 @Override
199 public String toString() {
200 return this.source + " -> " + this.target + " { "
201 + Joiner.on(" ").join(Ordering.natural().sortedCopy(this.context)) + " }";
202 }
203
204 public static List<Rule> parse(final String location) throws IOException {
205 try (Reader reader = IO.utf8Reader(IO.buffer(IO.read(location)))) {
206 return parse(reader);
207 }
208 }
209
210 public static List<Rule> parse(final Reader reader) throws IOException {
211
212 final BufferedReader in = reader instanceof BufferedReader ? (BufferedReader) reader
213 : new BufferedReader(reader);
214
215 final List<Rule> rules = Lists.newArrayList();
216 String line;
217 int lineIndex = 0;
218 while ((line = in.readLine()) != null) {
219 ++lineIndex;
220 line = line.trim();
221 if (line.startsWith("#") || line.isEmpty()) {
222 continue;
223 }
224 try {
225 final int index = line.indexOf("->");
226 final int index2 = line.indexOf('{');
227 final int index3 = line.indexOf('}');
228 final String source = line.substring(0, index).trim();
229 final String target = line.substring(index + 2,
230 index2 < 0 ? line.length() : index2).trim();
231 final List<String> context = Lists.newArrayList();
232 if (index2 > 0 && index3 > index2) {
233 for (final String token : line.substring(index2 + 1, index3).split("\\s+")) {
234 if (!token.isEmpty()) {
235 context.add(token);
236 }
237 }
238 }
239 rules.add(new Rule(source, target, context));
240 } catch (final Throwable ex) {
241 throw new IOException("Illegal replacement rule definition (line " + lineIndex
242 + "): " + line);
243 }
244 }
245 return rules;
246 }
247
248 }
249
250 }