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

dev.denwav.hypo.mappings.ChangeChain Maven / Gradle / Ivy

The newest version!
/*
 * Hypo, an extensible and pluggable Java bytecode analytical model.
 *
 * Copyright (C) 2023  Kyle Wood (DenWav)
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the Lesser GNU General Public License as published by
 * the Free Software Foundation, version 3 of the License only.
 *
 * 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 Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program.  If not, see .
 */

package dev.denwav.hypo.mappings;

import dev.denwav.hypo.core.HypoException;
import dev.denwav.hypo.mappings.contributors.ChangeContributor;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import org.cadixdev.lorenz.MappingSet;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

/**
 * Chain of {@link ChangeContributor} application steps (called "links" here to complete the metaphor) to be executed
 * one after the other, rather than together.
 *
 * 

Each link may have multiple {@link ChangeContributor contributors}, and there may be an arbitrary number of links * as well. Any link which has multiple contributors defined should make sure the contributors in that link will never * produce conflicting mappings changes. * *

Use {@link #create()} to create a new instance of this class. * {@link #applyChain(MappingSet, MappingsCompletionManager)} will apply each link one at a time and return the final * {@link MappingSet}. Listeners may be registered to respond to events during the chain process with * {@link #addLinkCompletedListener(LinkCompletionListener)} and {@link #addMappingSetListener(LinkMappingsListener)}. * * @see MappingsCompletionManager * @see ChangeRegistry */ public final class ChangeChain { private final @NotNull List> links = new ArrayList<>(); private @Nullable LinkCompletionListener completedLinkListener = null; private @Nullable LinkMappingsListener newMappingSetListener = null; private ChangeChain() {} /** * Create a new instance of {@link ChangeChain}. * * @return The new instance of {@link ChangeChain}. */ @Contract(value = "-> new", pure = true) public static @NotNull ChangeChain create() { return new ChangeChain(); } /** * Add a link to this chain with the list of {@link ChangeContributor change contributors} to run in the link. * * @param changes The list of contributors to run for this link. * @return {@code this} for chaining. */ @Contract("_ -> this") public @NotNull ChangeChain addLink(final @NotNull List changes) { this.links.add(changes); return this; } /** * Add a link to this chain with a single {@link ChangeContributor change contributor} to run in the link. * * @param change The contributor to run for this link. * @return {@code this} for chaining. */ @Contract("_ -> this") public @NotNull ChangeChain addLink(final @NotNull ChangeContributor change) { this.links.add(Collections.singletonList(change)); return this; } /** * Add a link to this chain with the list of {@link ChangeContributor change contributors} to run in the link. * * @param changes The contributors to run for this link. * @return {@code this} for chaining. */ @Contract("_ -> this") public @NotNull ChangeChain addLink(final @NotNull ChangeContributor @NotNull ... changes) { this.links.add(Arrays.asList(changes)); return this; } /** * Add a listener which will be called at the end of each link, but before the {@link ChangeRegistry} is applied to * the current {@link MappingSet}. * *

Only a single link completion listener may be registered at a time, calling this method multiple times will * overwrite the previous setting. * * @param listener The listener to run on the completion of a link before the changes are applied to the * {@link MappingSet}. * @return {@code this} for chaining. * @see #addMappingSetListener(LinkMappingsListener) */ @Contract("_ -> this") public @NotNull ChangeChain addLinkCompletedListener(final @NotNull LinkCompletionListener listener) { this.completedLinkListener = listener; return this; } /** * Add a listener which will be called after the changes in each link have been applied to the {@link MappingSet}. * *

Only a single mapping set listener may be registered at a time, calling this method multiple times will * overwrite the previous setting. * * @param listener The listener to run after the {@link MappingSet} for each link has been modified. * @return {@code this} for chaining. * @see #addLinkCompletedListener(LinkCompletionListener) */ @Contract("_ -> this") public @NotNull ChangeChain addMappingSetListener(final @NotNull LinkMappingsListener listener) { this.newMappingSetListener = listener; return this; } /** * Apply the links registered in this chain to the given {@link MappingSet} for the given * {@link MappingsCompletionManager}. The given {@link MappingSet} will not be modified. * * @param mappings The mappings to apply the chain to. * @param manager The completion manager to use for applying each link. * @return The modified {@link MappingSet} returned from the final ink in this chain. * @throws HypoException If the {@link MappingsCompletionManager} fails to complete the mappings for one of the * links. */ public @NotNull MappingSet applyChain( final @NotNull MappingSet mappings, final @NotNull MappingsCompletionManager manager ) throws HypoException { final LinkCompletionListener linkListener = this.completedLinkListener; final LinkMappingsListener mappingListener = this.newMappingSetListener; int counter = 0; MappingSet currentMappings = mappings; for (final List link : this.links) { final ChangeRegistry registry = manager.completeMappings(currentMappings, link); if (linkListener != null) { linkListener.accept(counter, currentMappings, registry); } currentMappings = registry.applyChanges(currentMappings); if (mappingListener != null) { mappingListener.accept(counter, currentMappings); } counter++; } return currentMappings; } /** * Functional interface for {@link #addLinkCompletedListener(LinkCompletionListener)}. */ @FunctionalInterface public interface LinkCompletionListener { /** * Method which will be called at the end of each link. * * @param index The link index for this change, starting at {@code 0}. * @param mappingSet The current {@link MappingSet}, before changes have been applied. This mapping set will not * be modified, instead it will be {@link MappingSet#copy() copied} and the changes will be * applied to the copy. * @param registry The {@link ChangeRegistry} with the changes which are about to be applied to the * {@code mappingSet}. */ void accept(final int index, final @NotNull MappingSet mappingSet, final @NotNull ChangeRegistry registry); } /** * Functional interface for {@link #addMappingSetListener(LinkMappingsListener)}. */ @FunctionalInterface public interface LinkMappingsListener { /** * Method which will be called after the {@link MappingSet} has been modified for each link. * * @param index The link index for this change, starting at {@code 0}. * @param mappingSet The new {@link MappingSet} after changes were applied. */ void accept(final int index, final @NotNull MappingSet mappingSet); } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy