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

org.algorithmx.rules.bind.Bindings Maven / Gradle / Ivy

There is a newer version: 0.79.50
Show newest version
/**
 * This software is licensed under the Apache 2 license, quoted below.
 *
 * Copyright (c) 1999-2019, Live Software & Consultants Inc ([email protected])
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.algorithmx.rules.bind;

import org.algorithmx.rules.bind.impl.DefaultScopedBindings;
import org.algorithmx.rules.bind.impl.SimpleBindings;
import org.algorithmx.rules.error.UnrulyException;
import org.algorithmx.rules.spring.util.Assert;
import org.algorithmx.rules.util.ReflectionUtils;

import java.beans.IntrospectionException;
import java.lang.reflect.InvocationTargetException;
import java.util.Arrays;
import java.util.Collection;
import java.util.Map;
import java.util.Set;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;

/**
 * The interface that is used to store and find Bindings.
 *
 * @author Max Arulananthan
 * @since 1.0
 */
public interface Bindings extends Iterable> {

    // Binding name for itself.
    String SELF_BIND_NAME = "bindings";

    /**
     * Creates an instance of the ScopedBindings with a self reference.
     *
     * @return new instance of the ScopedBindings with a self reference.
     */
    static ScopedBindings defaultBindings() {
        return defaultBindings(true);
    }

    /**
     * Creates an instance of the ScopedBindings.
     *
     * @param selfAware self aware of itself (reference to itself in the Bindings).
     * @return new instance of the ScopedBindings.
     */
    static ScopedBindings defaultBindings(boolean selfAware) {
        return new DefaultScopedBindings(selfAware);
    }

    /**
     * Creates Bindings and adds them all.
     *
     * @param declarations binding declarations.
     * @return this Bindings (fluent interface).
     */
    default Bindings bind(BindingDeclaration... declarations)  {
        Assert.notNull(declarations, "declarations cannot be null");
        Bindings result = new SimpleBindings();
        Arrays.stream(declarations).forEach(result::bind);
        return result;
    }

    /**
     * Creates a new Binding using a BindingDeclaration. The type of the Binding will be the type of the value.
     * In case the value is null then the type is Object.class. Note that generics are not available and hence the
     * type that is declared will NOT have any generic type.
     *
     * @param declaration declaration details.
     * @return this Bindings (fluent interface).
     */
    default Bindings bind(BindingDeclaration declaration) {
        Object value = declaration.value();
        Class type = value == null ? Object.class : value.getClass();
        return bind(declaration.name(), type, declaration.value());
    }

    /**
     * Declares a new Binding given a name, type and an initial value.
     *
     * @param name name of the Binding.
     * @param type type of the Binding.
     * @param  generic type of the Binding.
     * @return this Bindings (fluent interface).
     * @throws org.algorithmx.rules.bind.BindingAlreadyExistsException thrown if the Binding already exists.
     * @throws org.algorithmx.rules.bind.InvalidBindingException thrown if we cannot set initial value.
     * @see Binding
     */
    default  Bindings bind(String name, Class type) {
        return bind(name, TypeReference.with(type), null, null, true);
    }

    /**
     * Declares a new Binding given a name, type and an initial value.
     *
     * @param name name of the Binding.
     * @param type type reference of the Binding.
     * @param  generic type of the Binding.
     * @return this Bindings (fluent interface).
     * @throws org.algorithmx.rules.bind.BindingAlreadyExistsException thrown if the Binding already exists.
     * @throws org.algorithmx.rules.bind.InvalidBindingException thrown if we cannot set initial value.
     * @see Binding
     */
    default  Bindings bind(String name, TypeReference type) {
        return bind(name, type, null, null, true);
    }

    /**
     * Declares a new Binding given a name, type and an initial value.
     *
     * @param name name of the Binding.
     * @param type type of the Binding.
     * @param initialValue initial value of the Binding.
     * @param  generic type of the Binding.
     * @return this Bindings (fluent interface).
     * @throws org.algorithmx.rules.bind.BindingAlreadyExistsException thrown if the Binding already exists.
     * @throws org.algorithmx.rules.bind.InvalidBindingException thrown if we cannot set initial value.
     * @see Binding
     */
    default  Bindings bind(String name, Class type, T initialValue) {
        return bind(name, TypeReference.with(type), initialValue, null, true);
    }

