1 package org.unicode.cldr.draft; 2 3 import java.util.Arrays; 4 import java.util.HashMap; 5 import java.util.HashSet; 6 import java.util.Map; 7 import java.util.Map.Entry; 8 import java.util.Set; 9 10 import org.unicode.cldr.draft.XLocaleDistance.DistanceNode; 11 import org.unicode.cldr.draft.XLocaleDistance.DistanceTable; 12 import org.unicode.cldr.draft.XLocaleDistance.IdMakerFull; 13 import org.unicode.cldr.draft.XLocaleDistance.StringDistanceNode; 14 import org.unicode.cldr.draft.XLocaleDistance.StringDistanceTable; 15 16 import com.google.common.base.Objects; 17 import com.ibm.icu.util.Output; 18 19 final class IntDistanceNode extends DistanceNode { 20 final IntDistanceNode.IntDistanceTable distanceTable; 21 IntDistanceNode(int distance, IntDistanceNode.IntDistanceTable distanceTable)22 public IntDistanceNode(int distance, IntDistanceNode.IntDistanceTable distanceTable) { 23 super(distance); 24 this.distanceTable = distanceTable; 25 } 26 getDistanceTable()27 public IntDistanceNode.IntDistanceTable getDistanceTable() { 28 return distanceTable; 29 } 30 31 @Override equals(Object obj)32 public boolean equals(Object obj) { 33 IntDistanceNode other = (IntDistanceNode) obj; 34 return distance == other.distance && Objects.equal(distanceTable, other.distanceTable); 35 } 36 37 @Override hashCode()38 public int hashCode() { 39 return distance ^ Objects.hashCode(distanceTable); 40 } 41 42 @Override toString()43 public String toString() { 44 return "\ndistance: " + distance + ", " + distanceTable; 45 } 46 from(int distance, IntDistanceNode.IntDistanceTable otherTable)47 public static DistanceNode from(int distance, IntDistanceNode.IntDistanceTable otherTable) { 48 return otherTable == null ? new DistanceNode(distance) : new IntDistanceNode(distance, otherTable); 49 } 50 51 static class IntDistanceTable extends DistanceTable { 52 private static final IdMakerFull[] ids = { new IdMakerFull<String>("lang", XLocaleDistance.ANY), new IdMakerFull<String>("script", XLocaleDistance.ANY), 53 new IdMakerFull<String>("region", XLocaleDistance.ANY) }; 54 private static final IdMakerFull<IntDistanceNode.IntDistanceTable> cache = new IdMakerFull<>("table"); 55 56 private final IdMakerFull<String> id; 57 private final DistanceNode[][] distanceNodes; // map from desired, supported => node 58 IntDistanceTable(StringDistanceTable source)59 public IntDistanceTable(StringDistanceTable source) { 60 this(source, loadIds(source, 0)); 61 } 62 loadIds(StringDistanceTable source, int idNumber)63 private static int loadIds(StringDistanceTable source, int idNumber) { 64 IdMakerFull id = ids[idNumber]; // use different Id for language, script, region 65 for (Entry<String, Map<String, DistanceNode>> e1 : source.subtables.entrySet()) { 66 int desired = id.add(e1.getKey()); 67 for (Entry<String, DistanceNode> e2 : e1.getValue().entrySet()) { 68 int supported = id.add(e2.getKey()); 69 StringDistanceNode oldNode = (StringDistanceNode) e2.getValue(); 70 if (oldNode.distanceTable != null) { 71 loadIds((StringDistanceTable) oldNode.distanceTable, idNumber + 1); 72 } 73 } 74 } 75 return 0; 76 } 77 IntDistanceTable(StringDistanceTable source, int idNumber)78 private IntDistanceTable(StringDistanceTable source, int idNumber) { // move construction out later 79 id = ids[idNumber]; // use different Id for language, script, region 80 int size = id.size(); 81 distanceNodes = new DistanceNode[size][size]; 82 83 // fill in the values in the table 84 for (Entry<String, Map<String, DistanceNode>> e1 : source.subtables.entrySet()) { 85 int desired = id.add(e1.getKey()); 86 for (Entry<String, DistanceNode> e2 : e1.getValue().entrySet()) { 87 int supported = id.add(e2.getKey()); 88 DistanceNode oldNode = e2.getValue(); 89 final StringDistanceTable oldDistanceTable = (StringDistanceTable) oldNode.getDistanceTable(); 90 IntDistanceNode.IntDistanceTable otherTable = oldDistanceTable == null ? null 91 : cache.intern(new IntDistanceTable(oldDistanceTable, idNumber + 1)); 92 DistanceNode node = IntDistanceNode.from(oldNode.distance, otherTable); 93 distanceNodes[desired][supported] = node; 94 } 95 } 96 // now, to make star work, 97 // copy all the zero columns/rows down to any null value 98 for (int row = 0; row < size; ++row) { 99 for (int column = 0; column < size; ++column) { 100 DistanceNode value = distanceNodes[row][column]; 101 if (value != null) { 102 continue; 103 } 104 value = distanceNodes[0][column]; 105 if (value == null) { 106 value = distanceNodes[row][0]; 107 if (value == null) { 108 value = distanceNodes[0][0]; 109 } 110 } 111 distanceNodes[row][column] = value; 112 } 113 } 114 } 115 116 @Override getDistance(String desired, String supported, Output<DistanceTable> distanceTable, boolean starEquals)117 public int getDistance(String desired, String supported, Output<DistanceTable> distanceTable, boolean starEquals) { 118 final int desiredId = id.toId(desired); 119 final int supportedId = id.toId(supported); // can optimize later 120 DistanceNode value = distanceNodes[desiredId][supportedId]; 121 if (distanceTable != null) { 122 distanceTable.value = value.getDistanceTable(); 123 } 124 return starEquals && desiredId == supportedId && (desiredId != 0 || desired.equals(supported)) ? 0 125 : value.distance; 126 } 127 128 @Override equals(Object obj)129 public boolean equals(Object obj) { 130 IntDistanceNode.IntDistanceTable other = (IntDistanceNode.IntDistanceTable) obj; 131 if (!id.equals(other.id)) { 132 return false; 133 } 134 ; 135 return Arrays.deepEquals(distanceNodes, other.distanceNodes); 136 } 137 138 @Override hashCode()139 public int hashCode() { 140 return id.hashCode() ^ Arrays.deepHashCode(distanceNodes); 141 } 142 143 @Override toString()144 public String toString() { 145 return abbreviate("\t", new HashMap<DistanceNode, Integer>(), new StringBuilder(id.name + ": ")).toString(); 146 } 147 abbreviate(String indent, Map<DistanceNode, Integer> cache, StringBuilder result)148 private StringBuilder abbreviate(String indent, Map<DistanceNode, Integer> cache, StringBuilder result) { 149 for (int i = 0; i < distanceNodes.length; ++i) { 150 DistanceNode[] row = distanceNodes[i]; 151 for (int j = 0; j < row.length; ++j) { 152 DistanceNode value = row[j]; 153 if (value == null) { 154 continue; 155 } 156 result.append(value.distance); 157 IntDistanceNode.IntDistanceTable dt = (IntDistanceNode.IntDistanceTable) value.getDistanceTable(); 158 if (dt == null) { 159 result.append(";"); 160 continue; 161 } 162 Integer old = cache.get(value); 163 result.append("/"); 164 if (old != null) { 165 result.append(old + ";"); 166 } else { 167 final int table = cache.size(); 168 cache.put(value, table); 169 result.append("\n" + indent + table + "=" + dt.id.name + ": "); 170 dt.abbreviate(indent + "\t", cache, result); 171 } 172 } 173 } 174 return result; 175 } 176 177 @Override getCloser(int threshold)178 public Set<String> getCloser(int threshold) { 179 Set<String> result = new HashSet<>(); 180 for (int i = 0; i < distanceNodes.length; ++i) { 181 DistanceNode[] row = distanceNodes[i]; 182 for (int j = 0; j < row.length; ++j) { 183 DistanceNode value = row[j]; 184 if (value.distance < threshold) { 185 result.add(id.fromId(i)); 186 break; 187 } 188 } 189 } 190 return result; 191 } 192 193 @Override toString(boolean abbreviate)194 String toString(boolean abbreviate) { 195 return toString(); 196 } 197 } 198 }