package org.swellrt.server.box.objectapi; import java.util.Map.Entry; import org.swellrt.model.generic.ListType; import org.swellrt.model.generic.MapType; import org.swellrt.model.generic.Model; import org.swellrt.model.generic.Type; import org.swellrt.model.shared.ModelToJsonVisitor; import org.waveprotocol.wave.model.util.Preconditions; import com.google.gson.JsonArray; import com.google.gson.JsonElement; import com.google.gson.JsonObject; /** * Perform operations in collaborative objects using JSON data * * TODO implement unit tests * * @author pablojan@gmail.com (Pablo Ojanguren) * */ public class ObjectApi { private final static String APPEND_MARKER = "$"; public static JsonElement doGet(Model model, String path) throws ObjectApiException { ModelToJsonVisitor toJson = new ModelToJsonVisitor(); Type base = (Model.getField(model.getRoot(), path)); if (base != null) base.accept(toJson); return toJson.getResult(); } public static void doUpdate(Model model, String path, JsonElement jsonData) throws ObjectApiException { Type parent = model.getRoot(); if (path.contains(".")) { parent = Model.getField(parent, path.substring(path.lastIndexOf(".") + 1)); } doUpdate(model, path, parent, jsonData); } /** * Recursive method to create/update fields in a collaborative object with JSON expressions. * <br> * Having the following parameters: * <pre> * <code> * path = a.b.c * data = { json object } * </code> * </pre> * * The field <code>c</code> will be set or updated by the <code>data</code> object. * <br> * To update a specific field in an array, use a valid index as field: * * <pre> * <code> * path = a.b.c.3 * data = { "hello world" } * </code> * </pre> * * or use the special index <code>$</code> to append the data to the array: * * <pre> * <code> * path = a.b.c.$ * data = { "hello world" } * </code> * </pre> * * @param model the collaborative object * @param path route to the field where perform updates * @param parent the parent field of the field pointed by path * @param data a JSON object with new data to update or add */ private static void doUpdate(Model model, String path, Type parent, JsonElement data) { String keyOrIndex = path.substring(path.lastIndexOf(".")+1); if (data.isJsonNull()) { return; } else if (data.isJsonObject()) { // // POST /object/1234/map/key // { // fieldOne : "valueOne", // fieldTwo : "valueTwo" // } // JsonObject jsonObject = data.getAsJsonObject(); MapType map = model.createMap(); Type newField = setValue(parent, keyOrIndex, map); for (Entry<String, JsonElement> e: jsonObject.entrySet()) { // // POST /object/1234/map/key/fieldOne // { "valueOne" } // doUpdate(model, path+"."+e.getKey(), newField, e.getValue()); } } else if (data.isJsonArray()) { // // POST /object/1234/map/key // { // ['a', 'b', 'c'] // } // JsonArray jsonArray = data.getAsJsonArray(); ListType list = model.createList(); Type newField = setValue(parent, keyOrIndex, list); for (int i = 0; i < jsonArray.size(); i++) { // // POST /object/1234/map/key/0 // { "a" } // doUpdate(model, path+"."+APPEND_MARKER, newField, jsonArray.get(i)); } } else if (data.isJsonPrimitive()) { Type newField = null; try { Double d = data.getAsDouble(); newField = model.createNumber(d); } catch (Exception e) { } if (newField == null) { try { String s = data.getAsString(); newField = model.createString(s); } catch (Exception e) { } } if (newField == null) { try { Boolean b = data.getAsBoolean(); newField = model.createBoolean(b); } catch (Exception e) { } } if (newField != null) setValue(parent, keyOrIndex, newField); } } private static Type setValue(Type container, String indexOrKey, Type newField) { Preconditions.checkArgument(container != null, "Container field can't be null"); Preconditions.checkArgument(indexOrKey != null, "Index or Key can't be null"); Preconditions.checkArgument(newField != null, "Value can't be null"); if (container instanceof ListType) { ListType list = (ListType) container; if (indexOrKey.equals(APPEND_MARKER)) { return list.add(newField); } try { int index = Integer.valueOf(indexOrKey); return list.add(index, newField); } catch (NumberFormatException e) { throw new RuntimeException("Bad list index"); } } else if (container instanceof MapType) { MapType map = (MapType) container; return map.put(indexOrKey, newField); } return null; } }