VeLaEnvironment.java

/**
 * VStar: a statistical analysis tool for variable star data.
 * Copyright (C) 2010  AAVSO (http://www.aavso.org/)
 *
 * 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 <http://www.gnu.org/licenses/>. 
 */
package org.aavso.tools.vstar.vela;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Optional;
import java.util.Set;

/**
 * A generic symbol binding and lookup environment implementation,
 * the base class for all VeLa environments.
 */
public class VeLaEnvironment<T> {

    // Named operand cache
    protected Map<String, T> cache;

    // Constant binding map
    protected Set<String> constants;

    /**
     * Construct an environment with an empty name and constant set.
     */
    public VeLaEnvironment() {
        cache = new HashMap<String, T>();
        constants = new HashSet<String>();
    }

    /**
     * Construct an environment with a specified empty name and constant set.
     */
    public VeLaEnvironment(Map<String, T> map, Set<String> isBoundConstants) {
        cache = map;
        constants = isBoundConstants;
    }

    /**
     * Is this environment mutable (can be modified)?
     *
     * @return true if mutable, false if not
     */
    public boolean isMutable() {
        // default to not being mutable
        return false;
    }

    /**
     * Lookup the named symbol in the environment.
     * 
     * @param name The symbol's name.
     * @return An optional Operand instance if it exists.
     */
    public Optional<T> lookup(String name) {
        return Optional.ofNullable(cache.get(name.toUpperCase()));
    }

    /**
     * Does this environment contain the named binding?
     * 
     * @param name The name of the binding to lookup.
     * @return Whether the name is bound in this environment.
     * Less useful now, given the use of Optional values
     */
    @Deprecated 
    public boolean hasBinding(String name) {
        return cache.containsKey(name);
    }

    /**
     * Bind a value to a name.<br/>
     * It is an invariant that a constant binding cannot be overridden.
     * 
     * @param name       The name to which to bind the value.
     * @param value      The value to be bound.
     * @param isConstant Is this a constant binding?
     */
    public void bind(String name, T value, boolean isConstant) {
        boolean isBoundConstant = false;
        if (!constants.contains(name)) {
            if (!isConstant) {
                // Variable binding.
                cache.put(name, value);
            } else if (isConstant && !cache.containsKey(name)) {
                // Bind name to constant value.
                cache.put(name, value);
                constants.add(name);
            } else {
                // We are trying to bind a constant to a name in the presence
                // of a variable binding.
                isBoundConstant = true;
            }
        } else {
            isBoundConstant = true;
        }

        if (isBoundConstant) {
            throw new VeLaEvalError("'" + name + "' is a constant binding in this environment.");
        }
    }

    /**
     * Add all symbol bindings from another scope to this one.
     *
     * @param other The scope to be added to this one.
     */
    public void addAll(VeLaEnvironment<T> other) {
        cache.putAll(other.cache);
        constants.addAll(other.constants);
    }

    /**
     * Is this VeLa environment empty?
     */
    public boolean isEmpty() {
        return cache.isEmpty();
    }

    @Override
    public String toString() {
        StringBuffer buf = new StringBuffer();

        for (String name : cache.keySet()) {
            buf.append(" ");
            buf.append(name);
            buf.append(" = ");
            buf.append(cache.get(name));
            buf.append("\n");
        }

        return buf.toString();
    }
}