1 /* 2 * Copyright (C) 2011 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not 5 * use this file except in compliance with the License. You may obtain a copy of 6 * the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 * License for the specific language governing permissions and limitations under 14 * the License. 15 */ 16 17 package com.android.inputmethod.dictionarypack; 18 19 import android.content.ContentValues; 20 import android.content.Context; 21 import android.database.Cursor; 22 import android.util.Log; 23 24 import java.io.IOException; 25 import java.io.InputStreamReader; 26 import java.util.Collections; 27 import java.util.ArrayList; 28 import java.util.List; 29 30 /** 31 * Helper class to easy up manipulation of dictionary pack metadata. 32 */ 33 public class MetadataHandler { 34 35 public static final String TAG = MetadataHandler.class.getSimpleName(); 36 37 // The canonical file name for metadata. This is not the name of a real file on the 38 // device, but a symbolic name used in the database and in metadata handling. It is never 39 // tested against, only used for human-readability as the file name for the metadata. 40 public static final String METADATA_FILENAME = "metadata.json"; 41 42 /** 43 * Reads the data from the cursor and store it in metadata objects. 44 * @param results the cursor to read data from. 45 * @return the constructed list of wordlist metadata. 46 */ makeMetadataObject(final Cursor results)47 private static List<WordListMetadata> makeMetadataObject(final Cursor results) { 48 final ArrayList<WordListMetadata> buildingMetadata = new ArrayList<>(); 49 if (null != results && results.moveToFirst()) { 50 final int localeColumn = results.getColumnIndex(MetadataDbHelper.LOCALE_COLUMN); 51 final int typeColumn = results.getColumnIndex(MetadataDbHelper.TYPE_COLUMN); 52 final int descriptionColumn = 53 results.getColumnIndex(MetadataDbHelper.DESCRIPTION_COLUMN); 54 final int idIndex = results.getColumnIndex(MetadataDbHelper.WORDLISTID_COLUMN); 55 final int updateIndex = results.getColumnIndex(MetadataDbHelper.DATE_COLUMN); 56 final int fileSizeIndex = results.getColumnIndex(MetadataDbHelper.FILESIZE_COLUMN); 57 final int rawChecksumIndex = 58 results.getColumnIndex(MetadataDbHelper.RAW_CHECKSUM_COLUMN); 59 final int checksumIndex = results.getColumnIndex(MetadataDbHelper.CHECKSUM_COLUMN); 60 final int retryCountIndex = results.getColumnIndex(MetadataDbHelper.RETRY_COUNT_COLUMN); 61 final int localFilenameIndex = 62 results.getColumnIndex(MetadataDbHelper.LOCAL_FILENAME_COLUMN); 63 final int remoteFilenameIndex = 64 results.getColumnIndex(MetadataDbHelper.REMOTE_FILENAME_COLUMN); 65 final int versionIndex = results.getColumnIndex(MetadataDbHelper.VERSION_COLUMN); 66 final int formatVersionIndex = 67 results.getColumnIndex(MetadataDbHelper.FORMATVERSION_COLUMN); 68 do { 69 buildingMetadata.add(new WordListMetadata(results.getString(idIndex), 70 results.getInt(typeColumn), 71 results.getString(descriptionColumn), 72 results.getLong(updateIndex), 73 results.getLong(fileSizeIndex), 74 results.getString(rawChecksumIndex), 75 results.getString(checksumIndex), 76 results.getInt(retryCountIndex), 77 results.getString(localFilenameIndex), 78 results.getString(remoteFilenameIndex), 79 results.getInt(versionIndex), 80 results.getInt(formatVersionIndex), 81 0, results.getString(localeColumn))); 82 } while (results.moveToNext()); 83 } 84 return Collections.unmodifiableList(buildingMetadata); 85 } 86 87 /** 88 * Gets the whole metadata, for installed and not installed dictionaries. 89 * @param context The context to open files over. 90 * @param clientId the client id for retrieving the database. null for default (deprecated) 91 * @return The current metadata. 92 */ getCurrentMetadata(final Context context, final String clientId)93 public static List<WordListMetadata> getCurrentMetadata(final Context context, 94 final String clientId) { 95 // If clientId is null, we get a cursor on the default database (see 96 // MetadataDbHelper#getInstance() for more on this) 97 final Cursor results = MetadataDbHelper.queryCurrentMetadata(context, clientId); 98 // If null, we should return makeMetadataObject(null), so we go through. 99 try { 100 return makeMetadataObject(results); 101 } finally { 102 if (null != results) { 103 results.close(); 104 } 105 } 106 } 107 108 /** 109 * Gets the metadata, for a specific dictionary. 110 * 111 * @param context The context to open files over. 112 * @param clientId the client id for retrieving the database. null for default (deprecated). 113 * @param wordListId the word list ID. 114 * @param version the word list version. 115 * @return the current metaData 116 */ getCurrentMetadataForWordList(final Context context, final String clientId, final String wordListId, final int version)117 public static WordListMetadata getCurrentMetadataForWordList(final Context context, 118 final String clientId, final String wordListId, final int version) { 119 final ContentValues contentValues = MetadataDbHelper.getContentValuesByWordListId( 120 MetadataDbHelper.getDb(context, clientId), wordListId, version); 121 if (contentValues == null) { 122 // TODO: Figure out why this would happen. 123 // Check if this happens when the metadata gets updated in the background. 124 Log.e(TAG, String.format( "Unable to find the current metadata for wordlist " 125 + "(clientId=%s, wordListId=%s, version=%d) on the database", 126 clientId, wordListId, version)); 127 return null; 128 } 129 return WordListMetadata.createFromContentValues(contentValues); 130 } 131 132 /** 133 * Read metadata from a stream. 134 * @param input The stream to read from. 135 * @return The read metadata. 136 * @throws IOException if the input stream cannot be read 137 * @throws BadFormatException if the stream is not in a known format 138 */ readMetadata(final InputStreamReader input)139 public static List<WordListMetadata> readMetadata(final InputStreamReader input) 140 throws IOException, BadFormatException { 141 return MetadataParser.parseMetadata(input); 142 } 143 144 /** 145 * Finds a single WordListMetadata inside a whole metadata chunk. 146 * 147 * Searches through the whole passed metadata for the first WordListMetadata associated 148 * with the passed ID. If several metadata chunks with the same id are found, it will 149 * always return the one with the bigger FormatVersion that is less or equal than the 150 * maximum supported format version (as listed in UpdateHandler). 151 * This will NEVER return the metadata with a FormatVersion bigger than what is supported, 152 * even if it is the only word list with this ID. 153 * 154 * @param metadata the metadata to search into. 155 * @param id the word list ID of the metadata to find. 156 * @return the associated metadata, or null if not found. 157 */ findWordListById(final List<WordListMetadata> metadata, final String id)158 public static WordListMetadata findWordListById(final List<WordListMetadata> metadata, 159 final String id) { 160 WordListMetadata bestWordList = null; 161 int bestFormatVersion = Integer.MIN_VALUE; // To be sure we can't be inadvertently smaller 162 for (WordListMetadata wordList : metadata) { 163 if (id.equals(wordList.mId) 164 && wordList.mFormatVersion <= UpdateHandler.MAXIMUM_SUPPORTED_FORMAT_VERSION 165 && wordList.mFormatVersion > bestFormatVersion) { 166 bestWordList = wordList; 167 bestFormatVersion = wordList.mFormatVersion; 168 } 169 } 170 // If we didn't find any match we'll return null. 171 return bestWordList; 172 } 173 } 174