| // Copyright 2010-2014, Google Inc. |
| // All rights reserved. |
| // |
| // Redistribution and use in source and binary forms, with or without |
| // modification, are permitted provided that the following conditions are |
| // met: |
| // |
| // * Redistributions of source code must retain the above copyright |
| // notice, this list of conditions and the following disclaimer. |
| // * Redistributions in binary form must reproduce the above |
| // copyright notice, this list of conditions and the following disclaimer |
| // in the documentation and/or other materials provided with the |
| // distribution. |
| // * Neither the name of Google Inc. nor the names of its |
| // contributors may be used to endorse or promote products derived from |
| // this software without specific prior written permission. |
| // |
| // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
| // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
| // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
| // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
| // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
| // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
| // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| |
| package org.mozc.android.inputmethod.japanese.userdictionary; |
| |
| import org.mozc.android.inputmethod.japanese.MozcLog; |
| import org.mozc.android.inputmethod.japanese.MozcUtil; |
| import org.mozc.android.inputmethod.japanese.protobuf.ProtoUserDictionaryStorage.UserDictionary.Entry; |
| import org.mozc.android.inputmethod.japanese.protobuf.ProtoUserDictionaryStorage.UserDictionary.PosType; |
| import org.mozc.android.inputmethod.japanese.protobuf.ProtoUserDictionaryStorage.UserDictionaryCommand; |
| import org.mozc.android.inputmethod.japanese.protobuf.ProtoUserDictionaryStorage.UserDictionaryCommand.Builder; |
| import org.mozc.android.inputmethod.japanese.protobuf.ProtoUserDictionaryStorage.UserDictionaryCommand.CommandType; |
| import org.mozc.android.inputmethod.japanese.protobuf.ProtoUserDictionaryStorage.UserDictionaryCommandStatus; |
| import org.mozc.android.inputmethod.japanese.protobuf.ProtoUserDictionaryStorage.UserDictionaryCommandStatus.Status; |
| import org.mozc.android.inputmethod.japanese.protobuf.ProtoUserDictionaryStorage.UserDictionaryStorage; |
| import org.mozc.android.inputmethod.japanese.session.SessionExecutor; |
| import com.google.common.annotations.VisibleForTesting; |
| import com.google.common.base.Optional; |
| import com.google.common.base.Preconditions; |
| |
| import android.content.res.Resources; |
| import android.net.Uri; |
| |
| import java.io.BufferedOutputStream; |
| import java.io.File; |
| import java.io.FileOutputStream; |
| import java.io.IOException; |
| import java.util.AbstractList; |
| import java.util.EnumMap; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.zip.ZipEntry; |
| import java.util.zip.ZipFile; |
| import java.util.zip.ZipOutputStream; |
| |
| /** |
| */ |
| public class UserDictionaryToolModel { |
| private final SessionExecutor sessionExecutor; |
| @VisibleForTesting long sessionId; |
| |
| /** User dictionary storage which only contains dictionary id / name. */ |
| @VisibleForTesting UserDictionaryStorage storage = null; |
| |
| @VisibleForTesting long selectedDictionaryId = 0; |
| |
| // TODO(hidehiko): Move this bit into the server side. |
| @VisibleForTesting boolean dirty = false; |
| |
| // Unfortunately our target API level 7 doesn't support to passing any arguments to the dialog. |
| // Thus, as a workaround, we relay the edit target entry's index by this model. |
| private int editTargetIndex; |
| |
| // Pending status of import data. |
| private Uri importUri; |
| private String importData; |
| private ZipFile zipFile; |
| |
| // List "view" by proxying dictionary names in the storage. |
| private final List<String> dictionaryNameList = new AbstractList<String>() { |
| @Override |
| public String get(int index) { |
| return storage.getDictionaries(index).getName(); |
| } |
| |
| @Override |
| public int size() { |
| return (storage == null) ? 0 : storage.getDictionariesCount(); |
| } |
| }; |
| |
| public UserDictionaryToolModel(SessionExecutor sessionExecutor) { |
| this.sessionExecutor = sessionExecutor; |
| } |
| |
| /** |
| * Returns {@code true} if the storage is edited after loading. |
| */ |
| public boolean isDirty() { |
| return dirty; |
| } |
| |
| /** |
| * Creates a session to communicate with the native server about user dictionary editing. |
| * This method should be called before any other methods. |
| */ |
| public void createSession() { |
| UserDictionaryCommand command = UserDictionaryCommand.newBuilder() |
| .setType(CommandType.CREATE_SESSION) |
| .build(); |
| UserDictionaryCommandStatus status = sessionExecutor.sendUserDictionaryCommand(command); |
| if (status.getStatus() != Status.USER_DICTIONARY_COMMAND_SUCCESS) { |
| throw new IllegalStateException("UserDictionaryCommand session should be created always."); |
| } |
| sessionId = status.getSessionId(); |
| } |
| |
| /** |
| * Deletes the current session. |
| */ |
| public void deleteSession() { |
| UserDictionaryCommand command = UserDictionaryCommand.newBuilder() |
| .setType(CommandType.DELETE_SESSION) |
| .setSessionId(sessionId) |
| .build(); |
| UserDictionaryCommandStatus status = sessionExecutor.sendUserDictionaryCommand(command); |
| if (status.getStatus() != Status.USER_DICTIONARY_COMMAND_SUCCESS) { |
| MozcLog.e("Failed to delete user dictionary command session."); |
| } |
| sessionId = 0; |
| } |
| |
| /** |
| * Resumes the current session by loading the data from storage, |
| * and updates storage and selected id. |
| * @params defautlDictionaryName the name of the default dictionary, which is created when, |
| * e.g., LOAD operation is failing, or the storage gets empty because of the deletion of |
| * the last dictionary. |
| */ |
| public Status resumeSession(String defaultDictionaryName) { |
| ensureSession(); |
| |
| // Set default dictionary name. |
| { |
| UserDictionaryCommand command = UserDictionaryCommand.newBuilder() |
| .setType(CommandType.SET_DEFAULT_DICTIONARY_NAME) |
| .setSessionId(sessionId) |
| .setDictionaryName(defaultDictionaryName) |
| .build(); |
| // Ignore any errors. |
| sessionExecutor.sendUserDictionaryCommand(command); |
| } |
| |
| // Load the dictionary. |
| UserDictionaryCommand command = UserDictionaryCommand.newBuilder() |
| .setType(CommandType.LOAD) |
| .setSessionId(sessionId) |
| .setEnsureNonEmptyStorage(true) |
| .build(); |
| UserDictionaryCommandStatus status = sessionExecutor.sendUserDictionaryCommand(command); |
| |
| // Update the dictionary list regardless of the result of the LOAD command. |
| // Read new dictionary list. |
| { |
| Status updateStatus = updateStorage(); |
| if (updateStatus == Status.USER_DICTIONARY_COMMAND_SUCCESS) { |
| selectedDictionaryId = storage.getDictionaries(0).getId(); |
| } |
| } |
| |
| // Especially on the first time, there are no user dictionary file, so "file not found" |
| // is (a kind of) expected behavior. In order not to show "error" message to users, |
| // ignore the error here. |
| if (status.getStatus() == Status.FILE_NOT_FOUND) { |
| return Status.USER_DICTIONARY_COMMAND_SUCCESS; |
| } |
| return status.getStatus(); |
| } |
| |
| private void ensureSession() { |
| // First ping to the mozc server. |
| { |
| UserDictionaryCommand command = UserDictionaryCommand.newBuilder() |
| .setType(CommandType.NO_OPERATION) |
| .setSessionId(sessionId) |
| .build(); |
| UserDictionaryCommandStatus status = sessionExecutor.sendUserDictionaryCommand(command); |
| if (status.getStatus() == Status.USER_DICTIONARY_COMMAND_SUCCESS) { |
| return; |
| } |
| } |
| |
| // The session is somehow broken. Actually, because of Android architecture, |
| // we can have multiple Activity instances, but due to resource limitation the current |
| // limit of the number of sessions is only 1. So kick the other session out, and recreate |
| // our session again. |
| createSession(); |
| } |
| |
| /** |
| * Treis to save if necessary, and also tries to reload the server. |
| */ |
| public Status pauseSession() { |
| checkSession(); |
| |
| // Save the dictionary if necessary. |
| if (dirty) { |
| UserDictionaryCommand command = UserDictionaryCommand.newBuilder() |
| .setType(CommandType.SAVE) |
| .setSessionId(sessionId) |
| .build(); |
| UserDictionaryCommandStatus status = sessionExecutor.sendUserDictionaryCommand(command); |
| if (status.getStatus() != Status.USER_DICTIONARY_COMMAND_SUCCESS) { |
| return status.getStatus(); |
| } |
| |
| // When the save is succeeded, we need to reload the mozc server. |
| sessionExecutor.reload(); |
| } |
| |
| return Status.USER_DICTIONARY_COMMAND_SUCCESS; |
| } |
| |
| /** |
| * Returns the list "view" of dictionary names in the storage. |
| * The contents of the returned instance should be automatically updated when the storage |
| * is updated. |
| */ |
| public List<String> getDictionaryNameList() { |
| return dictionaryNameList; |
| } |
| |
| /** |
| * Sets the current selected dictionary based on the given index. |
| */ |
| public void setSelectedDictionaryByIndex(int index) { |
| if (storage == null || index < 0 || storage.getDictionariesCount() <= index) { |
| // Invalid state. |
| selectedDictionaryId = 0; |
| return; |
| } |
| |
| selectedDictionaryId = storage.getDictionaries(index).getId(); |
| } |
| |
| /** |
| * Returns the index of the current selected dictionary. |
| */ |
| public int getSelectedDictionaryIndex() { |
| int index = getSelectedDictionaryIndexInternal(); |
| if (index < 0) { |
| return 0; |
| } |
| return index; |
| } |
| |
| /** |
| * Returns the name of the current selected dictionary. |
| */ |
| public String getSelectedDictionaryName() { |
| int index = getSelectedDictionaryIndexInternal(); |
| if (index < -1) { |
| return null; |
| } |
| return storage.getDictionaries(index).getName(); |
| } |
| |
| private int getSelectedDictionaryIndexInternal() { |
| if (storage != null && selectedDictionaryId != 0) { |
| for (int i = 0; i < storage.getDictionariesCount(); ++i) { |
| if (storage.getDictionaries(i).getId() == selectedDictionaryId) { |
| return i; |
| } |
| } |
| } |
| |
| // Not found. |
| return -1; |
| } |
| |
| private void checkSession() { |
| if (sessionId == 0) { |
| throw new IllegalStateException("Session is not yet created."); |
| } |
| } |
| |
| /** |
| * Checks if we can add another dictionary to the storage. |
| */ |
| public Status checkNewDictionaryAvailability() { |
| UserDictionaryCommand command = UserDictionaryCommand.newBuilder() |
| .setType(CommandType.CHECK_NEW_DICTIONARY_AVAILABILITY) |
| .setSessionId(sessionId) |
| .build(); |
| return sessionExecutor.sendUserDictionaryCommand(command).getStatus(); |
| } |
| |
| public Status checkUndoability() { |
| UserDictionaryCommand command = UserDictionaryCommand.newBuilder() |
| .setType(CommandType.CHECK_UNDOABILITY) |
| .setSessionId(sessionId) |
| .build(); |
| UserDictionaryCommandStatus status = sessionExecutor.sendUserDictionaryCommand(command); |
| return status.getStatus(); |
| } |
| |
| public Status undo() { |
| int index = getSelectedDictionaryIndex(); |
| UserDictionaryCommand command = UserDictionaryCommand.newBuilder() |
| .setType(CommandType.UNDO) |
| .setSessionId(sessionId) |
| .build(); |
| |
| UserDictionaryCommandStatus status = sessionExecutor.sendUserDictionaryCommand(command); |
| if (status.getStatus() == Status.USER_DICTIONARY_COMMAND_SUCCESS) { |
| dirty = true; |
| |
| Status updateStatus = updateStorage(); |
| if (updateStatus != Status.USER_DICTIONARY_COMMAND_SUCCESS) { |
| return updateStatus; |
| } |
| |
| boolean found = false; |
| for (int i = 0; i < storage.getDictionariesCount(); ++i) { |
| if (storage.getDictionaries(i).getId() == selectedDictionaryId) { |
| found = true; |
| break; |
| } |
| } |
| |
| if (!found) { |
| setSelectedDictionaryByIndex(Math.min(index, storage.getDictionariesCount() - 1)); |
| } |
| } |
| |
| return status.getStatus(); |
| } |
| |
| /** |
| * Creates a new empty dictionary with the given dictionary name. |
| * Also update the selected dictionary to the created one. |
| */ |
| public Status createDictionary(String dictionaryName) { |
| UserDictionaryCommand command = UserDictionaryCommand.newBuilder() |
| .setType(CommandType.CREATE_DICTIONARY) |
| .setSessionId(sessionId) |
| .setDictionaryName(dictionaryName) |
| .build(); |
| UserDictionaryCommandStatus status = |
| sessionExecutor.sendUserDictionaryCommand(command); |
| if (status.getStatus() == Status.USER_DICTIONARY_COMMAND_SUCCESS) { |
| dirty = true; |
| |
| Status updateStatus = updateStorage(); |
| if (updateStatus != Status.USER_DICTIONARY_COMMAND_SUCCESS) { |
| return updateStatus; |
| } |
| selectedDictionaryId = status.getDictionaryId(); |
| } |
| return status.getStatus(); |
| } |
| |
| /** |
| * Renames the currently selected dictionary to the given dictionary name. |
| */ |
| public Status renameSelectedDictionary(String dictionaryName) { |
| UserDictionaryCommand command = UserDictionaryCommand.newBuilder() |
| .setType(CommandType.RENAME_DICTIONARY) |
| .setSessionId(sessionId) |
| .setDictionaryId(selectedDictionaryId) |
| .setDictionaryName(dictionaryName) |
| .build(); |
| UserDictionaryCommandStatus status = |
| sessionExecutor.sendUserDictionaryCommand(command); |
| if (status.getStatus() == Status.USER_DICTIONARY_COMMAND_SUCCESS) { |
| dirty = true; |
| Status updateStatus = updateStorage(); |
| if (updateStatus != Status.USER_DICTIONARY_COMMAND_SUCCESS) { |
| return updateStatus; |
| } |
| } |
| return status.getStatus(); |
| } |
| |
| /** |
| * Deletes the current selected dictionary. |
| * The new selected dictionary should be the "same" position of the dictionaries in the storage. |
| * If the dictionary gets empty, no dictionary is selected. |
| * If the deleted dictionary is at the end of the storage, |
| * the new selected dictionary will be the one at the end of the storage after |
| * deleting operation. |
| */ |
| public Status deleteSelectedDictionary() { |
| int index = getSelectedDictionaryIndex(); |
| UserDictionaryCommand command = UserDictionaryCommand.newBuilder() |
| .setType(CommandType.DELETE_DICTIONARY) |
| .setSessionId(sessionId) |
| .setDictionaryId(selectedDictionaryId) |
| .setEnsureNonEmptyStorage(true) |
| .build(); |
| UserDictionaryCommandStatus status = |
| sessionExecutor.sendUserDictionaryCommand(command); |
| if (status.getStatus() == Status.USER_DICTIONARY_COMMAND_SUCCESS) { |
| dirty = true; |
| Status updateStatus = updateStorage(); |
| if (updateStatus != Status.USER_DICTIONARY_COMMAND_SUCCESS) { |
| return updateStatus; |
| } |
| selectedDictionaryId = |
| storage.getDictionaries(Math.min(index, storage.getDictionariesCount() - 1)).getId(); |
| } |
| return status.getStatus(); |
| } |
| |
| public Optional<File> createExportFile( |
| Resources resources, String dictionaryName, File tempDirectory) { |
| Preconditions.checkNotNull(resources); |
| Preconditions.checkNotNull(dictionaryName); |
| Preconditions.checkNotNull(tempDirectory); |
| Preconditions.checkArgument(dictionaryName.length() > 0); |
| Preconditions.checkArgument(tempDirectory.isDirectory()); |
| |
| File exportFile = null; |
| ZipOutputStream zipStream = null; |
| try { |
| exportFile = File.createTempFile("export_temp_", ".zip", tempDirectory); |
| exportFile.deleteOnExit(); |
| zipStream = new ZipOutputStream(new BufferedOutputStream(new FileOutputStream(exportFile))); |
| zipStream.putNextEntry(new ZipEntry(dictionaryName + ".txt")); |
| |
| int entrySize = getEntrySize(); |
| int fetchSize = 1000; |
| int queryNum = (int) Math.ceil((double) entrySize / fetchSize); |
| for (int i = 0; i < queryNum; ++i) { |
| String exportString = getSelectedDictionaryAsString( |
| resources, i * fetchSize, Math.min((i + 1) * fetchSize, entrySize)); |
| // getBytes() always returns UTF-8 encoded byte stream because the default charset on |
| // Android is always UTF-8 and is immutable. |
| zipStream.write(exportString.getBytes()); |
| } |
| zipStream.close(); |
| } catch (IOException e) { |
| MozcLog.e("Failed to prepare export data. " + e.getMessage()); |
| if (zipStream != null){ |
| MozcUtil.closeIgnoringIOException(zipStream); |
| } |
| if (exportFile != null) { |
| exportFile.delete(); |
| } |
| return Optional.absent(); |
| } |
| return Optional.of(exportFile); |
| } |
| |
| /** |
| * Dump dictionary entries of which the index is in [beginIndex, endIndex) as string. |
| */ |
| private String getSelectedDictionaryAsString(Resources resources, int beginIndex, int endIndex) { |
| int entrySize = getEntrySize(); |
| Preconditions.checkElementIndex(beginIndex, endIndex); |
| Preconditions.checkArgument(endIndex <= entrySize); |
| StringBuilder builder = new StringBuilder(); |
| |
| Map<PosType, String> posNameCache = new EnumMap<PosType, String>(PosType.class); |
| for (Entry entry : getEntriesInternal(beginIndex, endIndex)) { |
| String posName = posNameCache.get(entry.getPos()); |
| if (posName == null) { |
| posName = resources.getString( |
| UserDictionaryUtil.getPosStringResourceIdForDictionaryExport(entry.getPos())); |
| posNameCache.put(entry.getPos(), posName); |
| } |
| |
| builder.append(entry.getKey()); |
| builder.append('\t'); |
| builder.append(entry.getValue()); |
| builder.append('\t'); |
| builder.append(posName); |
| builder.append('\t'); |
| builder.append(entry.getComment()); |
| builder.append('\n'); |
| } |
| return builder.toString(); |
| } |
| |
| private Status updateStorage() { |
| UserDictionaryCommand command = UserDictionaryCommand.newBuilder() |
| .setType(CommandType.GET_USER_DICTIONARY_NAME_LIST) |
| .setSessionId(sessionId) |
| .build(); |
| UserDictionaryCommandStatus status = sessionExecutor.sendUserDictionaryCommand(command); |
| if (status.getStatus() == Status.USER_DICTIONARY_COMMAND_SUCCESS) { |
| storage = status.getStorage(); |
| } else { |
| MozcLog.e("Failed to get dictionary name list: " + status.getStatus()); |
| } |
| return status.getStatus(); |
| } |
| |
| |
| /** |
| * Returns the list "view" of entries in the current selected dictionary. |
| * The contents of the returned instance should be automatically updated when the selected |
| * dictionary is updated. |
| */ |
| public List<Entry> getEntryList() { |
| return new AbstractList<Entry>() { |
| @Override |
| public Entry get(int index) { |
| return getEntryInternal(index); |
| } |
| |
| @Override |
| public int size() { |
| return getEntrySize(); |
| } |
| }; |
| } |
| |
| public void setEditTargetIndex(int editTargetIndex) { |
| this.editTargetIndex = editTargetIndex; |
| } |
| |
| public int getEditTargetIndex() { |
| return this.editTargetIndex; |
| } |
| |
| /** |
| * Returns the entry at current editTargetIndex in the selected dictionary. |
| */ |
| public Entry getEditTargetEntry() { |
| return getEntryInternal(editTargetIndex); |
| } |
| |
| private int getEntrySize() { |
| if (selectedDictionaryId == 0) { |
| return 0; |
| } |
| UserDictionaryCommand command = UserDictionaryCommand.newBuilder() |
| .setType(CommandType.GET_ENTRY_SIZE) |
| .setSessionId(sessionId) |
| .setDictionaryId(selectedDictionaryId) |
| .build(); |
| UserDictionaryCommandStatus status = sessionExecutor.sendUserDictionaryCommand(command); |
| if (status.getStatus() != Status.USER_DICTIONARY_COMMAND_SUCCESS) { |
| MozcLog.e("Unknown failure: " + status.getStatus()); |
| throw new RuntimeException("Unknown failure"); |
| } |
| return status.getEntrySize(); |
| } |
| |
| private Entry getEntryInternal(int index) { |
| return getEntriesInternal(index, index + 1).get(0); |
| } |
| |
| /** |
| * Returns dictionary entries of which the index is in [beginIndex, endIndex). |
| */ |
| private List<Entry> getEntriesInternal(int beginIndex, int endIndex) { |
| Builder builder = UserDictionaryCommand.newBuilder() |
| .setType(CommandType.GET_ENTRIES) |
| .setSessionId(sessionId) |
| .setDictionaryId(selectedDictionaryId); |
| for (int i = beginIndex; i < endIndex; ++i) { |
| builder.addEntryIndex(i); |
| } |
| UserDictionaryCommandStatus status = sessionExecutor.sendUserDictionaryCommand(builder.build()); |
| if (status.getStatus() != Status.USER_DICTIONARY_COMMAND_SUCCESS) { |
| MozcLog.e("Unknown failure: " + status.getStatus()); |
| throw new RuntimeException("Unknown failure"); |
| } |
| return status.getEntriesList(); |
| } |
| |
| /** |
| * Checks if we can add another entry to the selected dictionary. |
| */ |
| public Status checkNewEntryAvailability() { |
| UserDictionaryCommand command = UserDictionaryCommand.newBuilder() |
| .setType(CommandType.CHECK_NEW_ENTRY_AVAILABILITY) |
| .setSessionId(sessionId) |
| .setDictionaryId(selectedDictionaryId) |
| .build(); |
| return sessionExecutor.sendUserDictionaryCommand(command).getStatus(); |
| } |
| |
| /** |
| * Adds an entry to the selected dictionary. |
| */ |
| public Status addEntry(String word, String reading, PosType pos) { |
| UserDictionaryCommand command = UserDictionaryCommand.newBuilder() |
| .setType(CommandType.ADD_ENTRY) |
| .setSessionId(sessionId) |
| .setDictionaryId(selectedDictionaryId) |
| .setEntry(Entry.newBuilder() |
| .setKey(reading) |
| .setValue(word) |
| .setPos(pos)) |
| .build(); |
| UserDictionaryCommandStatus status = sessionExecutor.sendUserDictionaryCommand(command); |
| if (status.getStatus() == Status.USER_DICTIONARY_COMMAND_SUCCESS) { |
| dirty = true; |
| } |
| return status.getStatus(); |
| } |
| |
| /** |
| * Edits the entry, which is specified by editTargetIndex, in the selected dictionary. |
| */ |
| public Status editEntry(String word, String reading, PosType pos) { |
| UserDictionaryCommand command = UserDictionaryCommand.newBuilder() |
| .setType(CommandType.EDIT_ENTRY) |
| .setSessionId(sessionId) |
| .setDictionaryId(selectedDictionaryId) |
| .addEntryIndex(editTargetIndex) |
| .setEntry(Entry.newBuilder() |
| .setKey(reading) |
| .setValue(word) |
| .setPos(pos)) |
| .build(); |
| UserDictionaryCommandStatus status = sessionExecutor.sendUserDictionaryCommand(command); |
| if (status.getStatus() == Status.USER_DICTIONARY_COMMAND_SUCCESS) { |
| dirty = true; |
| } |
| return status.getStatus(); |
| } |
| |
| /** |
| * Deletes the entries specified by the given {@code indexList} in the selected dictionary. |
| */ |
| public Status deleteEntry(List<Integer> indexList) { |
| UserDictionaryCommand command = UserDictionaryCommand.newBuilder() |
| .setType(CommandType.DELETE_ENTRY) |
| .setSessionId(sessionId) |
| .setDictionaryId(selectedDictionaryId) |
| .addAllEntryIndex(indexList) |
| .build(); |
| UserDictionaryCommandStatus status = sessionExecutor.sendUserDictionaryCommand(command); |
| if (status.getStatus() == Status.USER_DICTIONARY_COMMAND_SUCCESS) { |
| dirty = true; |
| } |
| return status.getStatus(); |
| } |
| |
| public Uri getImportUri() { |
| return importUri; |
| } |
| |
| public void setImportUri(Uri uri) { |
| this.importUri = uri; |
| } |
| |
| public String getImportData() { |
| return importData; |
| } |
| |
| public void setImportData(String data) { |
| this.importData = data; |
| } |
| |
| public void resetImportState() { |
| importUri = null; |
| importData = null; |
| clearZipFile(); |
| } |
| |
| public ZipFile getZipFile() { |
| return zipFile; |
| } |
| |
| /** |
| * By the method, this instance takes the ownership of resource management of {@code zipFile}. |
| * (i.e., it must not to invoke close for the zipFile by the caller). |
| */ |
| public void setZipFile(ZipFile zipFile) { |
| clearZipFile(); |
| this.zipFile = zipFile; |
| } |
| |
| /** |
| * By the method, this instance releases the ownership of resource management of the |
| * current ZipFile. I.e., the caller needs to invoke close method if the returned ZipFile |
| * instance. |
| */ |
| public ZipFile releaseZipFile() { |
| ZipFile zipFile = this.zipFile; |
| this.zipFile = null; |
| return zipFile; |
| } |
| |
| private void clearZipFile() { |
| if (zipFile != null) { |
| MozcUtil.closeIgnoringIOException(zipFile); |
| zipFile = null; |
| } |
| } |
| |
| /** |
| * Invokes to import the data into a dictionary. |
| * After all operations, regardless of whether the operation is successfully done or not, |
| * resets the pending import state. |
| * @param dictionaryIndex is a position of the import destination dictionary in the storage. |
| * if it is set to -1, this method tries to create a new dictionary with guessing a |
| * dictionary name. |
| */ |
| public Status importData(int dictionaryIndex) { |
| try { |
| // Both importData and importUri should be set before this method's invocation. |
| if (importData == null || importUri == null) { |
| throw new NullPointerException(); |
| } |
| |
| UserDictionaryCommand.Builder builder = UserDictionaryCommand.newBuilder() |
| .setType(CommandType.IMPORT_DATA) |
| .setSessionId(sessionId) |
| .setData(importData); |
| if (dictionaryIndex < 0) { |
| builder.setDictionaryName( |
| UserDictionaryUtil.generateDictionaryNameByUri(importUri, dictionaryNameList)); |
| } else { |
| builder.setDictionaryId(storage.getDictionaries(dictionaryIndex).getId()); |
| } |
| |
| UserDictionaryCommandStatus status = |
| sessionExecutor.sendUserDictionaryCommand(builder.build()); |
| |
| // Regardless of the status, set dirty flag to be conservative, |
| // because even if the status is not SUCCESS, some entries might be imported. |
| dirty = true; |
| if (status.hasDictionaryId()) { |
| // Update the view. |
| Status updateStatus = updateStorage(); |
| if (updateStatus != Status.USER_DICTIONARY_COMMAND_SUCCESS) { |
| return updateStatus; |
| } |
| selectedDictionaryId = status.getDictionaryId(); |
| } |
| return status.getStatus(); |
| } finally { |
| // Reset the importing state regardless of whether the import operation is succeeded or not. |
| resetImportState(); |
| } |
| } |
| } |