Skip to content

Commit 5660ada

Browse files
committed
Warn with delete operations ongoing. Support for LDS
1 parent 5487588 commit 5660ada

File tree

4 files changed

+195
-103
lines changed

4 files changed

+195
-103
lines changed

Parse/src/main/java/com/parse/ParseDeleteOperation.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ public void encode(Parcel dest, ParseParcelEncoder parcelableEncoder) {
4040
dest.writeString(OP_NAME);
4141
}
4242

43-
@Override
43+
@Override
4444
public ParseFieldOperation mergeWithPrevious(ParseFieldOperation previous) {
4545
return this;
4646
}

Parse/src/main/java/com/parse/ParseObject.java

Lines changed: 58 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -386,8 +386,10 @@ public String toString() {
386386
private final ParseMulticastDelegate<ParseObject> saveEvent = new ParseMulticastDelegate<>();
387387

388388
/* package */ boolean isDeleted;
389+
/* package */ boolean isDeleting; // Since delete ops are queued, we don't need a counter.
389390
//TODO (grantland): Derive this off the EventuallyPins as opposed to +/- count.
390391
/* package */ int isDeletingEventually;
392+
private boolean ldsEnabledWhenParceling;
391393

392394
private static final ThreadLocal<String> isCreatingPointerForObjectId =
393395
new ThreadLocal<String>() {
@@ -2158,6 +2160,7 @@ private Task<Void> deleteAsync(final String sessionToken, Task<Void> toAwait) {
21582160
return toAwait.onSuccessTask(new Continuation<Void, Task<Void>>() {
21592161
@Override
21602162
public Task<Void> then(Task<Void> task) throws Exception {
2163+
isDeleting = true;
21612164
if (state.objectId() == null) {
21622165
return task.cast(); // no reason to call delete since it doesn't exist
21632166
}
@@ -2168,6 +2171,12 @@ public Task<Void> then(Task<Void> task) throws Exception {
21682171
public Task<Void> then(Task<Void> task) throws Exception {
21692172
return handleDeleteResultAsync();
21702173
}
2174+
}).continueWith(new Continuation<Void, Void>() {
2175+
@Override
2176+
public Void then(Task<Void> task) throws Exception {
2177+
isDeleting = false;
2178+
return null;
2179+
}
21712180
});
21722181
}
21732182

@@ -4237,7 +4246,26 @@ public void writeToParcel(Parcel dest, int flags) {
42374246

42384247
/* package */ void writeToParcel(Parcel dest, ParseParcelEncoder encoder) {
42394248
synchronized (mutex) {
4240-
// Write className and id regardless of state.
4249+
// Developer warnings.
4250+
ldsEnabledWhenParceling = Parse.isLocalDatastoreEnabled();
4251+
boolean saving = hasOutstandingOperations();
4252+
boolean deleting = isDeleting || isDeletingEventually > 0;
4253+
if (saving) {
4254+
Log.w(TAG, "About to parcel a ParseObject while a save / saveEventually operation is " +
4255+
"going on. If recovered from LDS, the unparceled object will be internally updated when " +
4256+
"these tasks end. If not, it will act as if these tasks have failed. This means that " +
4257+
"the subsequent call to save() will update again the same keys, and this is dangerous " +
4258+
"for certain operations, like increment(). To avoid inconsistencies, wait for operations " +
4259+
"to end before parceling.");
4260+
}
4261+
if (deleting) {
4262+
Log.w(TAG, "About to parcel a ParseObject while a delete / deleteEventually operation is " +
4263+
"going on. If recovered from LDS, the unparceled object will be internally updated when " +
4264+
"these tasks end. If not, it will assume it's not deleted, and might incorrectly " +
4265+
"return false for isDirty(). To avoid inconsistencies, wait for operations to end " +
4266+
"before parceling.");
4267+
}
4268+
// Write className and id first, regardless of state.
42414269
dest.writeString(getClassName());
42424270
String objectId = getObjectId();
42434271
dest.writeByte(objectId != null ? (byte) 1 : 0);
@@ -4247,26 +4275,23 @@ public void writeToParcel(Parcel dest, int flags) {
42474275
dest.writeByte(localId != null ? (byte) 1 : 0);
42484276
if (localId != null) dest.writeString(localId);
42494277
dest.writeByte(isDeleted ? (byte) 1 : 0);
4250-
// Squash the operations queue if needed.
4278+
// Care about dirty changes and ongoing tasks.
42514279
ParseOperationSet set;
4252-
if (hasOutstandingOperations()) { // There's more than one set.
4253-
Log.w(TAG, "About to parcel a ParseObject while a save / saveEventually operation is " +
4254-
"going on. The unparceled object will act as if these tasks had failed. This " +
4255-
"means that the subsequent call to save() will update again the same keys. " +
4256-
"This is dangerous for certain operations, like increment() and decrement(). " +
4257-
"To avoid inconsistencies, wait for save operations to end before parceling.");
4258-
ListIterator<ParseOperationSet> iterator = operationSetQueue.listIterator();
4280+
if (hasOutstandingOperations()) {
4281+
// There's more than one set. Squash the queue, creating copies
4282+
// to preserve the original queue when LDS is enabled.
42594283
set = new ParseOperationSet();
4260-
while (iterator.hasNext()) {
4261-
ParseOperationSet other = iterator.next();
4262-
other.mergeFrom(set);
4263-
set = other;
4284+
for (ParseOperationSet operationSet : operationSetQueue) {
4285+
ParseOperationSet copy = new ParseOperationSet(operationSet);
4286+
copy.mergeFrom(set);
4287+
set = copy;
42644288
}
42654289
} else {
42664290
set = operationSetQueue.getLast();
42674291
}
42684292
set.setIsSaveEventually(false);
42694293
set.toParcel(dest, encoder);
4294+
// Pass a Bundle to subclasses.
42704295
Bundle bundle = new Bundle();
42714296
onSaveInstanceState(bundle);
42724297
dest.writeBundle(bundle);
@@ -4286,28 +4311,31 @@ public ParseObject[] newArray(int size) {
42864311
};
42874312

42884313
/* package */ static ParseObject createFromParcel(Parcel source, ParseParcelDecoder decoder) {
4289-
ParseObject obj;
42904314
String className = source.readString();
4291-
if (source.readByte() == 1) { // We have an objectId.
4292-
obj = createWithoutData(className, source.readString());
4293-
} else {
4294-
obj = create(className);
4295-
}
4315+
String objectId = source.readByte() == 1 ? source.readString() : null;
4316+
// Create empty object (might be the same instance if LDS is enabled)
4317+
// and pass to decoder before unparceling child objects in State
4318+
ParseObject object = createWithoutData(className, objectId);
42964319
if (decoder instanceof ParseObjectParcelDecoder) {
4297-
((ParseObjectParcelDecoder) decoder).addKnownObject(obj);
4298-
}
4299-
State state = State.createFromParcel(source, decoder); // Returns ParseUser.State if needed
4300-
obj.setState(state); // This calls rebuildEstimatedData
4301-
if (source.readByte() == 1) obj.localId = source.readString();
4302-
if (source.readByte() == 1) obj.isDeleted = true;
4320+
((ParseObjectParcelDecoder) decoder).addKnownObject(object);
4321+
}
4322+
State state = State.createFromParcel(source, decoder);
4323+
object.setState(state);
4324+
if (source.readByte() == 1) object.localId = source.readString();
4325+
if (source.readByte() == 1) object.isDeleted = true;
4326+
// If object.ldsEnabledWhenParceling is true, we got this from OfflineStore.
4327+
// There is no need to restore operations in that case.
4328+
boolean restoreOperations = !object.ldsEnabledWhenParceling;
43034329
ParseOperationSet set = ParseOperationSet.fromParcel(source, decoder);
4304-
for (String key : set.keySet()) {
4305-
ParseFieldOperation op = set.get(key);
4306-
obj.performOperation(key, op); // Update ops and estimatedData
4330+
if (restoreOperations) {
4331+
for (String key : set.keySet()) {
4332+
ParseFieldOperation op = set.get(key);
4333+
object.performOperation(key, op); // Update ops and estimatedData
4334+
}
43074335
}
43084336
Bundle bundle = source.readBundle(ParseObject.class.getClassLoader());
4309-
obj.onRestoreInstanceState(bundle);
4310-
return obj;
4337+
object.onRestoreInstanceState(bundle);
4338+
return object;
43114339
}
43124340

43134341
/**

Parse/src/main/java/com/parse/ParseParcelEncoder.java

Lines changed: 6 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -32,21 +32,9 @@ public static ParseParcelEncoder get() {
3232
return INSTANCE;
3333
}
3434

35-
// TODO: remove this and user ParseEncoder.isValidType.
36-
/* package */ static boolean isValidType(Object value) {
37-
return value instanceof String
38-
|| value instanceof Number
39-
|| value instanceof Boolean
40-
|| value instanceof Date
41-
|| value instanceof List
42-
|| value instanceof Map
43-
|| value instanceof byte[]
44-
|| value == JSONObject.NULL
45-
|| value instanceof ParseObject
46-
|| value instanceof ParseACL
47-
// TODO: waiting merge || value instanceof ParseFile
48-
// TODO: waiting merge || value instanceof ParseGeoPoint
49-
|| value instanceof ParseRelation;
35+
private static boolean isValidType(Object value) {
36+
// This encodes to parcel what ParseEncoder does for JSON
37+
return ParseEncoder.isValidType(value);
5038
}
5139

5240
/* package */ final static String TYPE_OBJECT = "ParseObject";
@@ -82,10 +70,10 @@ public void encode(Object object, Parcel dest) {
8270
dest.writeString(TYPE_OP);
8371
((ParseFieldOperation) object).encode(dest, this);
8472

85-
} else if (object instanceof ParseFile) { // TODO
73+
} else if (object instanceof ParseFile) {
8674
throw new IllegalArgumentException("Not supported yet");
8775

88-
} else if (object instanceof ParseGeoPoint) { // TODO
76+
} else if (object instanceof ParseGeoPoint) {
8977
throw new IllegalArgumentException("Not supported yet");
9078

9179
} else if (object instanceof ParseACL) {
@@ -114,9 +102,6 @@ public void encode(Object object, Parcel dest) {
114102
encode(item, dest);
115103
}
116104

117-
} else if (object instanceof ParseRelation) {// TODO
118-
throw new IllegalArgumentException("Not supported yet.");
119-
120105
} else if (object == JSONObject.NULL) {
121106
dest.writeString(TYPE_JSON_NULL);
122107

@@ -133,7 +118,7 @@ public void encode(Object object, Parcel dest) {
133118
+ object.getClass().toString());
134119
}
135120

136-
} catch (IllegalArgumentException e) {
121+
} catch (Exception e) {
137122
throw new IllegalArgumentException("Could not encode this object into Parcel. "
138123
+ object.getClass().toString());
139124
}

0 commit comments

Comments
 (0)