// Path: src/main/java/org/skyscreamer/jsonassert/comparator/CustomComparator.java
// public class CustomComparator extends DefaultComparator {
// 
//     private final Collection<Customization> customizations;
// 
//     public CustomComparator(JSONCompareMode mode,  Customization... customizations) {
//         super(mode);
//         this.customizations = Arrays.asList(customizations);
//     }
// 
//     @Override
//     public void compareValues(String prefix, Object expectedValue, Object actualValue, JSONCompareResult result) throws JSONException {
//         Customization customization = getCustomization(prefix);
//         if (customization != null) {
//             try {
//     	        if (!customization.matches(prefix, actualValue, expectedValue, result)) {
//                     result.fail(prefix, expectedValue, actualValue);
//                 }
//             }
//             catch (ValueMatcherException e) {
//                 result.fail(prefix, e);
//             }
//         } else {
//             super.compareValues(prefix, expectedValue, actualValue, result);
//         }
//     }
// 
//     private Customization getCustomization(String path) {
//         for (Customization c : customizations)
//             if (c.appliesToPath(path))
//                 return c;
//         return null;
//     }
// }
// 
// Path: src/main/java/org/skyscreamer/jsonassert/comparator/JSONComparator.java
// public interface JSONComparator {
// 
//     /**
//      * Compares two {@link JSONObject}s and returns the result of the comparison in a {@link JSONCompareResult} object.
//      *
//      * @param expected the expected JSON object
//      * @param actual   the actual JSON object
//      * @return the result of the comparison
//      * @throws JSONException JSON parsing error
//      */
//     JSONCompareResult compareJSON(JSONObject expected, JSONObject actual) throws JSONException;
// 
//     /**
//      * Compares two {@link JSONArray}s and returns the result of the comparison in a {@link JSONCompareResult} object.
//      *
//      * @param expected the expected JSON array
//      * @param actual   the actual JSON array
//      * @return the result of the comparison
//      * @throws JSONException JSON parsing error
//      */
//     JSONCompareResult compareJSON(JSONArray expected, JSONArray actual) throws JSONException;
// 
//     /**
//      * Compares two {@link JSONObject}s on the provided path represented by {@code prefix} and
//      * updates the result of the comparison in the {@code result} {@link JSONCompareResult} object.
//      *
//      * @param prefix   the path in the json where the comparison happens
//      * @param expected the expected JSON object
//      * @param actual   the actual JSON object
//      * @param result   stores the actual state of the comparison result
//      * @throws JSONException JSON parsing error
//      */
//     void compareJSON(String prefix, JSONObject expected, JSONObject actual, JSONCompareResult result) throws JSONException;
// 
//     /**
//      * Compares two {@link Object}s on the provided path represented by {@code prefix} and
//      * updates the result of the comparison in the {@code result} {@link JSONCompareResult} object.
//      *
//      * @param prefix        the path in the json where the comparison happens
//      * @param expectedValue the expected value
//      * @param actualValue   the actual value
//      * @param result        stores the actual state of the comparison result
//      * @throws JSONException JSON parsing error
//      */
//     void compareValues(String prefix, Object expectedValue, Object actualValue, JSONCompareResult result) throws JSONException;
// 
//     /**
//      * Compares two {@link JSONArray}s on the provided path represented by {@code prefix} and
//      * updates the result of the comparison in the {@code result} {@link JSONCompareResult} object.
//      *
//      * @param prefix   the path in the json where the comparison happens
//      * @param expected the expected JSON array
//      * @param actual   the actual JSON array
//      * @param result   stores the actual state of the comparison result
//      * @throws JSONException JSON parsing error
//      */
//     void compareJSONArray(String prefix, JSONArray expected, JSONArray actual, JSONCompareResult result) throws JSONException;
// }
// 
// Path: src/main/java/org/skyscreamer/jsonassert/JSONCompare.java
// public static JSONCompareResult compareJSON(String expectedStr, String actualStr, JSONComparator comparator)
//         throws JSONException {
//     Object expected = JSONParser.parseJSON(expectedStr);
//     Object actual = JSONParser.parseJSON(actualStr);
//     if ((expected instanceof JSONObject) && (actual instanceof JSONObject)) {
//         return compareJSON((JSONObject) expected, (JSONObject) actual, comparator);
//     }
//     else if ((expected instanceof JSONArray) && (actual instanceof JSONArray)) {
//         return compareJSON((JSONArray)expected, (JSONArray)actual, comparator);
//     }
//     else if (expected instanceof JSONString && actual instanceof JSONString) {
//         return compareJson((JSONString) expected, (JSONString) actual);
//     }
//     else if (expected instanceof JSONObject) {
//         return new JSONCompareResult().fail("", expected, actual);
//     }
//     else {
//         return new JSONCompareResult().fail("", expected, actual);
//     }
// }


