fr.free.nrw.commons.contributions.ContributionDao.java Source code

Java tutorial

Introduction

Here is the source code for fr.free.nrw.commons.contributions.ContributionDao.java

Source

package fr.free.nrw.commons.contributions;

import android.content.ContentProviderClient;
import android.content.ContentValues;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteException;
import android.net.Uri;
import android.os.RemoteException;
import androidx.annotation.Nullable;
import android.text.TextUtils;
import fr.free.nrw.commons.settings.Prefs;

import org.apache.commons.lang3.StringUtils;
import java.util.Date;
import javax.inject.Inject;
import javax.inject.Named;
import javax.inject.Provider;
import timber.log.Timber;

import static fr.free.nrw.commons.contributions.ContributionDao.Table.ALL_FIELDS;
import static fr.free.nrw.commons.contributions.ContributionDao.Table.COLUMN_WIKI_DATA_ENTITY_ID;
import static fr.free.nrw.commons.contributions.ContributionsContentProvider.BASE_URI;
import static fr.free.nrw.commons.contributions.ContributionsContentProvider.uriForId;

public class ContributionDao {
    /*
    This sorts in the following order:
    Currently Uploading
    Failed (Sorted in ascending order of time added - FIFO)
    Queued to Upload (Sorted in ascending order of time added - FIFO)
    Completed (Sorted in descending order of time added)
        
    This is why Contribution.STATE_COMPLETED is -1.
     */
    static final String CONTRIBUTION_SORT = Table.COLUMN_STATE + " DESC, " + Table.COLUMN_UPLOADED + " DESC , ("
            + Table.COLUMN_TIMESTAMP + " * " + Table.COLUMN_STATE + ")";

    private final Provider<ContentProviderClient> clientProvider;

    @Inject
    public ContributionDao(@Named("contribution") Provider<ContentProviderClient> clientProvider) {
        this.clientProvider = clientProvider;
    }

    Cursor loadAllContributions() {
        ContentProviderClient db = clientProvider.get();
        try {
            return db.query(BASE_URI, ALL_FIELDS, "", null, CONTRIBUTION_SORT);
        } catch (RemoteException e) {
            return null;
        } finally {
            db.release();
        }
    }

    public void save(Contribution contribution) {
        ContentProviderClient db = clientProvider.get();
        try {
            if (contribution.getContentUri() == null) {
                contribution.setContentUri(db.insert(BASE_URI, toContentValues(contribution)));
            } else {
                db.update(contribution.getContentUri(), toContentValues(contribution), null, null);
            }
        } catch (RemoteException e) {
            throw new RuntimeException(e);
        } finally {
            db.release();
        }
    }

    public void delete(Contribution contribution) {
        ContentProviderClient db = clientProvider.get();
        try {
            if (contribution.getContentUri() == null) {
                // noooo
                throw new RuntimeException("tried to delete item with no content URI");
            } else {
                db.delete(contribution.getContentUri(), null, null);
            }
        } catch (RemoteException e) {
            throw new RuntimeException(e);
        } finally {
            db.release();
        }
    }

    ContentValues toContentValues(Contribution contribution) {
        ContentValues cv = new ContentValues();
        cv.put(Table.COLUMN_FILENAME, contribution.getFilename());
        if (contribution.getLocalUri() != null) {
            cv.put(Table.COLUMN_LOCAL_URI, contribution.getLocalUri().toString());
        }
        if (contribution.getImageUrl() != null) {
            cv.put(Table.COLUMN_IMAGE_URL, contribution.getImageUrl());
        }
        if (contribution.getDateUploaded() != null) {
            cv.put(Table.COLUMN_UPLOADED, contribution.getDateUploaded().getTime());
        }
        cv.put(Table.COLUMN_LENGTH, contribution.getDataLength());
        //This was always meant to store the date created..If somehow date created is not fetched while actually saving the contribution, lets save today's date
        cv.put(Table.COLUMN_TIMESTAMP, contribution.getDateCreated() == null ? System.currentTimeMillis()
                : contribution.getDateCreated().getTime());
        cv.put(Table.COLUMN_STATE, contribution.getState());
        cv.put(Table.COLUMN_TRANSFERRED, contribution.getTransferred());
        cv.put(Table.COLUMN_SOURCE, contribution.getSource());
        cv.put(Table.COLUMN_DESCRIPTION, contribution.getDescription());
        cv.put(Table.COLUMN_CREATOR, contribution.getCreator());
        cv.put(Table.COLUMN_MULTIPLE, contribution.getMultiple() ? 1 : 0);
        cv.put(Table.COLUMN_WIDTH, contribution.getWidth());
        cv.put(Table.COLUMN_HEIGHT, contribution.getHeight());
        cv.put(Table.COLUMN_LICENSE, contribution.getLicense());
        cv.put(Table.COLUMN_WIKI_DATA_ENTITY_ID, contribution.getWikiDataEntityId());
        return cv;
    }

    public Contribution fromCursor(Cursor cursor) {
        // Hardcoding column positions!
        //Check that cursor has a value to avoid CursorIndexOutOfBoundsException
        if (cursor.getCount() > 0) {
            int index;
            if (cursor.getColumnIndex(Table.COLUMN_LICENSE) == -1) {
                index = 15;
            } else {
                index = cursor.getColumnIndex(Table.COLUMN_LICENSE);
            }
            Contribution contribution = new Contribution(
                    uriForId(cursor.getInt(cursor.getColumnIndex(Table.COLUMN_ID))),
                    cursor.getString(cursor.getColumnIndex(Table.COLUMN_FILENAME)),
                    parseUri(cursor.getString(cursor.getColumnIndex(Table.COLUMN_LOCAL_URI))),
                    cursor.getString(cursor.getColumnIndex(Table.COLUMN_IMAGE_URL)),
                    parseTimestamp(cursor.getLong(cursor.getColumnIndex(Table.COLUMN_TIMESTAMP))),
                    cursor.getInt(cursor.getColumnIndex(Table.COLUMN_STATE)),
                    cursor.getLong(cursor.getColumnIndex(Table.COLUMN_LENGTH)),
                    parseTimestamp(cursor.getLong(cursor.getColumnIndex(Table.COLUMN_UPLOADED))),
                    cursor.getLong(cursor.getColumnIndex(Table.COLUMN_TRANSFERRED)),
                    cursor.getString(cursor.getColumnIndex(Table.COLUMN_SOURCE)),
                    cursor.getString(cursor.getColumnIndex(Table.COLUMN_DESCRIPTION)),
                    cursor.getString(cursor.getColumnIndex(Table.COLUMN_CREATOR)),
                    cursor.getInt(cursor.getColumnIndex(Table.COLUMN_MULTIPLE)) == 1,
                    cursor.getInt(cursor.getColumnIndex(Table.COLUMN_WIDTH)),
                    cursor.getInt(cursor.getColumnIndex(Table.COLUMN_HEIGHT)), cursor.getString(index));

            String wikidataEntityId = cursor.getString(cursor.getColumnIndex(COLUMN_WIKI_DATA_ENTITY_ID));
            if (!StringUtils.isBlank(wikidataEntityId)) {
                contribution.setWikiDataEntityId(wikidataEntityId);
            }

            return contribution;
        }

        return null;
    }

    @Nullable
    private static Date parseTimestamp(long timestamp) {
        return timestamp == 0 ? null : new Date(timestamp);
    }

    @Nullable
    private static Uri parseUri(String uriString) {
        return TextUtils.isEmpty(uriString) ? null : Uri.parse(uriString);
    }

    public static class Table {
        public static final String TABLE_NAME = "contributions";

        public static final String COLUMN_ID = "_id";
        public static final String COLUMN_FILENAME = "filename";
        public static final String COLUMN_LOCAL_URI = "local_uri";
        public static final String COLUMN_IMAGE_URL = "image_url";
        public static final String COLUMN_TIMESTAMP = "timestamp";
        public static final String COLUMN_STATE = "state";
        public static final String COLUMN_LENGTH = "length";
        public static final String COLUMN_UPLOADED = "uploaded";
        public static final String COLUMN_TRANSFERRED = "transferred"; // Currently transferred number of bytes
        public static final String COLUMN_SOURCE = "source";
        public static final String COLUMN_DESCRIPTION = "description";
        public static final String COLUMN_CREATOR = "creator"; // Initial uploader
        public static final String COLUMN_MULTIPLE = "multiple";
        public static final String COLUMN_WIDTH = "width";
        public static final String COLUMN_HEIGHT = "height";
        public static final String COLUMN_LICENSE = "license";
        public static final String COLUMN_WIKI_DATA_ENTITY_ID = "wikidataEntityID";

        // NOTE! KEEP IN SAME ORDER AS THEY ARE DEFINED UP THERE. HELPS HARD CODE COLUMN INDICES.
        public static final String[] ALL_FIELDS = { COLUMN_ID, COLUMN_FILENAME, COLUMN_LOCAL_URI, COLUMN_IMAGE_URL,
                COLUMN_TIMESTAMP, COLUMN_STATE, COLUMN_LENGTH, COLUMN_UPLOADED, COLUMN_TRANSFERRED, COLUMN_SOURCE,
                COLUMN_DESCRIPTION, COLUMN_CREATOR, COLUMN_MULTIPLE, COLUMN_WIDTH, COLUMN_HEIGHT, COLUMN_LICENSE,
                COLUMN_WIKI_DATA_ENTITY_ID };

