Content of file RepositoryFinderSupport.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>RepositoryFinderSupport.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 :: JavaFX</a> &gt; <a href="../index.html" class="el_bundle">it-tidalwave-bluemarine2-catalog</a> &gt; <a href="index.source.html" class="el_package">it.tidalwave.bluemarine2.model.impl.catalog.finder</a> &gt; <span class="el_source">RepositoryFinderSupport.java</span></div><h1>RepositoryFinderSupport.java</h1><pre class="source lang-java linenums"><span class="nc" id="L1">/*</span>
 * *********************************************************************************************************************
 *
 * 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.bluemarine2.model.impl.catalog.finder;

import javax.annotation.Nonnegative;
import javax.annotation.Nonnull;
import javax.inject.Inject;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Function;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Stream;
import java.io.IOException;
import java.io.InputStream;
import org.springframework.beans.factory.annotation.Configurable;
import org.springframework.util.StreamUtils;
import org.eclipse.rdf4j.model.IRI;
import org.eclipse.rdf4j.model.Value;
import org.eclipse.rdf4j.model.impl.SimpleValueFactory;
import org.eclipse.rdf4j.query.QueryLanguage;
import org.eclipse.rdf4j.query.TupleQuery;
import org.eclipse.rdf4j.query.TupleQueryResult;
import org.eclipse.rdf4j.repository.Repository;
import org.eclipse.rdf4j.repository.RepositoryConnection;
import it.tidalwave.util.Finder;
import it.tidalwave.util.Id;
import it.tidalwave.util.LoggingUtilities;
import it.tidalwave.util.ReflectionUtils;
import it.tidalwave.util.Task;
import it.tidalwave.util.spi.FinderSupport;
import it.tidalwave.role.ContextManager;
import it.tidalwave.bluemarine2.util.ImmutableTupleQueryResult;
import it.tidalwave.bluemarine2.model.impl.catalog.factory.RepositoryEntityFactory;
import it.tidalwave.bluemarine2.model.spi.CacheManager;
import it.tidalwave.bluemarine2.model.spi.CacheManager.Cache;
import it.tidalwave.bluemarine2.model.spi.SourceAwareFinder;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.Setter;
import lombok.ToString;
import lombok.extern.slf4j.Slf4j;
import static java.util.stream.Collectors.*;
import static java.nio.charset.StandardCharsets.UTF_8;
import static it.tidalwave.bluemarine2.util.RdfUtilities.streamOf;
import static it.tidalwave.bluemarine2.model.vocabulary.BMMO.*;

/***********************************************************************************************************************
 *
 * A base class for creating {@link Finder}s.
 *
 * @param &lt;ENTITY&gt;  the entity the {@code Finder} should find
 * @param &lt;FINDER&gt;  the subclass
 *
 * @stereotype      Finder
 *
 * @author  Fabrizio Giudici
 *
 **********************************************************************************************************************/
