/*
 * Copyright 2018, TeamDev. All rights reserved.
 *
 * Redistribution and use in source and/or binary forms, with or without
 * modification, must retain the above copyright notice and the following
 * disclaimer.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

package io.spine.json;

import com.google.common.annotations.VisibleForTesting;
import com.google.protobuf.InvalidProtocolBufferException;
import com.google.protobuf.Message;
import io.spine.type.KnownTypes;
import io.spine.type.UnknownTypeException;

import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Preconditions.checkState;
import static com.google.common.base.Throwables.getRootCause;
import static com.google.protobuf.util.JsonFormat.Parser;
import static com.google.protobuf.util.JsonFormat.Printer;
import static com.google.protobuf.util.JsonFormat.TypeRegistry;
import static com.google.protobuf.util.JsonFormat.parser;
import static com.google.protobuf.util.JsonFormat.printer;
import static io.spine.protobuf.Messages.builderFor;
import static io.spine.util.Exceptions.newIllegalArgumentException;

/**
 * Utilities for working with Json.
 *
 * @author Alexander Yevsyukov
 */
public final class Json {

    private static final TypeRegistry typeRegistry = KnownTypes.instance()
                                                               .typeRegistry();
    private static final Printer PRINTER = printer().usingTypeRegistry(typeRegistry);
    private static final Parser PARSER = parser().usingTypeRegistry(typeRegistry);

    /**
     * Prevents the utility class instantiation.
     */
    private Json() {
    }

    /**
     * Converts passed message into Json representation.
     *
     * @param message the message object
     * @return Json string
     */
    public static String toJson(Message message) {
        String result = toJson(message, PRINTER);
        return result;
    }

    /**
     * Converts the passed message into compact Json representation.
     *
     * <p>The resulted Json does not contain the line separators.
     *
     * @param message the {@code Message} object
     * @return the converted message to Json
     */
    public static String toCompactJson(Message message) {
        Printer compactPrinter = PRINTER.omittingInsignificantWhitespace();
        String result = toJson(message, compactPrinter);
        return result;
    }

    private static String toJson(Message message, Printer printer) {
        checkNotNull(message);
        String result;
        try {
            result = printer.print(message);
        } catch (InvalidProtocolBufferException e) {
            Throwable rootCause = getRootCause(e);
            throw new UnknownTypeException(rootCause);
        }
        checkState(result != null);
        return result;
    }

    @SuppressWarnings("unchecked") // It is OK as the builder is obtained by the specified class.
    public static <T extends Message> T fromJson(String json, Class<T> messageClass) {
        checkNotNull(json);
        try {
            Message.Builder messageBuilder = builderFor(messageClass);
            PARSER.merge(json, messageBuilder);
            T result = (T) messageBuilder.build();
            return result;
        } catch (InvalidProtocolBufferException e) {
            throw newIllegalArgumentException(e,
                                              "%s cannot be parsed to the %s class.",
                                              json, messageClass);
        }
    }

    @VisibleForTesting
    static TypeRegistry typeRegistry() {
        return typeRegistry;
    }
}

@@ -2214,16 +2214,110 @@
 istry);%0A
+    private static final Printer COMPACT_PRINTER = PRINTER.omittingInsignificantWhitespace();%0A
     priv
@@ -3085,84 +3085,8 @@
 ) %7B%0A
-        Printer compactPrinter = PRINTER.omittingInsignificantWhitespace();%0A
     
@@ -3125,22 +3125,23 @@
 ge, 
-compactPrinter
+COMPACT_PRINTER
 );%0A 

Introduce constant for compact Json printer