/** * File Name: fflib_AppBindingResolver * @description Resolver for returning the requested binding(s) * * @author architect ir. Wilhelmus G.J. Velzeboer * * Copyright (c), W.G.J. Velzeboer, * All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, * are permitted provided that the following conditions are met: * * - Redistributions of source code must retain the above author notice, * this list of conditions and the following disclaimer. * - Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * - Neither the name of the author nor the names of its contributors * may be used to endorse or promote products derived from this software without * specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL * THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ public virtual class fflib_AppBindingResolver implements fflib_IAppBindingResolver { // allow for dependency injection inside the resolver protected InternalBindings internalBindings { get { if (null == internalBindings) { internalBindings = new InternalBindings(); } return internalBindings; } private set; } protected fflib_IAppBinding bindingToResolve { get { if (bindingToResolve == null) { bindingToResolve = internalBindings.newAppBinding(); } return bindingToResolve; } private set; } protected fflib_IAppBindings bindings; private List modules; public static fflib_IAppBindingResolver newInstance() { return new fflib_AppBindingResolver(); } /** * Constructors */ public fflib_AppBindingResolver() { } /** * Providing routes to the AppBindingResolver constructor provides dependency injection inside the resolver * * @param routes The routes */ public fflib_AppBindingResolver(fflib_IAppBindingRouter routes) { this(routes.getRoutes()); } public fflib_AppBindingResolver(Map internalBindings) { this.internalBindings = new InternalBindings(internalBindings); } public fflib_IAppBindingResolver addModule(fflib_IAppBindingModule bindingModule) { if (null == this.modules) this.modules = new List(); this.modules.add(bindingModule); return this; } /** * Handles lazy loading of modules, if no custom module is configured (via addModule) the default module is returned * * @return Returns the configured modules */ public List getModules() { if (null == this.modules) { // Lazy load the default Binding module is none other is set. modules = new List { internalBindings.newAppBindingModule() }; } return modules; } public List resolve() { if (!hasBindingsLoaded()) loadBindings(); fflib_IAppBindings resolvedBindings = this.bindings .selectByNamespacePrefix(bindingToResolve.getNamespacePrefix()) .selectByType(bindingToResolve.getBindingType()) .selectByInterfaceType(bindingToResolve.getInterfaceType()) .selectByObject(bindingToResolve.getBindingObject()) .selectBySequence(bindingToResolve.getSequence()); this.bindingToResolve = internalBindings.newAppBinding(); return resolvedBindings.getBindings(); } protected Boolean hasBindingsLoaded() { return (this.bindings != null); } /** * Prepares a domain to store the bindings, * retrieves the bindings from the modules * and stores the bindings in this.bindings */ protected virtual void loadBindings() { this.bindings = internalBindings.newAppBindings(); List modules = getModules(); List appBindings = loadModules(modules); this.bindings.addBindings( appBindings ); } /** * Initialises all the modules and retrieves their binding. * Any child modules in all nested will also be initialised and processed * * @param modules The modules to load their binding * * @return Returns the bindings configured in the given modules */ protected virtual List loadModules(List modules) { List result = new List(); for (fflib_IAppBindingModule module : getModules()) { module.init(); List bindings = module.getBindings(); List subModules = getModulesFromBindings(bindings); if (subModules.isEmpty() == false) { result.addAll( loadModules(subModules) ); } result.addAll(bindings); } return result; } /** * Retrieves the bindings of the type module from the given list and returns their instance * * @param bindings The bindings to filter * * @return Returns the instances of Module bindings */ private List getModulesFromBindings(List bindings) { List result = new List(); for (fflib_IAppBinding binding : bindings) { if (binding.getBindingType() != fflib_AppBindingType.Module) continue; Object instance = binding.newImplInstance(); if (!(instance instanceof fflib_IAppBindingModule)) throw new DeveloperException( 'Binding module should be an instance of fflib_IAppBindingModule, instead found: ' + binding.getImplementationType().getName() ); result.add( (fflib_IAppBindingModule) instance ); } return result; } public class InternalBindings { private Map bindings; public InternalBindings() { } public InternalBindings(Map internalBindings) { this.bindings = internalBindings; } public fflib_IAppBinding newAppBinding() { System.Type interfaceType = fflib_IAppBinding.class; if (hasInternalBinding(interfaceType)) { Object instance = getInternalBinding(interfaceType); if (!(instance instanceof fflib_IAppBinding)) throw new DeveloperException('Invalid internal binding for fflib_IAppBinding'); return (fflib_IAppBinding) instance; } return new fflib_AppBinding(); } public fflib_IAppBindings newAppBindings() { System.Type interfaceType = fflib_IAppBindings.class; if (hasInternalBinding(interfaceType)) { Object instance = getInternalBinding(interfaceType); if (!(instance instanceof fflib_IAppBinding)) throw new DeveloperException('Invalid internal binding for fflib_IAppBindings'); return (fflib_IAppBindings) instance; } return new fflib_AppBindings(); } private Boolean hasInternalBinding(System.Type interfaceType) { return this.bindings != null && this.bindings.containsKey(interfaceType); } private Object getInternalBinding(System.Type interfaceType) { return this.bindings.get(interfaceType).newInstance(); } private fflib_IAppBindingModule newAppBindingModule() { System.Type interfaceType = fflib_IAppBindingModule.class; if (hasInternalBinding(interfaceType)) { Object instance = getInternalBinding(interfaceType); if (!(instance instanceof fflib_IAppBindingModule)) throw new DeveloperException('Invalid internal binding for fflib_IAppBindingModule'); return (fflib_IAppBindingModule) instance; } return new fflib_AppBindingMetaDataModule(); } } public class DeveloperException extends Exception {} }