* Copyright (c) 2000-present Liferay, Inc. All rights reserved.
 * This library is free software; you can redistribute it and/or modify it under
 * the terms of the GNU Lesser General Public License as published by the Free
 * Software Foundation; either version 2.1 of the License, or (at your option)
 * any later version.
 * This library is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
 * details.

package com.liferay.apio.architect.test.util.internal.json;

import static com.spotify.hamcrest.util.DescriptionUtils.describeNestedMismatches;
import static com.spotify.hamcrest.util.DescriptionUtils.indentDescription;

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

import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.function.Consumer;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import org.hamcrest.Description;
import org.hamcrest.Matcher;
import org.hamcrest.StringDescription;
import org.hamcrest.TypeSafeDiagnosingMatcher;

 * A {@code Matcher} that checks if an element is a correct JSON object.
 * @author Alejandro Hernández
public class IsJsonObject extends TypeSafeDiagnosingMatcher<JsonObject> {

	public IsJsonObject(
		Map<String, Matcher<? extends JsonElement>> entryMatchers,
		boolean strictMode) {

		_entryMatchers = entryMatchers;
		_strictMode = strictMode;

	public void describeTo(Description description) {

			(key, matcher) -> {
				description.appendText("  ");
				description.appendText(": ");

				Description innerDescription = new StringDescription();


				indentDescription(description, innerDescription);


	protected boolean matchesSafely(
		JsonObject jsonObject, Description description) {

		Map<String, Consumer<Description>> mismatchedKeys = new HashMap<>();

			(key, value) -> {
				JsonElement jsonElement = jsonObject.get(key);

				if (!value.matches(jsonElement)) {
						consumerDescription -> value.describeMismatch(
							jsonElement, consumerDescription));

		Set<String> keys = _entryMatchers.keySet();

		if (!mismatchedKeys.isEmpty()) {
			description.appendText("was a JSON object ");

				keys, description, mismatchedKeys,
				(text, innerDescription) -> innerDescription.appendText(text));

			return false;

		if (_strictMode && (jsonObject.size() > _entryMatchers.size())) {
			Set<String> jsonObjectKeys = new HashSet<>(jsonObject.keySet());


			Stream<String> stream = jsonObjectKeys.stream();

			String extraKeys = stream.collect(Collectors.joining(", "));

				"was a JSON object "
				"with more fields than validated. "
				"Extra keys: "

			return false;

		return true;

	private final Map<String, Matcher<? extends JsonElement>> _entryMatchers;
	private final boolean _strictMode;