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 }