org.rapla.rest.jsonpatch.mergepatch.server.ObjectMergePatch.java Source code

Java tutorial

Introduction

Here is the source code for org.rapla.rest.jsonpatch.mergepatch.server.ObjectMergePatch.java

Source

/*
* Copyright (c) 2014, Francis Galiegue (fgaliegue@gmail.com)
*
* This software is dual-licensed under:
*
* - the Lesser General Public License (LGPL) version 3.0 or, at your option, any
* later version;
* - the Apache Software License (ASL) version 2.0.
*
* The text of both licenses is available under the src/resources/ directory of
* this project (under the names LGPL-3.0.txt and ASL-2.0.txt respectively).
*
* Direct link to the sources:
*
* - LGPL 3.0: https://www.gnu.org/licenses/lgpl-3.0.txt
* - ASL 2.0: http://www.apache.org/licenses/LICENSE-2.0.txt
*/

package org.rapla.rest.jsonpatch.mergepatch.server;

import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

import com.google.gson.JsonElement;
import com.google.gson.JsonObject;

/**
* Merge patch for a JSON Object
*
* <p>This only takes care of the top level, and delegates to other {@link
* JsonMergePatch} instances for deeper levels.</p>
*/
final class ObjectMergePatch extends JsonMergePatch {
    private final Map<String, JsonElement> fields;
    private final Set<String> removals;

    ObjectMergePatch(final JsonElement content) {
        super(content);
        fields = asMap(content);
        removals = new HashSet<String>();

        for (final Map.Entry<String, JsonElement> entry : fields.entrySet())
            if (entry.getValue() == null)
                removals.add(entry.getKey());

        fields.keySet().removeAll(removals);
    }

    @Override
    public JsonElement apply(final JsonElement input) throws JsonPatchException {
        if (!input.isJsonObject())
            return mapToNode(fields);

        final Map<String, JsonElement> map = asMap(input);

        // Remove all entries which must be removed
        map.keySet().removeAll(removals);

        // Now cycle through what is left
        String memberName;
        JsonElement patchNode;

        for (final Map.Entry<String, JsonElement> entry : map.entrySet()) {
            memberName = entry.getKey();
            patchNode = fields.get(memberName);

            // Leave untouched if no mention in the patch
            if (patchNode == null)
                continue;

            // If the patch node is a primitive type, replace in the result.
            // Reminder: there cannot be a JSON null anymore
            if (patchNode.isJsonPrimitive()) {
                entry.setValue(patchNode); // no need for .deepCopy()
                continue;
            }

            final JsonMergePatch patch = JsonMergePatch.fromJson(patchNode);
            entry.setValue(patch.apply(entry.getValue()));
        }

        // Finally, if there are members in the patch not present in the input,
        // fill in members
        for (final String key : difference(fields.keySet(), map.keySet()))
            map.put(key, clearNulls(fields.get(key)));

        return mapToNode(map);
    }

    private Set<String> difference(Set<String> keySet, Set<String> keySet2) {
        LinkedHashSet<String> result = new LinkedHashSet<String>();
        for (String key : keySet) {
            if (!keySet2.contains(key)) {
                result.add(key);
            }
        }
        return result;
    }

    private Map<String, JsonElement> asMap(JsonElement input) {
        Map<String, JsonElement> result = new LinkedHashMap<String, JsonElement>();
        for (Entry<String, JsonElement> entry : input.getAsJsonObject().entrySet()) {
            JsonElement value = entry.getValue();
            String key = entry.getKey();
            result.put(key, value);
        }
        return result;
    }

    private static JsonElement mapToNode(final Map<String, JsonElement> map) {
        final JsonObject ret = new JsonObject();
        for (String key : map.keySet()) {
            JsonElement value = map.get(key);
            ret.add(key, value);
        }
        return ret;
    }
}