package com.door43.translationstudio.core; import android.content.Context; import android.support.v4.provider.DocumentFile; import com.door43.translationstudio.AppContext; import com.door43.util.Zip; import org.apache.commons.io.FileUtils; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; import java.util.List; /** * Holds details about the translation archive * TODO: this duplicates a lot of code from ArchiveImporter. Eventually it might be nice to refactor both so that there is less duplication. */ public class ArchiveDetails { public final long createdAt; public final TargetTranslationDetails[] targetTranslationDetails; /** * Creates a new instance of the archive details * @param timestamp */ private ArchiveDetails(long timestamp, TargetTranslationDetails[] targetTranslationDetails) { this.createdAt = timestamp; this.targetTranslationDetails = targetTranslationDetails; } /** * Reads the details from a translationStudio archive * @param archiveStream * @param preferredLocale * @param library * @return * @throws Exception */ public static ArchiveDetails newInstance(InputStream archiveStream, String preferredLocale, Library library) throws Exception { if(archiveStream != null) { File tempFile = File.createTempFile("targettranslation", "." + Translator.ARCHIVE_EXTENSION); FileUtils.copyInputStreamToFile(archiveStream, tempFile); String rawManifest = Zip.read(tempFile, "manifest.json"); if (rawManifest != null) { JSONObject json = new JSONObject(rawManifest); if (json.has("package_version")) { int manifestVersion = json.getInt("package_version"); switch (manifestVersion) { case 1: return parseV1Manifest(json); case 2: return parseV2Manifest(new FileInputStream(tempFile), json, preferredLocale, library); } } } } return null; } /** * Reads the details from a translationStudio archive * @param archive * @return * @throws IOException */ public static ArchiveDetails newInstance(File archive, String preferredLocale, Library library) throws Exception { if(archive != null && archive.exists()) { String rawManifest = Zip.read(archive, "manifest.json"); if(rawManifest != null) { JSONObject json = new JSONObject(rawManifest); if(json.has("package_version")) { int manifestVersion = json.getInt("package_version"); switch (manifestVersion) { case 1: return parseV1Manifest(json); case 2: return parseV2Manifest(new FileInputStream(archive), json, preferredLocale, library); } } } return null; } else { return null; } } /** * Reads the details from a translationStudio archive * @param archive * @return * @throws IOException */ public static ArchiveDetails newInstance(Context context, DocumentFile archive, String preferredLocale, Library library) throws Exception { if(archive != null && archive.exists()) { InputStream ais = context.getContentResolver().openInputStream(archive.getUri()); String rawManifest = Zip.readInputStream(ais, "manifest.json"); if (rawManifest != null) { JSONObject json = new JSONObject(rawManifest); if (json.has("package_version")) { int manifestVersion = json.getInt("package_version"); switch (manifestVersion) { case 1: return parseV1Manifest(json); case 2: return parseV2Manifest(context.getContentResolver().openInputStream(archive.getUri()), json, preferredLocale, library); } } } return null; } else { return null; } } private static ArchiveDetails parseV1Manifest(JSONObject json) { return null; } private static ArchiveDetails parseV2Manifest(InputStream ais, JSONObject archiveManifest, String preferredLocale, Library library) throws JSONException, IOException { List<TargetTranslationDetails> targetDetails = new ArrayList<>(); long timestamp = archiveManifest.getLong("timestamp"); JSONArray translationsJson = archiveManifest.getJSONArray("target_translations"); for(int i = 0; i < translationsJson.length(); i ++) { JSONObject translationRecordJson = translationsJson.getJSONObject(i); String path = translationRecordJson.getString("path"); String rawTranslationManifest = Zip.readInputStream(ais, path.replaceAll("/+$", "") + "/manifest.json"); if(rawTranslationManifest != null) { JSONObject manifest = new JSONObject(rawTranslationManifest); // migrate the manifest manifest = TargetTranslationMigrator.migrateManifest(manifest); if (manifest != null) { JSONObject targetLanguageJson = manifest.getJSONObject("target_language"); JSONObject projectJson = manifest.getJSONObject("project"); // get target language String targetLanguageName = null; String targetLanguageSlug = targetLanguageJson.getString("id"); LanguageDirection targetLangaugeDirection = LanguageDirection.get(targetLanguageJson.getString("direction")); if (targetLangaugeDirection == null) { targetLangaugeDirection = LanguageDirection.LeftToRight; } TargetLanguage tl = library.getTargetLanguage(targetLanguageSlug); if (tl != null) { targetLanguageName = tl.name; } else { targetLanguageName = targetLanguageSlug.toUpperCase(); } // get project String projectName = null; String projectSlug = projectJson.getString("id"); Project project = library.getProject(projectSlug, preferredLocale); if (project != null) { projectName = project.name; } else { projectName = projectSlug.toUpperCase(); } // git commit hash String commit = translationRecordJson.getString("commit_hash"); // translation type TranslationType translationType = TranslationType.get(manifest.getJSONObject("type").getString("id")); if (translationType == null) { translationType = TranslationType.TEXT; } // resource String resourceSlug = null; if (manifest.has("resource")) { resourceSlug = manifest.getJSONObject("resource").getString("id"); } // build id String targetTranslationId = TargetTranslation.generateTargetTranslationId(targetLanguageSlug, projectSlug, translationType, resourceSlug); targetDetails.add(new TargetTranslationDetails(targetTranslationId, targetLanguageSlug, targetLanguageName, projectSlug, projectName, targetLangaugeDirection, commit)); } } } ais.close(); return new ArchiveDetails(timestamp, targetDetails.toArray(new TargetTranslationDetails[targetDetails.size()])); } /** * Returns an empty archive * @return */ public static ArchiveDetails newDummyInstance() { return new ArchiveDetails(0, new TargetTranslationDetails[0]); } /** * Contains details about a target translation in the archive */ public static class TargetTranslationDetails { public final String targetTranslationSlug; public final String targetLanguageName; public final String projectSlug; public final String targetLanguageSlug; public final String projectName; public final LanguageDirection direction; public final String commitHash; /** * Creates a new instance of the archive details * @param targetTranslationSlug * @param targetLanguageSlug * @param targetLanguageName * @param projectSlug * @param projectName */ private TargetTranslationDetails(String targetTranslationSlug, String targetLanguageSlug, String targetLanguageName, String projectSlug, String projectName, LanguageDirection direction, String commitHash) { this.targetTranslationSlug = targetTranslationSlug; this.targetLanguageName = targetLanguageName; this.projectSlug = projectSlug; this.targetLanguageSlug = targetLanguageSlug; this.projectName = projectName; this.direction = direction; this.commitHash = commitHash; } } }