<commit_before>package com.badlogic.gdx.json;

import com.badlogic.gdx.json.annotations.*;
import com.badlogic.gdx.utils.*;
import com.badlogic.gdx.utils.reflect.*;

import java.util.Map;
import java.util.function.Consumer;

/**
 * Generic implementation of {@link com.badlogic.gdx.utils.Json.Serializer} which uses reflection information
 * to decide which fields to serialize.
 *
 * @see {@link com.badlogic.gdx.json.annotations.JsonSerializable}
 * @see {@link com.badlogic.gdx.json.annotations.JsonSerialize}
 */
public class AnnotatedJsonSerializer<T> implements Json.Serializer<T> {

	private static class FieldAdapter {

		Field field;
		JsonSerialize annotation;
		Json.Serializer<?> serializer;

		FieldAdapter(Field field) {
			this.field = field;
			annotation = field.getDeclaredAnnotation(JsonSerialize.class).getAnnotation(JsonSerialize.class);
		}

		void ensureAccess(Consumer<FieldAdapter> consumer) {
			boolean accessible = field.isAccessible();
			try {
				field.setAccessible(true);
				consumer.accept(this);
			} finally {
				field.setAccessible(accessible);
			}
		}

		@SuppressWarnings("unchecked")
		<T, V> V get(T instance) {
			try {
				return (V) field.get(instance);
			} catch (ReflectionException e) {
				throw new GdxRuntimeException(e);
			}
		}

		<T, V> void set(T instance, V value) {
			try {
				field.set(instance, value);
			} catch (ReflectionException e) {
				throw new GdxRuntimeException(e);
			}
		}

		String getName() {
			String name = annotation.name();
			return name.isEmpty() ? field.getName() : name;
		}

	}

	private Class<T> clazz;
	private JsonSerializable annotation;
	private Array<FieldAdapter> fieldAdapters = new Array<>();

	public AnnotatedJsonSerializer(Json json, Class<T> clazz) {
		this.clazz = clazz;
		createSerializer(json);
	}

	@Override
	public void write(Json json, T object, Class knownType) {

		json.writeObjectStart();

		if (annotation.dynamic()) {

			String typeName = null;

			if (!annotation.fullyQualifiedClassTag()) {
				typeName = json.getTag(object.getClass());
			}

			if (typeName == null) {
				typeName = object.getClass().getName();
			}

			json.writeValue("class", typeName, String.class);
		}

		fieldAdapters.forEach(adapter -> {

			if (isKnownContainerType(adapter)) {

				if (isArray(adapter)) {
					writeArray(json, object, adapter);
				} else if (isMap(adapter)) {
					writeMap(json, object, adapter);
				}

			} else {

				writeObject(json, object, adapter);

			}
		});

		json.writeObjectEnd();
	}

	private void writeObject(Json json, T object, FieldAdapter adapter) {

		adapter.ensureAccess(accessible -> {

			Object value = accessible.get(object);

			Class<?> fieldType = accessible.field.getType();

			if (value != null) {

				Class<?> valueType = value.getClass();
				if (!valueType.equals(fieldType)) {
					// todo: check for @JsonSerializable.dynamic() and warn/error
				}

			} else {

				if (!annotation.writeNull()) {
					return;
				}

			}

			if (fieldType.isArray()) {
				json.writeValue(accessible.getName(), value, fieldType, fieldType.getComponentType());
			} else if (annotation.encodeFP() && fieldType.equals(float.class)) {
				json.writeValue(accessible.getName(), JsonFloatSerializer.encodeFloatBits((float) value), fieldType);
			} else if (annotation.encodeFP() && fieldType.equals(double.class)) {
				json.writeValue(accessible.getName(), JsonFloatSerializer.encodeDoubleBits((double) value), fieldType);
			} else {
				json.writeValue(accessible.getName(), value, fieldType);
			}

		});
	}

	private void writeArray(Json json, T object, FieldAdapter adapter) {

		adapter.ensureAccess(accessible -> {

			Array<?> array = accessible.get(object);
			JsonArraySerializer<?> serializer = (JsonArraySerializer<?>) accessible.serializer;
			serializer.write(json, array, Array.class);

		});
	}

	private void writeMap(Json json, T object, FieldAdapter adapter) {

		adapter.ensureAccess(accessible -> {

			JsonMap map = accessible.annotation.map()[0];

			Class<?> clazz = map.map();
			JsonMapSerializer<?, ?> serializer = (JsonMapSerializer<?, ?>) accessible.serializer;

			if (Map.class.isAssignableFrom(clazz)) {
				Map<?, ?> value = accessible.get(object);
				serializer.write(json, value.entrySet(), Map.Entry::getKey, Map.Entry::getValue);
			} else if (ObjectMap.class.isAssignableFrom(clazz)) {
				ObjectMap<?, ?> value = accessible.get(object);
				serializer.write(json, value.entries(), e -> e.key, e -> e.value);
			}

		});
	}

