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