1863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamilton/* 2863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamilton * Copyright (C) 2009 The Android Open Source Project 3863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamilton * 4863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamilton * Licensed under the Apache License, Version 2.0 (the "License"); 5863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamilton * you may not use this file except in compliance with the License. 6863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamilton * You may obtain a copy of the License at 7863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamilton * 8863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamilton * http://www.apache.org/licenses/LICENSE-2.0 9863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamilton * 10863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamilton * Unless required by applicable law or agreed to in writing, software 11863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamilton * distributed under the License is distributed on an "AS IS" BASIS, 12863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamilton * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamilton * See the License for the specific language governing permissions and 14863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamilton * limitations under the License. 15863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamilton */ 16863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamilton 17863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamiltonpackage com.android.loaderapp.model; 18863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamilton 19863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamiltonimport android.content.ContentProviderOperation; 20863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamiltonimport android.content.ContentValues; 21863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamiltonimport android.content.Entity; 22863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamiltonimport android.content.ContentProviderOperation.Builder; 23863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamiltonimport android.content.Entity.NamedContentValues; 24863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamiltonimport android.net.Uri; 25863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamiltonimport android.provider.BaseColumns; 26863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamilton 27863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamiltonimport java.util.ArrayList; 28863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamiltonimport java.util.HashMap; 29863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamilton 30863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamilton 31863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamilton/** 32863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamilton * Describes a set of {@link ContentProviderOperation} that need to be 33863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamilton * executed to transform a database from one {@link Entity} to another. 34863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamilton */ 35863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamilton@Deprecated 36863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamiltonpublic class EntityDiff extends ArrayList<ContentProviderOperation> { 37863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamilton private EntityDiff() { 38863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamilton } 39863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamilton 40863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamilton /** 41863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamilton * Build the set of {@link ContentProviderOperation} needed to translate 42863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamilton * from "before" to "after". Tries its best to keep operations to 43863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamilton * minimal number required. Assumes that all {@link ContentValues} are 44863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamilton * keyed using {@link BaseColumns#_ID} values. 45863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamilton */ 46863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamilton public static EntityDiff buildDiff(Entity before, Entity after, Uri targetUri, 47863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamilton String childForeignKey) { 48863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamilton final EntityDiff diff = new EntityDiff(); 49863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamilton 50863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamilton Builder builder; 51863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamilton ContentValues values; 52863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamilton 53863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamilton if (before == null) { 54863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamilton // Before doesn't exist, so insert "after" values 55863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamilton builder = ContentProviderOperation.newInsert(targetUri); 56863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamilton builder.withValues(after.getEntityValues()); 57863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamilton diff.add(builder.build()); 58863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamilton 59863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamilton for (NamedContentValues child : after.getSubValues()) { 60863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamilton // Add builder with reference to original _id when needed 61863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamilton builder = ContentProviderOperation.newInsert(child.uri); 62863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamilton builder.withValues(child.values); 63863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamilton if (childForeignKey != null) { 64863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamilton builder.withValueBackReference(childForeignKey, 0); 65863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamilton } 66863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamilton diff.add(builder.build()); 67863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamilton } 68863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamilton 69863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamilton } else if (after == null) { 70863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamilton // After doesn't exist, so delete "before" values 71863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamilton for (NamedContentValues child : before.getSubValues()) { 72863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamilton builder = ContentProviderOperation.newDelete(child.uri); 73863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamilton builder.withSelection(getSelectIdClause(child.values), null); 74863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamilton diff.add(builder.build()); 75863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamilton } 76863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamilton 77863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamilton builder = ContentProviderOperation.newDelete(targetUri); 78863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamilton builder.withSelection(getSelectIdClause(before.getEntityValues()), null); 79863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamilton diff.add(builder.build()); 80863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamilton 81863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamilton } else { 82863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamilton // Somewhere between, so update any changed values 83863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamilton values = after.getEntityValues(); 84863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamilton if (!before.getEntityValues().equals(values)) { 85863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamilton // Top-level values changed, so update 86863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamilton builder = ContentProviderOperation.newUpdate(targetUri); 87863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamilton builder.withSelection(getSelectIdClause(values), null); 88863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamilton builder.withValues(values); 89863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamilton diff.add(builder.build()); 90863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamilton } 91863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamilton 92863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamilton // Build lookup maps for children on both sides 93863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamilton final HashMap<String, NamedContentValues> beforeChildren = buildChildrenMap(before); 94863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamilton final HashMap<String, NamedContentValues> afterChildren = buildChildrenMap(after); 95863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamilton 96863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamilton // Walk through "before" children looking for deletes and updates 97863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamilton for (NamedContentValues beforeChild : beforeChildren.values()) { 98863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamilton final String key = buildChildKey(beforeChild); 99863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamilton final NamedContentValues afterChild = afterChildren.get(key); 100863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamilton 101863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamilton if (afterChild == null) { 102863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamilton // After child doesn't exist, so delete "before" child 103863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamilton builder = ContentProviderOperation.newDelete(beforeChild.uri); 104863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamilton builder.withSelection(getSelectIdClause(beforeChild.values), null); 105863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamilton diff.add(builder.build()); 106863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamilton } else if (!beforeChild.values.equals(afterChild.values)) { 107863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamilton // After child still exists, and is different, so update 108863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamilton values = afterChild.values; 109863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamilton builder = ContentProviderOperation.newUpdate(afterChild.uri); 110863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamilton builder.withSelection(getSelectIdClause(values), null); 111863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamilton builder.withValues(values); 112863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamilton diff.add(builder.build()); 113863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamilton } 114863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamilton 115863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamilton // Remove the now-handled "after" child 116863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamilton afterChildren.remove(key); 117863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamilton } 118863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamilton 119863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamilton // Walk through remaining "after" children, which are inserts 120863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamilton for (NamedContentValues afterChild : afterChildren.values()) { 121863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamilton builder = ContentProviderOperation.newInsert(afterChild.uri); 122863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamilton builder.withValues(afterChild.values); 123863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamilton diff.add(builder.build()); 124863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamilton } 125863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamilton } 126863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamilton 127863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamilton return diff; 128863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamilton } 129863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamilton 130863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamilton private static String buildChildKey(NamedContentValues child) { 131863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamilton return child.uri.toString() + child.values.getAsString(BaseColumns._ID); 132863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamilton } 133863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamilton 134863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamilton private static String getSelectIdClause(ContentValues values) { 135863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamilton return BaseColumns._ID + "=" + values.getAsLong(BaseColumns._ID); 136863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamilton } 137863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamilton 138863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamilton private static HashMap<String, NamedContentValues> buildChildrenMap(Entity entity) { 139863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamilton final ArrayList<NamedContentValues> children = entity.getSubValues(); 140863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamilton final HashMap<String, NamedContentValues> childrenMap = new HashMap<String, NamedContentValues>( 141863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamilton children.size()); 142863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamilton for (NamedContentValues child : children) { 143863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamilton final String key = buildChildKey(child); 144863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamilton childrenMap.put(key, child); 145863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamilton } 146863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamilton return childrenMap; 147863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamilton } 148863e7a55dc45cd1210e4d07e5847f48dfe301876Jeff Hamilton} 149