All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.squarespace.cldrengine.locale.DistanceTable Maven / Gradle / Ivy

The newest version!
package com.squarespace.cldrengine.locale;

import static com.squarespace.cldrengine.locale.DistanceMap.ANY;

import java.util.Set;

import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.squarespace.cldrengine.api.LanguageTag;
import com.squarespace.cldrengine.internal.LocaleExternalData;
import com.squarespace.cldrengine.locale.DistanceMap.Node;
import com.squarespace.cldrengine.utils.JsonUtils;

public class DistanceTable {

  public static final int MAX_DISTANCE = 100;
  public static final int DEFAULT_THRESHOLD = 50;

  private static final DistanceMap MAP = load();

  /**
   * Returns the distance between the desired and supported locale, using the
   * default distance threshold.
   */
  public static int distance(LanguageTag desired, LanguageTag supported) {
    return distance(desired, supported, DEFAULT_THRESHOLD);
  }

  /**
   * Returns the distance between the desired and supported locale, using the
   * given distance threshold. Any distance equal to or greater than the threshold
   * will return the maximum distance.
   */
  public static int distance(LanguageTag desired, LanguageTag supported, int threshold) {
    // Query the top level LANGUAGE
    boolean langEquals = desired.language().equals(supported.language());
    Node node = MAP.get(desired.language(), supported.language());
    if (node == null) {
      node = MAP.get(ANY, ANY);
    }
    int distance = node.wildcard() ? (langEquals ? 0 : node.distance()) : node.distance();

    if (distance >= threshold) {
      return MAX_DISTANCE;
    }

    // Go to the next level SCRIPT
    DistanceMap map = node.map();

    boolean scriptEquals = desired.script().equals(supported.script());
    node = map.get(desired.script(), supported.script());
    if (node == null) {
      node = map.get(ANY, ANY);
    }
    distance += node.wildcard() ? (scriptEquals ? 0 : node.distance()) : node.distance();

    if (distance >= threshold) {
      return MAX_DISTANCE;
    }

    // Go to the next level TERRITORY
    map = node.map();

    // If the territories happen to be equal, distance is 0 so we're done.
    if (desired.region().equals(supported.region())) {
      return distance;
    }

    // Check if the map contains a distance between these two regions.
    node = map.get(desired.region(), supported.region());
    if (node == null) {
      // Compare the desired region against supported partitions, and vice-versa.
      node = scanTerritory(map, desired.region(), supported.region());
    }
    if (node != null) {
      distance += node.distance();
      return distance < threshold ? distance : MAX_DISTANCE;
    }

    // Find the maximum distance between the partitions.
    int maxDistance = 0;

    // These partition sets are always guaranteed to exist and contain at least 1 member.
    boolean match = false;
    Set desiredPartitions = PartitionTable.getRegionPartition(desired.region());
    Set supportedPartitions = PartitionTable.getRegionPartition(supported.region());

    for (String desiredPartition : desiredPartitions) {
      for (String supportedPartition : supportedPartitions) {
        node = map.get(desiredPartition, supportedPartition);
        if (node != null) {
          maxDistance = Math.max(maxDistance, node.distance());
          match = true;
        }
      }
    }

    if (!match) {
      node = map.get(ANY, ANY);
      maxDistance = Math.max(maxDistance, node.distance());
    }

    distance += maxDistance;
    return distance < threshold ? distance : MAX_DISTANCE;
  }

  /**
   * Scan the desired region against the supported partitions and vice versa.
   * Return the first matching node.
   */
  private static Node scanTerritory(DistanceMap map, String desired, String supported) {
    Node node;
    for (String partition : PartitionTable.getRegionPartition(desired)) {
      node = map.get(partition, supported);
      if (node != null) {
        return node;
      }
    }

    for (String partition : PartitionTable.getRegionPartition(supported)) {
      node = map.get(desired, partition);
      if (node != null) {
        return node;
      }
    }

    return null;
  }

  /**
   * The distance map JSON is auto-generated and assumed correct here, so
   * we omit type checking and error handling.
   */
  private static DistanceMap load() {
    JsonObject json = (JsonObject) JsonUtils.parse(LocaleExternalData.DISTANCEMAP);
    DistanceMap map = new DistanceMap();
    encode(map, json);
    return map;
  }

  private static void encode(DistanceMap map, JsonObject json) {
    for (String want : json.keySet()) {
      JsonObject child = json.getAsJsonObject(want);
      for (String have : child.keySet()) {
        JsonElement data = child.get(have);
        want = wildcard(want);
        have = wildcard(have);
        if (data.isJsonArray()) {
          JsonArray arr = data.getAsJsonArray();
          int distance = arr.get(0).getAsInt();
          Node node = map.put(want, have, distance);
          node.init();
          encode(node.map(), arr.get(1).getAsJsonObject());
        } else {
          int distance = data.getAsInt();
          map.put(want, have, distance);
        }
      }
    }
  }

  private static String wildcard(String val) {
    return val.equals("$") ? DistanceMap.ANY : val;
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy