Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Unverified Commit aa955cd0 authored by Marten Gajda's avatar Marten Gajda Committed by GitHub
Browse files

Refactor entity processor chain. Implements #533 (#551)

This is the first step of a major refactoring of the provider. This step essentially merges the "before*" and "after*" methods of `EntityProcessor` and converts most implementations into decorators.
parent 83fb936b
Loading
Loading
Loading
Loading
+2 −2
Original line number Diff line number Diff line
@@ -31,7 +31,7 @@ import android.util.Log;

import org.dmfs.provider.tasks.model.CursorContentValuesTaskAdapter;
import org.dmfs.provider.tasks.model.TaskAdapter;
import org.dmfs.provider.tasks.processors.tasks.TaskInstancesProcessor;
import org.dmfs.provider.tasks.processors.tasks.Instantiating;
import org.dmfs.rfc5545.DateTime;
import org.dmfs.tasks.contract.TaskContract;
import org.dmfs.tasks.contract.TaskContract.Instances;
@@ -55,7 +55,7 @@ public enum ContentOperation

            // request an update of all instance values
            ContentValues vals = new ContentValues(1);
            TaskInstancesProcessor.addUpdateRequest(vals);
            Instantiating.addUpdateRequest(vals);

            // execute update that triggers a recalculation of all due and start sorting values
            int count = context.getContentResolver().update(
+6 −103
Original line number Diff line number Diff line
@@ -16,121 +16,24 @@

package org.dmfs.provider.tasks;

import android.database.sqlite.SQLiteDatabase;

import org.dmfs.provider.tasks.model.EntityAdapter;
import org.dmfs.provider.tasks.processors.EntityProcessor;

import java.util.List;


/**
 * Provides handlers for INSERT, UPDATE and DELETE operations for {@link EntityAdapter}s.
 *
 * @author Marten Gajda <marten@dmfs.org>
 */
public enum ProviderOperation
{

    /**
     * Handles insert operations.
     * Insert operations.
     */
    INSERT
            {
                @Override
                <T extends EntityAdapter<?>> void executeBeforeProcessor(SQLiteDatabase db, EntityProcessor<T> processor, T entityAdapter, boolean isSyncAdapter)
                {
                    processor.beforeInsert(db, entityAdapter, isSyncAdapter);
                }


                @Override
                <T extends EntityAdapter<?>> void executeAfterProcessor(SQLiteDatabase db, EntityProcessor<T> processor, T entityAdapter, boolean isSyncAdapter)
                {
                    processor.afterInsert(db, entityAdapter, isSyncAdapter);
                }
            },
    INSERT,

    /**
     * Handles update operations.
     * Update operations.
     */
    UPDATE
            {
                @Override
                <T extends EntityAdapter<?>> void executeBeforeProcessor(SQLiteDatabase db, EntityProcessor<T> processor, T entityAdapter, boolean isSyncAdapter)
                {
                    processor.beforeUpdate(db, entityAdapter, isSyncAdapter);
                }


                @Override
                <T extends EntityAdapter<?>> void executeAfterProcessor(SQLiteDatabase db, EntityProcessor<T> processor, T entityAdapter, boolean isSyncAdapter)
                {
                    processor.afterUpdate(db, entityAdapter, isSyncAdapter);
                }
            },

    /**
     * Handles delete operations.
     */
    DELETE
            {
                @Override
                <T extends EntityAdapter<?>> void executeBeforeProcessor(SQLiteDatabase db, EntityProcessor<T> processor, T entityAdapter, boolean isSyncAdapter)
                {
                    processor.beforeDelete(db, entityAdapter, isSyncAdapter);
                }


                @Override
                <T extends EntityAdapter<?>> void executeAfterProcessor(SQLiteDatabase db, EntityProcessor<T> processor, T entityAdapter, boolean isSyncAdapter)
                {
                    processor.afterDelete(db, entityAdapter, isSyncAdapter);
                }
            };

    private final static String TAG = "OpenTasks.Operation";


    abstract <T extends EntityAdapter<?>> void executeBeforeProcessor(SQLiteDatabase db, EntityProcessor<T> processor, T entityAdapter, boolean isSyncAdapter);

    abstract <T extends EntityAdapter<?>> void executeAfterProcessor(SQLiteDatabase db, EntityProcessor<T> processor, T entityAdapter, boolean isSyncAdapter);

    UPDATE,

    /**
     * Executes this operation by running the respective methods of the given {@link EntityProcessor}s.
     *
     * @param db
     *         An {@link SQLiteDatabase}.
     * @param processors
     *         The {@link EntityProcessor} chain.
     * @param entityAdapter
     *         The {@link EntityAdapter} to operate on.
     * @param isSyncAdapter
     *         <code>true</code> if this operation is triggered by a sync adapter, false otherwise.
     * @param log
     *         An {@link ProviderOperationsLog} to log this operation.
     * @param authority
     *         The authority of this provider.
     * Delete operations.
     */
    public <T extends EntityAdapter<?>> void execute(SQLiteDatabase db, List<EntityProcessor<T>> processors, T entityAdapter, boolean isSyncAdapter,
                                                     ProviderOperationsLog log, String authority)
    {
        long start = System.currentTimeMillis();

        for (EntityProcessor<T> processor : processors)
        {
            executeBeforeProcessor(db, processor, entityAdapter, isSyncAdapter);
        }

        for (EntityProcessor<T> processor : processors)
        {
            executeAfterProcessor(db, processor, entityAdapter, isSyncAdapter);
        }

        if (this != UPDATE || entityAdapter.hasUpdates()) // don't log empty operations
        {
            log.log(this, entityAdapter.uri(authority));
        }
    }
    DELETE;
}
+31 −30
Original line number Diff line number Diff line
@@ -48,15 +48,14 @@ import org.dmfs.provider.tasks.model.CursorContentValuesTaskAdapter;
import org.dmfs.provider.tasks.model.ListAdapter;
import org.dmfs.provider.tasks.model.TaskAdapter;
import org.dmfs.provider.tasks.processors.EntityProcessor;
import org.dmfs.provider.tasks.processors.lists.ListExecutionProcessor;
import org.dmfs.provider.tasks.processors.lists.ListValidatorProcessor;
import org.dmfs.provider.tasks.processors.tasks.AutoUpdateProcessor;
import org.dmfs.provider.tasks.processors.tasks.ChangeListProcessor;
import org.dmfs.provider.tasks.processors.tasks.FtsProcessor;
import org.dmfs.provider.tasks.processors.tasks.RelationProcessor;
import org.dmfs.provider.tasks.processors.tasks.TaskExecutionProcessor;
import org.dmfs.provider.tasks.processors.tasks.TaskInstancesProcessor;
import org.dmfs.provider.tasks.processors.tasks.TaskValidatorProcessor;
import org.dmfs.provider.tasks.processors.lists.ListCommitProcessor;
import org.dmfs.provider.tasks.processors.tasks.AutoCompleting;
import org.dmfs.provider.tasks.processors.tasks.Instantiating;
import org.dmfs.provider.tasks.processors.tasks.Moving;
import org.dmfs.provider.tasks.processors.tasks.Relating;
import org.dmfs.provider.tasks.processors.tasks.Searchable;
import org.dmfs.provider.tasks.processors.tasks.TaskCommitProcessor;
import org.dmfs.provider.tasks.processors.tasks.Validating;
import org.dmfs.tasks.contract.TaskContract;
import org.dmfs.tasks.contract.TaskContract.Alarms;
import org.dmfs.tasks.contract.TaskContract.Categories;
@@ -71,10 +70,8 @@ import org.dmfs.tasks.contract.TaskContract.TaskListSyncColumns;
import org.dmfs.tasks.contract.TaskContract.TaskLists;
import org.dmfs.tasks.contract.TaskContract.Tasks;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;


@@ -116,14 +113,14 @@ public final class TaskProvider extends SQLiteContentProvider implements OnAccou
    private final static Set<String> TASK_LIST_SYNC_COLUMNS = new HashSet<String>(Arrays.asList(TaskLists.SYNC_ADAPTER_COLUMNS));

    /**
     * A list of {@link TaskProcessor}s to execute when doing operations on the tasks table.
     * A list of {@link EntityProcessor}s to execute when doing operations on the tasks table.
     */
    private List<EntityProcessor<TaskAdapter>> mTaskProcessors = new ArrayList<EntityProcessor<TaskAdapter>>(16);
    private EntityProcessor<TaskAdapter> mTaskProcessorChain;

    /**
     * A list of {@link ListProcessor}s to execute when doing operations on the task lists table.
     * A list of {@link EntityProcessor}s to execute when doing operations on the task lists table.
     */
    private List<EntityProcessor<ListAdapter>> mListProcessors = new ArrayList<EntityProcessor<ListAdapter>>(8);
    private EntityProcessor<ListAdapter> mListProcessorChain;

    /**
     * Our authority.
@@ -158,16 +155,9 @@ public final class TaskProvider extends SQLiteContentProvider implements OnAccou
    {
        mAuthority = AuthorityUtil.taskAuthority(getContext());

        mTaskProcessors.add(new TaskValidatorProcessor());
        mTaskProcessors.add(new AutoUpdateProcessor());
        mTaskProcessors.add(new RelationProcessor());
        mTaskProcessors.add(new TaskInstancesProcessor());
        mTaskProcessors.add(new FtsProcessor());
        mTaskProcessors.add(new ChangeListProcessor());
        mTaskProcessors.add(new TaskExecutionProcessor());
        mTaskProcessorChain = new Validating(new AutoCompleting(new Relating(new Instantiating(new Searchable(new Moving(new TaskCommitProcessor()))))));

        mListProcessors.add(new ListValidatorProcessor());
        mListProcessors.add(new ListExecutionProcessor());
        mListProcessorChain = new org.dmfs.provider.tasks.processors.lists.Validating(new ListCommitProcessor());

        mUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
        mUriMatcher.addURI(mAuthority, TaskContract.TaskLists.CONTENT_URI_PATH, LISTS);
@@ -743,7 +733,8 @@ public final class TaskProvider extends SQLiteContentProvider implements OnAccou
                    {
                        final ListAdapter list = new CursorContentValuesListAdapter(ListAdapter._ID.getFrom(cursor), cursor, new ContentValues());

                        ProviderOperation.DELETE.execute(db, mListProcessors, list, isSyncAdapter, mOperationsLog, mAuthority);
                        mListProcessorChain.delete(db, list, isSyncAdapter);
                        mOperationsLog.log(ProviderOperation.DELETE, list.uri(mAuthority));
                        count++;
                    }
                }
@@ -783,7 +774,9 @@ public final class TaskProvider extends SQLiteContentProvider implements OnAccou
                    {
                        final TaskAdapter task = new CursorContentValuesTaskAdapter(cursor, new ContentValues());

                        ProviderOperation.DELETE.execute(db, mTaskProcessors, task, isSyncAdapter, mOperationsLog, mAuthority);
                        mTaskProcessorChain.delete(db, task, isSyncAdapter);

                        mOperationsLog.log(ProviderOperation.DELETE, task.uri(mAuthority));
                        count++;
                    }
                }
@@ -881,7 +874,8 @@ public final class TaskProvider extends SQLiteContentProvider implements OnAccou
                list.set(ListAdapter.ACCOUNT_NAME, accountName);
                list.set(ListAdapter.ACCOUNT_TYPE, accountType);

                ProviderOperation.INSERT.execute(db, mListProcessors, list, isSyncAdapter, mOperationsLog, mAuthority);
                mListProcessorChain.insert(db, list, isSyncAdapter);
                mOperationsLog.log(ProviderOperation.INSERT, list.uri(mAuthority));

                rowId = list.id();
                result_uri = TaskContract.TaskLists.getContentUri(mAuthority);
@@ -891,7 +885,9 @@ public final class TaskProvider extends SQLiteContentProvider implements OnAccou
            case TASKS:
                final TaskAdapter task = new ContentValuesTaskAdapter(values);

                ProviderOperation.INSERT.execute(db, mTaskProcessors, task, isSyncAdapter, mOperationsLog, mAuthority);
                mTaskProcessorChain.insert(db, task, isSyncAdapter);

                mOperationsLog.log(ProviderOperation.INSERT, task.uri(mAuthority));

                rowId = task.id();
                result_uri = TaskContract.Tasks.getContentUri(mAuthority);
@@ -1005,7 +1001,8 @@ public final class TaskProvider extends SQLiteContentProvider implements OnAccou
                        // we need this, because the processors may change the values
                        final ListAdapter list = new CursorContentValuesListAdapter(listId, cursor, cursor.getCount() > 1 ? new ContentValues(values) : values);

                        ProviderOperation.UPDATE.execute(db, mListProcessors, list, isSyncAdapter, mOperationsLog, mAuthority);
                        mListProcessorChain.update(db, list, isSyncAdapter);
                        mOperationsLog.log(ProviderOperation.UPDATE, list.uri(mAuthority));
                        count++;
                    }
                }
@@ -1032,7 +1029,11 @@ public final class TaskProvider extends SQLiteContentProvider implements OnAccou
                        // we need this, because the processors may change the values
                        final TaskAdapter task = new CursorContentValuesTaskAdapter(cursor, cursor.getCount() > 1 ? new ContentValues(values) : values);

                        ProviderOperation.UPDATE.execute(db, mTaskProcessors, task, isSyncAdapter, mOperationsLog, mAuthority);
                        mTaskProcessorChain.update(db, task, isSyncAdapter);
                        if (task.hasUpdates())
                        {
                            mOperationsLog.log(ProviderOperation.UPDATE, task.uri(mAuthority));
                        }
                        count++;
                    }
                }
+5 −87
Original line number Diff line number Diff line
@@ -22,96 +22,14 @@ import org.dmfs.provider.tasks.model.EntityAdapter;


/**
 * EntityProcessors are called before and after any operation on an entity. They can be used to perform additional operations for each entity.
 *
 * @param <T>
 *         The type of the entity adapter.
 *
 * @author Marten Gajda <marten@dmfs.org>
 * @author Marten Gajda
 */
public interface EntityProcessor<T extends EntityAdapter<?>>
public interface EntityProcessor<T extends EntityAdapter<T>>
{
    /**
     * Called before an entity is inserted.
     *
     * @param db
     *         A writable database.
     * @param entityAdapter
     *         The {@link EntityAdapter} that's about to be inserted. You can modify the entity at this stage. {@link EntityAdapter#id()} will return an invalid
     *         value.
     * @param isSyncAdapter
     */
    public void beforeInsert(SQLiteDatabase db, T entityAdapter, boolean isSyncAdapter);

    /**
     * Called after an entity has been inserted.
     *
     * @param db
     *         A writable database.
     * @param entityAdapter
     *         The {@link EntityAdapter} that's has been inserted. Modifying the entity has no effect.
     * @param isSyncAdapter
     */
    public void afterInsert(SQLiteDatabase db, T entityAdapter, boolean isSyncAdapter);

    /**
     * Called before an entity is updated.
     *
     * @param db
     *         A writable database.
     * @param entityAdapter
     *         The {@link EntityAdapter} that's about to be updated. You can modify the entity at this stage.
     * @param isSyncAdapter
     */
    public void beforeUpdate(SQLiteDatabase db, T entityAdapter, boolean isSyncAdapter);
    T insert(SQLiteDatabase db, T entityAdapter, boolean isSyncAdapter);

    /**
     * Called after an entity has been updated.
     *
     * @param db
     *         A writable database.
     * @param entityAdapter
     *         The {@link EntityAdapter} that's has been updated. Modifying the entity has no effect.
     * @param isSyncAdapter
     */
    public void afterUpdate(SQLiteDatabase db, T entityAdapter, boolean isSyncAdapter);
    T update(SQLiteDatabase db, T entityAdapter, boolean isSyncAdapter);

    /**
     * Called before an entity is deleted.
     * <p>
     * Note that may be called twice for each entity. Once when the entity is marked deleted by the UI and once when it's actually removed by the sync adapter.
     * Both cases can be distinguished by the isSyncAdapter parameter. If an entity is removed because it was deleted on the server, this will be called only
     * once with <code>isSyncAdapter == true</code>.
     * </p>
     * <p>
     * Also note that no processor is called when an entity is removed automatically by a database trigger (e.g. when an entire task list is removed).
     * </p>
     *
     * @param db
     *         A writable database.
     * @param entityAdapter
     *         The {@link EntityAdapter} that's about to be deleted. Modifying the entity has no effect.
     * @param isSyncAdapter
     */
    public void beforeDelete(SQLiteDatabase db, T entityAdapter, boolean isSyncAdapter);
    void delete(SQLiteDatabase db, T entityAdapter, boolean isSyncAdapter);

    /**
     * Called after an entity is deleted.
     * <p>
     * Note that may be called twice for each entity. Once when the entity is marked deleted by the UI and once when it's actually removed by the sync adapter.
     * Both cases can be distinguished by the isSyncAdapter parameter. If an entity is removed because it was deleted on the server, this will be called only
     * once with <code>isSyncAdapter == true</code>.
     * </p>
     * <p>
     * Also note that no processor is called when an entity is removed automatically by a database trigger (e.g. when an entire task list is removed).
     * </p>
     *
     * @param db
     *         A writable database.
     * @param entityAdapter
     *         The {@link EntityAdapter} that was deleted. The value of {@link EntityAdapter#id()} contains the id of the deleted entity. Modifying the entity
     *         has no effect.
     * @param isSyncAdapter
     */
    public void afterDelete(SQLiteDatabase db, T entityAdapter, boolean isSyncAdapter);
}
+67 −0
Original line number Diff line number Diff line
@@ -17,57 +17,51 @@
package org.dmfs.provider.tasks.processors;

import android.database.sqlite.SQLiteDatabase;
import android.util.Log;

import org.dmfs.provider.tasks.model.EntityAdapter;


/**
 * A default implementation of {@link EntityProcessor} that does nothing. It can be used as the basis of concrete {@link EntityProcessor}s without having to
 * override all the methods.
 *
 * @author Marten Gajda <marten@dmfs.org>
 * @author Marten Gajda
 */
public abstract class AbstractEntityProcessor<T extends EntityAdapter<?>> implements EntityProcessor<T>
{
    @Override
    public void beforeInsert(SQLiteDatabase db, T list, boolean isSyncAdapter)
public final class Logging<T extends EntityAdapter<T>> implements EntityProcessor<T>
{
        // the default implementation doesn't do anything
    }
    public static final String TAG = "Logging EntityProcessor";
    private final EntityProcessor<T> mDelegate;


    @Override
    public void afterInsert(SQLiteDatabase db, T list, boolean isSyncAdapter)
    public Logging(EntityProcessor<T> delegate)
    {
        // the default implementation doesn't do anything
        mDelegate = delegate;
    }


    @Override
    public void beforeUpdate(SQLiteDatabase db, T list, boolean isSyncAdapter)
    public T insert(SQLiteDatabase db, T entityAdapter, boolean isSyncAdapter)
    {
        // the default implementation doesn't do anything
        Log.d(TAG, "before insert");
        T result = mDelegate.insert(db, entityAdapter, isSyncAdapter);
        Log.d(TAG, "after insert on " + entityAdapter.id());
        return result;
    }


    @Override
    public void afterUpdate(SQLiteDatabase db, T list, boolean isSyncAdapter)
    public T update(SQLiteDatabase db, T entityAdapter, boolean isSyncAdapter)
    {
        // the default implementation doesn't do anything
        Log.d(TAG, "before update of " + entityAdapter.id());
        T result = mDelegate.update(db, entityAdapter, isSyncAdapter);
        Log.d(TAG, "after update of " + entityAdapter.id());
        return result;
    }


    @Override
    public void beforeDelete(SQLiteDatabase db, T list, boolean isSyncAdapter)
    public void delete(SQLiteDatabase db, T entityAdapter, boolean isSyncAdapter)
    {
        // the default implementation doesn't do anything
        Log.d(TAG, "before delete of " + entityAdapter.id());
        mDelegate.delete(db, entityAdapter, isSyncAdapter);
        Log.d(TAG, "after delete of " + entityAdapter.id());
    }


    @Override
    public void afterDelete(SQLiteDatabase db, T list, boolean isSyncAdapter)
    {
        // the default implementation doesn't do anything
    }

}
Loading