        public static final String DROP_TABLE_STATEMENT = "DROP TABLE IF EXISTS " + TABLE_NAME;

        public static final String CREATE_TABLE_STATEMENT = "CREATE TABLE " + TABLE_NAME + " ("
                + "_id INTEGER PRIMARY KEY," + "filename STRING," + "local_uri STRING," + "image_url STRING,"
                + "uploaded INTEGER," + "timestamp INTEGER," + "state INTEGER," + "length INTEGER,"
                + "transferred INTEGER," + "source STRING," + "description STRING," + "creator STRING,"
                + "multiple INTEGER," + "width INTEGER," + "height INTEGER," + "LICENSE STRING,"
                + "wikidataEntityID STRING" + ");";

        // Upgrade from version 1 ->
        static final String ADD_CREATOR_FIELD = "ALTER TABLE " + TABLE_NAME + " ADD COLUMN creator STRING;";
        static final String ADD_DESCRIPTION_FIELD = "ALTER TABLE " + TABLE_NAME + " ADD COLUMN description STRING;";

        // Upgrade from version 2 ->
        static final String ADD_MULTIPLE_FIELD = "ALTER TABLE " + TABLE_NAME + " ADD COLUMN multiple INTEGER;";
        static final String SET_DEFAULT_MULTIPLE = "UPDATE " + TABLE_NAME + " SET multiple = 0";

        // Upgrade from version 5 ->
        static final String ADD_WIDTH_FIELD = "ALTER TABLE " + TABLE_NAME + " ADD COLUMN width INTEGER;";
        static final String SET_DEFAULT_WIDTH = "UPDATE " + TABLE_NAME + " SET width = 0";
        static final String ADD_HEIGHT_FIELD = "ALTER TABLE " + TABLE_NAME + " ADD COLUMN height INTEGER;";
        static final String SET_DEFAULT_HEIGHT = "UPDATE " + TABLE_NAME + " SET height = 0";
        static final String ADD_LICENSE_FIELD = "ALTER TABLE " + TABLE_NAME + " ADD COLUMN license STRING;";
        static final String SET_DEFAULT_LICENSE = "UPDATE " + TABLE_NAME + " SET license='"
                + Prefs.Licenses.CC_BY_SA_3 + "';";

        // Upgrade from version 8 ->
        static final String ADD_WIKI_DATA_ENTITY_ID_FIELD = "ALTER TABLE " + TABLE_NAME
                + " ADD COLUMN wikidataEntityID STRING;";

        public static void onCreate(SQLiteDatabase db) {
            db.execSQL(CREATE_TABLE_STATEMENT);
        }

        public static void onDelete(SQLiteDatabase db) {
            db.execSQL(DROP_TABLE_STATEMENT);
            onCreate(db);
        }

        public static void onUpdate(SQLiteDatabase db, int from, int to) {
            if (from == to) {
                return;
            }

            //Considering the crashes we have been facing recently, lets blindly add this column to any table which has ever existed
            runQuery(db, ADD_WIKI_DATA_ENTITY_ID_FIELD);

            if (from == 1) {
                runQuery(db, ADD_DESCRIPTION_FIELD);
                runQuery(db, ADD_CREATOR_FIELD);
                from++;
                onUpdate(db, from, to);
                return;
            }
            if (from == 2) {
                runQuery(db, ADD_MULTIPLE_FIELD);
                runQuery(db, SET_DEFAULT_MULTIPLE);
                from++;
                onUpdate(db, from, to);
                return;
            }
            if (from == 3) {
                // Do nothing
                from++;
                onUpdate(db, from, to);
                return;
            }
            if (from == 4) {
                // Do nothing -- added Category
                from++;
                onUpdate(db, from, to);
                return;
            }
            if (from == 5) {
                // Added width and height fields
                runQuery(db, ADD_WIDTH_FIELD);
                runQuery(db, SET_DEFAULT_WIDTH);
                runQuery(db, ADD_HEIGHT_FIELD);
                runQuery(db, SET_DEFAULT_HEIGHT);
                runQuery(db, ADD_LICENSE_FIELD);
                runQuery(db, SET_DEFAULT_LICENSE);
                from++;
                onUpdate(db, from, to);
                return;
            }
            if (from > 5) {
                // Added place field
                from = to;
                onUpdate(db, from, to);
                return;
            }
        }

        /**
         * perform the db.execSQl with handled exceptions
         */
        private static void runQuery(SQLiteDatabase db, String query) {
            try {
                db.execSQL(query);
            } catch (SQLiteException e) {
                Timber.e("Exception performing query: " + query + " message: " + e.getMessage());
            }
        }

    }
}