Skip to content

Commit

Permalink
CLDR-17418 GenerateLanguageContainment: avoid loops
Browse files Browse the repository at this point in the history
See #3535

Update Containment to not stack overflow when there are containment loops.
Report the loops (sparingly) to console.
See CLDR-15020

(cherry picked from commit d1ae5db)
  • Loading branch information
srl295 committed Feb 28, 2024
1 parent 3efe98d commit 4b7d87a
Show file tree
Hide file tree
Showing 2 changed files with 50 additions and 5 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
Expand Down Expand Up @@ -59,6 +60,8 @@ public class Containment {
static final Map<String, Integer> toOrder = new LinkedHashMap<>();
static int level = 0;
static int order;
/** track whether any errors, such as loops, were detected */
public static boolean hadErrors = false;

static {
initOrder("001");
Expand Down Expand Up @@ -188,6 +191,7 @@ private static void resetOrder(String newTerritory, String oldTerritory) {
// }
final Integer oldOrder = toOrder.get(oldTerritory);
if (oldOrder == null) {
hadErrors = true;
throw new IllegalArgumentException(oldTerritory + " not yet defined");
}
toOrder.put(newTerritory, oldOrder);
Expand All @@ -203,37 +207,74 @@ public Set<String> getSubontinents() {

public static Set<List<String>> getAllDirected(Multimap<String, String> multimap, String lang) {
LinkedHashSet<List<String>> result = new LinkedHashSet<>();
getAllDirected(multimap, lang, new ArrayList<String>(), result);
getAllDirected(
multimap,
lang,
new ArrayList<String>(),
result,
new LinkedList<String>(),
new HashSet<String>());
return result;
}

private static void getAllDirected(
Multimap<String, String> multimap,
String lang,
ArrayList<String> target,
Set<List<String>> targets) {
target.add(lang);
Set<List<String>> targets,
List<String> depth,
Set<String> loops) {
int alreadySawThis = depth.indexOf(lang);
if (alreadySawThis != -1) {
hadErrors = true;
if (loops.add(lang)) {
System.err.println(
"WARNING: CLDR-15020 getAllDirected(): Loops Detected: "
+ String.join(" -> ", depth)
+ " -> "
+ lang);
} // else: already reported it.
return;
} else {
depth.add(lang);
}
if (!target.add(lang)) {
hadErrors = true;
throw new StackOverflowError("Error: Saw " + lang + " multiple times in this target.");
} else if (depth.size() > 999) {
hadErrors = true;
throw new StackOverflowError("Error: Too deep getting " + lang);
}
Collection<String> parents = multimap.get(lang);
int size = parents.size();
if (size == 0) {
targets.add(target);
} else if (size == 1) {
for (String parent : parents) {
if (parent.equals(lang)) {
hadErrors = true;
System.err.println("ERR: " + lang + " is its own parent");
} else {
getAllDirected(multimap, parent, target, targets);
getAllDirected(multimap, parent, target, targets, depth, loops);
}
}
} else {
for (String parent : parents) {
if (parent.equals(lang)) {
hadErrors = true;
System.err.println("ERR: " + lang + " is its own parent");
} else {
getAllDirected(multimap, parent, (ArrayList<String>) target.clone(), targets);
getAllDirected(
multimap,
parent,
(ArrayList<String>) target.clone(),
targets,
depth,
loops);
}
}
}
depth.remove(lang); // pop stack
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -345,6 +345,10 @@ void add(List<String> chain) {

public static void main(String[] args) throws IOException {
new GenerateLanguageContainment().run(args);
if (Containment.hadErrors) {
System.err.println("ERROR: Containment Errors detected, see errors above.");
System.exit(1);
}
}

void run(String[] args) throws IOException {
Expand Down

0 comments on commit 4b7d87a

Please sign in to comment.