com.anrisoftware.sscontrol.core.groovy.statementstable.StatementsTable.java Source code

Java tutorial

Introduction

Here is the source code for com.anrisoftware.sscontrol.core.groovy.statementstable.StatementsTable.java

Source

/*
 * Copyright 2013-2015 Erwin Mller <erwin.mueller@deventm.org>
 *
 * This file is part of sscontrol-core.
 *
 * sscontrol-core 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.
 *
 * sscontrol-core 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 sscontrol-core. If not, see <http://www.gnu.org/licenses/>.
 */
package com.anrisoftware.sscontrol.core.groovy.statementstable;

import java.io.Serializable;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

import javax.inject.Inject;

import org.apache.commons.lang3.builder.ToStringBuilder;
import org.codehaus.groovy.runtime.InvokerHelper;

import com.anrisoftware.sscontrol.core.groovy.statementsmap.StatementsException;
import com.anrisoftware.sscontrol.core.listproperty.PropertyToListFactory;
import com.google.inject.assistedinject.Assisted;

/**
 * Stored allowed script statements as table.
 * <p>
 *
 * <pre>
 * public Object methodMissing(String name, Object args)
 *         throws StatementsException {
 *     statementsMap.methodMissing(name, args);
 *     return null;
 * }
 *
 * </pre>
 *
 * @author Erwin Mueller, erwin.mueller@deventm.org
 * @since 1.0
 */
@SuppressWarnings("serial")
public class StatementsTable implements Serializable {

    static final String NAME_KEY = "name";

    private static final String ARBITRARY_KEY = "_arbitrary";

    private static final String SERVICE = "service";

    private final Object service;

    private final String name;

    private final Map<String, Map<String, Map<String, Object>>> args;

    private final Map<String, Set<String>> allowed;

    @Inject
    private StatementsTableLogger log;

    @Inject
    private PropertyToListFactory toListFactory;

    /**
     * @see StatementsTableFactory#create(Object, String)
     */
    @Inject
    StatementsTable(@Assisted Object service, @Assisted String name) {
        this.service = service;
        this.name = name;
        this.args = new HashMap<String, Map<String, Map<String, Object>>>();
        this.allowed = new HashMap<String, Set<String>>();
    }

    /**
     * Adds allowed script statements.
     *
     * <pre>
     * statement "value", key: "value"
     * </pre>
     *
     * @param names
     *            the array with script statement {@link String} names.
     */
    public void addAllowed(String... names) {
        for (String name : names) {
            allowed.put(name, new HashSet<String>());
        }
    }

    /**
     * Adds allowed script statements.
     *
     * <pre>
     * statement "value", key: "value"
     * </pre>
     *
     * @param names
     *            the array with script statement {@link Enum} names.
     */
    public void addAllowed(Enum<?>... names) {
        addAllowed(convert(names));
    }

    /**
     * Adds allowed statement keys.
     *
     * <pre>
     * statement "name", key: "value"
     * </pre>
     *
     * @param name
     *            the statement {@link String} name.
     *
     * @param keys
     *            the array with allowed {@link String} keys.
     *
     * @see #mapValue(String, String)
     */
    public void addAllowedKeys(String name, String... keys) {
        Set<String> set = allowed.get(name);
        set.addAll(Arrays.asList(keys));
    }

    /**
     * Adds allowed statement keys.
     *
     * <pre>
     * statement "name", key: "value"
     * </pre>
     *
     * @param name
     *            the statement {@link Enum} name.
     *
     * @param keys
     *            the array with allowed {@link Enum} keys.
     *
     * @see #mapValue(String, String)
     */
    public void addAllowedKeys(Enum<?> name, Enum<?>... keys) {
        addAllowedKeys(name.toString(), convert(keys));
    }

    /**
     * Set allow arbitrary keys for the statements.
     *
     * <pre>
     * statement "name", foo: "value"
     * statement "name", bar: "value"
     * </pre>
     *
     * @param allow
     *            set to {@code true} to allow arbitrary keys.
     *
     * @param names
     *            the array with statement {@link String} names.
     *
     * @see #mapValue(String, String)
     */
    public void setAllowArbitraryKeys(boolean allow, String... names) {
        if (allow) {
            for (String name : names) {
                addAllowedKeys(name, ARBITRARY_KEY);
            }
        }
    }

