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

reflex.Scope Maven / Gradle / Ivy

There is a newer version: 3.0.4
Show newest version
/**
 * The MIT License (MIT)
 *
 * Copyright (c) 2011-2016 Incapture Technologies LLC
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */
package reflex;

import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

import com.fasterxml.jackson.annotation.JsonIgnore;

import reflex.value.ReflexValue;

public class Scope {

    private boolean assignOnce = false;
    @SuppressWarnings("unused")
    private String scopeName = "general";
    private Scope parent;
    @JsonIgnore
    private Scope globalScope;
    @JsonIgnore
    private Scope constantScope;
    private String pendingComment = null;

    private Map variables;

    public Scope() {

    }

    // NB. Added this back in as the variables are retrieved in the NetBeans
    // REPL debugger
    public Map retrieveVariables() {
        return variables;
    }

    public static Scope defrostScope(Scope globalScope, Scope constantScope, Scope myScope) {
        Scope me = new Scope(null, true);
        me.constantScope = constantScope;
        me.globalScope = globalScope;
        me.variables = myScope.variables == null ? new HashMap() : myScope.variables;
        return me;
    }

    public static Scope getInitialScope() {
        Scope constScope = new Scope(null, true);
        constScope.scopeName = "Constant";
        Scope globalScope = new Scope(constScope, false);
        globalScope.scopeName = "Global";
        constScope.globalScope = globalScope;
        constScope.constantScope = constScope;
        globalScope.globalScope = globalScope;
        globalScope.constantScope = constScope;
        return globalScope;
    }

    /**
     * Creates a {@link Scope} that does not contain any of the variables inside Scope, but still knows about the global and constant scopes
     *
     * @param seedScope The {@link Scope} that has the global and constant scopes we want to use
     * @return
     */
    public static Scope createIsolatedScope(Scope seedScope) {
        Scope newScope = new Scope(seedScope.globalScope, false);
        newScope.constantScope = seedScope.constantScope;
        newScope.globalScope = seedScope.globalScope;
        return newScope;
    }

    public static Scope getNextScopeDown(Scope scope) {
        Scope ret = new Scope(scope);
        return ret;
    }

    private Scope(Scope parent, boolean assignOnce) {
        this.parent = parent;
        this.assignOnce = assignOnce;
        variables = new HashMap();
    }

    protected Scope(Scope p) {
        parent = p;
        variables = new HashMap();
        globalScope = p.globalScope;
        constantScope = p.constantScope;
    }

    public void assign(String var, ReflexValue value) {
        assign(var, value, "");
    }

    public void assign(String var, ReflexValue value, String namespacePrefix) {
        if (resolve(var, namespacePrefix) != null) {
            // There is already such a variable, re-assign it
            if (assignOnce) {
                throw new ReflexException(0, "Assignment into constant variable");
            }
            this.reAssign(var, value);
        } else {
            // Here, if we have a.b = value we really want to create "a" which is a map
            // and then create a key "b" in "a" that has the value given.
            // (but we should check for existence of a first)
            if (!tryDottedAssign(var, value, namespacePrefix)) {
                if (assignOnce) {
                    // A new constant
                    variables.put(namespacePrefix + var, value);
                } else {
                    // A newly declared variable
                    variables.put(var, value);
                }
            }
        }
    }

    private boolean tryDottedAssign(String var, ReflexValue value, String namespacePrefix) {
        if (var.indexOf('.') == -1) {
            return false;
        }
        String[] parts = var.split("\\.");
        ReflexValue existingValue = resolve(parts[0], namespacePrefix);
        if (existingValue == null) {
            existingValue = new ReflexValue(new HashMap());
            variables.put(parts[0], existingValue);
        }
        if (existingValue.isMap()) {
            return dottedAssignMap(existingValue, parts, value);
        } else if (existingValue.isStruct()) {
            return existingValue.asStruct().dottedAssign(parts, value);
        } else {
            throw new ReflexException(0, "Cannot assign in this way to a non-map");
        }

    }

    private boolean dottedAssignMap(ReflexValue existingValue, String[] parts, ReflexValue value) {
        Map mapper = existingValue.asMap();
        for (int i = 1; i < parts.length - 1; i++) {
            if (mapper.containsKey(parts[i])) {
                Object val = mapper.get(parts[i]);
                if (val instanceof ReflexValue) {
                    existingValue = (ReflexValue) val;
                    if (existingValue.isMap()) {
                        mapper = existingValue.asMap();
                    } else {
                        throw new ReflexException(0, "Cannot assign in this way to a part of a map that is not itself a map");
                    }
                } else if (val instanceof Map) {
                    mapper = (Map) val;
                }
            } else {
                if (i != (parts.length - 1)) {
                    StringBuilder sb = new StringBuilder();
                    sb.append(parts[0]);
                    for (int j = 1; j <= i; j++) {
                        sb.append(".").append(parts[j]);
                    }
                    throw new ReflexException(0, "Cannot assign a value to "+sb.toString()+" because it is not declared as a map ");
                }
                mapper.put(parts[i], new HashMap());
                mapper = (Map) mapper.get(parts[i]);
            }
        }

        mapper.put(parts[parts.length - 1], value);
        return true;
    }

    public void clearScope() {
        variables = new HashMap();
    }

    public Scope copy() {
        // Create a shallow copy of this scope. Used in case functions are
        // are recursively called. If we wouldn't create a copy in such cases,
        // changing the variables would result in changes to the Maps from
        // other "recursive scopes".
        return Scope.createShallowCopy(this);
    }

    public static Scope createShallowCopy(Scope s) {
        Scope ret = new Scope(s);
        ret.parent = null;
        ret.variables = new HashMap(s.variables);
        return ret;
    }

    public Scope getParent() {
        return parent;
    }

    public Set> retrieveVariableSet() {
        return variables.entrySet();
    }

    public Scope parent() {
        return parent;
    }

    private void reAssign(String identifier, ReflexValue value) {
        if (variables.containsKey(identifier)) {
            // The variable is declared in this scope
            // IF the variable exists, look at the type, because we might want to coerce the assignment
            // variables.get(identifier).reassign(value);
            variables.put(identifier, value);
        } else if (parent != null) {
            // The variable was not declared in this scope, so let
            // the parent scope re-assign it
            parent.reAssign(identifier, value);
        }
    }

    public ReflexValue resolve(String var) {
        return resolve(var, "");
    }

    public ReflexValue resolve(String var, String namespacePrefix) {
        ReflexValue value;
        if (assignOnce) { // constants always contain a prefix, if there is any
            value = variables.get(namespacePrefix + var);
        } else {
            value = variables.get(var);
        }

        if (value != null) {
            // The variable resides in this scope
            return value;
        } else if (parent != null) {
            // Let the parent scope look for the variable
            return parent.resolve(var, namespacePrefix);
        } else {
            // Unknown variable
            return null;
        }
    }

    public String getAndUsePendingComment() {
        if (pendingComment != null) {
            String ret = pendingComment;
            pendingComment = null;
            return ret;
        }
        return null;
    }

    public String setPendingComment(String comment) {
        String ret = pendingComment;
        pendingComment = comment;
        return ret;
    }

    public Scope getConstantScope() {
        return constantScope;
    }

    public Scope getGlobalScope() {
        return globalScope;
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy