001/*
002 * JGrapes Event Driven Framework
003 * Copyright (C) 2017-2018 Michael N. Lipp
004 * 
005 * This program is free software; you can redistribute it and/or modify it 
006 * under the terms of the GNU Affero General Public License as published by 
007 * the Free Software Foundation; either version 3 of the License, or 
008 * (at your option) any later version.
009 * 
010 * This program is distributed in the hope that it will be useful, but 
011 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
012 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License 
013 * for more details.
014 * 
015 * You should have received a copy of the GNU Affero General Public License along 
016 * with this program; if not, see <http://www.gnu.org/licenses/>.
017 */
018
019package org.jgrapes.core;
020
021import java.util.Optional;
022import java.util.function.Supplier;
023
024/**
025 * Implemented by classes that allow arbitrary objects to be associated
026 * with instances.
027 */
028public interface Associator {
029
030    /**
031     * Establishes a "named" association to an associated object. Note that 
032     * anything that represents an id can be used as value for 
033     * parameter `name`, it does not necessarily have to be a string.
034     * 
035     * Passing `null` as parameter `with` clears the association.
036     *
037     * @param <A> the associator's type
038     * @param by the "name"
039     * @param with the object to be associated
040     * @return the sub channel for easy chaining
041     */
042    @SuppressWarnings({ "PMD.ShortVariable", "PMD.AvoidDuplicateLiterals" })
043    <A extends Associator> A setAssociated(Object by, Object with);
044
045    /**
046     * Retrieves the associated object following the association 
047     * with the given "name". This general version of the method
048     * supports the retrieval of values of arbitrary types
049     * associated by any "name" types. 
050     * 
051     * @param by the "name"
052     * @param type the type of the value to be retrieved
053     * @param <V> the type of the value to be retrieved
054     * @return the associate with the given type, if any
055     */
056    @SuppressWarnings("PMD.ShortVariable")
057    <V> Optional<V> associated(Object by, Class<V> type);
058
059    /**
060     * Retrieves the associated object following the association 
061     * with the given "name". If no association exists, the
062     * object is created and the association is established.  
063     * 
064     * @param by the "name"
065     * @param supplier the supplier
066     * @param <V> the type of the value to be retrieved
067     * @return the associate, if any
068     */
069    @SuppressWarnings({ "unchecked", "PMD.ShortVariable" })
070    default <V> V associated(Object by, Supplier<V> supplier) {
071        return (V) associated(by, Object.class).orElseGet(() -> {
072            V associated = supplier.get();
073            setAssociated(by, associated);
074            return associated;
075        });
076    }
077
078    /**
079     * Retrieves the associated object following the association 
080     * with the given name. This convenience methods simplifies the
081     * retrieval of String values associated by a (real) name.
082     * 
083     * @param by the name
084     * @return the associate, if any
085     */
086    @SuppressWarnings("PMD.ShortVariable")
087    default Optional<String> associated(String by) {
088        return associated(by, String.class);
089    }
090
091    /**
092     * Retrieves the associated object following the association 
093     * with the given class. The associated object must be an instance
094     * of the given class.
095     * 
096     * @param <V> the type of the value
097     * @param by the name
098     * @return the associate, if any
099     */
100    @SuppressWarnings("PMD.ShortVariable")
101    default <V> Optional<V> associated(Class<V> by) {
102        return associated(by, by);
103    }
104
105    /**
106     * Assumes the associated object to be of type
107     * `Supplier<Optional<V>>`. Invokes the supplier and returns the
108     * result.
109     * 
110     * @param <V> the type of the value
111     * @param by the name
112     * @return the associate, if any
113     */
114    @SuppressWarnings({ "unchecked", "PMD.ShortVariable" })
115    default <V> Optional<V> associatedGet(Class<V> by) {
116        return associated(by, Supplier.class)
117            .flatMap(s -> ((Supplier<Optional<V>>) s).get());
118    }
119}