com.parse.ParseRelation Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of parse-android Show documentation
Show all versions of parse-android Show documentation
A library that gives you access to the powerful Parse cloud platform from your Android app.
/*
* Copyright (c) 2015-present, Parse, LLC.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
package com.parse;
import java.lang.ref.WeakReference;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
/**
* A class that is used to access all of the children of a many-to-many relationship. Each instance
* of Parse.Relation is associated with a particular parent object and key.
*/
public class ParseRelation {
private final Object mutex = new Object();
// The owning object of this ParseRelation.
private WeakReference parent;
// The object Id of the parent.
private String parentObjectId;
// The classname of the parent to retrieve the parent ParseObject in case the parent is GC'ed.
private String parentClassName;
// The key of the relation in the parent object.
private String key;
// The className of the target objects.
private String targetClass;
// For offline caching, we keep track of every object we've known to be in the relation.
private Set knownObjects = new HashSet<>();
/* package */ ParseRelation(ParseObject parent, String key) {
this.parent = new WeakReference<>(parent);
this.parentObjectId = parent.getObjectId();
this.parentClassName = parent.getClassName();
this.key = key;
this.targetClass = null;
}
/* package */ ParseRelation(String targetClass) {
this.parent = null;
this.parentObjectId = null;
this.parentClassName = null;
this.key = null;
this.targetClass = targetClass;
}
/**
* Parses a relation from JSON.
*/
/* package */ ParseRelation(JSONObject jsonObject, ParseDecoder decoder) {
this.parent = null;
this.parentObjectId = null;
this.parentClassName = null;
this.key = null;
this.targetClass = jsonObject.optString("className", null);
JSONArray objectsArray = jsonObject.optJSONArray("objects");
if (objectsArray != null) {
for (int i = 0; i < objectsArray.length(); ++i) {
knownObjects.add((ParseObject)decoder.decode(objectsArray.optJSONObject(i)));
}
}
}
/* package */ void ensureParentAndKey(ParseObject someParent, String someKey) {
synchronized (mutex) {
if (parent == null) {
parent = new WeakReference<>(someParent);
parentObjectId = someParent.getObjectId();
parentClassName = someParent.getClassName();
}
if (key == null) {
key = someKey;
}
if (parent.get() != someParent) {
throw new IllegalStateException(
"Internal error. One ParseRelation retrieved from two different ParseObjects.");
}
if (!key.equals(someKey)) {
throw new IllegalStateException(
"Internal error. One ParseRelation retrieved from two different keys.");
}
}
}
/**
* Adds an object to this relation.
*
* @param object
* The object to add to this relation.
*/
public void add(T object) {
synchronized (mutex) {
ParseRelationOperation operation =
new ParseRelationOperation<>(Collections.singleton(object), null);
targetClass = operation.getTargetClass();
getParent().performOperation(key, operation);
knownObjects.add(object);
}
}
/**
* Removes an object from this relation.
*
* @param object
* The object to remove from this relation.
*/
public void remove(T object) {
synchronized (mutex) {
ParseRelationOperation operation =
new ParseRelationOperation<>(null, Collections.singleton(object));
targetClass = operation.getTargetClass();
getParent().performOperation(key, operation);
knownObjects.remove(object);
}
}
/**
* Gets a query that can be used to query the objects in this relation.
*
* @return A ParseQuery that restricts the results to objects in this relations.
*/
public ParseQuery getQuery() {
synchronized (mutex) {
ParseQuery.State.Builder builder;
if (targetClass == null) {
builder = new ParseQuery.State.Builder(parentClassName)
.redirectClassNameForKey(key);
} else {
builder = new ParseQuery.State.Builder<>(targetClass);
}
builder.whereRelatedTo(getParent(), key);
return new ParseQuery<>(builder);
}
}
/* package */ JSONObject encodeToJSON(ParseEncoder objectEncoder) throws JSONException {
synchronized (mutex) {
JSONObject relation = new JSONObject();
relation.put("__type", "Relation");
relation.put("className", targetClass);
JSONArray knownObjectsArray = new JSONArray();
for (ParseObject knownObject : knownObjects) {
try {
knownObjectsArray.put(objectEncoder.encodeRelatedObject(knownObject));
} catch (Exception e) {
// This is just for caching, so if an object can't be encoded for any reason, drop it.
}
}
relation.put("objects", knownObjectsArray);
return relation;
}
}
/* package */ String getTargetClass() {
synchronized (mutex) {
return targetClass;
}
}
/* package */ void setTargetClass(String className) {
synchronized (mutex) {
targetClass = className;
}
}
/**
* Adds an object that is known to be in the relation. This is used for offline caching.
*/
/* package */ void addKnownObject(ParseObject object) {
synchronized (mutex) {
knownObjects.add(object);
}
}
/**
* Removes an object that is known to not be in the relation. This is used for offline caching.
*/
/* package */ void removeKnownObject(ParseObject object) {
synchronized (mutex) {
knownObjects.remove(object);
}
}
/**
* Returns true iff this object was ever known to be in the relation. This is used for offline
* caching.
*/
/* package */ boolean hasKnownObject(ParseObject object) {
synchronized (mutex) {
return knownObjects.contains(object);
}
}
/* package for tests */ ParseObject getParent() {
if(parent == null){
return null;
}
if(parent.get() == null){
return ParseObject.createWithoutData(parentClassName, parentObjectId);
}
return parent.get();
}
/* package for tests */ String getKey() {
return key;
}
/* package for tests */ Set getKnownObjects() {
return knownObjects;
}
}