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

com.github.fge.jsonpatch.diff.LCS Maven / Gradle / Ivy

Go to download

JSON Patch (RFC 6902) and JSON Merge Patch (RFC 7386) implementation in Java

There is a newer version: 1.9
Show newest version
/*
 * Copyright (c) 2014, Francis Galiegue ([email protected])
 *
 * 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 this file and of both licenses is available at the root of this
 * project or, if you have the jar distribution, in directory META-INF/, 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 com.github.fge.jsonpatch.diff;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.github.fge.jackson.JsonNumEquals;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Equivalence;
import com.google.common.base.Preconditions;
import com.google.common.collect.Lists;

import java.util.List;

/**
 * Longest common subsequence algorithm implementation
 *
 * 

This is an adaptation of the code found at Rosetta * Code for {@link ArrayNode} instances.

* *

For instance, given these two arrays:

* *
    *
  • {@code [ 1, 2, 3, 4, 5, 6, 7, 8, 9 ]},
  • *
  • {@code [ 1, 2, 10, 11, 5, 12, 8, 9 ]}
  • *
* *

this code will return {@code [ 1, 2, 5, 8, 9 ]}.

*/ final class LCS { private static final Equivalence EQUIVALENCE = JsonNumEquals.getInstance(); private LCS() { } /** * Get the longest common subsequence of elements of two array nodes * *

This is an implementation of the classic 'diff' algorithm often used * to compare text files line by line.

* * @param first first array node to compare * @param second second array node to compare */ @VisibleForTesting static List getLCS(final JsonNode first, final JsonNode second) { Preconditions.checkArgument(first.isArray(), "LCS can only work on JSON arrays"); Preconditions.checkArgument(second.isArray(), "LCS can only work on JSON arrays"); final int minSize = Math.min(first.size(), second.size()); List l1 = Lists.newArrayList(first); List l2 = Lists.newArrayList(second); final List ret = head(l1, l2); final int headSize = ret.size(); l1 = l1.subList(headSize, l1.size()); l2 = l2.subList(headSize, l2.size()); final List tail = tail(l1, l2); final int trim = tail.size(); l1 = l1.subList(0, l1.size() - trim); l2 = l2.subList(0, l2.size() - trim); if (headSize < minSize) ret.addAll(doLCS(l1, l2)); ret.addAll(tail); return ret; } static IndexedJsonArray doLCS(final JsonNode first, final JsonNode second) { return new IndexedJsonArray(getLCS(first, second)); } /** * Compute longest common subsequence out of two lists * *

When entering this function, both lists are trimmed from their * common leading and trailing nodes.

* * @param l1 the first list * @param l2 the second list * @return the longest common subsequence */ private static List doLCS(final List l1, final List l2) { final List lcs = Lists.newArrayList(); // construct LCS lengths matrix final int size1 = l1.size(); final int size2 = l2.size(); final int[][] lengths = new int[size1 + 1][size2 + 1]; JsonNode node1; JsonNode node2; int len; for (int i = 0; i < size1; i++) for (int j = 0; j < size2; j++) { node1 = l1.get(i); node2 = l2.get(j); len = EQUIVALENCE.equivalent(node1, node2) ? lengths[i][j] + 1 : Math.max(lengths[i + 1][j], lengths[i][j + 1]); lengths[i + 1][j + 1] = len; } // return result out of the LCS lengths matrix int x = size1, y = size2; while (x > 0 && y > 0) { if (lengths[x][y] == lengths[x - 1][y]) x--; else if (lengths[x][y] == lengths[x][y - 1]) y--; else { lcs.add(l1.get(x - 1)); x--; y--; } } return Lists.reverse(lcs); } /** * Return a list with common head elements of two lists * *

Note that the arguments are NOT altered.

* * @param l1 first list * @param l2 second list * @return a list of common head elements */ private static List head(final List l1, final List l2) { final List ret = Lists.newArrayList(); final int len = Math.min(l1.size(), l2.size()); JsonNode node; for (int index = 0; index < len; index++) { node = l1.get(index); if (!EQUIVALENCE.equivalent(node, l2.get(index))) break; ret.add(node); } return ret; } /** * Return the list of common tail elements of two lists * *

Note that the arguments are NOT altered. Elements are returned in * their order of appearance.

* * @param l1 first list * @param l2 second list * @return a list of common tail elements */ private static List tail(final List l1, final List l2) { final List l = head(Lists.reverse(l1), Lists.reverse(l2)); return Lists.reverse(l); } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy