com.axelor.script.ScriptBindings.java Source code

Java tutorial

Introduction

Here is the source code for com.axelor.script.ScriptBindings.java

Source

/**
 * Axelor Business Solutions
 *
 * Copyright (C) 2005-2016 Axelor (<http://axelor.com>).
 *
 * This program is free software: you can redistribute it and/or  modify
 * it under the terms of the GNU Affero General Public License, version 3,
 * as published by the Free Software Foundation.
 *
 * 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 com.axelor.script;

import static com.axelor.common.StringUtils.isBlank;

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

import javax.script.SimpleBindings;

import org.joda.time.DateTime;
import org.joda.time.LocalDate;
import org.joda.time.LocalDateTime;

import com.axelor.app.AppSettings;
import com.axelor.auth.AuthUtils;
import com.axelor.db.EntityHelper;
import com.axelor.db.JPA;
import com.axelor.db.Model;
import com.axelor.inject.Beans;
import com.axelor.rpc.Context;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import com.google.common.primitives.Longs;

public class ScriptBindings extends SimpleBindings {

    private static final String MODEL_KEY = "_model";

    private static final Set<String> META_VARS = ImmutableSet.of("__id__", "__ids__", "__this__", "__self__",
            "__user__", "__ref__", "__parent__", "__date__", "__time__", "__datetime__", "__config__");

    private Map<String, Object> variables;
    private Map<String, Object> configContext;

    public ScriptBindings(Map<String, Object> variables) {
        this.variables = this.tryContext(variables);
    }

    private Map<String, Object> tryContext(Map<String, Object> variables) {
        if (variables instanceof Context) {
            return variables;
        }
        Class<?> klass = null;
        try {
            klass = Class.forName((String) variables.get(MODEL_KEY));
        } catch (NullPointerException | ClassNotFoundException e) {
            return variables;
        }
        return Context.create(variables, klass);
    }

    @SuppressWarnings("all")
    private Object getSpecial(String name) throws Exception {
        switch (name) {
        case "__id__":
            if (variables.containsKey("id"))
                return Longs.tryParse(variables.get("id").toString());
            if (variables.containsKey("_id"))
                return Longs.tryParse(variables.get("_id").toString());
            return ((Context) variables).asType(Model.class).getId();
        case "__ids__":
            return variables.get("_ids");
        case "__this__":
            return ((Context) variables).asType(Model.class);
        case "__parent__":
            return ((Context) variables).getParentContext();
        case "__date__":
            return new LocalDate();
        case "__time__":
            return new LocalDateTime();
        case "__datetime__":
            return new DateTime();
        case "__config__":
            if (configContext == null) {
                configContext = new ConfigContext();
            }
            return configContext;
        case "__user__":
            return AuthUtils.getUser();
        case "__self__":
            Model bean = ((Context) variables).asType(Model.class);
            if (bean == null || bean.getId() == null)
                return null;
            return JPA.em().getReference(EntityHelper.getEntityClass(bean), bean.getId());
        case "__ref__":
            Map values = (Map) variables.get("_ref");
            Class<?> klass = Class.forName((String) values.get("_model"));
            return JPA.em().getReference(klass, Long.parseLong(values.get("id").toString()));
        }
        return null;
    }

    public <T> T asType(Class<T> type) {
        Preconditions.checkState(variables instanceof Context,
                "Invalid binding, only Context bindings can be converted.");
        return ((Context) variables).asType(type);
    }

    @Override
    public boolean containsKey(Object key) {
        return META_VARS.contains(key) || super.containsKey(key) || variables.containsKey(key);
    }

    @Override
    public Set<String> keySet() {
        Set<String> keys = Sets.newHashSet(super.keySet());
        keys.addAll(variables.keySet());
        keys.addAll(META_VARS);
        return keys;
    }

    @Override
    public Object get(Object key) {

        if (super.containsKey(key)) {
            return super.get(key);
        }

        if (variables.containsKey(key)) {
            return variables.get(key);
        }

        if (META_VARS.contains(key)) {
            String name = key.toString();
            Object value = null;
            try {
                value = getSpecial(name);
            } catch (Exception e) {
            }
            super.put(name, value);
            return value;
        }

        return null;
    }

    @Override
    public Object put(String name, Object value) {
        if (META_VARS.contains(name) || name.startsWith("$") || name.startsWith("__")) {
            return super.put(name, value);
        }
        return variables.put(name, value);
    }

    @Override
    public void putAll(Map<? extends String, ? extends Object> toMerge) {
        final Map<String, Object> values = Maps.newHashMap();
        for (String name : toMerge.keySet()) {
            Object value = toMerge.get(name);
            if (META_VARS.contains(name)) {
                this.put(name, value);
            } else {
                values.put(name, values);
            }
        }
        variables.putAll(values);
    }

    @SuppressWarnings("serial")
    private static class ConfigContext extends HashMap<String, Object> {

        private static Map<String, String> CONFIG;
        private Map<String, Object> values = new HashMap<>();

        public ConfigContext() {
            if (CONFIG == null) {
                CONFIG = new HashMap<>();
                final Properties properties = AppSettings.get().getProperties();
                for (final Object item : properties.keySet()) {
                    final String name = item.toString();
                    final String expr = properties.getProperty(name);
                    if (!name.startsWith("context.") || isBlank(expr)) {
                        continue;
                    }
                    CONFIG.put(name.substring(8), expr);
                }
            }
        }

        @Override
        public Set<String> keySet() {
            return CONFIG.keySet();
        }

        @Override
        public boolean containsKey(Object key) {
            return CONFIG.containsKey(key);
        }

        @Override
        public Object get(Object key) {

            if (values.containsKey(key) || !containsKey(key)) {
                return values.get(key);
            }

            final String name = (String) key;
            final String expr = CONFIG.get(key);
            final String[] parts = expr.split("\\:", 2);
            final Object invalid = new Object();

            Class<?> klass = null;
            Object value = invalid;

            try {
                klass = Class.forName(parts[0]);
            } catch (ClassNotFoundException e) {
            }

            if (klass == null) {
                value = adapt(expr);
                values.put(name, value);
                return value;
            }

            try {
                value = klass.getField(parts[1]).get(null);
            } catch (Exception e) {
            }
            try {
                value = klass.getMethod(parts[1]).invoke(null);
            } catch (Exception e) {
            }

            if (value != invalid) {
                values.put(name, value);
                return value;
            }

            final Object instance = Beans.get(klass);

            if (parts.length == 1) {
                value = instance;
                values.put(name, value);
                return value;
            }

            try {
                value = klass.getMethod(parts[1]).invoke(instance);
            } catch (Exception e) {
            }

            if (value == invalid) {
                throw new RuntimeException("Invalid configuration: " + name + " = " + expr);
            }

            values.put(name, value);
            return value;
        }

        private Object adapt(String value) {
            if (isBlank(value)) {
                return null;
            }
            if ("true".equals(value.toLowerCase())) {
                return true;
            }
            if ("false".equals(value.toLowerCase())) {
                return false;
            }
            try {
                return Integer.parseInt(value);
            } catch (Exception e) {
            }
            return value;
        }
    }
}