    /**
     * Declares a new Binding given a name, type and an initial value.
     *
     * @param name name of the Binding.
     * @param type type reference of the Binding.
     * @param initialValue initial value of the Binding.
     * @param  generic type of the Binding.
     * @return this Bindings (fluent interface).
     * @throws org.algorithmx.rules.bind.BindingAlreadyExistsException thrown if the Binding already exists.
     * @throws org.algorithmx.rules.bind.InvalidBindingException thrown if we cannot set initial value.
     * @see Binding
     */
    default  Bindings bind(String name, TypeReference type, T initialValue) {
        return bind(name, type, initialValue, null, true);
    }

    /**
     * Declares a new Binding given a name, type and an initial value.
     *
     * @param name name of the Binding.
     * @param type type of the Binding.
     * @param initialValue initial value of the Binding.
     * @param validationCheck validation to be performed when the value is changed.
     * @param  generic type of the Binding.
     * @return this Bindings (fluent interface).
     * @throws org.algorithmx.rules.bind.BindingAlreadyExistsException thrown if the Binding already exists.
     * @throws org.algorithmx.rules.bind.InvalidBindingException thrown if we cannot set initial value.
     * @see Binding
     */
    default  Bindings bind(String name, Class type, T initialValue, Predicate validationCheck) {
        return bind(name, TypeReference.with(type), initialValue, validationCheck, true);
    }

    /**
     * Declares a new Binding given a name, type and an initial value.
     *
     * @param name name of the Binding.
     * @param type type reference of the Binding.
     * @param initialValue initial value of the Binding.
     * @param validationCheck validation to be performed when the value is changed.
     * @param  generic type of the Binding.
     * @return this Bindings (fluent interface).
     * @throws org.algorithmx.rules.bind.BindingAlreadyExistsException thrown if the Binding already exists.
     * @throws org.algorithmx.rules.bind.InvalidBindingException thrown if we cannot set initial value.
     * @see Binding
     */
    default  Bindings bind(String name, TypeReference type, T initialValue, Predicate validationCheck) {
        return bind(name, type, initialValue, validationCheck, true);
    }

    /**
     * Declares a new Binding given a name, type and an initial value.
     *
     * @param name name of the Binding.
     * @param type type of the Binding.
     * @param initialValue initial value of the Binding.
     * @param validationCheck validation to be performed when the value is changed.
     * @param mutable determines whether the value is mutable.
     * @param  generic type of the Binding.
     * @return this Bindings (fluent interface).
     * @throws org.algorithmx.rules.bind.BindingAlreadyExistsException thrown if the Binding already exists.
     * @throws org.algorithmx.rules.bind.InvalidBindingException thrown if we cannot set initial value.
     * @see Binding
     */
    default  Bindings bind(String name, Class type, T initialValue, Predicate validationCheck, boolean mutable) {
        return bind(name, TypeReference.with(type), initialValue, validationCheck, mutable);
    }

    /**
     * Declares a new Binding given a name, type and an initial value.
     *
     * @param name name of the Binding.
     * @param type type reference of the Binding.
     * @param initialValue initial value of the Binding.
     * @param validationCheck validation to be performed when the value is changed.
     * @param mutable determines whether the value is mutable.
     * @param  generic type of the Binding.
     * @return this Bindings (fluent interface).
     * @throws org.algorithmx.rules.bind.BindingAlreadyExistsException thrown if the Binding already exists.
     * @throws org.algorithmx.rules.bind.InvalidBindingException thrown if we cannot set initial value.
     * @see Binding
     */
     Bindings bind(String name, TypeReference type, T initialValue, Predicate validationCheck, boolean mutable)
            throws BindingAlreadyExistsException, InvalidBindingException;

    /**
     * Declares a new Binding given a name, type and the value will be retrieved using the supplied Supplier.
     *
     * @param name name of the Binding.
     * @param valueSupplier the value of the Binding will be retrieved using this Supplier.
     * @param type type reference of the Binding.
     * @param  generic type of the Binding.
     * @return this Bindings (fluent interface).
     * @throws org.algorithmx.rules.bind.BindingAlreadyExistsException thrown if the Binding already exists.
     */
    default  Bindings bind(String name, Supplier valueSupplier, Class type) {
        return bind(name, valueSupplier, TypeReference.with(type));
    }