    /**
     * Set allow arbitrary keys for the statements.
     *
     * <pre>
     * statement "name", foo: "value"
     * statement "name", bar: "value"
     * </pre>
     *
     * @param allow
     *            set to {@code true} to allow arbitrary keys.
     *
     * @param names
     *            the array with statement {@link Enum} names.
     *
     * @see #mapValue(String, String)
     */
    public void setAllowArbitraryKeys(boolean allow, Enum<?>... names) {
        setAllowArbitraryKeys(allow, convert(names));
    }

    /**
     * Returns the statement value with the specified name.
     * <p>
     *
     * The following statement returns ["foo", "bar"]:
     *
     * <pre>
     * statement "foo"
     * statement "bar"
     * </pre>
     *
     * @param name
     *            the {@link String} name.
     *
     * @return the {@link Set} of values or {@code null}.
     */
    @SuppressWarnings("unchecked")
    public <T> Set<T> tableValues(String name) {
        Map<String, Map<String, Object>> map = args.get(name);
        if (map != null) {
            return (Set<T>) map.keySet();
        } else {
            return null;
        }
    }

    /**
     * Returns the statement value with the specified name.
     * <p>
     *
     * The following statement returns ["foo", "bar"]:
     *
     * <pre>
     * statement "foo"
     * statement "bar"
     * </pre>
     *
     * @param name
     *            the {@link Enum} name.
     *
     * @return the {@link Set} of values or {@code null}.
     */
    public <T> Set<T> tableValues(Enum<?> name) {
        return tableValues(name.toString());
    }

    /**
     * Returns the statement table values with the specified name.
     * <p>
     *
     * The following statements returns the table for the key "keyFoo"
     * {@code ["foo": "value1", "bar": "value2"]}
     *
     * <pre>
     * statement "foo", keyFoo: "value1"
     * statement "bar", keyFoo: "value2", keyBar: "value3"
     * </pre>
     *
     * @param name
     *            the {@link String} statement name.
     *
     * @param key
     *            the {@link String} key.
     *
     * @return the {@link Object} value or {@code null}.
     */
    @SuppressWarnings("unchecked")
    public <T> Map<String, T> tableKeys(String name, String key) {
        Map<String, Map<String, Object>> map = args.get(name);
        if (map == null) {
            return null;
        }
        Map<String, T> res = new HashMap<String, T>();
        for (Entry<String, Map<String, Object>> t : map.entrySet()) {
            Object keyValue = t.getValue().get(key);
            if (keyValue != null) {
                res.put(t.getKey(), (T) keyValue);
            }
        }
        return res.size() == 0 ? null : res;
    }

    /**
     * Returns the statement table values with the specified name.
     * <p>
     *
     * The following statements returns the table for the key "keyFoo"
     * {@code ["foo": "value1", "bar": "value2"]}
     *
     * <pre>
     * statement "foo", keyFoo: "value1"
     * statement "bar", keyFoo: "value2", keyBar: "value3"
     * </pre>
     *
     * @param name
     *            the {@link Enum} statement name.
     *
     * @param key
     *            the {@link String} key.
     *
     * @return the {@link Object} value or {@code null}.
     */
    public <T> Map<String, T> tableKeys(Enum<?> name, String key) {
        return tableKeys(name.toString(), key);
    }

    /**
     * Returns the statement table values with the specified name.
     * <p>
     *
     * The following statements returns the table for the key "keyFoo"
     * {@code ["foo": "value1", "bar": "value2"]}
     *
     * <pre>
     * statement "foo", keyFoo: "value1"
     * statement "bar", keyFoo: "value2", keyBar: "value3"
     * </pre>
     *
     * @param name
     *            the {@link Enum} statement name.
     *
     * @param key
     *            the {@link Enum} key.
     *
     * @return the {@link Object} value or {@code null}.
     */
    public <T> Map<String, T> tableKeys(Enum<?> name, Enum<?> key) {
        return tableKeys(name.toString(), key.toString());
    }

    /**
     * Returns the statement table values with the specified name.
     * <p>
     *
     * The following statements returns the table for the key "keyFoo"
     * {@code ["foo": ["value1"], "bar": ["value2", "value3"]]}
     *
     * <pre>
     * statement "foo", keyFoo: "value1"
     * statement "bar", keyFoo: "value2, value3", keyBar: "value4"
     * </pre>
     *
     * @param name
     *            the {@link String} statement name.
     *
     * @param key
     *            the {@link String} key.
     *
     * @return the {@link Object} value or {@code null}.
     */
    @SuppressWarnings("unchecked")
    public <T> Map<String, List<T>> tableKeysAsList(String name, String key) {
        Map<String, Map<String, Object>> map = args.get(name);
        if (map == null) {
            return null;
        }
        List<T> list;
        Map<String, List<T>> res = new HashMap<String, List<T>>();
        for (Entry<String, Map<String, Object>> t : map.entrySet()) {
            Object keyValue = t.getValue().get(key);
            if (keyValue != null) {
                list = (List<T>) toListFactory.create(keyValue).getList();
                res.put(t.getKey(), list);
            }
        }
        return res.size() == 0 ? null : res;
    }

    /**
     * Returns the statement table values with the specified name.
     * <p>
     *
     * The following statements returns the table for the key "keyFoo"
     * {@code ["foo": ["value1"], "bar": ["value2", "value3"]]}
     *
     * <pre>
     * statement "foo", keyFoo: "value1"
     * statement "bar", keyFoo: "value2, value3", keyBar: "value4"
     * </pre>
     *
     * @param name
     *            the {@link Enum} statement name.
     *
     * @param key
     *            the {@link Enum} key.
     *
     * @return the {@link Object} value or {@code null}.
     */
    public <T> Map<String, List<T>> tableKeysAsList(Enum<?> name, Enum<?> key) {
        return tableKeysAsList(name.toString(), key.toString());
    }

    /**
     * Puts the statement value with the specified name.
     *
     * @param name
     *            the {@link String} statement name.
     *
     * @param table
     *            the {@link String} table name.
     *
     * @param key
     *            the {@link String} key.
     *
     * @param value
     *            the {@link Object} value.
     *
     * @throws StatementsException
     *             if the statement is now allowed.
     */
    public void putMapValue(String name, String table, String key, Object value) throws StatementsException {
        log.checkName(allowed, name);
        Set<String> allowedKeys = allowed.get(name);
        if (!allowedKeys.contains(ARBITRARY_KEY)) {
            log.checkKey(this, allowedKeys, name, key);
        }
        Map<String, Map<String, Object>> map = getArgsMap(name);
        map.get(table).put(key, value);
        log.statementValueAdded(this, name, table, key, value);
    }

    /**
     * Puts the statement value with the specified name.
     *
     * @param name
     *            the {@link String} name.
     *
     * @param table
     *            the {@link String} table name.
     *
     * @throws StatementsException
     *             if the statement is now allowed.
     */
    public void putTable(String name, String table) throws StatementsException {
        log.checkName(allowed, name);
        Map<String, Map<String, Object>> map = getArgsMap(name);
        if (!map.containsKey(table)) {
            map.put(table, new HashMap<String, Object>());
        }
    }

    public Object methodMissing(String name, Object obj) throws StatementsException {
        @SuppressWarnings("rawtypes")
        List list = InvokerHelper.asList(obj);
        if (list.size() == 2) {
            String table = list.get(1).toString();
            putTable(name, table);
            @SuppressWarnings("unchecked")
            Map<String, Object> map = (Map<String, Object>) list.get(0);
            for (Map.Entry<String, Object> entry : map.entrySet()) {
                putMapValue(name, table, entry.getKey(), entry.getValue());
            }
        }
        if (list.size() == 1) {
            String table = list.get(0).toString();
            putTable(name, table);
        }
        return null;
    }

    private Map<String, Map<String, Object>> getArgsMap(String name) {
        Map<String, Map<String, Object>> map = args.get(name);
        if (map == null) {
            map = new HashMap<String, Map<String, Object>>();
            args.put(name, map);
        }
        return map;
    }

    public String getName() {
        return name;
    }

    public Object getService() {
        return service;
    }

    private String[] convert(Enum<?>[] names) {
        String[] snames = new String[names.length];
        for (int i = 0; i < names.length; i++) {
            snames[i] = names[i].toString();
        }
        return snames;
    }

    @Override
    public String toString() {
        return new ToStringBuilder(this).append(SERVICE, service.toString()).toString();
    }

}