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

wf.utils.java.file.yamlconfiguration.serialization.ConfigurationSerialization Maven / Gradle / Ivy

There is a newer version: 3.3.4
Show newest version
/* 
 * This file is part of YamlConfiguration.
 * 
 * Implementation of SnakeYAML to be easy to use with files.
 * 
 * Copyright (C) 2010-2014 The Bukkit Project (https://bukkit.org/)
 * Copyright (C) 2014-2021 SpigotMC Pty. Ltd. (https://www.spigotmc.org/)
 * Copyright (C) 2020-2021 BSPF Systems, LLC (https://bspfsystems.org/)
 * 
 * Many of the files in this project are sourced from the Bukkit API as
 * part of The Bukkit Project (https://bukkit.org/), now maintained by
 * SpigotMC Pty. Ltd. (https://www.spigotmc.org/). These files can be found
 * at https://github.com/Bukkit/Bukkit/ and https://hub.spigotmc.org/stash/,
 * respectively.
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * This program 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 General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see .
 */

package wf.utils.java.file.yamlconfiguration.serialization;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.HashMap;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;

import wf.utils.java.file.yamlconfiguration.configuration.Configuration;
import wf.utils.jetbrains.annotations.NotNull;
import wf.utils.jetbrains.annotations.Nullable;