    /**
     * Declares a new Binding given a name, type and the value will be retrieved using the supplied Supplier.
     *
     * @param name name of the Binding.
     * @param valueSupplier the value of the Binding will be retrieved using this Supplier.
     * @param type type reference of the Binding.
     * @param  generic type of the Binding.
     * @return this Bindings (fluent interface).
     * @throws org.algorithmx.rules.bind.BindingAlreadyExistsException thrown if the Binding already exists.
     */
     Bindings bind(String name, Supplier valueSupplier, TypeReference type) throws BindingAlreadyExistsException;

    /**
     * Binds the given Binding into this set of Bindings. Follows the same rules as adding a new Binding with name, type, etc.
     *
     * @param binding existing Binding.
     * @param  generic type of the Binding.
     * @return this Bindings (fluent interface).
     * @throws org.algorithmx.rules.bind.BindingAlreadyExistsException thrown if the Binding already exists.
     */
     Bindings bind(Binding binding);

    /**
     * Binds all the given Bindings into this set of Bindings. Follows the same rules as adding a new Binding with name, type, etc.
     * The execution will stop if a Binding already exists.
     *
     *
     * @param bindings existing Bindings.
     * @param  generic type of the Binding.
     * @return this Bindings (fluent interface).
     * @throws org.algorithmx.rules.bind.BindingAlreadyExistsException thrown if a Binding already exists.
     */
    default  Bindings bind(Binding...bindings) {
        return bind(Arrays.asList(bindings));
    }

    /**
     * Binds all the given Bindings into this set of Bindings. Follows the same rules as adding a new Binding with name, type, etc.
     * The execution will stop if a Binding already exists.
     *
     *
     * @param bindings existing Bindings.
     * @param  generic type of the Binding.
     * @return this Bindings (fluent interface).
     * @throws org.algorithmx.rules.bind.BindingAlreadyExistsException thrown if a Binding already exists.
     */
     Bindings bind(Collection> bindings);

    /**
     * Binds each readable property on the given Bean.
     *
     * @param bean parent bean.
     * @return this Bindings (fluent interface).
     * @throws org.algorithmx.rules.bind.BindingAlreadyExistsException thrown if a Binding already exists.
     */
    default Bindings bindProperties(Object bean) {
        return bindProperties(bean, (String name) -> name);
    }

    /**
     * Binds each readable property on the given Bean.
     *
     * @param bean parent bean.
     * @param nameGenerator generator that will determine the Binding name for each property.
     * @return this Bindings (fluent interface).
     * @throws org.algorithmx.rules.bind.BindingAlreadyExistsException thrown if a Binding already exists.
     */
    default Bindings bindProperties(Object bean, Function nameGenerator) {
        Assert.notNull(bean, "bean cannot be null.");
        Assert.notNull(nameGenerator, "nameGenerator cannot be null.");

        try {
            ReflectionUtils.traverseProperties(bean.getClass(), property -> property.getReadMethod() != null,
                    property -> {
                        try {
                            // Get the value via the getter
                            Object value = property.getReadMethod().invoke(bean);
                            // Bind the property
                            bind(nameGenerator.apply(property.getName()), TypeReference.with(
                                    property.getReadMethod().getGenericReturnType()), value);
                        } catch (IllegalAccessException | InvocationTargetException e) {
                            // Couldn't get the value
                            throw new UnrulyException("Error trying to retrieve property [" + property.getName()
                                    + "] on Bean class [" + bean.getClass() + "]", e);
                        }
                    });
        } catch (IntrospectionException e) {
            throw new UnrulyException("Error trying to Introspect [" + bean.getClass() + "]", e);
        }

        return this;
    }

    /**
     * Binds each declared field on the given Bean.
     *
     * @param bean parent bean.
     * @return this Bindings (fluent interface).
     * @throws org.algorithmx.rules.bind.BindingAlreadyExistsException thrown if a Binding already exists.
     */
    default Bindings bindFields(Object bean) {
        return bindFields(bean, (String name) -> name);
    }

    /**
     * Binds each declared field on the given Bean.
     *
     * @param bean parent bean.
     * @param nameGenerator generator that will determine the Binding name for each field.
     * @return this Bindings (fluent interface).
     * @throws org.algorithmx.rules.bind.BindingAlreadyExistsException thrown if a Binding already exists.
     */
    default Bindings bindFields(Object bean, Function nameGenerator) {
        Assert.notNull(bean, "bean cannot be null.");
        Assert.notNull(nameGenerator, "nameGenerator cannot be null.");

        ReflectionUtils.traverseFields(bean.getClass(), null, field -> {
            try {
                Object value = field.get(bean);
                bind(nameGenerator.apply(field.getName()), TypeReference.with(field.getGenericType()), value);
            } catch (IllegalAccessException e) {
                // Couldn't get the value
                throw new UnrulyException("Error trying to retrieve field [" + field.getName()
                        + "] on Bean class [" + bean.getClass() + "]", e);
            }
        });

        return this;
    }


    /**
     * Retrieves the number of Bindings.
     *
     * @return number of Bindings.
     */
    int size();

    /**
     * Clears all the Bindings.
     */
    void clear();

    /**
     * Determines if a Binding with given name exists.
     *
     * @param name name of the Binding.
     * @return true if Binding exists; false otherwise.
     */
    default boolean contains(String name) {
        return getBinding(name) != null;
    }

    /**
     * Determines if the Binding with given name and types exists.
     *
     * @param name name of the Binding.
     * @param type class type of the Binding.
     * @param  generic type of the Binding.
     * @return true if Binding exists; false otherwise.
     */
    default  boolean contains(String name, Class type) {
        return contains(name, TypeReference.with(type));
    }

    /**
     * Determines if the Binding with given name and types exists.
     *
     * @param name name of the Binding.
     * @param type generic type of the Binding.
     * @param  generic type of the Binding.
     * @return true if Binding exists; false otherwise.
     */
    default  boolean contains(String name, TypeReference type) {
        Binding result = getBinding(name);
        return result != null
                ? result.isTypeAcceptable(type.getType())
                ? true : false
                : false;
    }

    /**
     * Retrieves the Binding identified by the given name.
     *
     * @param name name of the Binding.
     * @param  generic type of the Binding.
     * @return Binding if found; null otherwise.
     */
     Binding getBinding(String name);

    /**
     * Retrieves the value of the Binding with the given name.
     *
     * @param name name of the Binding.
     * @param  desired Type.
     * @param  generic type of the Binding.
     * @return value if Binding is found.
     * @throws NoSuchBindingException if Binding is not found.
     */
    default  T get(String name) {
        Binding result = getBinding(name);
        // Could not find Binding
        if (result == null) throw new NoSuchBindingException(name);
        return result.getValue();
    }

    /**
     * Sets the value of Binding with the given name.
     *
     * @param name name of the Binding.
     * @param value desired new value.
     * @param  generic type of the Binding.
     * @throws NoSuchBindingException if Binding is not found.
     */
    default  void set(String name, T value) {
        Binding result = getBinding(name);
        // Could not find Binding
        if (result == null) throw new NoSuchBindingException(name);
        result.setValue(value);
    }

    /**
     * Retrieves the Binding identified by the given name.
     *
     * @param name name of the Binding.
     * @param type type of the Binding.
     * @param  generic type of the Binding.
     * @return Binding if found; null otherwise.
     */
    default  Binding getBinding(String name, Class type) {
        return getBinding(name, TypeReference.with(type));
    }

    /**
     * Retrieves the Binding identified by the given name and type.
     *
     * @param name name of the Binding.
     * @param type type of the Binding.
     * @param  generic type of the Binding.
     * @return Binding if found; null otherwise.
     */
     Binding getBinding(String name, TypeReference type);

    /**
     * Retrieves all the Bindings of the given type.
     *
     * @param type desired type.
     * @param  generic type of the Binding.
     * @return all matching Bindings.
     */
    default  Set> getBindings(Class type) {
        return getBindings(TypeReference.with(type));
    }

    /**
     * Retrieves all the Bindings of the given type.
     *
     * @param type desired type.
     * @param  generic type of the Binding.
     * @return all matching Bindings.
     */
     Set> getBindings(TypeReference type);

    /**
     * Retrieves the Binding values as an Unmodifiable Map.
     *
     * @return unmodifiable Map of the Binding values.
     */
    Map asMap();

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy