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

org.bspfsystems.yamlconfiguration.serialization.ConfigurationSerialization Maven / Gradle / Ivy

There is a newer version: 3.0.1
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-2023 SpigotMC Pty. Ltd. (https://www.spigotmc.org/)
 * Copyright (C) 2020-2024 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 org.bspfsystems.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 org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.slf4j.LoggerFactory;

/**
 * A utility class for storing and retrieving classes for configurations.
 * 

* Synchronized with the commit on 19-November-2023. */ public final class ConfigurationSerialization { public static final String SERIALIZED_TYPE_KEY = "=="; private static final Map> ALIASES = new HashMap>(); private final Class clazz; /** * Constructs a configuration serialization for the given configuration * serializable. * * @param clazz The class of the configuration serializable. */ private ConfigurationSerialization(@NotNull final Class clazz) { this.clazz = clazz; } /** * Deserializes the given map into a configuration serializable. * * @param map The serialized data as a map. * @return The deserialized configuration serializable, 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 method of the configuration serializable that performs the * deserialization. If one cannot be found, {@code null} will be returned. * * @param name The name of the method to retrieve. * @return The method that performs the deserialization, or {@code null} if * one cannot 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 given map via the given method. If any * throwable is thrown, {@code null} will be returned. * * @param method The method to use to deserialize the data. * @param map The data to deserialize. * @return The deserialized configuration serializable, or {@code null} if * an issue occurs during deserialization. */ @Nullable private ConfigurationSerializable deserializeViaMethod(@NotNull final Method method, @NotNull final Map map) { try { final ConfigurationSerializable result = (ConfigurationSerializable) method.invoke(null, map); if (result == null) { LoggerFactory.getLogger(ConfigurationSerialization.class).error("Could not call method '" + method.toString() + "' of " + this.clazz.getName() + " for deserialization: method returned null."); } else { return result; } } catch (final Throwable t) { LoggerFactory.getLogger(ConfigurationSerialization.class).error("Could not call method '" + method.toString() + "' of " + this.clazz.getName() + " for deserialization.", t instanceof InvocationTargetException ? t.getCause() : t); } return null; } /** * Gets the constructor of the configuration serializable that performs the * deserialization. If one cannot be found, {@code null} will be returned. * * @return The constructor that performs the deserialization, or * {@code null} if one cannot be found. */ @Nullable private Constructor getConstructor() { try { return this.clazz.getConstructor(Map.class); } catch (final NoSuchMethodException | SecurityException e) { return null; } } /** * Deserializes the data in the given map via the given constructor. If any * throwable is thrown, {@code null} will be returned. * * @param constructor The constructor to use to deserialize the data. * data. * @param map The data to deserialize. * @return The deserialized configuration serializable, or {@code null} if * an issue occurs during deserialization. */ @Nullable private ConfigurationSerializable deserializeViaConstructor(@NotNull final Constructor constructor, @NotNull final Map map) { try { return constructor.newInstance(map); } catch (final Throwable t) { LoggerFactory.getLogger(ConfigurationSerialization.class.getName()).error("Could not call constructor '" + constructor.toString() + "' of " + clazz + "for deserialization.", t instanceof InvocationTargetException ? t.getCause() : t); } return null; } /** * Attempts to deserialize the given map into a new instance of the given * class. *

* The class must implement configuration serializable, including the extra * methods and/or constructor as specified in the javadocs of a * configuration serializable. *

* If a new instance could not be made (an example being the class not fully * implementing the interface), {@code null} will be returned. * * @param map The map to deserialize. * @param clazz The class to deserialize into. * @return The new instance of the specified 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 map into a new instance of any known * type of configuration serializable that may be indicated by the data * contained within the map itself. *

* The class must implement configuration serializable, including the extra * methods and/or constructor as specified in the javadocs of a * configuration serializable. *

* If a new instance could not be made (an example being the class not fully * implementing the interface), {@code null} will be returned. * * @param map The map to deserialize. * @return The new instance of the given data. */ @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 (final 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 configuration serializable class by any and all * aliases/delegate deserializations. * * @param clazz The 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 given configuration serializable class. * * @param clazz The class to register. * @param alias Alias to register the 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. * * @param alias The alias to unregister. */ public static void unregisterClass(@NotNull final String alias) { ConfigurationSerialization.ALIASES.remove(alias); } /** * Unregisters any aliases for the given configuration serializable class * * @param clazz The 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 configuration serializable class by its * alias. * * @param alias The alias of the configuration serializable to retrieve. * @return The requested configuration serializable, 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 configuration serializable class. * * @param clazz The class to retrieve the primary alias of. * @return The primary alias of the given configuration serializable. */ @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 - 2024 Weber Informatics LLC | Privacy Policy