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

io.activej.json.JsonCodecFactory Maven / Gradle / Ivy

The newest version!
package io.activej.json;

import io.activej.common.builder.AbstractBuilder;
import io.activej.common.builder.Rebuildable;
import io.activej.json.annotations.JsonNullable;
import io.activej.json.annotations.JsonSubclasses;
import io.activej.types.TypeT;
import io.activej.types.scanner.TypeScannerRegistry;

import java.lang.reflect.AnnotatedType;
import java.lang.reflect.Type;
import java.time.LocalDate;
import java.util.List;
import java.util.Map;

import static io.activej.common.Checks.checkArgument;
import static io.activej.types.Utils.getAnnotation;
import static io.activej.types.Utils.hasAnnotation;

public class JsonCodecFactory implements Rebuildable {
	private static final JsonCodecFactory DEFAULT_INSTANCE = JsonCodecFactory.builder().build();

	private final TypeScannerRegistry> registry;

	private JsonCodecFactory(TypeScannerRegistry> registry) {this.registry = registry;}

	public static JsonCodecFactory defaultInstance() {
		return DEFAULT_INSTANCE;
	}

	public static Builder builder() {
		JsonCodecFactory factory = new JsonCodecFactory(TypeScannerRegistry.create());
		//noinspection unchecked,rawtypes
		return factory.new Builder()
			.with(String.class, ctx -> JsonCodecs.ofString())

			.with(byte.class, ctx -> JsonCodecs.ofByte())
			.with(short.class, ctx -> JsonCodecs.ofShort())
			.with(int.class, ctx -> JsonCodecs.ofInteger())
			.with(long.class, ctx -> JsonCodecs.ofLong())
			.with(float.class, ctx -> JsonCodecs.ofFloat())
			.with(double.class, ctx -> JsonCodecs.ofDouble())
			.with(boolean.class, ctx -> JsonCodecs.ofBoolean())
			.with(char.class, ctx -> JsonCodecs.ofCharacter())

			.with(Byte.class, ctx -> JsonCodecs.ofByte())
			.with(Short.class, ctx -> JsonCodecs.ofShort())
			.with(Integer.class, ctx -> JsonCodecs.ofInteger())
			.with(Long.class, ctx -> JsonCodecs.ofLong())
			.with(Float.class, ctx -> JsonCodecs.ofFloat())
			.with(Double.class, ctx -> JsonCodecs.ofDouble())
			.with(Boolean.class, ctx -> JsonCodecs.ofBoolean())
			.with(Character.class, ctx -> JsonCodecs.ofCharacter())

			.with(LocalDate.class, ctx -> JsonCodecs.ofLocalDate())

			.with(Enum.class, ctx -> JsonCodecs.ofEnum((Class) ctx.getRawType()))

			.with(List.class, ctx -> JsonCodecs.ofList(ctx.scanTypeArgument(0)))
			.with(Map.class, ctx -> {
				AnnotatedType keyAnnotatedType = ctx.getTypeArgument(0);
				Class keyType = (Class) keyAnnotatedType.getType();
				if (keyAnnotatedType.getAnnotations().length == 0 && keyType == String.class) {
					//noinspection unchecked
					return JsonCodecs.ofMap((JsonCodec) ctx.scanTypeArgument(1));
				}
				if (Number.class.isAssignableFrom(keyType)) {
					//noinspection unchecked
					JsonKeyCodec keyCodec = JsonKeyCodec.ofNumberKey((Class) keyType);
					return JsonCodecs.ofMap(keyCodec, ctx.scanTypeArgument(1));
				}
				throw new IllegalArgumentException("TODO");
			})

			.with(Object.class, ctx -> {
				throw new UnsupportedOperationException();
			})
			;
	}

	public final class Builder extends AbstractBuilder {
		private Builder() {}

		public Builder with(TypeT typeT, TypeScannerRegistry.Mapping> fn) {
			checkNotBuilt(this);
			return with(typeT.getType(), fn);
		}

		public Builder with(Type type, TypeScannerRegistry.Mapping> fn) {
			checkNotBuilt(this);
			registry.with(type, ctx -> {
				JsonCodec jsonCodec;

				JsonSubclasses annotation;
				if ((annotation = getAnnotation(ctx.getAnnotations(), JsonSubclasses.class)) != null) {
					SubclassJsonCodec.Builder subclassBuilder = SubclassJsonCodec.builder();
					Class[] subclasses = annotation.value();
					String[] tags = annotation.tags();
					checkArgument(tags.length == 0 || tags.length == subclasses.length);
					for (int i = 0; i < subclasses.length; i++) {
						//noinspection unchecked
						Class subclass = (Class) subclasses[i];
						//noinspection unchecked
						JsonCodec codec = (JsonCodec) ctx.scan(subclass);
						if (tags.length != 0) {
							subclassBuilder.with(subclass, tags[i], codec);
						} else {
							subclassBuilder.with(subclass, codec);
						}
					}
					jsonCodec = subclassBuilder.build();
				} else {
					jsonCodec = fn.apply(ctx);
				}

				if (hasAnnotation(ctx.getAnnotations(), JsonNullable.class)) {
					if (!(jsonCodec instanceof JsonCodecs.NullableJsonCodec)) {
						jsonCodec = jsonCodec.nullable();
					}
				}

				return jsonCodec;
			});
			return this;
		}

		@Override
		protected JsonCodecFactory doBuild() {
			return JsonCodecFactory.this;
		}
	}

	@Override
	public Builder rebuild() {
		return new JsonCodecFactory(TypeScannerRegistry.copyOf(this.registry)).new Builder();
	}

	public  JsonCodec resolve(Class type) {
		//noinspection unchecked
		return (JsonCodec) registry.scanner().scan(type);
	}

	public  JsonCodec resolve(Type type) {
		//noinspection unchecked
		return (JsonCodec) registry.scanner().scan(type);
	}

	public  JsonCodec resolve(TypeT type) {
		//noinspection unchecked
		return (JsonCodec) registry.scanner().scan(type.getAnnotatedType());
	}

}