JsonFormatter.java

package space.sunqian.fs.data.json;

import space.sunqian.annotation.Nonnull;
import space.sunqian.annotation.Nullable;
import space.sunqian.fs.base.bytes.BytesBuilder;
import space.sunqian.fs.base.chars.CharsKit;
import space.sunqian.fs.data.ByteDataFormatter;
import space.sunqian.fs.data.CharDataFormatter;
import space.sunqian.fs.data.DataFormattingException;
import space.sunqian.fs.io.IOKit;
import space.sunqian.fs.object.convert.ObjectConverter;
import space.sunqian.fs.object.schema.ObjectSchemaParser;

import java.io.InputStream;
import java.io.OutputStream;
import java.nio.ByteBuffer;
import java.nio.channels.ReadableByteChannel;
import java.nio.channels.WritableByteChannel;

/**
 * Represents JSON data formatter that formats a given JSON data. By default, it uses {@link CharsKit#defaultCharset()}
 * to formats the data in bytes.
 * <p>
 * Note the JsonFormatter does not support {@link JsonData}, using {@link JsonData}'s own methods (such as
 * {@link JsonData#writeTo(Appendable)}) or converting the  {@link JsonData} (such as {@link JsonData#asMap()}) to
 * format.
 *
 * @author sunqian
 */
public interface JsonFormatter extends ByteDataFormatter<Object>, CharDataFormatter<Object> {

    /**
     * Returns the default {@link JsonFormatter}, which uses {@link ObjectSchemaParser#defaultCachedParser()} and
     * {@link ObjectConverter#defaultConverter()} to pars and convert objects, and doesn't ignore the properties of
     * which values are {@code null}, or {@code null} values of map, when formatting.
     * <p>
     * The returned formatter will format {@code byte[]}/{@link ByteBuffer} as Base64 string, and it is the only way to
     * format binary data.
     *
     * @return the default {@link JsonFormatter}
     */
    static @Nonnull JsonFormatter defaultFormatter() {
        return JsonFormatterBack.defaultFormatter();
    }

    /**
     * Returns the default {@link JsonFormatter}, which uses {@link ObjectSchemaParser#defaultCachedParser()} and
     * {@link ObjectConverter#defaultConverter()} to pars and convert objects, and does ignore the properties of which
     * values are {@code null}, or {@code null} values of map, when formatting.
     * <p>
     * The returned formatter will format {@code byte[]}/{@link ByteBuffer} as Base64 string, and it is the only way to
     * format binary data.
     *
     * @return the default {@link JsonFormatter}
     */
    static @Nonnull JsonFormatter newFormatter(boolean ignoreNullValue) {
        return JsonFormatterBack.newFormatter(ignoreNullValue);
    }

    /**
     * Returns a new {@link JsonFormatter} with the specified {@link ObjectSchemaParser} and {@link ObjectConverter} to
     * parse and convert objects.
     * <p>
     * The returned formatter will format {@code byte[]}/{@link ByteBuffer} as Base64 string, and it is the only way to
     * format binary data.
     *
     * @param objectParser    the specified {@link ObjectSchemaParser}
     * @param objectConverter the specified {@link ObjectConverter}
     * @param ignoreNullValue whether to ignore the properties of which values are {@code null}, or {@code null} values
     *                        of map, when formatting
     * @return a new {@link JsonFormatter} with the specified {@link ObjectSchemaParser} and {@link ObjectConverter} to
     * parse and convert objects
     */
    static @Nonnull JsonFormatter newFormatter(
        @Nonnull ObjectSchemaParser objectParser,
        @Nonnull ObjectConverter objectConverter,
        boolean ignoreNullValue
    ) {
        return JsonFormatterBack.newFormatter(objectParser, objectConverter, ignoreNullValue);
    }

    /**
     * Formates and writes the given data as JSON string to the given output stream, using
     * {@link CharsKit#defaultCharset()}.
     * <p>
     * Note for the data of type {@code byte[]}, {@link ByteBuffer}, {@link InputStream} or {@link ReadableByteChannel},
     * it will be read and formatted as Base64 string; for the data of type {@code char[]}, it will be formatted as JSON
     * string rather than JSON array.
     *
     * @param data the given data to be formatted
     * @param out  the output stream to write to
     * @throws DataFormattingException if any error occurs during formatting
     */
    @Override
    default void formatTo(@Nullable Object data, @Nonnull OutputStream out) throws DataFormattingException {
        formatTo(data, IOKit.newWriter(out));
    }

    /**
     * Formates and writes the given data as JSON string to the given writable byte channel, using
     * {@link CharsKit#defaultCharset()}.
     * <p>
     * Note for the data of type {@code byte[]}, {@link ByteBuffer}, {@link InputStream} or {@link ReadableByteChannel},
     * it will be read and formatted as Base64 string; for the data of type {@code char[]}, it will be formatted as JSON
     * string rather than JSON array.
     *
     * @param data    the given data to be formatted
     * @param channel the output channel to write to
     * @throws DataFormattingException if any error occurs during formatting
     */
    @SuppressWarnings("DataFlowIssue")
    @Override
    default void formatTo(@Nullable Object data, @Nonnull WritableByteChannel channel) throws DataFormattingException {
        ByteDataFormatter.super.formatTo(data, channel);
    }

    /**
     * Formates and writes the given data as JSON string to the given appender.
     * <p>
     * Note for the data of type {@code byte[]}, {@link ByteBuffer}, {@link InputStream} or {@link ReadableByteChannel},
     * it will be read and formatted as Base64 string; for the data of type {@code char[]}, it will be formatted as JSON
     * string rather than JSON array.
     *
     * @param data     the given data to be formatted
     * @param appender the output appender to write to
     * @throws DataFormattingException if any error occurs during formatting
     */
    @Override
    void formatTo(@Nullable Object data, @Nonnull Appendable appender) throws DataFormattingException;

    /**
     * Formates and writes the given data as a JSON string.
     * <p>
     * Note for the data of type {@code byte[]}, {@link ByteBuffer}, {@link InputStream} or {@link ReadableByteChannel},
     * it will be read and formatted as Base64 string; for the data of type {@code char[]}, it will be formatted as JSON
     * string rather than JSON array.
     *
     * @param data the given data to be formatted
     * @return the JSON string
     * @throws DataFormattingException if any error occurs during formatting
     */
    @Nonnull
    default String format(@Nullable Object data) throws DataFormattingException {
        StringBuilder sb = new StringBuilder();
        formatTo(data, sb);
        return sb.toString();
    }

    /**
     * Formates and writes the given data as JSON string to a byte array, using {@link CharsKit#defaultCharset()}.
     * <p>
     * Note for the data of type {@code byte[]}, {@link ByteBuffer}, {@link InputStream} or {@link ReadableByteChannel},
     * it will be read and formatted as Base64 string; for the data of type {@code char[]}, it will be formatted as JSON
     * string rather than JSON array.
     *
     * @param data the given data to be formatted
     * @return the byte array formatted from the given data
     * @throws DataFormattingException if any error occurs during formatting
     */
    default byte @Nonnull [] formatBytes(@Nullable Object data) throws DataFormattingException {
        BytesBuilder out = new BytesBuilder();
        formatTo(data, out);
        return out.toByteArray();
    }
}