/* * Copyright (C) 2011 The original author or authors. * * 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.zapta.apps.maniana.persistence; import org.json.JSONException; import android.content.Context; import com.zapta.apps.maniana.annotations.MainActivityScope; import com.zapta.apps.maniana.main.MainActivityState; import com.zapta.apps.maniana.model.AppModel; import com.zapta.apps.maniana.persistence.ModelReadingResult.ModelLoadingOutcome; import com.zapta.apps.maniana.util.FileUtil; import com.zapta.apps.maniana.util.FileUtil.FileReadResult; import com.zapta.apps.maniana.util.FileUtil.FileReadResult.FileReadOutcome; import com.zapta.apps.maniana.util.LogUtil; /** * Manages model persistence. * * @author Tal Dayan */ // TODO: add syncrhonization and make it an app scope level. @MainActivityScope public class ModelPersistence { /** Path to file where model is persisted. */ public static final String DATA_FILE_NAME = "maniana_data.json"; /** Static lock protecting the access to the data file. */ public static final Object sDataFileLock = new Object(); /** Read the model file from the internal storage. */ public static final ModelReadingResult readModelFile(Context context, AppModel resultModel) { ModelReadingResult result = readModelFileInternal(context, resultModel, DATA_FILE_NAME, false); if (result.outcome.isOk()) { // Model is same as persistence file and no version change. resultModel.setClean(); } else { // Model need to be rewritten resultModel.setDirty(); } return result; } /** * Caller is expected to manager the model's dirty bit. In case of an error, the returned model * is cleared. */ private static final ModelReadingResult readModelFileInternal(Context context, AppModel resultModel, String fileName, boolean isAsset) { LogUtil.info("Going to read data from " + (isAsset ? "assert" : "data File") + " " + fileName); resultModel.clear(); // Try to read the model file final FileReadResult fileReadResult; synchronized (sDataFileLock) { fileReadResult = FileUtil.readFileToString(context, fileName, isAsset); } if (fileReadResult.outcome == FileReadOutcome.NOT_FOUND) { return new ModelReadingResult(ModelLoadingOutcome.FILE_NOT_FOUND); } // Try to parse the json file try { PersistenceMetadata resultMetadata = new PersistenceMetadata(); ModelDeserialization.deserializeModel(resultModel, resultMetadata, fileReadResult.content); return new ModelReadingResult(ModelLoadingOutcome.FILE_READ_OK, resultMetadata); } catch (JSONException e) { LogUtil.error(e, "Error parsing model JSON"); resultModel.clear(); return new ModelReadingResult(ModelLoadingOutcome.FILE_HAS_ERRORS); } } public static final void writeModelFile(MainActivityState mainActivityState, AppModel model, PersistenceMetadata metadata) { LogUtil.info("Saving model to file: " + DATA_FILE_NAME); final String json = ModelSerialization.serializeModel(model, metadata); synchronized (sDataFileLock) { FileUtil.writeStringToFile(mainActivityState.context(), json, DATA_FILE_NAME, Context.MODE_PRIVATE); } // Model reflects persisted state. model.setClean(); } }