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 by the "name"
038     * @param with the object to be associated
039     * @return the sub channel for easy chaining
040     */
041    @SuppressWarnings({ "PMD.ShortVariable", "PMD.AvoidDuplicateLiterals" })
042    Associator setAssociated(Object by, Object with);
043
044    /**
045     * Retrieves the associated object following the association 
046     * with the given "name". This general version of the method
047     * supports the retrieval of values of arbitrary types
048     * associated by any "name" types. 
049     * 
050     * @param by the "name"
051     * @param type the type of the value to be retrieved
052     * @param <V> the type of the value to be retrieved
053     * @return the associate with the given type, if any
054     */
055    @SuppressWarnings("PMD.ShortVariable")
056    <V> Optional<V> associated(Object by, Class<V> type);
057
058    /**
059     * Retrieves the associated object following the association 
060     * with the given "name". If no association exists, the
061     * object is created and the association is established.  
062     * 
063     * @param by the "name"
064     * @param supplier the supplier
065     * @param <V> the type of the value to be retrieved
066     * @return the associate, if any
067     */
068    @SuppressWarnings({ "unchecked", "PMD.ShortVariable" })
069    default <V> V associated(Object by, Supplier<V> supplier) {
070        return (V) associated(by, Object.class).orElseGet(() -> {
071            V associated = supplier.get();
072            setAssociated(by, associated);
073            return associated;
074        });
075    }
076
077    /**
078     * Retrieves the associated object following the association 
079     * with the given name. This convenience methods simplifies the
080     * retrieval of String values associated by a (real) name.
081     * 
082     * @param by the name
083     * @return the associate, if any
084     */
085    @SuppressWarnings("PMD.ShortVariable")
086    default Optional<String> associated(String by) {
087        return associated(by, String.class);
088    }
089
090    /**
091     * Retrieves the associated object following the association 
092     * with the given class. The associated object must be an instance
093     * of the given class.
094     * 
095     * @param <V> the type of the value
096     * @param by the name
097     * @return the associate, if any
098     */
099    @SuppressWarnings("PMD.ShortVariable")
100    default <V> Optional<V> associated(Class<V> by) {
101        return associated(by, by);
102    }
103
104    /**
105     * Assumes the associated object to be of type
106     * `Supplier<Optional<V>>`. Invokes the supplier and returns the
107     * result.
108     * 
109     * @param <V> the type of the value
110     * @param by the name
111     * @return the associate, if any
112     */
113    @SuppressWarnings({ "unchecked", "PMD.ShortVariable" })
114    default <V> Optional<V> associatedGet(Class<V> by) {
115        return associated(by, Supplier.class)
116            .flatMap(s -> ((Supplier<Optional<V>>) s).get());
117    }
118}