All Downloads are FREE. Search and download functionalities are using the official Maven repository.

co.easimart.EasimartRelation Maven / Gradle / Ivy

package co.easimart;

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 Easimart.Relation is associated with a particular parent object and key.
 */
public class EasimartRelation {
  private final Object mutex = new Object();

  // The owning object of this EasimartRelation.
  private WeakReference parent;

  // The object Id of the parent.
  private String parentObjectId;

  // The classname of the parent to retrieve the parent EasimartObject 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 */ EasimartRelation(EasimartObject parent, String key) {
    this.parent = new WeakReference<>(parent);
    this.parentObjectId = parent.getObjectId();
    this.parentClassName = parent.getClassName();
    this.key = key;
    this.targetClass = null;
  }

  /* package */ EasimartRelation(String targetClass) {
    this.parent = null;
    this.parentObjectId = null;
    this.parentClassName = null;
    this.key = null;
    this.targetClass = targetClass;
  }

  /**
   * Easimarts a relation from JSON.
   */
  /* package */ EasimartRelation(JSONObject jsonObject, EasimartDecoder 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((EasimartObject)decoder.decode(objectsArray.optJSONObject(i)));
      }
    }
  }

  /* package */ void ensureParentAndKey(EasimartObject 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 EasimartRelation retrieved from two different EasimartObjects.");
      }
      if (!key.equals(someKey)) {
        throw new IllegalStateException(
            "Internal error. One EasimartRelation 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) {
      EasimartRelationOperation operation =
          new EasimartRelationOperation<>(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) {
      EasimartRelationOperation operation =
          new EasimartRelationOperation<>(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 EasimartQuery that restricts the results to objects in this relations.
   */
  public EasimartQuery getQuery() {
    synchronized (mutex) {
      EasimartQuery.State.Builder builder;
      if (targetClass == null) {
        builder = new EasimartQuery.State.Builder(parentClassName)
            .redirectClassNameForKey(key);
      } else {
        builder = new EasimartQuery.State.Builder<>(targetClass);
      }
      builder.whereRelatedTo(getParent(), key);
      return new EasimartQuery<>(builder);
    }
  }

  /* package */ JSONObject encodeToJSON(EasimartEncoder objectEncoder) throws JSONException {
    synchronized (mutex) {
      JSONObject relation = new JSONObject();
      relation.put("__type", "Relation");
      relation.put("className", targetClass);
      JSONArray knownObjectsArray = new JSONArray();
      for (EasimartObject 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(EasimartObject 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(EasimartObject 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(EasimartObject object) {
    synchronized (mutex) {
      return knownObjects.contains(object);
    }
  }

  /* package for tests */ EasimartObject getParent() {
    if(parent == null){
      return null;
    }
    if(parent.get() == null){
      return EasimartObject.createWithoutData(parentClassName, parentObjectId);
    }
    return parent.get();
  }

  /* package for tests */ String getKey() {
    return key;
  }

  /* package for tests */ Set getKnownObjects() {
    return knownObjects;
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy