kenh.xscript.Environment.java Source code

Java tutorial

Introduction

Here is the source code for kenh.xscript.Environment.java

Source

/*
 * xScript (XML Script Language)
 * Copyright 2015 and beyond, Kenneth Huang
 * 
 * This file is part of xScript.
 * 
 * xScript is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License version 3
 * as published by the Free Software Foundation.
 * 
 * xScript 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 Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public
 * License along with xScript.  If not, see <http://www.gnu.org/licenses/>. 
 */

package kenh.xscript;

import java.util.*;

import kenh.expl.Parser;
import org.apache.commons.lang3.StringUtils;

/**
 * The subclass of ExpL's environment, to support xScript.
 * 
 * @author Kenneth
 * @since 1.0
 *
 */
public class Environment extends kenh.expl.Environment {

    /**
     * The element package loaded by system properties should use this prefix.
     */
    private static final String ELEMENTS_PATH_PREFIX = "kenh.xscript.element.packages";

    /**
     * All element packages where xScript find the <code>Element</code>.
     */
    private Map<String, String> elementPackages = Collections.synchronizedMap(new LinkedHashMap());

    /**
     * The variable name of method elements. 
     * It lists all methods element.
     */
    private static final String KEY_METHODS = "{methods}";

    /**
     * The variable name of public variables. 
     * It lists all public variables ( the variables have public scope ).
     */
    private static final String KEY_PUBLIC = "{public}";

    /**
     * The variable name of constant variables.
     * It lists all constants ( can't change the value ).
     */
    private static final String KEY_CONSTANT = "{constant}";

    /**
     * Constructor
     */
    public Environment() {
        this(null);
    }

    /**
     * Constructor
     * @param parser
     */
    public Environment(Parser parser) {
        super(parser);

        setElementPackage("xScript", "kenh.xscript.elements"); // the base package for elements
        setFunctionPackage("xScript", "kenh.xscript.functions"); // the function package of xScript

        loadElementPackages_SystemProperties();
        loadElementPackages_Extension();

        initial();
    }

    /**
     * Initial method
     */
    private void initial() {

        // 
        Map<String, Element> methods = Collections.synchronizedMap(new LinkedHashMap());
        this.setVariable(KEY_METHODS, methods);

        // public
        List<String> publics = Collections.synchronizedList(new LinkedList());
        this.setVariable(KEY_PUBLIC, publics);

        // 
        List<String> constant = Collections.synchronizedList(new LinkedList());
        this.setVariable(KEY_CONSTANT, constant);

        constant.add(KEY_METHODS);
        constant.add(KEY_PUBLIC);
        constant.add(KEY_CONSTANT);

        publics.add(KEY_METHODS);
        publics.add(KEY_PUBLIC);
        publics.add(KEY_CONSTANT);

    }

    /**
     * Retrieves the constants.
     * @return
     */
    public List<String> getContants() {
        Object obj = getVariable(KEY_CONSTANT);
        try {
            return (List<String>) obj;
        } catch (Exception e) {
            return new LinkedList<String>();
        }
    }

    /**
     * Retrieves the public variables.
     * @return
     */
    public List<String> getPublics() {
        Object obj = getVariable(KEY_PUBLIC);
        try {
            return (List<String>) obj;
        } catch (Exception e) {
            return new LinkedList<String>();
        }
    }

    /**
     * Retrieves all method elements
     * @return
     */
    public Map<String, Element> getMethods() {
        Object obj = getVariable(KEY_METHODS);
        try {
            return (Map<String, Element>) obj;
        } catch (Exception e) {
            return new LinkedHashMap<String, Element>();
        }
    }

    /**
     * Load element packages from system properties.
     * If system property has name starts with <code>kenh.xscript.element.packages</code>,
     * it will be loaded by xScript.
     */
    private void loadElementPackages_SystemProperties() {
        Properties p = System.getProperties();
        Set keys = p.keySet();
        for (Object key_ : keys) {
            if (key_ instanceof String) {
                String key = (String) key_;
                if (StringUtils.startsWith(key, ELEMENTS_PATH_PREFIX + ".")) {
                    String name = StringUtils.substringAfter(key, ELEMENTS_PATH_PREFIX + ".");
                    String funcPackage = p.getProperty(key);
                    setElementPackage(name, funcPackage);
                }
            }
        }
    }

    /**
     * Load element packages through <code>Extension</code>.
     */
    private void loadElementPackages_Extension() {
        ServiceLoader<Extension> es = ServiceLoader.load(Extension.class);
        for (Extension e : es) {
            if (e != null) {
                Map<String, String> p = e.getElementPackages();
                if (p != null && p.size() > 0) {
                    Set<String> keys = p.keySet();
                    for (String key : keys) {
                        String elementPackage = p.get(key);
                        setElementPackage(key, elementPackage);
                    }
                }
            }
        }
    }

    /**
     * Adds a new element package.
     * @param nameSpace  the name of element package
     * @param elementPackage
     * @return
     */
    public boolean setElementPackage(String nameSpace, String elementPackage) {
        if (StringUtils.isBlank(nameSpace))
            return false;
        if (StringUtils.isBlank(elementPackage))
            return false;

        if (elementPackages.containsKey(nameSpace)) {
            String p = elementPackages.get(nameSpace);
            if (p.equals(elementPackage))
                return true;
            else
                return false;

        } else {
            elementPackages.put(nameSpace, elementPackage);
            return true;
        }
    }

    /**
     * Initials an element through element package and element name.
     * @param elementPackage
     * @param elementName
     * @return
     */
    private Element getElement_(String elementPackage, String elementName) {
        if (StringUtils.isBlank(elementPackage))
            return null;
        if (StringUtils.isBlank(elementName))
            return null;

        try {
            if (StringUtils.isNotBlank(elementPackage)) {
                Element element = (Element) Class
                        .forName(elementPackage + "." + StringUtils.capitalize(elementName)).newInstance();
                return element;
            }
        } catch (Exception e) {
        }
        return null;
    }

    /**
     * Initials an element.
     * This mehtod will loop all element packages, 
     * retrieves the first element that matching the name.
     * @param elementName
     * @return
     */
    public Element getElement(String elementName) {
        if (StringUtils.isBlank(elementName))
            return null;

        Iterator<String> iterator = elementPackages.values().iterator();
        while (iterator.hasNext()) {
            String elementPackage = iterator.next();
            Element element = getElement_(elementPackage, elementName);
            if (element != null)
                return element;
        }
        return null;

    }

    /**
     * Initials an element by the name space of element.
     * @param elementNS
     * @param elementName
     * @return
     */
    public Element getElement(String elementNS, String elementName) {
        if (StringUtils.isBlank(elementName))
            return null;

        if (StringUtils.isNotBlank(elementNS)) {
            if (elementPackages.containsKey(elementNS)) {
                return getElement_(elementPackages.get(elementNS), elementName);
            } else {
                // use name space
                return getElement_(elementNS, elementName);
            }
        } else {
            return getElement(elementName);
        }

    }

    @Override
    public Object removeVariable(String key) {
        return removeVariable(key, true);
    }

    /**
     * Constant variable can't be removed.
     */
    @Override
    public Object removeVariable(String key, boolean callback) {
        List<String> constant = this.getContants();
        if (constant != null && constant.contains(key))
            throw new UnsupportedOperationException("[" + key + "] is a constant.");

        Object obj = super.removeVariable(key, callback);

        List<String> publics = this.getPublics();
        if (publics.contains(key)) {
            publics.remove(key);
            logger.trace("Remove public factor: " + key);
        }

        return obj;
    }

    /**
     * Adds a constant.
     * @param key
     * @param obj
     * @param isConstant  is constant variable or nor.
     */
    public void setVariable(String key, Object obj, boolean isConstant) throws UnsupportedOperationException {
        this.setVariable(key, obj);

        if (isConstant) {
            List<String> constant = (List<String>) this.getVariable(KEY_CONSTANT);
            if (!constant.contains(key)) {
                constant.add(key);
                logger.trace("Add constant factor: " + key);
            }
        }
    }

    /**
     * Adds a public variable.
     * @param key
     * @param obj
     * @param isConstant  is constant variable or nor.
     */
    public void setPublicVariable(String key, Object obj, boolean isConstant) throws UnsupportedOperationException {
        this.setVariable(key, obj);

        List<String> publics = (List<String>) this.getVariable(KEY_PUBLIC);
        if (!publics.contains(key)) {
            publics.add(key);
            logger.trace("Add public factor: " + key);
        }

        if (isConstant) {
            List<String> constant = (List<String>) this.getVariable(KEY_CONSTANT);
            if (!constant.contains(key)) {
                constant.add(key);
                logger.trace("Add constant factor: " + key);
            }
        }
    }

    /**
     * Constant variable can't change.
     */
    @Override
    public void setVariable(String key, Object obj) throws UnsupportedOperationException {
        List<String> constant = (List<String>) this.getVariable(KEY_CONSTANT);
        if (constant != null && constant.contains(key))
            throw new UnsupportedOperationException("[" + key + "] is a constant.");

        super.setVariable(key, obj);
    }

    /**
     * Check variable is public variable or not.
     * @param key
     * @return
     */
    public boolean isPublicVariable(String key) {
        List<String> list = getPublics();
        return list.contains(key);
    }

    /**
     * Check variable is constant or not.
     * @param key
     * @return
     */
    public boolean isConstant(String key) {
        List<String> list = this.getContants();
        return list.contains(key);
    }

    @Override
    public void callback() {
        super.callback();

        initial();
    }

}