Content of file PriorityAsSupport.java.html

<?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 :: Model</a> &gt; <a href="../index.html" class="el_bundle">it-tidalwave-bluemarine2-commons</a> &gt; <a href="index.source.html" class="el_package">it.tidalwave.util.spi</a> &gt; <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 &quot;License&quot;); 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 &quot;AS IS&quot; 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 &lt;T&gt; Collection&lt;T&gt; findRoles (@Nonnull final Class&lt;T&gt; type);
      }

    @Nonnull
    private final Object owner; // for logging only

    @Nonnull
    private final Optional&lt;RoleProvider&gt; 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&lt;Object&gt; 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&lt;Object&gt; 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 &lt;T&gt; T as (@Nonnull final Class&lt;T&gt; 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 &lt;T&gt; T as (@Nonnull final Class&lt;T&gt; type, @Nonnull final NotFoundBehaviour&lt;T&gt; notFoundBehaviour)
      {
<span class="pc" id="L117">        return maybeAs(type).orElseGet(() -&gt; 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 &lt;T&gt; Optional&lt;T&gt; maybeAs (@Nonnull final Class&lt;T&gt; 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 &lt;T&gt; Collection&lt;T&gt; asMany (@Nonnull final Class&lt;T&gt; type)
      {
<span class="fc" id="L150">        log.trace(&quot;asMany({}) - {}&quot;, type, owner);</span>
<span class="fc" id="L151">        final List&lt;T&gt; unordered = new ArrayList&lt;&gt;(super.asMany(type));</span>
<span class="pc" id="L152">        additionalRoleProvider.ifPresent(r -&gt; 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&lt;T&gt; result = new ArrayList&lt;&gt;();</span>
<span class="fc" id="L160">        unordered.forEach(item -&gt; addInOrder(result, item));</span>
<span class="fc" id="L161">        log.trace(&quot;&gt;&gt;&gt;&gt; returning {}&quot;, 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 &lt;T&gt; void addInOrder (@Nonnull final List&lt;T&gt; list, @Nonnull final T item)
      {
<span class="fc" id="L174">        log.trace(&quot;&gt;&gt;&gt;&gt; add in order {} into {}&quot;, item, list);</span>
<span class="pc" id="L175">        final Optional&lt;T&gt; firstAncestor = list.stream().filter(i -&gt; 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(&quot;&gt;&gt;&gt;&gt;&gt;&gt;&gt;&gt; add in order {} &quot;, list);</span>
<span class="fc" id="L179">      }</span>

    /*******************************************************************************************************************
     *
     ******************************************************************************************************************/
    private static &lt;T&gt; 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) &amp;&amp; (bBoundDatumClass != null))</span>
          {
<span class="nc" id="L191">            return aBoundDatumClass.datumType()[0].isAssignableFrom(bBoundDatumClass.datumType()[0]); // FIXME: multiple classes?</span>
multiple classes?
} <span class="nc" id="L194"> return a.getClass().isAssignableFrom(b.getClass());</span> } } </pre><div class="footer"><span class="right">Created with <a href="http://www.jacoco.org/jacoco">JaCoCo</a> 0.8.7.202105040129</span></div></body></html>