/* * This file is a part of the SteamWar software. * * Copyright (C) 2021 SteamWar.de-Serverteam * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero 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 Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ package de.steamwar.bausystem.linkage; import de.steamwar.bausystem.BauSystem; import lombok.experimental.UtilityClass; import org.bukkit.Bukkit; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.util.*; import java.util.logging.Level; import java.util.stream.Collectors; @UtilityClass public class LinkageUtils { private Map, Object> objectMap = new HashMap<>(); private Set fieldsToLink = new HashSet<>(); public void link() { internalLinkOrUnlink(false, Linked.class); internalLinkFields(); } public void unlink() { internalLinkOrUnlink(true, Linked.class); } private void internalLinkOrUnlink(boolean unlink, Class toLink) { try (BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(BauSystem.class.getResourceAsStream("/de.steamwar.bausystem/" + toLink.getTypeName().replace("$", "."))))) { bufferedReader.lines().forEach(s -> { try { linkOrUnlink(Class.forName(s), unlink); } catch (ClassNotFoundException e) { // ignored } catch (Exception e) { Bukkit.getLogger().log(Level.WARNING, e.getMessage(), e); } }); } catch (IOException e) { Bukkit.shutdown(); } catch (NullPointerException e) { // Ignored } } private void internalLinkFields() { for (Field field : fieldsToLink) { LinkedInstance linkedInstance = field.getDeclaredAnnotation(LinkedInstance.class); if (linkedInstance == null) { continue; } Object object = objectMap.getOrDefault(field.getType(), null); Object source = objectMap.getOrDefault(field.getDeclaringClass(), null); try { field.setAccessible(true); field.set(source, object); } catch (IllegalAccessException e) { // Ignored } } } private void linkOrUnlink(Class clazz, boolean unlink) { Linked[] linkages = clazz.getDeclaredAnnotationsByType(Linked.class); if (linkages.length == 0) { return; } List linkageTypeList = Arrays.stream(linkages).filter(Objects::nonNull).map(Linked::value).filter(linkageType -> linkageType.getLinkagePredicate().test(clazz)).filter(linkageType -> linkageType.isUnlink() == unlink).collect(Collectors.toList()); if (linkageTypeList.isEmpty()) { return; } linkageTypeList.sort(Comparator.comparingInt(LinkageType::getOrder)); if (unlink) { Object object = objectMap.remove(clazz); if (object == null) { return; } linkageTypeList.forEach(linkageType -> linkageType.getLinkageConsumer().accept(object)); } else { Object object = objectMap.computeIfAbsent(clazz, LinkageUtils::constructInstance); linkageTypeList.forEach(linkageType -> linkageType.getLinkageConsumer().accept(object)); for (Field field : clazz.getDeclaredFields()) { LinkedInstance linkedInstance = field.getDeclaredAnnotation(LinkedInstance.class); if (linkedInstance == null) { continue; } fieldsToLink.add(field); } } } private Object constructInstance(Class clazz) { try { Constructor constructor = clazz.getDeclaredConstructor(); constructor.setAccessible(true); //NOSONAR return constructor.newInstance(); } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException | InstantiationException e) { throw new SecurityException(e.getMessage(), e.getCause()); } } }