blob: 161a6b821a22aff73fef617f5beb5dd9d61a846a [file] [log] [blame]
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.libcore.timezone.tzlookup;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.text.ParseException;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
/**
* A class that knows about the structure of the backward file.
*/
final class BackwardFile {
private final Map<String, String> links = new HashMap<>();
private BackwardFile() {}
static BackwardFile parse(String backwardFile) throws IOException, ParseException {
BackwardFile backward = new BackwardFile();
List<String> lines = Files
.readAllLines(Paths.get(backwardFile), StandardCharsets.US_ASCII);
// Remove comments
List<String> linkLines =
lines.stream()
.filter(s -> !(s.startsWith("#") || s.isEmpty()))
.collect(Collectors.toList());
for (String linkLine : linkLines) {
String[] fields = linkLine.split("\t+");
if (fields.length < 3 || !fields[0].equals("Link")) {
throw new ParseException("Line is malformed: " + linkLine, 0);
}
backward.addLink(fields[1], fields[2]);
}
return backward;
}
/**
* Add a link entry.
*
* @param target the new tz ID
* @param linkName the old tz ID
*/
private void addLink(String target, String linkName) {
String oldValue = links.put(linkName, target);
if (oldValue != null) {
throw new IllegalStateException("Duplicate link from " + linkName);
}
}
/** Returns a mapping from linkName (old tz ID) to target (new tz ID). */
Map<String, String> getDirectLinks() {
// Validate links for cycles and collapse the links if there are links to links. There's a
// simple check to confirm that no chain is longer than a fixed length, to guard against
// cycles.
final int maxChainLength = 2;
Map<String, String> collapsedLinks = new HashMap<>();
for (String fromId : links.keySet()) {
int chainLength = 0;
String currentId = fromId;
String lastId = null;
while ((currentId = links.get(currentId)) != null) {
chainLength++;
lastId = currentId;
if (chainLength >= maxChainLength) {
throw new IllegalStateException(
"Chain from " + fromId + " is longer than " + maxChainLength);
}
}
if (chainLength == 0) {
throw new IllegalStateException("Null Link targetId for " + fromId);
}
collapsedLinks.put(fromId, lastId);
}
return Collections.unmodifiableMap(collapsedLinks);
}
}