<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"><html xmlns="http://www.w3.org/1999/xhtml" lang="en"><head><meta http-equiv="Content-Type" content="text/html;charset=UTF-8"/><link rel="stylesheet" href="../../jacoco-resources/report.css" type="text/css"/><link rel="shortcut icon" href="../../jacoco-resources/report.gif" type="image/gif"/><title>PriorityAsSupport.java</title><link rel="stylesheet" href="../../jacoco-resources/prettify.css" type="text/css"/><script type="text/javascript" src="../../jacoco-resources/prettify.js"></script></head><body onload="window['PR_TAB_WIDTH']=4;prettyPrint()"><div class="breadcrumb" id="breadcrumb"><span class="info"><a href="../../jacoco-sessions.html" class="el_session">Sessions</a></span><a href="../../index.html" class="el_report">blueMarine II :: UI</a> > <a href="../index.html" class="el_bundle">it-tidalwave-bluemarine2-commons</a> > <a href="index.source.html" class="el_package">it.tidalwave.util.spi</a> > <span class="el_source">PriorityAsSupport.java</span></div><h1>PriorityAsSupport.java</h1><pre class="source lang-java linenums">/*
* *********************************************************************************************************************
*
* blueMarine II: Semantic Media Centre
* http://tidalwave.it/projects/bluemarine2
*
* Copyright (C) 2015 - 2021 by Tidalwave s.a.s. (http://tidalwave.it)
*
* *********************************************************************************************************************
*
* 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.
*
* *********************************************************************************************************************
*
* git clone https://bitbucket.org/tidalwave/bluemarine2-src
* git clone https://github.com/tidalwave-it/bluemarine2-src
*
* *********************************************************************************************************************
*/
package it.tidalwave.util.spi;
import javax.annotation.Nonnull;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import it.tidalwave.util.As;
import it.tidalwave.util.AsException;
import it.tidalwave.dci.annotation.DciRole;
import lombok.extern.slf4j.Slf4j;
/***********************************************************************************************************************
*
* A specialisation of {@link AsSupport} that deals with multiple roles of the same type by prioritising them; they
* are ordered from most relevant to least relevant (where relevance is associated to specialisation, that is most
* specialised roles, or roles associated via {@code @DciRole} to most specialised datum classes, are most relevant).
*
* FIXME: could be seen as a replacement to {@code AsSupport}?
*
* @author Fabrizio Giudici
*
**********************************************************************************************************************/
<span class="fc" id="L51">@Slf4j</span>
public class PriorityAsSupport extends AsSupport implements As
{
@FunctionalInterface
public static interface RoleProvider
{
@Nonnull
public <T> Collection<T> findRoles (@Nonnull final Class<T> type);
}
@Nonnull
private final Object owner; // for logging only
@Nonnull
private final Optional<RoleProvider> additionalRoleProvider;
public PriorityAsSupport (final Object owner)
{
<span class="nc" id="L69"> this(owner, Collections.emptyList());</span>
<span class="nc" id="L70"> }</span>
public PriorityAsSupport (@Nonnull final Object owner, @Nonnull final Collection<Object> rolesOrFactories)
{
<span class="fc" id="L74"> super(owner, rolesOrFactories);</span>
<span class="fc" id="L75"> this.owner = owner;</span>
<span class="fc" id="L76"> this.additionalRoleProvider = Optional.empty();</span>
<span class="fc" id="L77"> }</span>
public PriorityAsSupport (@Nonnull final Object owner,
@Nonnull final RoleProvider additionalRoleProvider,
@Nonnull final Collection<Object> rolesOrFactories)
{
<span class="nc" id="L83"> super(owner, rolesOrFactories);</span>
<span class="nc" id="L84"> this.owner = owner;</span>
<span class="nc" id="L85"> this.additionalRoleProvider = Optional.of(additionalRoleProvider);</span>
<span class="nc" id="L86"> }</span>
/*******************************************************************************************************************
*
* {@inheritDoc}
*
* Returned roles can be associated both to this type and to the delegate; the one with the higher priority is
* returned. See {@link #asMany(java.lang.Class)} for further details.
*
* @see #asMany(java.lang.Class)
*
******************************************************************************************************************/
@Override @Nonnull
public <T> T as (@Nonnull final Class<T> type)
{
<span class="fc" id="L101"> return as(type, As.Defaults.throwAsException(type));</span>
}
/*******************************************************************************************************************
*
* {@inheritDoc}
*
* Returned roles can be associated both to this type and to the delegate; the one with the higher priority is
* returned. See {@link #asMany(java.lang.Class)} for further details.
*
* @see #asMany(java.lang.Class)
*
******************************************************************************************************************/
@Override @Nonnull
public <T> T as (@Nonnull final Class<T> type, @Nonnull final NotFoundBehaviour<T> notFoundBehaviour)
{
<span class="pc" id="L117"> return maybeAs(type).orElseGet(() -> notFoundBehaviour.run(new AsException(type)));</span>
}
/*******************************************************************************************************************
*
* {@inheritDoc}
*
* Returned roles can be associated both to this type and to the delegate; the one with the higher priority is
* returned. See {@link #asMany(java.lang.Class)} for further details.
*
* @see #asMany(java.lang.Class)
*
******************************************************************************************************************/
@Override @Nonnull
public <T> Optional<T> maybeAs (@Nonnull final Class<T> type)
{
<span class="fc" id="L133"> return asMany(type).stream().findFirst();</span>
}
/*******************************************************************************************************************
*
* {@inheritDoc}
*
* Returned roles can be associated both to this type and to the delegate; the one with the higher priority is
* returned. The ones associated to this type come with higher priority (this makes sense, being this class a
* decorator, specific roles could be associated to it). But given that the default implementation of asMany()
* doesn't guarantee ant order (see TFT-192) there's something to take care of. Currently this method contains
* some hardwired priority logics.
*
******************************************************************************************************************/
@Override @Nonnull
public <T> Collection<T> asMany (@Nonnull final Class<T> type)
{
<span class="fc" id="L150"> log.trace("asMany({}) - {}", type, owner);</span>
<span class="fc" id="L151"> final List<T> unordered = new ArrayList<>(super.asMany(type));</span>
<span class="pc" id="L152"> additionalRoleProvider.ifPresent(r -> unordered.addAll(r.findRoles(type)));</span>
//
// Need a kind of bubble sort, because:
// a) the original sequence might have a meaning; for instance, additional roles added by
// additionalRoleProvider are appended and, generally, they should stay low in priority.
// b) there is not always a well-defined way to define a relation order between the elements.
//
<span class="fc" id="L159"> final List<T> result = new ArrayList<>();</span>
<span class="fc" id="L160"> unordered.forEach(item -> addInOrder(result, item));</span>
<span class="fc" id="L161"> log.trace(">>>> returning {}", result);</span>
<span class="fc" id="L163"> return result;</span>
}
/*******************************************************************************************************************
*
* Adds an item to the list, just before the first existing item which whose datum class is an instance of a
* subclass of its datum class.
*
******************************************************************************************************************/
private static <T> void addInOrder (@Nonnull final List<T> list, @Nonnull final T item)
{
<span class="fc" id="L174"> log.trace(">>>> add in order {} into {}", item, list);</span>
<span class="pc" id="L175"> final Optional<T> firstAncestor = list.stream().filter(i -> isDatumAncestor(i, item)).findFirst();</span>
<span class="fc" id="L176"> final int index = firstAncestor.map(list::indexOf).orElse(list.size());</span>
<span class="fc" id="L177"> list.add(index, item);</span>
<span class="fc" id="L178"> log.trace(">>>>>>>> add in order {} ", list);</span>
<span class="fc" id="L179"> }</span>
/*******************************************************************************************************************
*
******************************************************************************************************************/
private static <T> boolean isDatumAncestor (@Nonnull final T a, @Nonnull final T b)
{
<span class="nc" id="L186"> final DciRole aBoundDatumClass = a.getClass().getAnnotation(DciRole.class);</span>
<span class="nc" id="L187"> final DciRole bBoundDatumClass = b.getClass().getAnnotation(DciRole.class);</span>
<span class="nc bnc" id="L189" title="All 4 branches missed."> if ((aBoundDatumClass != null) && (bBoundDatumClass != null))</span>
{
<span class="nc" id="L191"> return aBoundDatumClass.datumType()[0].isAssignableFrom(bBoundDatumClass.datumType()[0]); // FIXME: multiple classes?</span>