org.algorithmx.rules.bind.Bindings Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of ruling-class Show documentation
Show all versions of ruling-class Show documentation
Java Rule Engine for the masses
/**
* 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();
}