Skip to content

Commit 1e1f146

Browse files
committed
Merge pull request #311 from ParsePlatform/grantland.copy
Add copying
2 parents 62f9912 + a1ab2eb commit 1e1f146

File tree

9 files changed

+182
-17
lines changed

9 files changed

+182
-17
lines changed

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

+19-10
Original file line numberDiff line numberDiff line change
@@ -103,32 +103,41 @@ public static void setDefaultACL(ParseACL acl, boolean withAccessForCurrentUser)
103103
return getDefaultACLController().get();
104104
}
105105

106+
// State
107+
private final Map<String, Permissions> permissionsById = new HashMap<>();
106108
private boolean shared;
109+
107110
/**
108111
* A lazy user that hasn't been saved to Parse.
109112
*/
110113
//TODO (grantland): This should be a list for multiple lazy users with read/write permissions.
111114
private ParseUser unresolvedUser;
112115

113-
private Map<String, Permissions> permissionsById;
114-
115116
/**
116117
* Creates an ACL with no permissions granted.
117118
*/
118119
public ParseACL() {
119-
permissionsById = new HashMap<>();
120+
// do nothing
120121
}
121122

122-
/* package */ ParseACL copy() {
123-
ParseACL copy = new ParseACL();
124-
for (String id : permissionsById.keySet()) {
125-
copy.permissionsById.put(id, new Permissions(permissionsById.get(id)));
123+
/**
124+
* Creates a copy of {@code acl}.
125+
*
126+
* @param acl
127+
* The acl to copy.
128+
*/
129+
public ParseACL(ParseACL acl) {
130+
for (String id : acl.permissionsById.keySet()) {
131+
permissionsById.put(id, new Permissions(acl.permissionsById.get(id)));
126132
}
127-
copy.unresolvedUser = unresolvedUser;
133+
unresolvedUser = acl.unresolvedUser;
128134
if (unresolvedUser != null) {
129-
unresolvedUser.registerSaveListener(new UserResolutionListener(copy));
135+
unresolvedUser.registerSaveListener(new UserResolutionListener(this));
130136
}
131-
return copy;
137+
}
138+
139+
/* package for tests */ ParseACL copy() {
140+
return new ParseACL(this);
132141
}
133142

134143
boolean isShared() {

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

+10
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,16 @@ public ParseGeoPoint(double latitude, double longitude) {
5858
setLongitude(longitude);
5959
}
6060

61+
/**
62+
* Creates a copy of {@code point};
63+
*
64+
* @param point
65+
* The point to copy.
66+
*/
67+
public ParseGeoPoint(ParseGeoPoint point) {
68+
this(point.getLatitude(), point.getLongitude());
69+
}
70+
6171
/**
6272
* Set latitude. Valid range is (-90.0, 90.0). Extremes should not be used.
6373
*

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -3397,7 +3397,7 @@ private ParseACL getACL(boolean mayCopy) {
33973397
throw new RuntimeException("only ACLs can be stored in the ACL key");
33983398
}
33993399
if (mayCopy && ((ParseACL) acl).isShared()) {
3400-
ParseACL copy = ((ParseACL) acl).copy();
3400+
ParseACL copy = new ParseACL((ParseACL) acl);
34013401
estimatedData.put(KEY_ACL, copy);
34023402
return copy;
34033403
}

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

+41-4
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,31 @@ private static void checkArgument(boolean expression, Object errorMessage) {
5555
private Boolean pushToAndroid;
5656
private JSONObject data;
5757

58+
public Builder() {
59+
// do nothing
60+
}
61+
62+
public Builder(State state) {
63+
this.channelSet = state.channelSet() == null
64+
? null
65+
: Collections.unmodifiableSet(new HashSet<>(state.channelSet()));
66+
this.query = state.queryState() == null
67+
? null
68+
: new ParseQuery<>(new ParseQuery.State.Builder<ParseInstallation>(state.queryState()));
69+
this.expirationTime = state.expirationTime();
70+
this.expirationTimeInterval = state.expirationTimeInterval();
71+
this.pushToIOS = state.pushToIOS();
72+
this.pushToAndroid = state.pushToAndroid();
73+
// Since in state.build() we check data is not null, we do not need to check it again here.
74+
JSONObject copyData = null;
75+
try {
76+
copyData = new JSONObject(state.data().toString());
77+
} catch (JSONException e) {
78+
// Swallow this silently since it is impossible to happen
79+
}
80+
this.data = copyData;
81+
}
82+
5883
public Builder expirationTime(Long expirationTime) {
5984
this.expirationTime = expirationTime;
6085
expirationTimeInterval = null;
@@ -184,15 +209,27 @@ public JSONObject data() {
184209
/* package for test */ final State.Builder builder;
185210

186211
/**
187-
* Creates a new push notification. The default channel is the empty string, also known as the
188-
* global broadcast channel, but this value can be overridden using {@link #setChannel(String)},
189-
* {@link #setChannels(Collection)} or {@link #setQuery(ParseQuery)}. Before sending the push
190-
* notification you must call either {@link #setMessage(String)} or {@link #setData(JSONObject)}.
212+
* Creates a new push notification.
213+
*
214+
* The default channel is the empty string, also known as the global broadcast channel, but this
215+
* value can be overridden using {@link #setChannel(String)}, {@link #setChannels(Collection)} or
216+
* {@link #setQuery(ParseQuery)}. Before sending the push notification you must call either
217+
* {@link #setMessage(String)} or {@link #setData(JSONObject)}.
191218
*/
192219
public ParsePush() {
193220
this(new State.Builder());
194221
}
195222

223+
/**
224+
* Creates a copy of {@code push}.
225+
*
226+
* @param push
227+
* The push to copy.
228+
*/
229+
public ParsePush(ParsePush push) {
230+
this(new State.Builder(push.builder.build()));
231+
}
232+
196233
private ParsePush(State.Builder builder) {
197234
this.builder = builder;
198235
}

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

+17
Original file line numberDiff line numberDiff line change
@@ -910,6 +910,17 @@ public ParseQuery(String theClassName) {
910910
this(new State.Builder<T>(theClassName));
911911
}
912912

913+
/**
914+
* Constructs a copy of {@code query};
915+
*
916+
* @param query
917+
* The query to copy.
918+
*/
919+
public ParseQuery(ParseQuery<T> query) {
920+
this(new State.Builder<>(query.getBuilder()));
921+
user = query.user;
922+
}
923+
913924
/* package */ ParseQuery(State.Builder<T> builder) {
914925
this.builder = builder;
915926
}
@@ -918,6 +929,12 @@ public ParseQuery(String theClassName) {
918929
return builder;
919930
}
920931

932+
/**
933+
* Sets the user to be used for this query.
934+
*
935+
*
936+
* The query will use the user if set, otherwise it will read the current user.
937+
*/
921938
/* package for tests */ ParseQuery<T> setUser(ParseUser user) {
922939
this.user = user;
923940
return this;

Parse/src/test/java/com/parse/ParseACLTest.java

+2-2
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ public void testCopy() throws Exception {
8888
// setReadAccess()
8989
reset(unresolvedUser);
9090

91-
ParseACL copiedACL = acl.copy();
91+
ParseACL copiedACL = new ParseACL(acl);
9292

9393
assertEquals(1, copiedACL.getPermissionsById().size());
9494
assertTrue(copiedACL.getPermissionsById().containsKey(UNRESOLVED_KEY));
@@ -111,7 +111,7 @@ public void testCopyWithSaveListener() throws Exception {
111111
// setReadAccess()
112112
reset(unresolvedUser);
113113

114-
ParseACL copiedACL = acl.copy();
114+
ParseACL copiedACL = new ParseACL(acl);
115115

116116
// Make sure the callback is called
117117
ArgumentCaptor<GetCallback> callbackCaptor = ArgumentCaptor.forClass(GetCallback.class);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
/*
2+
* Copyright (c) 2015-present, Parse, LLC.
3+
* All rights reserved.
4+
*
5+
* This source code is licensed under the BSD-style license found in the
6+
* LICENSE file in the root directory of this source tree. An additional grant
7+
* of patent rights can be found in the PATENTS file in the same directory.
8+
*/
9+
package com.parse;
10+
11+
import org.junit.Test;
12+
13+
import static org.junit.Assert.*;
14+
15+
public class ParseGeoPointTest {
16+
17+
@Test
18+
public void testConstructors() {
19+
ParseGeoPoint point = new ParseGeoPoint();
20+
assertEquals(0, point.getLatitude(), 0);
21+
assertEquals(0, point.getLongitude(), 0);
22+
23+
double lat = 1.0;
24+
double lng = 2.0;
25+
point = new ParseGeoPoint(lat, lng);
26+
assertEquals(lat, point.getLatitude(), 0);
27+
assertEquals(lng, point.getLongitude(), 0);
28+
29+
ParseGeoPoint copy = new ParseGeoPoint(point);
30+
assertEquals(lat, copy.getLatitude(), 0);
31+
assertEquals(lng, copy.getLongitude(), 0);
32+
}
33+
}

Parse/src/test/java/com/parse/ParsePushStateTest.java

+39
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,10 @@
88
*/
99
package com.parse;
1010

11+
import org.json.JSONException;
1112
import org.json.JSONObject;
1213
import org.junit.Test;
14+
import org.mockito.internal.util.collections.Sets;
1315
import org.skyscreamer.jsonassert.JSONAssert;
1416
import org.skyscreamer.jsonassert.JSONCompareMode;
1517

@@ -20,8 +22,13 @@
2022
import java.util.Set;
2123

2224
import static org.junit.Assert.assertEquals;
25+
import static org.junit.Assert.assertFalse;
26+
import static org.junit.Assert.assertNotSame;
27+
import static org.junit.Assert.assertSame;
2328
import static org.junit.Assert.assertTrue;
2429
import static org.junit.Assert.fail;
30+
import static org.mockito.Mockito.mock;
31+
import static org.mockito.Mockito.when;
2532

2633
public class ParsePushStateTest {
2734

@@ -56,6 +63,38 @@ public void testDefaultsWithData() throws Exception {
5663

5764
//endregion
5865

66+
@Test
67+
public void testCopy() throws JSONException {
68+
ParsePush.State state = mock(ParsePush.State.class);
69+
when(state.expirationTime()).thenReturn(1L);
70+
when(state.expirationTimeInterval()).thenReturn(2L);
71+
Set channelSet = Sets.newSet("one", "two");
72+
when(state.channelSet()).thenReturn(channelSet);
73+
JSONObject data = new JSONObject();
74+
data.put("foo", "bar");
75+
when(state.data()).thenReturn(data);
76+
when(state.pushToAndroid()).thenReturn(true);
77+
when(state.pushToIOS()).thenReturn(false);
78+
ParseQuery.State<ParseInstallation> queryState =
79+
new ParseQuery.State.Builder<>(ParseInstallation.class).build();
80+
when(state.queryState()).thenReturn(queryState);
81+
82+
ParsePush.State copy = new ParsePush.State.Builder(state).build();
83+
assertSame(1L, copy.expirationTime());
84+
assertSame(2L, copy.expirationTimeInterval());
85+
Set channelSetCopy = copy.channelSet();
86+
assertNotSame(channelSet, channelSetCopy);
87+
assertTrue(channelSetCopy.size() == 2 && channelSetCopy.contains("one"));
88+
JSONObject dataCopy = copy.data();
89+
assertNotSame(data, dataCopy);
90+
assertEquals("bar", dataCopy.get("foo"));
91+
assertTrue(copy.pushToAndroid());
92+
assertFalse(copy.pushToIOS());
93+
ParseQuery.State<ParseInstallation> queryStateCopy = copy.queryState();
94+
assertNotSame(queryState, queryStateCopy);
95+
assertEquals("_Installation", queryStateCopy.className());
96+
}
97+
5998
//region testExpirationTime
6099

61100
@Test

Parse/src/test/java/com/parse/ParseQueryTest.java

+20
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727

2828
import static org.hamcrest.CoreMatchers.instanceOf;
2929
import static org.junit.Assert.assertEquals;
30+
import static org.junit.Assert.assertNotSame;
3031
import static org.junit.Assert.assertNull;
3132
import static org.junit.Assert.assertSame;
3233
import static org.junit.Assert.assertThat;
@@ -66,6 +67,25 @@ public void testConstructors() {
6667
assertSame(builder, query.getBuilder());
6768
}
6869

70+
@Test
71+
public void testCopy() throws InterruptedException {
72+
ParseQuery<ParseObject> query = new ParseQuery<>("TestObject");
73+
query.setUser(new ParseUser());
74+
query.whereEqualTo("foo", "bar");
75+
ParseQuery.State.Builder<ParseObject> builder = query.getBuilder();
76+
ParseQuery.State<ParseObject> state = query.getBuilder().build();
77+
78+
ParseQuery<ParseObject> queryCopy = new ParseQuery<>(query);
79+
ParseQuery.State.Builder<ParseObject> builderCopy = queryCopy.getBuilder();
80+
ParseQuery.State<ParseObject> stateCopy = queryCopy.getBuilder().build();
81+
82+
assertNotSame(query, queryCopy);
83+
assertSame(query.getUserAsync(state).getResult(), queryCopy.getUserAsync(stateCopy).getResult());
84+
85+
assertNotSame(builder, builderCopy);
86+
assertSame(state.constraints().get("foo"), stateCopy.constraints().get("foo"));
87+
}
88+
6989
// ParseUser#setUser is for tests only
7090
@Test
7191
public void testSetUser() throws ParseException {

0 commit comments

Comments
 (0)