import org.json.JSONException;
import org.junit.Test;
import org.skyscreamer.jsonassert.comparator.CustomComparator;
import org.skyscreamer.jsonassert.comparator.JSONComparator;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.skyscreamer.jsonassert.JSONCompare.compareJSON;
            "      \"bar\": {\n" +
            "        \"baz\": \"actual\"\n" +
            "      }\n" +
            "    }\n" +
            "  }\n" +
            "}";
    String rootDeepWildcardExpected = "{\n" +
            "  \"baz\": \"expected\",\n" +
            "  \"root\": {\n" +
            "    \"baz\": \"expected\",\n" +
            "    \"foo\": {\n" +
            "      \"baz\": \"expected\",\n" +
            "      \"bar\": {\n" +
            "        \"baz\": \"expected\"\n" +
            "      }\n" +
            "    }\n" +
            "  }\n" +
            "}";

    int comparatorCallCount = 0;
    ValueMatcher<Object> comparator = new ValueMatcher<Object>() {
        @Override
        public boolean equal(Object o1, Object o2) {
            comparatorCallCount++;
            return o1.toString().equals("actual") && o2.toString().equals("expected");
        }
    };

    @Test
    public void whenPathMatchesInCustomizationThenCallCustomMatcher() throws JSONException {
// Path: src/main/java/org/skyscreamer/jsonassert/comparator/CustomComparator.java
// public class CustomComparator extends DefaultComparator {
// 
//     private final Collection<Customization> customizations;
// 
//     public CustomComparator(JSONCompareMode mode,  Customization... customizations) {
//         super(mode);
//         this.customizations = Arrays.asList(customizations);
//     }
// 
//     @Override
//     public void compareValues(String prefix, Object expectedValue, Object actualValue, JSONCompareResult result) throws JSONException {
//         Customization customization = getCustomization(prefix);
//         if (customization != null) {
//             try {
//     	        if (!customization.matches(prefix, actualValue, expectedValue, result)) {
//                     result.fail(prefix, expectedValue, actualValue);
//                 }
//             }
//             catch (ValueMatcherException e) {
//                 result.fail(prefix, e);
//             }
//         } else {
//             super.compareValues(prefix, expectedValue, actualValue, result);
//         }
//     }
// 
//     private Customization getCustomization(String path) {
//         for (Customization c : customizations)
//             if (c.appliesToPath(path))
//                 return c;
//         return null;
//     }
// }
// 
// Path: src/main/java/org/skyscreamer/jsonassert/comparator/JSONComparator.java
// public interface JSONComparator {
// 
//     /**
//      * Compares two {@link JSONObject}s and returns the result of the comparison in a {@link JSONCompareResult} object.
//      *
//      * @param expected the expected JSON object
//      * @param actual   the actual JSON object
//      * @return the result of the comparison
//      * @throws JSONException JSON parsing error
//      */
//     JSONCompareResult compareJSON(JSONObject expected, JSONObject actual) throws JSONException;
// 
//     /**
//      * Compares two {@link JSONArray}s and returns the result of the comparison in a {@link JSONCompareResult} object.
//      *
//      * @param expected the expected JSON array
//      * @param actual   the actual JSON array
//      * @return the result of the comparison
//      * @throws JSONException JSON parsing error
//      */
//     JSONCompareResult compareJSON(JSONArray expected, JSONArray actual) throws JSONException;
// 
//     /**
//      * Compares two {@link JSONObject}s on the provided path represented by {@code prefix} and
//      * updates the result of the comparison in the {@code result} {@link JSONCompareResult} object.
//      *
//      * @param prefix   the path in the json where the comparison happens
//      * @param expected the expected JSON object
//      * @param actual   the actual JSON object
//      * @param result   stores the actual state of the comparison result
//      * @throws JSONException JSON parsing error
//      */
//     void compareJSON(String prefix, JSONObject expected, JSONObject actual, JSONCompareResult result) throws JSONException;
// 
//     /**
//      * Compares two {@link Object}s on the provided path represented by {@code prefix} and
//      * updates the result of the comparison in the {@code result} {@link JSONCompareResult} object.
//      *
//      * @param prefix        the path in the json where the comparison happens
//      * @param expectedValue the expected value
//      * @param actualValue   the actual value
//      * @param result        stores the actual state of the comparison result
//      * @throws JSONException JSON parsing error
//      */
//     void compareValues(String prefix, Object expectedValue, Object actualValue, JSONCompareResult result) throws JSONException;
// 
//     /**
//      * Compares two {@link JSONArray}s on the provided path represented by {@code prefix} and
//      * updates the result of the comparison in the {@code result} {@link JSONCompareResult} object.
//      *
//      * @param prefix   the path in the json where the comparison happens
//      * @param expected the expected JSON array
//      * @param actual   the actual JSON array
//      * @param result   stores the actual state of the comparison result
//      * @throws JSONException JSON parsing error
//      */
//     void compareJSONArray(String prefix, JSONArray expected, JSONArray actual, JSONCompareResult result) throws JSONException;
// }
// 
// Path: src/main/java/org/skyscreamer/jsonassert/JSONCompare.java
// public static JSONCompareResult compareJSON(String expectedStr, String actualStr, JSONComparator comparator)
//         throws JSONException {
//     Object expected = JSONParser.parseJSON(expectedStr);
//     Object actual = JSONParser.parseJSON(actualStr);
//     if ((expected instanceof JSONObject) && (actual instanceof JSONObject)) {
//         return compareJSON((JSONObject) expected, (JSONObject) actual, comparator);
//     }
//     else if ((expected instanceof JSONArray) && (actual instanceof JSONArray)) {
//         return compareJSON((JSONArray)expected, (JSONArray)actual, comparator);
//     }
//     else if (expected instanceof JSONString && actual instanceof JSONString) {
//         return compareJson((JSONString) expected, (JSONString) actual);
//     }
//     else if (expected instanceof JSONObject) {
//         return new JSONCompareResult().fail("", expected, actual);
//     }
//     else {
//         return new JSONCompareResult().fail("", expected, actual);
//     }
// }


// Path: src/test/java/org/skyscreamer/jsonassert/JSONCustomComparatorTest.java
import org.json.JSONException;
import org.junit.Test;
import org.skyscreamer.jsonassert.comparator.CustomComparator;
import org.skyscreamer.jsonassert.comparator.JSONComparator;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.skyscreamer.jsonassert.JSONCompare.compareJSON;

            "      \"bar\": {\n" +
            "        \"baz\": \"actual\"\n" +
            "      }\n" +
            "    }\n" +
            "  }\n" +
            "}";
    String rootDeepWildcardExpected = "{\n" +
            "  \"baz\": \"expected\",\n" +
            "  \"root\": {\n" +
            "    \"baz\": \"expected\",\n" +
            "    \"foo\": {\n" +
            "      \"baz\": \"expected\",\n" +
            "      \"bar\": {\n" +
            "        \"baz\": \"expected\"\n" +
            "      }\n" +
            "    }\n" +
            "  }\n" +
            "}";

    int comparatorCallCount = 0;
    ValueMatcher<Object> comparator = new ValueMatcher<Object>() {
        @Override
        public boolean equal(Object o1, Object o2) {
            comparatorCallCount++;
            return o1.toString().equals("actual") && o2.toString().equals("expected");
        }
    };

    @Test
    public void whenPathMatchesInCustomizationThenCallCustomMatcher() throws JSONException {
        JSONComparator jsonCmp = new CustomComparator(JSONCompareMode.STRICT, new Customization("first", comparator));