	@Override
	@SuppressWarnings("unchecked")
	public T read(Json json, JsonValue jsonData, Class type) {
<commit_msg>Fixed map serialization.<commit_after>package com.badlogic.gdx.json;

import com.badlogic.gdx.json.annotations.*;
import com.badlogic.gdx.utils.*;
import com.badlogic.gdx.utils.reflect.*;

import java.util.Map;
import java.util.function.Consumer;

/**
 * Generic implementation of {@link com.badlogic.gdx.utils.Json.Serializer} which uses reflection information
 * to decide which fields to serialize.
 *
 * @see {@link com.badlogic.gdx.json.annotations.JsonSerializable}
 * @see {@link com.badlogic.gdx.json.annotations.JsonSerialize}
 */
public class AnnotatedJsonSerializer<T> implements Json.Serializer<T> {

	private static class FieldAdapter {

		Field field;
		JsonSerialize annotation;
		Json.Serializer<?> serializer;

		FieldAdapter(Field field) {
			this.field = field;
			annotation = field.getDeclaredAnnotation(JsonSerialize.class).getAnnotation(JsonSerialize.class);
		}

		void ensureAccess(Consumer<FieldAdapter> consumer) {
			boolean accessible = field.isAccessible();
			try {
				field.setAccessible(true);
				consumer.accept(this);
			} finally {
				field.setAccessible(accessible);
			}
		}

		@SuppressWarnings("unchecked")
		<T, V> V get(T instance) {
			try {
				return (V) field.get(instance);
			} catch (ReflectionException e) {
				throw new GdxRuntimeException(e);
			}
		}

		<T, V> void set(T instance, V value) {
			try {
				field.set(instance, value);
			} catch (ReflectionException e) {
				throw new GdxRuntimeException(e);
			}
		}

		String getName() {
			String name = annotation.name();
			return name.isEmpty() ? field.getName() : name;
		}

	}

	private Class<T> clazz;
	private JsonSerializable annotation;
	private Array<FieldAdapter> fieldAdapters = new Array<>();

	public AnnotatedJsonSerializer(Json json, Class<T> clazz) {
		this.clazz = clazz;
		createSerializer(json);
	}

	@Override
	public void write(Json json, T object, Class knownType) {

		json.writeObjectStart();

		if (annotation.dynamic()) {

			String typeName = null;

			if (!annotation.fullyQualifiedClassTag()) {
				typeName = json.getTag(object.getClass());
			}

			if (typeName == null) {
				typeName = object.getClass().getName();
			}

			json.writeValue("class", typeName, String.class);
		}

		fieldAdapters.forEach(adapter -> {

			if (isKnownContainerType(adapter)) {

				if (isArray(adapter)) {
					writeArray(json, object, adapter);
				} else if (isMap(adapter)) {
					writeMap(json, object, adapter);
				}

			} else {

				writeObject(json, object, adapter);

			}
		});

		json.writeObjectEnd();
	}

	private void writeObject(Json json, T object, FieldAdapter adapter) {

		adapter.ensureAccess(accessible -> {

			Object value = accessible.get(object);

			Class<?> fieldType = accessible.field.getType();

			if (value != null) {

				Class<?> valueType = value.getClass();
				if (!valueType.equals(fieldType)) {
					// todo: check for @JsonSerializable.dynamic() and warn/error
				}

			} else {

				if (!annotation.writeNull()) {
					return;
				}

			}

			if (fieldType.isArray()) {
				json.writeValue(accessible.getName(), value, fieldType, fieldType.getComponentType());
			} else if (annotation.encodeFP() && fieldType.equals(float.class)) {
				json.writeValue(accessible.getName(), JsonFloatSerializer.encodeFloatBits((float) value), fieldType);
			} else if (annotation.encodeFP() && fieldType.equals(double.class)) {
				json.writeValue(accessible.getName(), JsonFloatSerializer.encodeDoubleBits((double) value), fieldType);
			} else {
				json.writeValue(accessible.getName(), value, fieldType);
			}

		});
	}

	private void writeArray(Json json, T object, FieldAdapter adapter) {

		adapter.ensureAccess(accessible -> {

			Array<?> array = accessible.get(object);
			JsonArraySerializer<?> serializer = (JsonArraySerializer<?>) accessible.serializer;
			serializer.write(json, array, Array.class);

		});
	}

	private void writeMap(Json json, T object, FieldAdapter adapter) {

		adapter.ensureAccess(accessible -> {

			JsonMap map = accessible.annotation.map()[0];

			Class<?> clazz = map.map();
			JsonMapSerializer<?, ?> serializer = (JsonMapSerializer<?, ?>) accessible.serializer;

			if (Map.class.isAssignableFrom(clazz)) {

				Map<?, ?> value = accessible.get(object);

				if (value != null) {
					serializer.write(json, value.entrySet(), Map.Entry::getKey, Map.Entry::getValue);
				} else {
					if (annotation.writeNull()) {
						json.writeValue(accessible.getName(), Map.class.cast(null), Map.class);
					}
				}

			} else if (ObjectMap.class.isAssignableFrom(clazz)) {

				ObjectMap<?, ?> value = accessible.get(object);

				if (value != null) {
					serializer.write(json, value.entries(), e -> e.key, e -> e.value);
				} else {
					if (annotation.writeNull()) {
						json.writeValue(accessible.getName(), ObjectMap.class.cast(null), ObjectMap.class);
					}
				}

			}

		});
	}

	@Override
	@SuppressWarnings("unchecked")
	public T read(Json json, JsonValue jsonData, Class type) {

{'fraction_non_alphanumeric': 0.14074382748202938, 'fraction_numerical': 0.00020835503698301907, 'mean_word_length': 3.8830111902339777, 'pattern_counts': {'":': 0, '<': 43, '<?xml version=': 0, '>': 55, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2768811', 'n_tokens_mistral': 3482, 'n_tokens_neox': 2921, 'n_words': 618}