/**
 * Utility class for storing and retrieving classes for {@link Configuration}.
 * 

* Synchronized with the commit on 07-June-2022. */ public final class ConfigurationSerialization { public static final String SERIALIZED_TYPE_KEY = "=="; private static final Map> ALIASES = new HashMap>(); private final Class clazz; /** * Constructs a new {@link ConfigurationSerialization} for the given * {@link ConfigurationSerializable}. * * @param clazz The {@link ConfigurationSerializable} {@link Class}. */ private ConfigurationSerialization(@NotNull final Class clazz) { this.clazz = clazz; } /** * Deserializes the given {@link Map} into a * {@link ConfigurationSerializable}. * * @param map The serialized data as a {@link Map}. * @return The deserialized {@link Object}, or {@code null} if the data * cannot be deserialized. */ @Nullable private ConfigurationSerializable deserialize(@NotNull final Map map) { ConfigurationSerializable result = null; Method method = this.getMethod("deserialize"); if (method != null) { result = this.deserializeViaMethod(method, map); } if (result == null) { method = this.getMethod("valueOf"); if (method != null) { result = this.deserializeViaMethod(method, map); } } if (result == null) { final Constructor constructor = this.getConstructor(); if (constructor != null) { result = this.deserializeViaConstructor(constructor, map); } } return result; } /** * Gets the {@link Method} of the {@link ConfigurationSerializable} that * performs the deserialization. If none can be found, {@code null} will be * returned. * * @param name The name of the {@link Method} to retrieve. * @return The {@link Method} that performs the deserialization, or * {@code null} if none can be found. */ @Nullable private Method getMethod(@NotNull final String name) { try { final Method method = this.clazz.getDeclaredMethod(name, Map.class); if (!ConfigurationSerializable.class.isAssignableFrom(method.getReturnType())) { return null; } if (!Modifier.isStatic(method.getModifiers())) { return null; } return method; } catch (NoSuchMethodException | SecurityException e) { return null; } } /** * Deserializes the data in the {@link Map} via the given {@link Method}. If * any {@link Throwable} is thrown, {@code null} will be returned. * * @param method The {@link Method} to use to deserialize the data. * @param map The data to deserialize. * @return The deserialized {@link ConfigurationSerializable}, or * {@code null} if there is an issue. */ @Nullable private ConfigurationSerializable deserializeViaMethod(@NotNull final Method method, @NotNull final Map map) { try { final ConfigurationSerializable result = (ConfigurationSerializable) method.invoke(null, map); if (result == null) { Logger.getLogger(ConfigurationSerialization.class.getName()).log(Level.SEVERE, "Could not call method '" + method.toString() + "' of " + clazz + " for deserialization: method returned null."); } else { return result; } } catch (Throwable t) { Logger.getLogger(ConfigurationSerialization.class.getName()).log(Level.SEVERE, "Could not call method '" + method.toString() + "' of " + clazz + " for deserialization.", t instanceof InvocationTargetException ? t.getCause() : t); } return null; } /** * Gets the {@link Constructor} of the {@link ConfigurationSerializable} * that performs the deserialization. If none can be found, {@code null} * will be returned. * * @return The {@link Constructor} that performs the deserialization, or * {@code null} if none can be found. */ @Nullable private Constructor getConstructor() { try { return this.clazz.getConstructor(Map.class); } catch (NoSuchMethodException | SecurityException e) { return null; } } /** * Deserializes the data in the {@link Map} via the given * {@link Constructor}. If any {@link Throwable} is thrown, {@code null} * will be returned. * * @param constructor The {@link Constructor} to use to deserialize the * data. * @param map The data as a {@link Map} to deserialize. * @return The deserialized {@link ConfigurationSerializable}, or * {@code null} if there is an issue. */ @Nullable private ConfigurationSerializable deserializeViaConstructor(@NotNull final Constructor constructor, @NotNull final Map map) { try { return constructor.newInstance(map); } catch (Throwable t) { Logger.getLogger(ConfigurationSerialization.class.getName()).log(Level.SEVERE, "Could not call constructor '" + constructor.toString() + "' of " + clazz + "for deserialization.", t instanceof InvocationTargetException ? t.getCause() : t); } return null; } /** * Attempts to deserialize the given {@link Map} into a new instance of the * given {@link Class}. *

* The {@link Class} must implement {@link ConfigurationSerializable}, * including the extra methods as specified in the javadoc of a * {@link ConfigurationSerializable}. *

* If a new instance could not be made (an example being the {@link Class} * not fully implementing the interface), {@code null} will be returned. * * @param map The {@link Map} to deserialize. * @param clazz The {@link Class} to deserialize into. * @return The new instance of the specified {@link Class}. */ @Nullable public static ConfigurationSerializable deserializeObject(@NotNull final Map map, @NotNull final Class clazz) { return new ConfigurationSerialization(clazz).deserialize(map); } /** * Attempts to deserialize the given arguments into a new instance of the * given class. *

* The class must implement {@link ConfigurationSerializable}, including * the extra methods as specified in the javadoc of * ConfigurationSerializable. *

* If a new instance could not be made, an example being the class not * fully implementing the interface, null will be returned. * * @param map The data as a {@link Map} to deserialize. * @return The {@link ConfigurationSerializable} with the data from the * given {@link Map}. */ @Nullable public static ConfigurationSerializable deserializeObject(@NotNull final Map map) { Class clazz = null; if (map.containsKey(ConfigurationSerialization.SERIALIZED_TYPE_KEY)) { try { final String alias = (String) map.get(ConfigurationSerialization.SERIALIZED_TYPE_KEY); if (alias == null) { throw new IllegalArgumentException("Cannot have null alias."); } clazz = ConfigurationSerialization.getClassByAlias(alias); if (clazz == null) { throw new IllegalArgumentException("The specified class does not exist ('" + alias + "')."); } } catch(ClassCastException e) { e.fillInStackTrace(); throw e; } } else { throw new IllegalArgumentException("The map does not contain type key ('" + ConfigurationSerialization.SERIALIZED_TYPE_KEY + "')."); } return new ConfigurationSerialization(clazz).deserialize(map); } /** * Registers the given {@link ConfigurationSerializable} {@link Class} by * its alias. * * @param clazz The {@link Class} to register. */ public static void registerClass(@NotNull final Class clazz) { final DelegateDeserialization delegate = clazz.getAnnotation(DelegateDeserialization.class); if (delegate == null) { ConfigurationSerialization.registerClass(clazz, getAlias(clazz)); ConfigurationSerialization.registerClass(clazz, clazz.getName()); } } /** * Registers the given alias to the specified * {@link ConfigurationSerializable} {@link Class} * * @param clazz The {@link Class} to register. * @param alias Alias to register the {@link Class} as. * @see SerializableAs */ public static void registerClass(@NotNull final Class clazz, @NotNull final String alias) { ConfigurationSerialization.ALIASES.put(alias, clazz); } /** * Unregisters the specified alias from a {@link ConfigurationSerializable}. * * @param alias The alias to unregister. */ public static void unregisterClass(@NotNull final String alias) { ConfigurationSerialization.ALIASES.remove(alias); } /** * Unregisters any aliases for the specified * {@link ConfigurationSerializable} {@link Class}. * * @param clazz The {@link Class} to unregister. */ public static void unregisterClass(@NotNull final Class clazz) { ConfigurationSerialization.ALIASES.entrySet().removeIf(entry -> entry.getValue().equals(clazz)); } /** * Attempts to get a registered {@link ConfigurationSerializable} * {@link Class} by its alias. * * @param alias The alias of the {@link ConfigurationSerializable} to * retrieve. * @return The requested {@link ConfigurationSerializable}, or {@code null} * if one is not found. */ @Nullable public static Class getClassByAlias(@NotNull final String alias) { return ConfigurationSerialization.ALIASES.get(alias); } /** * Gets the primary alias for the given {@link ConfigurationSerializable} * {@link Class}. * * @param clazz The {@link Class} to retrieve the alias of. * @return The requested primary alias of the given * {@link ConfigurationSerializable}. */ @NotNull public static String getAlias(@NotNull final Class clazz) { DelegateDeserialization delegate = clazz.getAnnotation(DelegateDeserialization.class); if (delegate != null && delegate.value() != clazz) { return ConfigurationSerialization.getAlias(delegate.value()); } final SerializableAs alias = clazz.getAnnotation(SerializableAs.class); if (alias != null) { return alias.value(); } return clazz.getName(); } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy