package com.parse; import android.os.Parcel; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; 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 java.util.Set; /** * Utility methods to deal with {@link ParseFieldOperation} decoding, both from JSON objects and * from {@link Parcel}s. */ /* package */ final class ParseFieldOperations { private ParseFieldOperations() { } /** * A function that creates a ParseFieldOperation from a JSONObject or a Parcel. */ private interface ParseFieldOperationFactory { ParseFieldOperation decode(JSONObject object, ParseDecoder decoder) throws JSONException; ParseFieldOperation decode(Parcel source, ParseParcelDecoder decoder); } // 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, ParseFieldOperationFactory factory) { opDecoderMap.put(opName, factory); } /** * Registers a list of default decoder functions that convert a JSONObject with an __op field, * or a Parcel with a op name string, into a ParseFieldOperation. */ static void registerDefaultDecoders() { registerDecoder(ParseRelationOperation.OP_NAME_BATCH, new ParseFieldOperationFactory() { @Override public ParseFieldOperation decode(JSONObject object, ParseDecoder decoder) throws JSONException { ParseFieldOperation op = null; JSONArray ops = object.getJSONArray("ops"); for (int i = 0; i < ops.length(); ++i) { ParseFieldOperation nextOp = ParseFieldOperations.decode(ops.getJSONObject(i), decoder); op = nextOp.mergeWithPrevious(op); } return op; } @Override public ParseFieldOperation decode(Parcel source, ParseParcelDecoder decoder) { // Decode AddRelation and then RemoveRelation ParseFieldOperation add = ParseFieldOperations.decode(source, decoder); ParseFieldOperation remove = ParseFieldOperations.decode(source, decoder); return remove.mergeWithPrevious(add); } }); registerDecoder(ParseDeleteOperation.OP_NAME, new ParseFieldOperationFactory() { @Override public ParseFieldOperation decode(JSONObject object, ParseDecoder decoder) throws JSONException { return ParseDeleteOperation.getInstance(); } @Override public ParseFieldOperation decode(Parcel source, ParseParcelDecoder decoder) { return ParseDeleteOperation.getInstance(); } }); registerDecoder(ParseIncrementOperation.OP_NAME, new ParseFieldOperationFactory() { @Override public ParseFieldOperation decode(JSONObject object, ParseDecoder decoder) throws JSONException { return new ParseIncrementOperation((Number) decoder.decode(object.opt("amount"))); } @Override public ParseFieldOperation decode(Parcel source, ParseParcelDecoder decoder) { return new ParseIncrementOperation((Number) decoder.decode(source)); } }); registerDecoder(ParseAddOperation.OP_NAME, new ParseFieldOperationFactory() { @Override public ParseFieldOperation decode(JSONObject object, ParseDecoder decoder) throws JSONException { return new ParseAddOperation((Collection) decoder.decode(object.opt("objects"))); } @Override public ParseFieldOperation decode(Parcel source, ParseParcelDecoder decoder) { int size = source.readInt(); List list = new ArrayList<>(size); for (int i = 0; i < size; i++) { list.add(i, decoder.decode(source)); } return new ParseAddOperation(list); } }); registerDecoder(ParseAddUniqueOperation.OP_NAME, new ParseFieldOperationFactory() { @Override public ParseFieldOperation decode(JSONObject object, ParseDecoder decoder) throws JSONException { return new ParseAddUniqueOperation((Collection) decoder.decode(object.opt("objects"))); } @Override public ParseFieldOperation decode(Parcel source, ParseParcelDecoder decoder) { int size = source.readInt(); List list = new ArrayList<>(size); for (int i = 0; i < size; i++) { list.add(i, decoder.decode(source)); } return new ParseAddUniqueOperation(list); } }); registerDecoder(ParseRemoveOperation.OP_NAME, new ParseFieldOperationFactory() { @Override public ParseFieldOperation decode(JSONObject object, ParseDecoder decoder) throws JSONException { return new ParseRemoveOperation((Collection) decoder.decode(object.opt("objects"))); } @Override public ParseFieldOperation decode(Parcel source, ParseParcelDecoder decoder) { int size = source.readInt(); List list = new ArrayList<>(size); for (int i = 0; i < size; i++) { list.add(i, decoder.decode(source)); } return new ParseRemoveOperation(list); } }); registerDecoder(ParseRelationOperation.OP_NAME_ADD, new ParseFieldOperationFactory() { @Override public ParseFieldOperation decode(JSONObject object, ParseDecoder decoder) throws JSONException { JSONArray objectsArray = object.optJSONArray("objects"); List objectsList = (List) decoder.decode(objectsArray); return new ParseRelationOperation<>(new HashSet<>(objectsList), null); } @Override public ParseFieldOperation decode(Parcel source, ParseParcelDecoder decoder) { int size = source.readInt(); Set set = new HashSet<>(size); for (int i = 0; i < size; i++) { set.add((ParseObject) decoder.decode(source)); } return new ParseRelationOperation<>(set, null); } }); registerDecoder(ParseRelationOperation.OP_NAME_REMOVE, new ParseFieldOperationFactory() { @Override public ParseFieldOperation decode(JSONObject object, ParseDecoder decoder) throws JSONException { JSONArray objectsArray = object.optJSONArray("objects"); List objectsList = (List) decoder.decode(objectsArray); return new ParseRelationOperation<>(null, new HashSet<>(objectsList)); } @Override public ParseFieldOperation decode(Parcel source, ParseParcelDecoder decoder) { int size = source.readInt(); Set set = new HashSet<>(size); for (int i = 0; i < size; i++) { set.add((ParseObject) decoder.decode(source)); } return new ParseRelationOperation<>(null, set); } }); registerDecoder(ParseSetOperation.OP_NAME, new ParseFieldOperationFactory() { @Override public ParseFieldOperation decode(JSONObject object, ParseDecoder decoder) throws JSONException { return null; // Not called. } @Override public ParseFieldOperation decode(Parcel source, ParseParcelDecoder decoder) { return new ParseSetOperation(decoder.decode(source)); } }); } /** * Converts a parsed JSON object into a ParseFieldOperation. * * @param encoded * A JSONObject containing an __op field. * @return A ParseFieldOperation. */ static ParseFieldOperation decode(JSONObject encoded, ParseDecoder decoder) throws JSONException { String op = encoded.optString("__op"); ParseFieldOperationFactory factory = opDecoderMap.get(op); if (factory == null) { throw new RuntimeException("Unable to decode operation of type " + op); } return factory.decode(encoded, decoder); } /** * Reads a ParseFieldOperation out of the given Parcel. * * @param source * The source Parcel. * @param decoder * The given ParseParcelableDecoder. * * @return A ParseFieldOperation. */ static ParseFieldOperation decode(Parcel source, ParseParcelDecoder decoder) { String op = source.readString(); ParseFieldOperationFactory factory = opDecoderMap.get(op); if (factory == null) { throw new RuntimeException("Unable to decode operation of type " + op); } return factory.decode(source, 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; } }