/*
* Copyright 2014 sonaive.com. All rights reserved.
*
* 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.sonaive.v2ex.sync;
import android.content.ContentProviderOperation;
import android.content.ContentResolver;
import android.content.Context;
import android.content.OperationApplicationException;
import android.net.Uri;
import android.os.Bundle;
import android.os.RemoteException;
import com.google.gson.JsonParser;
import com.google.gson.stream.JsonReader;
import com.sonaive.v2ex.io.FeedsHandler;
import com.sonaive.v2ex.io.JSONHandler;
import com.sonaive.v2ex.io.MembersHandler;
import com.sonaive.v2ex.io.NodesHandler;
import com.sonaive.v2ex.io.ReviewsHandler;
import com.sonaive.v2ex.provider.V2exContract;
import java.io.IOException;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.HashMap;
import static com.sonaive.v2ex.util.LogUtils.LOGD;
import static com.sonaive.v2ex.util.LogUtils.LOGE;
import static com.sonaive.v2ex.util.LogUtils.makeLogTag;
/**
* Created by liutao on 12/10/14.
*/
public class V2exDataHandler {
private static final String TAG = makeLogTag(V2exDataHandler.class);
public static final String ARG_DATA_KEY = "arg_data_key";
public static final String DATA_KEY_MEMBERS = "members";
public static final String DATA_KEY_FEEDS = "feeds";
public static final String DATA_KEY_NODES = "nodes";
public static final String DATA_KEY_REVIEWS = "reviews";
Context mContext = null;
// Handlers for each entity type:
MembersHandler mMembersHandler = null;
FeedsHandler mFeedsHandler = null;
NodesHandler mNodesHandler = null;
ReviewsHandler mReviewsHandler = null;
// Convenience map that maps the key name to its corresponding handler (e.g.
// "blocks" to mBlocksHandler (to avoid very tedious if-elses)
HashMap<String, JSONHandler> mHandlerForKey = new HashMap<>();
// Tally of total content provider operations we carried out (for statistical purposes)
private int mContentProviderOperationsDone = 0;
public V2exDataHandler(Context ctx) {
mContext = ctx;
}
/**
* Parses the data in the given objects and imports the data into the
* content provider.
*
* @param dataBodies The collection of JSON objects to parse and import.
* @throws java.io.IOException If there is a problem parsing the data.
*/
public void applyData(Bundle[] dataBodies) throws IOException {
LOGD(TAG, "Applying data from " + dataBodies.length + " files");
// create handlers for each data type
mHandlerForKey.put(DATA_KEY_MEMBERS, mMembersHandler = new MembersHandler(mContext));
mHandlerForKey.put(DATA_KEY_FEEDS, mFeedsHandler = new FeedsHandler(mContext));
mHandlerForKey.put(DATA_KEY_NODES, mNodesHandler = new NodesHandler(mContext));
mHandlerForKey.put(DATA_KEY_REVIEWS, mReviewsHandler = new ReviewsHandler(mContext));
// process the jsons. This will call each of the handlers when appropriate to deal
// with the objects we see in the data.
LOGD(TAG, "Processing " + dataBodies.length + " JSON objects.");
for (int i = 0; i < dataBodies.length; i++) {
LOGD(TAG, "Processing json object #" + (i + 1) + " of " + dataBodies.length);
String key = dataBodies[i].getString(V2exDataHandler.ARG_DATA_KEY);
processDataBody(dataBodies[i], key);
}
// produce the necessary content provider operations
ArrayList<ContentProviderOperation> batch = new ArrayList<ContentProviderOperation>();
for (Bundle dataBody : dataBodies) {
String key = dataBody.getString(V2exDataHandler.ARG_DATA_KEY);
LOGD(TAG, "Building content provider operations for: " + key);
mHandlerForKey.get(key).makeContentProviderOperations(batch);
LOGD(TAG, "Content provider operations so far: " + batch.size());
}
LOGD(TAG, "Total content provider operations: " + batch.size());
// finally, push the changes into the Content Provider
LOGD(TAG, "Applying " + batch.size() + " content provider operations.");
try {
int operations = batch.size();
if (operations > 0) {
mContext.getContentResolver().applyBatch(V2exContract.CONTENT_AUTHORITY, batch);
}
LOGD(TAG, "Successfully applied " + operations + " content provider operations.");
mContentProviderOperationsDone += operations;
} catch (RemoteException ex) {
LOGE(TAG, "RemoteException while applying content provider operations.");
throw new RuntimeException("Error executing content provider batch operation", ex);
} catch (OperationApplicationException ex) {
LOGE(TAG, "OperationApplicationException while applying content provider operations.");
throw new RuntimeException("Error executing content provider batch operation", ex);
}
// notify all top-level paths
LOGD(TAG, "Notifying changes on all top-level paths on Content Resolver.");
ContentResolver resolver = mContext.getContentResolver();
for (String path : V2exContract.TOP_LEVEL_PATHS) {
Uri uri = V2exContract.BASE_CONTENT_URI.buildUpon().appendPath(path).build();
resolver.notifyChange(uri, null);
}
LOGD(TAG, "Done applying data.");
}
public int getContentProviderOperationsDone() {
return mContentProviderOperationsDone;
}
/**
* Processes data body and calls the appropriate data type handlers
* to process each of the objects represented therein.
*
* @param dataBundle The body of data to process
* @throws IOException If there is an error parsing the data.
*/
private void processDataBody(Bundle dataBundle, String key) throws IOException {
if (mHandlerForKey.containsKey(key)) {
String dataBody = mHandlerForKey.get(key).getBody(dataBundle);
JsonReader reader = new JsonReader(new StringReader(dataBody));
JsonParser parser = new JsonParser();
// pass the value to the corresponding handler
mHandlerForKey.get(key).process(parser.parse(reader));
}
}
}