001/*
002 * JDrupes Builder
003 * Copyright (C) 2026 Michael N. Lipp
004 * 
005 * This program is free software: you can redistribute it and/or modify
006 * it under the terms of the GNU Affero General Public License as
007 * published by the Free Software Foundation, either version 3 of the
008 * License, or (at your option) any later version.
009 *
010 * This program is distributed in the hope that it will be useful,
011 * but WITHOUT ANY WARRANTY; without even the implied warranty of
012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
013 * GNU Affero General Public License for more details.
014 *
015 * You should have received a copy of the GNU Affero General Public License
016 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
017 */
018
019package org.jdrupes.builder.core;
020
021import java.util.ArrayList;
022import java.util.List;
023import java.util.concurrent.Callable;
024import java.util.concurrent.CopyOnWriteArrayList;
025import java.util.concurrent.ExecutorService;
026import java.util.concurrent.Future;
027
028/// Supports inheriting scoped values in a task submitted to an
029/// executor service.
030///
031public final class ScopedValueInheritance {
032
033    private static List<ScopedValue<?>> registry = new CopyOnWriteArrayList<>();
034
035    private ScopedValueInheritance() {
036        // Make javadoc happy
037    }
038
039    /// Adds the value to the registry.
040    ///
041    /// @param value the value
042    ///
043    public static void add(ScopedValue<?> value) {
044        registry.add(value);
045    }
046
047    /// Executes the task with the registered scoped values inherited
048    /// from the current thread.
049    ///
050    /// @param <T> the generic type
051    /// @param executor the executor
052    /// @param task the task
053    /// @return the future
054    ///
055    @SuppressWarnings("unchecked")
056    public static <T> Future<T> submitTo(ExecutorService executor,
057            Callable<T> task) {
058        // Capture values
059        final var scoped = new ArrayList<>(registry);
060        final var values = new ArrayList<>(scoped.size());
061        for (var scopedVar : scoped) {
062            values.add(scopedVar.isBound() ? scopedVar.get() : null);
063        }
064
065        // Restore values in submitted task
066        return executor.submit(() -> {
067            var scopedIterator = scoped.iterator();
068            var valuesIterator = values.iterator();
069            ScopedValue.Carrier carriers = null;
070            if (scopedIterator.hasNext()) {
071                carriers = ScopedValue.where(
072                    (ScopedValue<Object>) scopedIterator.next(),
073                    valuesIterator.next());
074            }
075            while (scopedIterator.hasNext()) {
076                carriers = carriers.where(
077                    (ScopedValue<Object>) scopedIterator.next(),
078                    valuesIterator.next());
079            }
080            if (carriers == null) {
081                return task.call();
082            }
083            return carriers.call(task::call);
084        });
085    }
086}