001/*
002 * Copyright (c) 2012-2018 Institut National des Sciences Appliquées de Lyon (INSA Lyon) and others
003 *
004 * This program and the accompanying materials are made available under the
005 * terms of the Eclipse Public License 2.0 which is available at
006 * http://www.eclipse.org/legal/epl-2.0.
007 *
008 * SPDX-License-Identifier: EPL-2.0
009 */
010
011package gololang;
012
013/**
014 * A dynamic variable has the semantics of an inheritable thread-local reference.
015 * This class is modeled after the eponymous class from the Scala standard library.
016 *
017 * @see java.lang.InheritableThreadLocal
018 */
019public final class DynamicVariable {
020
021  private final InheritableThreadLocal<Object> threadLocal;
022
023  /**
024   * Creates a new dynamic variable with an initial value.
025   *
026   * @param init the initial value.
027   */
028  public DynamicVariable(final Object init) {
029    super();
030    threadLocal = new InheritableThreadLocal<Object>() {
031      @Override
032      protected Object initialValue() {
033        return init;
034      }
035    };
036  }
037
038  /**
039   * Returns the thread-local value of the dynamic variable.
040   *
041   * @return the value.
042   */
043  public Object value() {
044    return threadLocal.get();
045  }
046
047  /**
048   * Changes the dynamic variable value. The new value is only visible from the calling thread, and will be seen by
049   * future child threads.
050   *
051   * @param value the new thread-local value.
052   * @return this dynamic variable.
053   */
054  public DynamicVariable value(Object value) {
055    threadLocal.set(value);
056    return this;
057  }
058
059  /**
060   * Given a value, calls a function {@code func}. The previous value is put pack as the dynamic variable value
061   * once {@code func} has completed.
062   *
063   * @param value the value for the course of the execution of {@code func}.
064   * @param func a 0-arity function.
065   * @return the result of the call to {@code func}.
066   * @throws Throwable in case an exception occurs.
067   */
068  public Object withValue(Object value, FunctionReference func) throws Throwable {
069    if (!func.acceptArity(0)) {
070      throw new IllegalArgumentException("withValue requires a function with no parameters");
071    }
072    Object oldValue = value();
073    this.value(value);
074    try {
075      return func.invoke();
076    } finally {
077      this.value(oldValue);
078    }
079  }
080
081  @Override
082  public String toString() {
083    return String.format("DynamicVariable{value=%s}", value());
084  }
085}