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

co.easimart.EasimartFieldOperation Maven / Gradle / Ivy

package co.easimart;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

/**
 * A EasimartFieldOperation represents a modification to a value in a EasimartObject. For example,
 * setting, deleting, or incrementing a value are all different kinds of EasimartFieldOperations.
 * EasimartFieldOperations themselves can be considered to be immutable.
 */
/** package */ interface EasimartFieldOperation {
  /**
   * Converts the EasimartFieldOperation to a data structure (typically a JSONObject) that can be
   * converted to JSON and sent to Easimart as part of a save operation.
   * 
   * @param objectEncoder
   *          An object responsible for serializing EasimartObjects.
   * @return An object to be jsonified.
   */
  Object encode(EasimartEncoder objectEncoder) throws JSONException;

  /**
   * Returns a field operation that is composed of a previous operation followed by this operation.
   * This will not mutate either operation. However, it may return self if the current operation is
   * not affected by previous changes. For example:
   * 
   * 
   * {increment by 2}.mergeWithPrevious({set to 5}) -> {set to 7}
   * {set to 5}.mergeWithPrevious({increment by 2}) -> {set to 5}
   * {add "foo"}.mergeWithPrevious({delete}) -> {set to ["foo"]}
   * {delete}.mergeWithPrevious({add "foo"}) -> {delete}
   * 
* * @param previous * The most recent operation on the field, or null if none. * @return A new EasimartFieldOperation or this. */ EasimartFieldOperation mergeWithPrevious(EasimartFieldOperation previous); /** * Returns a new estimated value based on a previous value and this operation. This value is not * intended to be sent to Easimart, but it used locally on the client to inspect the most likely * current value for a field. The key and object are used solely for EasimartRelation to be able to * construct objects that refer back to its parent. * * @param oldValue * The previous value for the field. * @param key * The key that this value is for. * @return The new value for the field. */ Object apply(Object oldValue, String key); } final class EasimartFieldOperations { private EasimartFieldOperations() { } /** * A function that creates a EasimartFieldOperation from a JSONObject. */ private interface EasimartFieldOperationFactory { EasimartFieldOperation decode(JSONObject object, EasimartDecoder decoder) throws JSONException; } // A map of all known decoders. private static Map opDecoderMap = new HashMap<>(); /** * Registers a single factory for a given __op field value. */ private static void registerDecoder(String opName, EasimartFieldOperationFactory factory) { opDecoderMap.put(opName, factory); } /** * Registers a list of default decoder functions that convert a JSONObject with an __op field into * a EasimartFieldOperation. */ static void registerDefaultDecoders() { registerDecoder("Batch", new EasimartFieldOperationFactory() { @Override public EasimartFieldOperation decode(JSONObject object, EasimartDecoder decoder) throws JSONException { EasimartFieldOperation op = null; JSONArray ops = object.getJSONArray("ops"); for (int i = 0; i < ops.length(); ++i) { EasimartFieldOperation nextOp = EasimartFieldOperations.decode(ops.getJSONObject(i), decoder); op = nextOp.mergeWithPrevious(op); } return op; } }); registerDecoder("Delete", new EasimartFieldOperationFactory() { @Override public EasimartFieldOperation decode(JSONObject object, EasimartDecoder decoder) throws JSONException { return EasimartDeleteOperation.getInstance(); } }); registerDecoder("Increment", new EasimartFieldOperationFactory() { @Override public EasimartFieldOperation decode(JSONObject object, EasimartDecoder decoder) throws JSONException { return new EasimartIncrementOperation((Number) decoder.decode(object.opt("amount"))); } }); registerDecoder("Add", new EasimartFieldOperationFactory() { @Override public EasimartFieldOperation decode(JSONObject object, EasimartDecoder decoder) throws JSONException { return new EasimartAddOperation((Collection) decoder.decode(object.opt("objects"))); } }); registerDecoder("AddUnique", new EasimartFieldOperationFactory() { @Override public EasimartFieldOperation decode(JSONObject object, EasimartDecoder decoder) throws JSONException { return new EasimartAddUniqueOperation((Collection) decoder.decode(object.opt("objects"))); } }); registerDecoder("Remove", new EasimartFieldOperationFactory() { @Override public EasimartFieldOperation decode(JSONObject object, EasimartDecoder decoder) throws JSONException { return new EasimartRemoveOperation((Collection) decoder.decode(object.opt("objects"))); } }); registerDecoder("AddRelation", new EasimartFieldOperationFactory() { @Override public EasimartFieldOperation decode(JSONObject object, EasimartDecoder decoder) throws JSONException { JSONArray objectsArray = object.optJSONArray("objects"); List objectsList = (List) decoder.decode(objectsArray); return new EasimartRelationOperation<>(new HashSet<>(objectsList), null); } }); registerDecoder("RemoveRelation", new EasimartFieldOperationFactory() { @Override public EasimartFieldOperation decode(JSONObject object, EasimartDecoder decoder) throws JSONException { JSONArray objectsArray = object.optJSONArray("objects"); List objectsList = (List) decoder.decode(objectsArray); return new EasimartRelationOperation<>(null, new HashSet<>(objectsList)); } }); } /** * Converts a parsed JSON object into a PFFieldOperation. * * @param encoded * A JSONObject containing an __op field. * @return A PFFieldOperation. */ static EasimartFieldOperation decode(JSONObject encoded, EasimartDecoder decoder) throws JSONException { String op = encoded.optString("__op"); EasimartFieldOperationFactory factory = opDecoderMap.get(op); if (factory == null) { throw new RuntimeException("Unable to decode operation of type " + op); } return factory.decode(encoded, decoder); } /** * Converts a JSONArray into an ArrayList. */ static ArrayList jsonArrayAsArrayList(JSONArray array) { ArrayList result = new ArrayList<>(array.length()); for (int i = 0; i < array.length(); ++i) { try { result.add(array.get(i)); } catch (JSONException e) { // This can't actually happen. throw new RuntimeException(e); } } return result; } }