diff options
Diffstat (limited to 'tzdata/tools/src')
-rw-r--r-- | tzdata/tools/src/main/libcore/tzdata/update/tools/CreateTzDataBundle.java | 127 | ||||
-rw-r--r-- | tzdata/tools/src/main/libcore/tzdata/update/tools/TzDataBundleBuilder.java | 134 |
2 files changed, 261 insertions, 0 deletions
diff --git a/tzdata/tools/src/main/libcore/tzdata/update/tools/CreateTzDataBundle.java b/tzdata/tools/src/main/libcore/tzdata/update/tools/CreateTzDataBundle.java new file mode 100644 index 0000000..cdb004a --- /dev/null +++ b/tzdata/tools/src/main/libcore/tzdata/update/tools/CreateTzDataBundle.java @@ -0,0 +1,127 @@ +/* + * Copyright (C) 2015 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 libcore.tzdata.update.tools; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.io.Reader; +import java.util.Properties; +import libcore.tzdata.update.ConfigBundle; +import libcore.tzdata.update.FileUtils; + +/** + * A command-line tool for creating a TZ data update bundle. + * + * Args: + * tzdata.properties file - the file describing the bundle (see template file in tzdata/tools) + * output file - the name of the file to be generated + */ +public class CreateTzDataBundle { + + private CreateTzDataBundle() {} + + public static void main(String[] args) throws Exception { + if (args.length != 2) { + printUsage(); + System.exit(1); + } + File f = new File(args[0]); + if (!f.exists()) { + System.err.println("Properties file " + f + " not found"); + printUsage(); + System.exit(2); + } + Properties p = loadProperties(f); + TzDataBundleBuilder builder = new TzDataBundleBuilder() + .setTzDataVersion(getMandatoryProperty(p, "tzdata.version")) + .addBionicTzData(getMandatoryPropertyFile(p, "bionic.file")) + .addIcuTzData(getMandatoryPropertyFile(p, "icu.file")); + + int i = 1; + while (true) { + String localFileNameProperty = "checksum.file.local." + i; + String localFileName = p.getProperty(localFileNameProperty); + String onDeviceFileNameProperty = "checksum.file.ondevice." + i; + String onDeviceFileName = p.getProperty(onDeviceFileNameProperty); + boolean foundLocalFileNameProperty = localFileName != null; + boolean foundOnDeviceFileNameProperty = onDeviceFileName != null; + if (!foundLocalFileNameProperty && !foundOnDeviceFileNameProperty) { + break; + } else if (foundLocalFileNameProperty != foundOnDeviceFileNameProperty) { + System.out.println("Properties file must specify both, or neither of: " + + localFileNameProperty + " and " + onDeviceFileNameProperty); + System.exit(5); + } + + long checksum = FileUtils.calculateChecksum(new File(localFileName)); + builder.addChecksum(onDeviceFileName, checksum); + i++; + } + if (i == 1) { + // For safety we enforce >= 1 checksum entry. The installer does not require it. + System.out.println("There must be at least one checksum file"); + System.exit(6); + } + System.out.println("Update contains checksums for " + (i-1) + " files"); + + ConfigBundle bundle = builder.build(); + File outputFile = new File(args[1]); + try (OutputStream os = new FileOutputStream(outputFile)) { + os.write(bundle.getBundleBytes()); + } + System.out.println("Wrote: " + outputFile); + } + + private static File getMandatoryPropertyFile(Properties p, String propertyName) { + String fileName = getMandatoryProperty(p, propertyName); + File file = new File(fileName); + if (!file.exists()) { + System.out.println( + "Missing file: " + file + " for property " + propertyName + " does not exist."); + printUsage(); + System.exit(4); + } + return file; + } + + private static String getMandatoryProperty(Properties p, String propertyName) { + String value = p.getProperty(propertyName); + if (value == null) { + System.out.println("Missing property: " + propertyName); + printUsage(); + System.exit(3); + } + return value; + } + + private static Properties loadProperties(File f) throws IOException { + Properties p = new Properties(); + try (Reader reader = new InputStreamReader(new FileInputStream(f))) { + p.load(reader); + } + return p; + } + + private static void printUsage() { + System.out.println("Usage:"); + System.out.println("\t" + CreateTzDataBundle.class.getName() + + " <tzupdate.properties file> <output file>"); + } +} diff --git a/tzdata/tools/src/main/libcore/tzdata/update/tools/TzDataBundleBuilder.java b/tzdata/tools/src/main/libcore/tzdata/update/tools/TzDataBundleBuilder.java new file mode 100644 index 0000000..3550c6f --- /dev/null +++ b/tzdata/tools/src/main/libcore/tzdata/update/tools/TzDataBundleBuilder.java @@ -0,0 +1,134 @@ +/* + * Copyright (C) 2015 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 libcore.tzdata.update.tools; + +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.util.zip.ZipEntry; +import java.util.zip.ZipOutputStream; +import libcore.tzdata.update.ConfigBundle; + +/** + * A class for creating a {@link ConfigBundle} containing timezone update data. + */ +public final class TzDataBundleBuilder { + + private String tzDataVersion; + private StringBuilder checksumsFileContent = new StringBuilder(); + private File zoneInfoFile; + private File icuTzDataFile; + + public TzDataBundleBuilder setTzDataVersion(String tzDataVersion) { + this.tzDataVersion = tzDataVersion; + return this; + } + + public TzDataBundleBuilder addChecksum(String fileName, long checksum) { + checksumsFileContent.append(Long.toString(checksum)) + .append(',') + .append(fileName) + .append('\n'); + return this; + } + + public TzDataBundleBuilder addBionicTzData(File zoneInfoFile) { + this.zoneInfoFile = zoneInfoFile; + return this; + } + + public TzDataBundleBuilder addIcuTzData(File icuTzDataFile) { + this.icuTzDataFile = icuTzDataFile; + return this; + } + + /** + * Builds a {@link libcore.tzdata.update.ConfigBundle}. + */ + public ConfigBundle build() throws IOException { + if (tzDataVersion == null) { + throw new IllegalStateException("Missing tzDataVersion"); + } + if (zoneInfoFile == null) { + throw new IllegalStateException("Missing zoneInfo file"); + } + + return buildUnvalidated(); + } + + // For use in tests. + public TzDataBundleBuilder clearChecksumEntries() { + checksumsFileContent.setLength(0); + return this; + } + + // For use in tests. + public TzDataBundleBuilder clearBionicTzData() { + this.zoneInfoFile = null; + return this; + } + + /** + * For use in tests. Use {@link #build()}. + */ + public ConfigBundle buildUnvalidated() throws IOException { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + try (ZipOutputStream zos = new ZipOutputStream(baos)) { + addZipEntry(zos, ConfigBundle.CHECKSUMS_FILE_NAME, + checksumsFileContent.toString().getBytes(StandardCharsets.UTF_8)); + if (tzDataVersion != null) { + addZipEntry(zos, ConfigBundle.TZ_DATA_VERSION_FILE_NAME, + tzDataVersion.getBytes(StandardCharsets.UTF_8)); + } + if (zoneInfoFile != null) { + addZipEntry(zos, ConfigBundle.ZONEINFO_FILE_NAME, + readFileAsByteArray(zoneInfoFile)); + } + if (icuTzDataFile != null) { + addZipEntry(zos, ConfigBundle.ICU_DATA_FILE_NAME, + readFileAsByteArray(icuTzDataFile)); + } + } + return new ConfigBundle(baos.toByteArray()); + } + + private static void addZipEntry(ZipOutputStream zos, String name, byte[] content) + throws IOException { + ZipEntry zipEntry = new ZipEntry(name); + zipEntry.setSize(content.length); + zos.putNextEntry(zipEntry); + zos.write(content); + zos.closeEntry(); + } + + /** + * Returns the contents of 'path' as a byte array. + */ + public static byte[] readFileAsByteArray(File file) throws IOException { + byte[] buffer = new byte[8192]; + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + try (FileInputStream fis = new FileInputStream(file)) { + int count; + while ((count = fis.read(buffer)) != -1) { + baos.write(buffer, 0, count); + } + } + return baos.toByteArray(); + } +} + |