12aee0983597c8b0e65766c4b278d4b1c7d13605fJeff Hamilton/* 22aee0983597c8b0e65766c4b278d4b1c7d13605fJeff Hamilton * Copyright (C) 2009 The Android Open Source Project 32aee0983597c8b0e65766c4b278d4b1c7d13605fJeff Hamilton * 42aee0983597c8b0e65766c4b278d4b1c7d13605fJeff Hamilton * Licensed under the Apache License, Version 2.0 (the "License"); 52aee0983597c8b0e65766c4b278d4b1c7d13605fJeff Hamilton * you may not use this file except in compliance with the License. 62aee0983597c8b0e65766c4b278d4b1c7d13605fJeff Hamilton * You may obtain a copy of the License at 72aee0983597c8b0e65766c4b278d4b1c7d13605fJeff Hamilton * 82aee0983597c8b0e65766c4b278d4b1c7d13605fJeff Hamilton * http://www.apache.org/licenses/LICENSE-2.0 92aee0983597c8b0e65766c4b278d4b1c7d13605fJeff Hamilton * 102aee0983597c8b0e65766c4b278d4b1c7d13605fJeff Hamilton * Unless required by applicable law or agreed to in writing, software 112aee0983597c8b0e65766c4b278d4b1c7d13605fJeff Hamilton * distributed under the License is distributed on an "AS IS" BASIS, 122aee0983597c8b0e65766c4b278d4b1c7d13605fJeff Hamilton * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 132aee0983597c8b0e65766c4b278d4b1c7d13605fJeff Hamilton * See the License for the specific language governing permissions and 142aee0983597c8b0e65766c4b278d4b1c7d13605fJeff Hamilton * limitations under the License. 152aee0983597c8b0e65766c4b278d4b1c7d13605fJeff Hamilton */ 162aee0983597c8b0e65766c4b278d4b1c7d13605fJeff Hamilton 172aee0983597c8b0e65766c4b278d4b1c7d13605fJeff Hamiltonpackage com.android.loaderapp.model; 182aee0983597c8b0e65766c4b278d4b1c7d13605fJeff Hamilton 192aee0983597c8b0e65766c4b278d4b1c7d13605fJeff Hamiltonimport android.content.ContentProviderOperation; 202aee0983597c8b0e65766c4b278d4b1c7d13605fJeff Hamiltonimport android.content.ContentValues; 212aee0983597c8b0e65766c4b278d4b1c7d13605fJeff Hamiltonimport android.content.Entity; 222aee0983597c8b0e65766c4b278d4b1c7d13605fJeff Hamiltonimport android.content.ContentProviderOperation.Builder; 232aee0983597c8b0e65766c4b278d4b1c7d13605fJeff Hamiltonimport android.content.Entity.NamedContentValues; 242aee0983597c8b0e65766c4b278d4b1c7d13605fJeff Hamiltonimport android.net.Uri; 252aee0983597c8b0e65766c4b278d4b1c7d13605fJeff Hamiltonimport android.provider.BaseColumns; 262aee0983597c8b0e65766c4b278d4b1c7d13605fJeff Hamilton 272aee0983597c8b0e65766c4b278d4b1c7d13605fJeff Hamiltonimport java.util.ArrayList; 282aee0983597c8b0e65766c4b278d4b1c7d13605fJeff Hamiltonimport java.util.HashMap; 292aee0983597c8b0e65766c4b278d4b1c7d13605fJeff Hamilton 302aee0983597c8b0e65766c4b278d4b1c7d13605fJeff Hamilton 312aee0983597c8b0e65766c4b278d4b1c7d13605fJeff Hamilton/** 322aee0983597c8b0e65766c4b278d4b1c7d13605fJeff Hamilton * Describes a set of {@link ContentProviderOperation} that need to be 332aee0983597c8b0e65766c4b278d4b1c7d13605fJeff Hamilton * executed to transform a database from one {@link Entity} to another. 342aee0983597c8b0e65766c4b278d4b1c7d13605fJeff Hamilton */ 352aee0983597c8b0e65766c4b278d4b1c7d13605fJeff Hamilton@Deprecated 362aee0983597c8b0e65766c4b278d4b1c7d13605fJeff Hamiltonpublic class EntityDiff extends ArrayList<ContentProviderOperation> { 372aee0983597c8b0e65766c4b278d4b1c7d13605fJeff Hamilton private EntityDiff() { 382aee0983597c8b0e65766c4b278d4b1c7d13605fJeff Hamilton } 392aee0983597c8b0e65766c4b278d4b1c7d13605fJeff Hamilton 402aee0983597c8b0e65766c4b278d4b1c7d13605fJeff Hamilton /** 412aee0983597c8b0e65766c4b278d4b1c7d13605fJeff Hamilton * Build the set of {@link ContentProviderOperation} needed to translate 422aee0983597c8b0e65766c4b278d4b1c7d13605fJeff Hamilton * from "before" to "after". Tries its best to keep operations to 432aee0983597c8b0e65766c4b278d4b1c7d13605fJeff Hamilton * minimal number required. Assumes that all {@link ContentValues} are 442aee0983597c8b0e65766c4b278d4b1c7d13605fJeff Hamilton * keyed using {@link BaseColumns#_ID} values. 452aee0983597c8b0e65766c4b278d4b1c7d13605fJeff Hamilton */ 462aee0983597c8b0e65766c4b278d4b1c7d13605fJeff Hamilton public static EntityDiff buildDiff(Entity before, Entity after, Uri targetUri, 472aee0983597c8b0e65766c4b278d4b1c7d13605fJeff Hamilton String childForeignKey) { 482aee0983597c8b0e65766c4b278d4b1c7d13605fJeff Hamilton final EntityDiff diff = new EntityDiff(); 492aee0983597c8b0e65766c4b278d4b1c7d13605fJeff Hamilton 502aee0983597c8b0e65766c4b278d4b1c7d13605fJeff Hamilton Builder builder; 512aee0983597c8b0e65766c4b278d4b1c7d13605fJeff Hamilton ContentValues values; 522aee0983597c8b0e65766c4b278d4b1c7d13605fJeff Hamilton 532aee0983597c8b0e65766c4b278d4b1c7d13605fJeff Hamilton if (before == null) { 542aee0983597c8b0e65766c4b278d4b1c7d13605fJeff Hamilton // Before doesn't exist, so insert "after" values 552aee0983597c8b0e65766c4b278d4b1c7d13605fJeff Hamilton builder = ContentProviderOperation.newInsert(targetUri); 562aee0983597c8b0e65766c4b278d4b1c7d13605fJeff Hamilton builder.withValues(after.getEntityValues()); 572aee0983597c8b0e65766c4b278d4b1c7d13605fJeff Hamilton diff.add(builder.build()); 582aee0983597c8b0e65766c4b278d4b1c7d13605fJeff Hamilton 592aee0983597c8b0e65766c4b278d4b1c7d13605fJeff Hamilton for (NamedContentValues child : after.getSubValues()) { 602aee0983597c8b0e65766c4b278d4b1c7d13605fJeff Hamilton // Add builder with reference to original _id when needed 612aee0983597c8b0e65766c4b278d4b1c7d13605fJeff Hamilton builder = ContentProviderOperation.newInsert(child.uri); 622aee0983597c8b0e65766c4b278d4b1c7d13605fJeff Hamilton builder.withValues(child.values); 632aee0983597c8b0e65766c4b278d4b1c7d13605fJeff Hamilton if (childForeignKey != null) { 642aee0983597c8b0e65766c4b278d4b1c7d13605fJeff Hamilton builder.withValueBackReference(childForeignKey, 0); 652aee0983597c8b0e65766c4b278d4b1c7d13605fJeff Hamilton } 662aee0983597c8b0e65766c4b278d4b1c7d13605fJeff Hamilton diff.add(builder.build()); 672aee0983597c8b0e65766c4b278d4b1c7d13605fJeff Hamilton } 682aee0983597c8b0e65766c4b278d4b1c7d13605fJeff Hamilton 692aee0983597c8b0e65766c4b278d4b1c7d13605fJeff Hamilton } else if (after == null) { 702aee0983597c8b0e65766c4b278d4b1c7d13605fJeff Hamilton // After doesn't exist, so delete "before" values 712aee0983597c8b0e65766c4b278d4b1c7d13605fJeff Hamilton for (NamedContentValues child : before.getSubValues()) { 722aee0983597c8b0e65766c4b278d4b1c7d13605fJeff Hamilton builder = ContentProviderOperation.newDelete(child.uri); 732aee0983597c8b0e65766c4b278d4b1c7d13605fJeff Hamilton builder.withSelection(getSelectIdClause(child.values), null); 742aee0983597c8b0e65766c4b278d4b1c7d13605fJeff Hamilton diff.add(builder.build()); 752aee0983597c8b0e65766c4b278d4b1c7d13605fJeff Hamilton } 762aee0983597c8b0e65766c4b278d4b1c7d13605fJeff Hamilton 772aee0983597c8b0e65766c4b278d4b1c7d13605fJeff Hamilton builder = ContentProviderOperation.newDelete(targetUri); 782aee0983597c8b0e65766c4b278d4b1c7d13605fJeff Hamilton builder.withSelection(getSelectIdClause(before.getEntityValues()), null); 792aee0983597c8b0e65766c4b278d4b1c7d13605fJeff Hamilton diff.add(builder.build()); 802aee0983597c8b0e65766c4b278d4b1c7d13605fJeff Hamilton 812aee0983597c8b0e65766c4b278d4b1c7d13605fJeff Hamilton } else { 822aee0983597c8b0e65766c4b278d4b1c7d13605fJeff Hamilton // Somewhere between, so update any changed values 832aee0983597c8b0e65766c4b278d4b1c7d13605fJeff Hamilton values = after.getEntityValues(); 842aee0983597c8b0e65766c4b278d4b1c7d13605fJeff Hamilton if (!before.getEntityValues().equals(values)) { 852aee0983597c8b0e65766c4b278d4b1c7d13605fJeff Hamilton // Top-level values changed, so update 862aee0983597c8b0e65766c4b278d4b1c7d13605fJeff Hamilton builder = ContentProviderOperation.newUpdate(targetUri); 872aee0983597c8b0e65766c4b278d4b1c7d13605fJeff Hamilton builder.withSelection(getSelectIdClause(values), null); 882aee0983597c8b0e65766c4b278d4b1c7d13605fJeff Hamilton builder.withValues(values); 892aee0983597c8b0e65766c4b278d4b1c7d13605fJeff Hamilton diff.add(builder.build()); 902aee0983597c8b0e65766c4b278d4b1c7d13605fJeff Hamilton } 912aee0983597c8b0e65766c4b278d4b1c7d13605fJeff Hamilton 922aee0983597c8b0e65766c4b278d4b1c7d13605fJeff Hamilton // Build lookup maps for children on both sides 932aee0983597c8b0e65766c4b278d4b1c7d13605fJeff Hamilton final HashMap<String, NamedContentValues> beforeChildren = buildChildrenMap(before); 942aee0983597c8b0e65766c4b278d4b1c7d13605fJeff Hamilton final HashMap<String, NamedContentValues> afterChildren = buildChildrenMap(after); 952aee0983597c8b0e65766c4b278d4b1c7d13605fJeff Hamilton 962aee0983597c8b0e65766c4b278d4b1c7d13605fJeff Hamilton // Walk through "before" children looking for deletes and updates 972aee0983597c8b0e65766c4b278d4b1c7d13605fJeff Hamilton for (NamedContentValues beforeChild : beforeChildren.values()) { 982aee0983597c8b0e65766c4b278d4b1c7d13605fJeff Hamilton final String key = buildChildKey(beforeChild); 992aee0983597c8b0e65766c4b278d4b1c7d13605fJeff Hamilton final NamedContentValues afterChild = afterChildren.get(key); 1002aee0983597c8b0e65766c4b278d4b1c7d13605fJeff Hamilton 1012aee0983597c8b0e65766c4b278d4b1c7d13605fJeff Hamilton if (afterChild == null) { 1022aee0983597c8b0e65766c4b278d4b1c7d13605fJeff Hamilton // After child doesn't exist, so delete "before" child 1032aee0983597c8b0e65766c4b278d4b1c7d13605fJeff Hamilton builder = ContentProviderOperation.newDelete(beforeChild.uri); 1042aee0983597c8b0e65766c4b278d4b1c7d13605fJeff Hamilton builder.withSelection(getSelectIdClause(beforeChild.values), null); 1052aee0983597c8b0e65766c4b278d4b1c7d13605fJeff Hamilton diff.add(builder.build()); 1062aee0983597c8b0e65766c4b278d4b1c7d13605fJeff Hamilton } else if (!beforeChild.values.equals(afterChild.values)) { 1072aee0983597c8b0e65766c4b278d4b1c7d13605fJeff Hamilton // After child still exists, and is different, so update 1082aee0983597c8b0e65766c4b278d4b1c7d13605fJeff Hamilton values = afterChild.values; 1092aee0983597c8b0e65766c4b278d4b1c7d13605fJeff Hamilton builder = ContentProviderOperation.newUpdate(afterChild.uri); 1102aee0983597c8b0e65766c4b278d4b1c7d13605fJeff Hamilton builder.withSelection(getSelectIdClause(values), null); 1112aee0983597c8b0e65766c4b278d4b1c7d13605fJeff Hamilton builder.withValues(values); 1122aee0983597c8b0e65766c4b278d4b1c7d13605fJeff Hamilton diff.add(builder.build()); 1132aee0983597c8b0e65766c4b278d4b1c7d13605fJeff Hamilton } 1142aee0983597c8b0e65766c4b278d4b1c7d13605fJeff Hamilton 1152aee0983597c8b0e65766c4b278d4b1c7d13605fJeff Hamilton // Remove the now-handled "after" child 1162aee0983597c8b0e65766c4b278d4b1c7d13605fJeff Hamilton afterChildren.remove(key); 1172aee0983597c8b0e65766c4b278d4b1c7d13605fJeff Hamilton } 1182aee0983597c8b0e65766c4b278d4b1c7d13605fJeff Hamilton 1192aee0983597c8b0e65766c4b278d4b1c7d13605fJeff Hamilton // Walk through remaining "after" children, which are inserts 1202aee0983597c8b0e65766c4b278d4b1c7d13605fJeff Hamilton for (NamedContentValues afterChild : afterChildren.values()) { 1212aee0983597c8b0e65766c4b278d4b1c7d13605fJeff Hamilton builder = ContentProviderOperation.newInsert(afterChild.uri); 1222aee0983597c8b0e65766c4b278d4b1c7d13605fJeff Hamilton builder.withValues(afterChild.values); 1232aee0983597c8b0e65766c4b278d4b1c7d13605fJeff Hamilton diff.add(builder.build()); 1242aee0983597c8b0e65766c4b278d4b1c7d13605fJeff Hamilton } 1252aee0983597c8b0e65766c4b278d4b1c7d13605fJeff Hamilton } 1262aee0983597c8b0e65766c4b278d4b1c7d13605fJeff Hamilton 1272aee0983597c8b0e65766c4b278d4b1c7d13605fJeff Hamilton return diff; 1282aee0983597c8b0e65766c4b278d4b1c7d13605fJeff Hamilton } 1292aee0983597c8b0e65766c4b278d4b1c7d13605fJeff Hamilton 1302aee0983597c8b0e65766c4b278d4b1c7d13605fJeff Hamilton private static String buildChildKey(NamedContentValues child) { 1312aee0983597c8b0e65766c4b278d4b1c7d13605fJeff Hamilton return child.uri.toString() + child.values.getAsString(BaseColumns._ID); 1322aee0983597c8b0e65766c4b278d4b1c7d13605fJeff Hamilton } 1332aee0983597c8b0e65766c4b278d4b1c7d13605fJeff Hamilton 1342aee0983597c8b0e65766c4b278d4b1c7d13605fJeff Hamilton private static String getSelectIdClause(ContentValues values) { 1352aee0983597c8b0e65766c4b278d4b1c7d13605fJeff Hamilton return BaseColumns._ID + "=" + values.getAsLong(BaseColumns._ID); 1362aee0983597c8b0e65766c4b278d4b1c7d13605fJeff Hamilton } 1372aee0983597c8b0e65766c4b278d4b1c7d13605fJeff Hamilton 1382aee0983597c8b0e65766c4b278d4b1c7d13605fJeff Hamilton private static HashMap<String, NamedContentValues> buildChildrenMap(Entity entity) { 1392aee0983597c8b0e65766c4b278d4b1c7d13605fJeff Hamilton final ArrayList<NamedContentValues> children = entity.getSubValues(); 1402aee0983597c8b0e65766c4b278d4b1c7d13605fJeff Hamilton final HashMap<String, NamedContentValues> childrenMap = new HashMap<String, NamedContentValues>( 1412aee0983597c8b0e65766c4b278d4b1c7d13605fJeff Hamilton children.size()); 1422aee0983597c8b0e65766c4b278d4b1c7d13605fJeff Hamilton for (NamedContentValues child : children) { 1432aee0983597c8b0e65766c4b278d4b1c7d13605fJeff Hamilton final String key = buildChildKey(child); 1442aee0983597c8b0e65766c4b278d4b1c7d13605fJeff Hamilton childrenMap.put(key, child); 1452aee0983597c8b0e65766c4b278d4b1c7d13605fJeff Hamilton } 1462aee0983597c8b0e65766c4b278d4b1c7d13605fJeff Hamilton return childrenMap; 1472aee0983597c8b0e65766c4b278d4b1c7d13605fJeff Hamilton } 1482aee0983597c8b0e65766c4b278d4b1c7d13605fJeff Hamilton} 149