<span class="fc" id="L88">@Configurable @Slf4j</span>
public class RepositoryFinderSupport&lt;ENTITY, FINDER extends Finder&lt;ENTITY&gt;&gt;
        extends FinderSupport&lt;ENTITY, FINDER&gt;
        implements SourceAwareFinder&lt;ENTITY, FINDER&gt;
  {
    private static final String REGEX_BINDING_TAG = &quot;^@([A-Za-z0-9]*)@&quot;;

    private static final String REGEX_BINDING_TAG_LINE = REGEX_BINDING_TAG + &quot;.*$&quot;;

    private static final String REGEX_COMMENT = &quot;^ *#.*&quot;;

    private static final String PREFIXES = &quot;PREFIX foaf:  &lt;http://xmlns.com/foaf/0.1/&gt;\n&quot;
                                         + &quot;PREFIX rdf:   &lt;http://www.w3.org/1999/02/22-rdf-syntax-ns#&gt;\n&quot;
                                         + &quot;PREFIX rel:   &lt;http://purl.org/vocab/relationship/&gt;\n&quot;
                                         + &quot;PREFIX bmmo:  &lt;http://bluemarine.tidalwave.it/2015/04/mo/&gt;\n&quot;
                                         + &quot;PREFIX mo:    &lt;http://purl.org/ontology/mo/&gt;\n&quot;
                                         + &quot;PREFIX vocab: &lt;http://dbtune.org/musicbrainz/resource/vocab/&gt;\n&quot;
                                         + &quot;PREFIX xs:    &lt;http://www.w3.org/2001/XMLSchema#&gt;\n&quot;;

    private static final String QUERY_COUNT_HOLDER = &quot;queryCount&quot;;

    private static final long serialVersionUID = 1896412264314804227L;

<span class="fc" id="L111">    private static final SimpleValueFactory FACTORY = SimpleValueFactory.getInstance();</span>

    @Nonnull
    protected final transient Repository repository;

    @Nonnull
    private final Class&lt;ENTITY&gt; entityClass;

    @Nonnull
    private final String idName;

    @Nonnull
    private final transient Optional&lt;Id&gt; id;

    @Nonnull
    private final transient Optional&lt;Value&gt; source;

    @Nonnull
    private final transient Optional&lt;Value&gt; sourceFallback;

    @Inject
    private transient ContextManager contextManager;

    @Inject
    private transient RepositoryEntityFactory entityFactory;

    @Inject
    private transient CacheManager cacheManager;

    // FIXME: move to a stats bean
<span class="fc" id="L141">    private static final AtomicInteger queryCount = new AtomicInteger();</span>

<span class="nc" id="L143">    @Getter @Setter</span>
<span class="fc" id="L144">    private static boolean dumpThreadOnQuery = false;</span>

    /*******************************************************************************************************************
     *
     *
     *
     ******************************************************************************************************************/
<span class="pc bnc" id="L151" title="All 16 branches missed.">    @RequiredArgsConstructor(staticName = &quot;withSparql&quot;) @EqualsAndHashCode @ToString</span>
    protected static class QueryAndParameters
      {
<span class="fc" id="L154">        @Getter @Nonnull</span>
        private final String sparql;

<span class="fc" id="L157">        @Nonnull</span>
        private final List&lt;Object&gt; parameters = new ArrayList&lt;&gt;();

        @Nonnull
        public QueryAndParameters withParameter (@Nonnull final String name, @Nonnull final Optional&lt;? extends Value&gt; value)
          {
<span class="fc" id="L163">            return value.map(v -&gt; withParameter(name, v)).orElse(this);</span>
          }

        @Nonnull
        public QueryAndParameters withParameter (@Nonnull final String name, @Nonnull final Value value)
          {
<span class="fc" id="L169">            parameters.addAll(List.of(name, value));</span>
<span class="fc" id="L170">            return this;</span>
          }

        @Nonnull
        public Object[] getParameters()
          {
<span class="fc" id="L176">            return parameters.toArray();</span>
          }

        @Nonnull
        private String getCountSparql()
          {
<span class="fc" id="L182">            return String.format(&quot;SELECT (COUNT(*) AS ?%s)%n  {%n%s%n  }&quot;,</span>
                                 QUERY_COUNT_HOLDER,
<span class="fc" id="L184">                                 sparql.replaceAll(&quot;ORDER BY[\\s\\S]*&quot;, &quot;&quot;));</span>
          }
      }

    /*******************************************************************************************************************
     *
     *
     *
     ******************************************************************************************************************/
    protected RepositoryFinderSupport (@Nonnull final Repository repository, @Nonnull final String idName)
<span class="pc bpc" id="L194" title="9 of 18 branches missed.">      {</span>
<span class="fc" id="L195">        this.repository = repository;</span>
<span class="fc" id="L196">        this.entityClass = (Class&lt;ENTITY&gt;)ReflectionUtils.getTypeArguments(RepositoryFinderSupport.class, getClass()).get(0);</span>
<span class="fc" id="L197">        this.idName = idName;</span>
<span class="fc" id="L198">        this.id = Optional.empty();</span>
<span class="fc" id="L199">        this.source = Optional.of(O_SOURCE_EMBEDDED); // FIXME: resets</span>
<span class="fc" id="L200">        this.sourceFallback = Optional.empty(); // FIXME: resets</span>
resets
<span class="pc bpc" id="L201" title="2 of 4 branches missed."> }</span> /******************************************************************************************************************* * * * ******************************************************************************************************************/ private RepositoryFinderSupport (@Nonnull final Repository repository, @Nonnull final Class&lt;ENTITY&gt; entityClass, @Nonnull final String idName, @Nonnull final Optional&lt;Id&gt; id, @Nonnull final Optional&lt;Value&gt; source, @Nonnull final Optional&lt;Value&gt; sourceFallback) <span class="pc bpc" id="L214" title="9 of 18 branches missed."> {</span> <span class="fc" id="L215"> this.repository = repository;</span> <span class="fc" id="L216"> this.entityClass = entityClass;</span> <span class="fc" id="L217"> this.idName = idName;</span> <span class="fc" id="L218"> this.id = id;</span> <span class="fc" id="L219"> this.source = source;</span> <span class="fc" id="L220"> this.sourceFallback = sourceFallback;</span> <span class="pc bpc" id="L221" title="2 of 4 branches missed."> }</span> /******************************************************************************************************************* * * Clone constructor. * ******************************************************************************************************************/ public RepositoryFinderSupport (@Nonnull final RepositoryFinderSupport&lt;ENTITY, FINDER&gt; other, @Nonnull final Object override) { <span class="pc bpc" id="L231" title="9 of 18 branches missed."> super(other, override);</span> <span class="fc" id="L232"> final RepositoryFinderSupport&lt;ENTITY, FINDER&gt; source = getSource(RepositoryFinderSupport.class, other, override);</span> <span class="fc" id="L233"> this.repository = source.repository;</span> <span class="fc" id="L234"> this.entityClass = source.entityClass;</span> <span class="fc" id="L235"> this.idName = source.idName;</span> <span class="fc" id="L236"> this.id = source.id;</span> <span class="fc" id="L237"> this.source = source.source;</span> <span class="fc" id="L238"> this.sourceFallback = source.sourceFallback;</span> <span class="pc bpc" id="L239" title="2 of 4 branches missed."> }</span> /******************************************************************************************************************* * * {@inheritDoc} * ******************************************************************************************************************/ @Override @Nonnull protected final List&lt;? extends ENTITY&gt; computeNeededResults() { <span class="fc" id="L249"> return query(QueryAndParameters::getSparql,</span> <span class="fc" id="L250"> result -&gt; createEntities(repository, entityClass, result),</span> <span class="fc" id="L251"> result -&gt; String.format(&quot;%d entities&quot;, result.size()));</span> } /******************************************************************************************************************* * * {@inheritDoc} * ******************************************************************************************************************/ @Override @Nonnegative public int count() { <span class="fc" id="L262"> return query(QueryAndParameters::getCountSparql,</span> <span class="fc" id="L263"> result -&gt; Integer.parseInt(result.next().getValue(QUERY_COUNT_HOLDER).stringValue()),</span> <span class="fc" id="L264"> result -&gt; String.format(&quot;%d&quot;, result));</span> } /******************************************************************************************************************* * * {@inheritDoc} * ******************************************************************************************************************/ @Override @Nonnull public FINDER withId (@Nonnull final Id id) { <span class="nc" id="L275"> return clonedWith(new RepositoryFinderSupport(repository,</span> entityClass, idName, <span class="nc" id="L278"> Optional.of(id),</span> source, sourceFallback)); } /******************************************************************************************************************* * * {@inheritDoc} * ******************************************************************************************************************/ @Override @Nonnull public FINDER importedFrom (@Nonnull final Optional&lt;Id&gt; optionalSource) { <span class="fc" id="L291"> return optionalSource.map(this::importedFrom).orElse((FINDER)this);</span> } /******************************************************************************************************************* * * {@inheritDoc} * ******************************************************************************************************************/ @Override @Nonnull public FINDER importedFrom (@Nonnull final Id source) { <span class="fc" id="L302"> return clonedWith(new RepositoryFinderSupport(repository,</span> entityClass, idName, id, <span class="fc" id="L306"> Optional.of(FACTORY.createLiteral(source.toString())),</span> sourceFallback)); } /******************************************************************************************************************* * * {@inheritDoc} * ******************************************************************************************************************/ @Override @Nonnull public FINDER withFallback (@Nonnull final Optional&lt;Id&gt; sourceFallback) { <span class="fc" id="L318"> return sourceFallback.map(this::withFallback).orElse((FINDER)this);</span> } /******************************************************************************************************************* * * {@inheritDoc} * ******************************************************************************************************************/ @Override @Nonnull public FINDER withFallback (@Nonnull final Id sourceFallback) { <span class="fc" id="L329"> return clonedWith(new RepositoryFinderSupport(repository,</span> entityClass, idName, id, source, <span class="fc" id="L334"> Optional.of(FACTORY.createLiteral(sourceFallback.toString()))));</span> } /******************************************************************************************************************* * * Returns the count of queries performed so far. * * @return the count of queries * ******************************************************************************************************************/ @Nonnegative public static int getQueryCount() { <span class="fc" id="L347"> return queryCount.intValue();</span> } /******************************************************************************************************************* * * Resets the count of queries performed so far. * ******************************************************************************************************************/ public static void resetQueryCount() { <span class="fc" id="L357"> queryCount.set(0);</span> <span class="fc" id="L358"> }</span> /******************************************************************************************************************* * * Prepares the SPARQL query and its parameters. * * @return the SPARQL query and its parameters * ******************************************************************************************************************/ @Nonnull protected /* abstract */ QueryAndParameters prepareQuery() { <span class="nc" id="L370"> throw new UnsupportedOperationException(&quot;Must be implemented by subclasses&quot;);</span> } /******************************************************************************************************************* * * Performs a query, eventually using the cache. * * @param sparqlSelector a function that select the SPARQL statement to use * @param finalizer a function to transform the query raw result into the final result * @param resultToString a function that provide the logging string for the result * @return the found entities * ******************************************************************************************************************/ @Nonnull private &lt;E&gt; E query (@Nonnull final Function&lt;QueryAndParameters, String&gt; sparqlSelector, @Nonnull final Function&lt;TupleQueryResult, E&gt; finalizer, @Nonnull final Function&lt;E, String&gt; resultToString) { <span class="fc" id="L388"> log.info(&quot;query() - {}&quot;, entityClass);</span> <span class="fc" id="L389"> final long baseTime = System.nanoTime();</span> <span class="fc" id="L390"> final QueryAndParameters queryAndParameters = prepareQuery()</span> <span class="fc" id="L391"> .withParameter(idName, id.map(this::iriFor))</span> <span class="fc" id="L392"> .withParameter(&quot;source&quot;, source)</span> <span class="fc bfc" id="L393" title="All 2 branches covered."> .withParameter(&quot;fallback&quot;, sourceFallback.equals(source) ? Optional.empty() : sourceFallback);</span> <span class="fc" id="L394"> final Object[] parameters = queryAndParameters.getParameters();</span> <span class="fc" id="L395"> final String originalSparql = sparqlSelector.apply(queryAndParameters);</span> <span class="fc" id="L396"> final String sparql = PREFIXES + Stream.of(originalSparql.split(&quot;\n&quot;))</span> <span class="fc" id="L397"> .filter(s -&gt; matchesTag(s, parameters))</span> <span class="fc" id="L398"> .map(s -&gt; s.replaceAll(REGEX_BINDING_TAG, &quot;&quot;))</span> <span class="fc" id="L399"> .collect(joining(&quot;\n&quot;));</span> <span class="fc" id="L400"> log(originalSparql, sparql, parameters);</span> <span class="fc" id="L401"> final E result = query(sparql, finalizer, parameters);</span> <span class="fc" id="L402"> queryCount.incrementAndGet();</span> <span class="fc" id="L403"> final long elapsedTime = System.nanoTime() - baseTime;</span> <span class="fc" id="L404"> log.info(&quot;&gt;&gt;&gt;&gt; query returned {} in {} msec&quot;, resultToString.apply(result), elapsedTime / 1E6);</span> <span class="fc" id="L405"> LoggingUtilities.dumpStack(this, dumpThreadOnQuery);</span> <span class="fc" id="L407"> return result;</span> } /******************************************************************************************************************* * * Performs a query. * * @param sparql the SPARQL of the query * @param finalizer a function to transform the query raw result into the final result * @param parameters an optional set of parameters of the query (&quot;name&quot;, value, &quot;name&quot;, value ,,,) * @return the found entities * ******************************************************************************************************************/ @Nonnull private &lt;R&gt; R query (@Nonnull final String sparql, @Nonnull final Function&lt;TupleQueryResult, R&gt; finalizer, @Nonnull final Object ... parameters) { <span class="fc" id="L425"> try (final RepositoryConnection connection = repository.getConnection())</span> { <span class="fc" id="L427"> final TupleQuery query = connection.prepareTupleQuery(QueryLanguage.SPARQL, sparql);</span> <span class="fc bfc" id="L429" title="All 2 branches covered."> for (int i = 0; i &lt; parameters.length; i += 2)</span> { <span class="fc" id="L431"> query.setBinding((String)parameters[i], (Value)parameters[i + 1]);</span> } // // Don't cache entities because they are injected with DCI roles in function of the context. // Caching tuples is safe. <span class="fc" id="L436"> final Cache cache = cacheManager.getCache(RepositoryFinderSupport.class);</span> <span class="fc" id="L437"> final String key = String.format(&quot;%s # %s&quot;, compacted(sparql), Arrays.toString(parameters));</span> <span class="fc" id="L439"> try (final ImmutableTupleQueryResult result = cache.getCachedObject(key,</span> <span class="fc" id="L440"> () -&gt; new ImmutableTupleQueryResult(query.evaluate())))</span> { // ImmutableTupleQueryResult is not thread safe, so clone the cached instance <span class="fc" id="L443"> return finalizer.apply(new ImmutableTupleQueryResult(result));</span> } } } /******************************************************************************************************************* * * Facility method that creates an {@link IRI} given an {@link Id}. * * @param id the {@code Id} * @return the {@code IRI} * ******************************************************************************************************************/ @Nonnull protected Value iriFor (@Nonnull final Id id) { <span class="fc" id="L459"> return FACTORY.createIRI(id.stringValue());</span> } /******************************************************************************************************************* * * ******************************************************************************************************************/ @Nonnull protected Value literalFor (final boolean b) { <span class="nc" id="L469"> return FACTORY.createLiteral(b);</span> } /******************************************************************************************************************* * * Reads a SPARQL statement from a named resource * * @param clazz the reference class * @param name the resource name * @return the SPARQL statement * ******************************************************************************************************************/ @Nonnull protected static String readSparql (@Nonnull final Class&lt;?&gt; clazz, @Nonnull final String name) { <span class="fc" id="L484"> try (final InputStream is = clazz.getResourceAsStream(name))</span> { <span class="fc" id="L486"> return Stream.of(StreamUtils.copyToString(is, UTF_8)</span> <span class="fc" id="L487"> .split(&quot;\n&quot;))</span> <span class="fc bfc" id="L488" title="All 2 branches covered."> .filter(s -&gt; !s.matches(REGEX_COMMENT))</span> <span class="fc" id="L489"> .collect(joining(&quot;\n&quot;));</span> } <span class="nc" id="L491"> catch (IOException e)</span> { <span class="nc" id="L493"> throw new RuntimeException(e);</span> } } /******************************************************************************************************************* * * Instantiates an entity for each given {@link TupleQueryResult}. Entities are instantiated in the DCI contents * associated to this {@link Finder} - see {@link #getContexts()}. * * @param &lt;E&gt; the static type of the entities to instantiate * @param repository the repository we're querying * @param entityClass the dynamic type of the entities to instantiate * #param queryResult the {@code TupleQueryResult} * @return the instantiated entities * ******************************************************************************************************************/ @Nonnull private &lt;E&gt; List&lt;E&gt; createEntities (@Nonnull final Repository repository, @Nonnull final Class&lt;E&gt; entityClass, @Nonnull final TupleQueryResult queryResult) { <span class="fc" id="L514"> return contextManager.runWithContexts(getContexts(), new Task&lt;&gt;()</span> <span class="fc" id="L515"> {</span> @Override @Nonnull public List&lt;E&gt; run() { <span class="fc" id="L519"> return streamOf(queryResult)</span> <span class="fc" id="L520"> .map(bindingSet -&gt; entityFactory.createEntity(repository, entityClass, bindingSet))</span> <span class="fc" id="L521"> .collect(toList());</span> } }); // TODO: requires TheseFoolishThings 3.1-ALPHA-3 // return contextManager.runWithContexts(getContexts(), () -&gt; streamOf(queryResult) // .map(bindingSet -&gt; entityFactory.createEntity(repository, entityClass, bindingSet)) // .collect(toList())); } /******************************************************************************************************************* * * Returns {@code true} if the given string contains a binding tag (in the form {@code @TAG@}) that matches one * of the bindings; or if there are no binding tags. This is used as a filter to eliminate portions of SPARQL * queries that don't match any binding. * * @param string the string * @param bindings the bindings * @return {@code true} if there is a match * ******************************************************************************************************************/ private static boolean matchesTag (@Nonnull final String string, @Nonnull final Object[] bindings) { <span class="fc" id="L543"> final Pattern patternBindingTagLine = Pattern.compile(REGEX_BINDING_TAG_LINE);</span> <span class="fc" id="L544"> final Matcher matcher = patternBindingTagLine.matcher(string);</span> <span class="fc bfc" id="L546" title="All 2 branches covered."> if (!matcher.matches())</span> { <span class="fc" id="L548"> return true;</span> } <span class="fc" id="L551"> final String tag = matcher.group(1);</span> <span class="fc bfc" id="L553" title="All 2 branches covered."> for (int i = 0; i &lt; bindings.length; i+= 2)</span> { <span class="fc bfc" id="L555" title="All 2 branches covered."> if (tag.equals(bindings[i]))</span> { <span class="fc" id="L557"> return true;</span> } } <span class="fc" id="L561"> return false;</span> } /******************************************************************************************************************* * * Logs the query at various detail levels. * * @param originalSparql the original SPARQL statement * @param sparql the SPARQL statement after binding tag filtering * @param bindings the bindings * ******************************************************************************************************************/ private void log (@Nonnull final String originalSparql, @Nonnull final String sparql, @Nonnull final Object ... bindings) { <span class="pc bpc" id="L577" title="1 of 2 branches missed."> if (log.isTraceEnabled())</span> { <span class="nc" id="L579"> Stream.of(originalSparql.split(&quot;\n&quot;)).forEach(s -&gt; log.trace(&quot;&gt;&gt;&gt;&gt; original query: {}&quot;, s));</span> } <span class="pc bpc" id="L582" title="1 of 2 branches missed."> if (log.isDebugEnabled())</span> { <span class="nc" id="L584"> Stream.of(sparql.split(&quot;\n&quot;)).forEach(s -&gt; log.debug(&quot;&gt;&gt;&gt;&gt; query: {}&quot;, s));</span> } <span class="pc bpc" id="L587" title="2 of 4 branches missed."> if (!log.isDebugEnabled() &amp;&amp; log.isInfoEnabled())</span> { <span class="fc" id="L589"> log.info(&quot;&gt;&gt;&gt;&gt; query: {}&quot;, compacted(sparql));</span> } <span class="pc bpc" id="L592" title="1 of 2 branches missed."> if (log.isInfoEnabled())</span> { <span class="fc" id="L594"> log.info(&quot;&gt;&gt;&gt;&gt; query parameters: {}&quot;, Arrays.toString(bindings));</span> } <span class="fc" id="L596"> }</span> /******************************************************************************************************************* * * ******************************************************************************************************************/ @Nonnull private static String compacted (@Nonnull final String sparql) { <span class="fc" id="L605"> return sparql.replace(&quot;\n&quot;, &quot; &quot;).replaceAll(&quot;\\s+&quot;, &quot; &quot;).trim();</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>