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

com.bazaarvoice.jolt.Diffy Maven / Gradle / Ivy

There is a newer version: 0.1.8
Show newest version
/*
 * Copyright 2013 Bazaarvoice, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.bazaarvoice.jolt;

import java.util.List;
import java.util.Map;

/**
 * JSON Diff tool that will walk two "JSON" objects simultaneously and identify mismatches.
 *
 * Algorithm :
 *   1) make a copy of both input objects
 *   2) walk both objects and _remove_ items that match
 *   3) return what is left of the two objects in the Result
 *
 * In the case a full / "sucessful" match, Diffy returns a Result object with isEmpty() == true.
 */
public class Diffy {

    private final JsonUtil jsonUtil;

    public Diffy() {
        jsonUtil = JsonUtils.getDefaultJsonUtil();
    }

    /**
     * Pass in a custom jsonUtil to use for the cloneJson method.
     */
    public Diffy( JsonUtil jsonUtil ) {
        this.jsonUtil = jsonUtil;
    }

    public Result diff(Object expected, Object actual) {
        Object expectedCopy = jsonUtil.cloneJson( expected );
        Object actualCopy = jsonUtil.cloneJson( actual );
        return diffHelper( expectedCopy, actualCopy );
    }

    @SuppressWarnings( "unchecked" )
    protected Result diffHelper(Object expected, Object actual) {
        if (expected instanceof Map) {
            if (!(actual instanceof Map)) {
                return new Result( expected, actual );
            }
            return diffMap( (Map) expected, (Map) actual );
        }
        else if (expected instanceof List) {
            if (!(actual instanceof List)) {
                return new Result( expected, actual );
            }
            return diffList( (List) expected, (List) actual );
        }
        return this.diffScalar( expected, actual );
    }

    protected Result diffMap(Map expected, Map actual) {

        // Make a copy of the expected keySet so that we can remove things w/out concurrent mod exceptions
        String[] expectedKeys = expected.keySet().toArray( new String[ expected.keySet().size() ] );
        for (String key : expectedKeys ) {
            Result subResult = diffHelper( expected.get( key ), actual.get( key ) );
            if (subResult.isEmpty()) {
                expected.remove( key );
                actual.remove( key );
            }
        }
        if (expected.isEmpty() && actual.isEmpty()) {
            return new Result();
        }
        return new Result( expected, actual );
    }

    protected Result diffList(List expected, List actual) {
        int shortlen = Math.min( expected.size(), actual.size() );
        boolean emptyDiff = true;
        for (int i=0; i