package com.jbirdvegas.mgerrit.database;
/*
* Copyright (C) 2013 Android Open Kang Project (AOKP)
* Author: Evan Conway (P4R4N01D), 2013
*
* 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.
*/
import android.content.ContentValues;
import android.content.Context;
import android.content.UriMatcher;
import android.database.sqlite.SQLiteDatabase;
import android.net.Uri;
import android.os.Handler;
import android.support.v4.content.CursorLoader;
import com.jbirdvegas.mgerrit.helpers.DBParams;
import com.jbirdvegas.mgerrit.objects.CommitterObject;
import com.jbirdvegas.mgerrit.objects.JSONCommit;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
/*
* Virtual table combining both Users and Changes tables for join queries.
* The content provider needs a URI to determine where to query which maps
* to a specific table. Since it does not make sense to favour one table
* over another when quering, a virtual/join table is used instead.
*
* Note: Insertion or removal is not supported on this table, only queries
*/
public class UserChanges extends DatabaseTable {
// Table name
public static final String TABLE = "UserChanges";
// --- Columns in Changes table---
// The Change-Id of the change.
public static final String C_CHANGE_ID = Changes.C_CHANGE_ID;
//The subject of the change (header line of the commit message).
public static final String C_SUBJECT = Changes.C_SUBJECT;
//The status of the change (NEW, SUBMITTED, MERGED, ABANDONED, DRAFT).
public static final String C_STATUS = Changes.C_STATUS;
// The name of the project (References Project table)
public static final String C_PROJECT = Changes.C_PROJECT;
// The owner of the change (References User table)
public static final String C_OWNER = Changes.C_OWNER;
/* The timestamp of when the change was created.
* Store as ISO8601 string ("YYYY-MM-DD HH:MM:SS.SSS"). */
public static final String C_CREATED = Changes.C_CREATED;
/* The timestamp of when the change was last updated.
* Store as ISO8601 string ("YYYY-MM-DD HH:MM:SS.SSS"). */
public static final String C_UPDATED = Changes.C_UPDATED;
// The topic to which this change belongs.
public static final String C_TOPIC = Changes.C_TOPIC;
// The legacy numeric ID of the change (used in the web address)
public static final String C_COMMIT_NUMBER = Changes.C_COMMIT_NUMBER;
// The name of the target branch. The refs/heads/ prefix is omitted.
public static final String C_BRANCH = Changes.C_BRANCH;
// --- Columns in Users table ---
// The numeric ID of the account (Identical to UserChanges.C_OWNER)
public static final String C_USER_ID = Users.C_ACCOUNT_ID;
/* The email address the user prefers to be contacted through.
* Although this appears unique, users can change their prefered email which may change this */
public static final String C_EMAIL = Users.C_EMAIL;
// The full name of the user.
public static final String C_NAME = Users.C_NAME;
// --- Content Provider stuff ---
public static final int ITEM_LIST = UriType.UserChangesList.ordinal();
public static final int ITEM_ID = UriType.UserChangesID.ordinal();
public static final Uri CONTENT_URI = Uri.parse(DatabaseFactory.BASE_URI + TABLE);
public static final String CONTENT_TYPE = DatabaseFactory.BASE_MIME_LIST + TABLE;
public static final String CONTENT_ITEM_TYPE = DatabaseFactory.BASE_MIME_ITEM + TABLE;
// Sort by condition for querying results.
public static final String SORT_BY = C_UPDATED + " DESC";
public static final String[] CHANGE_LIST_PROJECTION = new String[] {
Changes.TABLE + ".rowid AS _id", C_CHANGE_ID, C_SUBJECT, C_PROJECT, C_UPDATED,
C_STATUS, C_TOPIC, C_USER_ID, C_EMAIL, C_NAME, C_BRANCH,
C_COMMIT_NUMBER };
private static UserChanges mInstance = null;
private MyObserver mObserver;
public static UserChanges getInstance() {
if (mInstance == null) mInstance = new UserChanges();
return mInstance;
}
@Override
public void create(String TAG, SQLiteDatabase db) {
// This is not a real table (do nothing)
}
@SuppressWarnings("unused")
public static void addURIMatches(UriMatcher _urim)
{
_urim.addURI(DatabaseFactory.AUTHORITY, TABLE, ITEM_LIST);
_urim.addURI(DatabaseFactory.AUTHORITY, TABLE + "/#", ITEM_ID);
}
/** Insert the list of commits into the database **/
public static int insertCommits(Context context, List<JSONCommit> commits) {
List<ContentValues> values = new ArrayList<>();
Set<CommitterObject> committers = new HashSet<>();
for (JSONCommit commit : commits) {
ContentValues row = new ContentValues(9);
row.put(C_CHANGE_ID, commit.getChangeId());
row.put(C_SUBJECT, commit.getSubject());
row.put(C_COMMIT_NUMBER, commit.getCommitNumber());
row.put(C_CREATED, trimDate(commit.getCreatedDate()));
row.put(C_UPDATED, trimDate(commit.getLastUpdatedDate()));
row.put(C_OWNER, commit.getOwnerObject().getAccountId());
row.put(C_PROJECT, commit.getProject());
row.put(C_STATUS, commit.getStatus().toString());
row.put(C_TOPIC, commit.getTopic());
row.put(C_BRANCH, commit.getBranch());
values.add(row);
committers.add(commit.getOwnerObject());
}
// Insert the list of users into the database as well.
CommitterObject usersArray[] = new CommitterObject[committers.size()];
Users.insertUsers(context, committers.toArray(usersArray));
// Now insert the commits
Uri uri = DBParams.insertWithReplace(Changes.CONTENT_URI);
ContentValues valuesArray[] = new ContentValues[values.size()];
return context.getContentResolver().bulkInsert(uri, values.toArray(valuesArray));
}
/**
* Update the Change details for a change
* @param context Context for database access
* @param commit A change object containing the data for the commit to be updated.
* Only data visible to the change list will be updated.
* @return Whether any rows were updated
*/
public static boolean updateChange(Context context, JSONCommit commit) {
if (commit == null || commit.getChangeId() == null) return false;
ContentValues values = new ContentValues(9);
if (commit.getSubject() != null){
values.put(C_SUBJECT, commit.getSubject());
} if (commit.getLastUpdatedDate() != null){
values.put(C_UPDATED, trimDate(commit.getLastUpdatedDate()));
} if (commit.getOwnerObject() != null) {
values.put(C_OWNER, commit.getOwnerObject().getAccountId());
} if (commit.getStatus() != null) {
values.put(C_STATUS, commit.getStatus().toString());
}
// The topic could have been cleared
values.put(C_TOPIC, commit.getTopic());
return context.getContentResolver().update(Changes.CONTENT_URI, values,
C_CHANGE_ID + " = ?", new String[] { commit.getChangeId() }) > 0;
}
/**
* List the commits for a given change status and subject
* @param context Context for database access
* @param status The change status to search for
* @param subject A full or partial commit message string to search for
* @return A CursorLoader
*/
public static CursorLoader findCommitsWithSubject(Context context, String status,
String subject) {
StringBuilder builder = new StringBuilder();
List<String> bindArgs = new ArrayList<>();
if (subject != null) {
builder.append(" AND ").append(C_PROJECT).append(" LIKE ?");
bindArgs.add("%" + subject + "%");
}
return findCommits(context, status, builder, bindArgs);
}
// Removes the extraneous 0s off the milliseconds in server timestamps
private static String trimDate(String date) {
return date.substring(0, date.length() - 6);
}
/**
* List the commits for a given change status and subject
* @param context Context for database access
* @param status The change status to search for
* @param query A constructed where query string
* @param args Any bind arguments to be bound to the SQL query
* @return A CursorLoader
*/
public static CursorLoader findCommits(Context context, String status,
String query, List<String> args) {
StringBuilder builder;
if (query == null) {
builder = new StringBuilder("");
} else {
builder = new StringBuilder(query);
}
if (args == null) {
args = new ArrayList<>();
}
return findCommits(context, status, builder, args);
}
/**
* Helper method for change list search queries
* @param context Context for database access
* @param status The change status to search for
* @param builder A string builder to help form the where query
* @param bindArgs Any bind arguments to be bound to the SQL query
* @return A CursorLoader
*/
private static CursorLoader findCommits(Context context, String status,
StringBuilder builder, List<String> bindArgs) {
if (builder.length() > 0) builder.append(" AND ");
StringBuilder where = builder.append(C_STATUS).append(" = ?").append(" AND ")
.append(Changes.TABLE).append(".").append(C_OWNER)
.append(" = ").append(Users.TABLE).append(".").append(C_USER_ID);
status = JSONCommit.Status.getStatusString(status);
bindArgs.add(status);
String valuesArray[] = new String[bindArgs.size()];
return new CursorLoader(context, CONTENT_URI, CHANGE_LIST_PROJECTION,
where.toString(), bindArgs.toArray(valuesArray), SORT_BY);
}
/**
* Get the commit properties for a change.
* @param context Context for database access
* @param changeid The Change-Id of the change to get the properties
* @return A CursorLoader
*/
public static CursorLoader getCommitProperties(Context context, String changeid) {
Uri uri = DBParams.fetchOneRow(CONTENT_URI);
return new CursorLoader(context, uri, CHANGE_LIST_PROJECTION,
C_CHANGE_ID + " = ? AND " + Changes.TABLE + "." + Changes.C_OWNER
+ " = " + Users.TABLE + "." + Users.C_ACCOUNT_ID,
new String[] { changeid }, null);
}
@Override
protected void registerContentObserver(Context context) {
mObserver = new MyObserver(new Handler(), context, CONTENT_URI);
context.getContentResolver().registerContentObserver(Users.CONTENT_URI, true,
mObserver);
context.getContentResolver().registerContentObserver(Changes.CONTENT_URI, true,
mObserver);
}
@Override
protected void unRegisterContentObserver(Context context) {
context.getContentResolver().unregisterContentObserver(mObserver);
}
}