Home | History | Annotate | Download | only in model
      1 /*
      2  * Copyright (C) 2009 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.android.loaderapp.model;
     18 
     19 import android.content.ContentProviderOperation;
     20 import android.content.ContentValues;
     21 import android.content.Entity;
     22 import android.content.ContentProviderOperation.Builder;
     23 import android.content.Entity.NamedContentValues;
     24 import android.net.Uri;
     25 import android.provider.BaseColumns;
     26 
     27 import java.util.ArrayList;
     28 import java.util.HashMap;
     29 
     30 
     31 /**
     32  * Describes a set of {@link ContentProviderOperation} that need to be
     33  * executed to transform a database from one {@link Entity} to another.
     34  */
     35 @Deprecated
     36 public class EntityDiff extends ArrayList<ContentProviderOperation> {
     37     private EntityDiff() {
     38     }
     39 
     40     /**
     41      * Build the set of {@link ContentProviderOperation} needed to translate
     42      * from "before" to "after". Tries its best to keep operations to
     43      * minimal number required. Assumes that all {@link ContentValues} are
     44      * keyed using {@link BaseColumns#_ID} values.
     45      */
     46     public static EntityDiff buildDiff(Entity before, Entity after, Uri targetUri,
     47             String childForeignKey) {
     48         final EntityDiff diff = new EntityDiff();
     49 
     50         Builder builder;
     51         ContentValues values;
     52 
     53         if (before == null) {
     54             // Before doesn't exist, so insert "after" values
     55             builder = ContentProviderOperation.newInsert(targetUri);
     56             builder.withValues(after.getEntityValues());
     57             diff.add(builder.build());
     58 
     59             for (NamedContentValues child : after.getSubValues()) {
     60                 // Add builder with reference to original _id when needed
     61                 builder = ContentProviderOperation.newInsert(child.uri);
     62                 builder.withValues(child.values);
     63                 if (childForeignKey != null) {
     64                     builder.withValueBackReference(childForeignKey, 0);
     65                 }
     66                 diff.add(builder.build());
     67             }
     68 
     69         } else if (after == null) {
     70             // After doesn't exist, so delete "before" values
     71             for (NamedContentValues child : before.getSubValues()) {
     72                 builder = ContentProviderOperation.newDelete(child.uri);
     73                 builder.withSelection(getSelectIdClause(child.values), null);
     74                 diff.add(builder.build());
     75             }
     76 
     77             builder = ContentProviderOperation.newDelete(targetUri);
     78             builder.withSelection(getSelectIdClause(before.getEntityValues()), null);
     79             diff.add(builder.build());
     80 
     81         } else {
     82             // Somewhere between, so update any changed values
     83             values = after.getEntityValues();
     84             if (!before.getEntityValues().equals(values)) {
     85                 // Top-level values changed, so update
     86                 builder = ContentProviderOperation.newUpdate(targetUri);
     87                 builder.withSelection(getSelectIdClause(values), null);
     88                 builder.withValues(values);
     89                 diff.add(builder.build());
     90             }
     91 
     92             // Build lookup maps for children on both sides
     93             final HashMap<String, NamedContentValues> beforeChildren = buildChildrenMap(before);
     94             final HashMap<String, NamedContentValues> afterChildren = buildChildrenMap(after);
     95 
     96             // Walk through "before" children looking for deletes and updates
     97             for (NamedContentValues beforeChild : beforeChildren.values()) {
     98                 final String key = buildChildKey(beforeChild);
     99                 final NamedContentValues afterChild = afterChildren.get(key);
    100 
    101                 if (afterChild == null) {
    102                     // After child doesn't exist, so delete "before" child
    103                     builder = ContentProviderOperation.newDelete(beforeChild.uri);
    104                     builder.withSelection(getSelectIdClause(beforeChild.values), null);
    105                     diff.add(builder.build());
    106                 } else if (!beforeChild.values.equals(afterChild.values)) {
    107                     // After child still exists, and is different, so update
    108                     values = afterChild.values;
    109                     builder = ContentProviderOperation.newUpdate(afterChild.uri);
    110                     builder.withSelection(getSelectIdClause(values), null);
    111                     builder.withValues(values);
    112                     diff.add(builder.build());
    113                 }
    114 
    115                 // Remove the now-handled "after" child
    116                 afterChildren.remove(key);
    117             }
    118 
    119             // Walk through remaining "after" children, which are inserts
    120             for (NamedContentValues afterChild : afterChildren.values()) {
    121                 builder = ContentProviderOperation.newInsert(afterChild.uri);
    122                 builder.withValues(afterChild.values);
    123                 diff.add(builder.build());
    124             }
    125         }
    126 
    127         return diff;
    128     }
    129 
    130     private static String buildChildKey(NamedContentValues child) {
    131         return child.uri.toString() + child.values.getAsString(BaseColumns._ID);
    132     }
    133 
    134     private static String getSelectIdClause(ContentValues values) {
    135         return BaseColumns._ID + "=" + values.getAsLong(BaseColumns._ID);
    136     }
    137 
    138     private static HashMap<String, NamedContentValues> buildChildrenMap(Entity entity) {
    139         final ArrayList<NamedContentValues> children = entity.getSubValues();
    140         final HashMap<String, NamedContentValues> childrenMap = new HashMap<String, NamedContentValues>(
    141                 children.size());
    142         for (NamedContentValues child : children) {
    143             final String key = buildChildKey(child);
    144             childrenMap.put(key, child);
    145         }
    146         return childrenMap;
    147     }
    148 }
    149