<?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>CodecComponent.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">MTAS</a> &gt; <a href="index.source.html" class="el_package">mtas.codec.util</a> &gt; <span class="el_source">CodecComponent.java</span></div><h1>CodecComponent.java</h1><pre class="source lang-java linenums">package mtas.codec.util;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.StringReader;
import java.io.UnsupportedEncodingException;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Base64;
import java.util.Base64.Decoder;
import java.util.Base64.Encoder;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import mtas.analysis.token.MtasToken;
import mtas.analysis.token.MtasTokenString;
import mtas.codec.util.CodecSearchTree.MtasTreeHit;
import mtas.codec.util.collector.MtasDataCollector;
import mtas.parser.function.MtasFunctionParser;
import mtas.parser.function.ParseException;
import mtas.parser.function.util.MtasFunctionParserFunction;
import mtas.parser.function.util.MtasFunctionParserFunctionDefault;
import mtas.search.spans.util.MtasSpanQuery;

import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.ArrayUtils;
import org.apache.lucene.util.BytesRef;
import org.noggit.JSONParser;
import org.noggit.ObjectBuilder;

/**
 * The Class CodecComponent.
 */
public class CodecComponent {

  /**
   * Instantiates a new codec component.
   */
<span class="nc" id="L53">  private CodecComponent() {</span>
<span class="nc" id="L54">  }</span>

  /**
   * The Class ComponentFields.
   */
  public static class ComponentFields {

    /** The list. */
    public Map&lt;String, ComponentField&gt; list;

    /** The collection. */
    public List&lt;ComponentCollection&gt; collection;

    /** The do document. */
    public boolean doDocument;

    /** The do kwic. */
    public boolean doKwic;

    /** The do list. */
    public boolean doList;

    /** The do group. */
    public boolean doGroup;

    /** The do term vector. */
    public boolean doTermVector;

    /** The do stats. */
    public boolean doStats;

    /** The do stats spans. */
    public boolean doStatsSpans;

    /** The do stats positions. */
    public boolean doStatsPositions;

    /** The do stats tokens. */
    public boolean doStatsTokens;

    /** The do prefix. */
    public boolean doPrefix;

    /** The do facet. */
    public boolean doFacet;

    /** The do collection. */
    public boolean doCollection;

    /**
     * Instantiates a new component fields.
     */
<span class="fc" id="L106">    public ComponentFields() {</span>
<span class="fc" id="L107">      list = new HashMap&lt;&gt;();</span>
<span class="fc" id="L108">      collection = new ArrayList&lt;&gt;();</span>
<span class="fc" id="L109">      doDocument = false;</span>
<span class="fc" id="L110">      doKwic = false;</span>
<span class="fc" id="L111">      doList = false;</span>
<span class="fc" id="L112">      doGroup = false;</span>
<span class="fc" id="L113">      doStats = false;</span>
<span class="fc" id="L114">      doTermVector = false;</span>
<span class="fc" id="L115">      doStatsSpans = false;</span>
<span class="fc" id="L116">      doStatsPositions = false;</span>
<span class="fc" id="L117">      doStatsTokens = false;</span>
<span class="fc" id="L118">      doPrefix = false;</span>
<span class="fc" id="L119">      doFacet = false;</span>
<span class="fc" id="L120">      doCollection = false;</span>
<span class="fc" id="L121">    }</span>
  }

  /**
   * The Interface BasicComponent.
   */
  public abstract static interface BasicComponent {
  }

  /**
   * The Class ComponentField.
   */
  public static class ComponentField implements BasicComponent {

    /** The unique key field. */
    public String uniqueKeyField;

    /** The document list. */
    public List&lt;ComponentDocument&gt; documentList;

    /** The kwic list. */
    public List&lt;ComponentKwic&gt; kwicList;

    /** The list list. */
    public List&lt;ComponentList&gt; listList;

    /** The group list. */
    public List&lt;ComponentGroup&gt; groupList;

    /** The facet list. */
    public List&lt;ComponentFacet&gt; facetList;

    /** The term vector list. */
    public List&lt;ComponentTermVector&gt; termVectorList;

    /** The stats position list. */
    public List&lt;ComponentPosition&gt; statsPositionList;

    /** The stats token list. */
    public List&lt;ComponentToken&gt; statsTokenList;

    /** The stats span list. */
    public List&lt;ComponentSpan&gt; statsSpanList;

    /** The span query list. */
    public List&lt;MtasSpanQuery&gt; spanQueryList;

    /** The prefix. */
    public ComponentPrefix prefix;

    /**
     * Instantiates a new component field.
     *
     * @param uniqueKeyField the unique key field
     */
<span class="fc" id="L176">    public ComponentField(String uniqueKeyField) {</span>
<span class="fc" id="L177">      this.uniqueKeyField = uniqueKeyField;</span>
      // initialise
<span class="fc" id="L179">      documentList = new ArrayList&lt;&gt;();</span>
<span class="fc" id="L180">      kwicList = new ArrayList&lt;&gt;();</span>
<span class="fc" id="L181">      listList = new ArrayList&lt;&gt;();</span>
<span class="fc" id="L182">      groupList = new ArrayList&lt;&gt;();</span>
<span class="fc" id="L183">      facetList = new ArrayList&lt;&gt;();</span>
<span class="fc" id="L184">      termVectorList = new ArrayList&lt;&gt;();</span>
<span class="fc" id="L185">      statsPositionList = new ArrayList&lt;&gt;();</span>
<span class="fc" id="L186">      statsTokenList = new ArrayList&lt;&gt;();</span>
<span class="fc" id="L187">      statsSpanList = new ArrayList&lt;&gt;();</span>
<span class="fc" id="L188">      spanQueryList = new ArrayList&lt;&gt;();</span>
<span class="fc" id="L189">      prefix = null;</span>
<span class="fc" id="L190">    }</span>
  }

  /**
   * The Class ComponentPrefix.
   */
  public static class ComponentPrefix implements BasicComponent {

    /** The key. */
    public String key;

    /** The single position list. */
    public SortedSet&lt;String&gt; singlePositionList;

    /** The multiple position list. */
    public SortedSet&lt;String&gt; multiplePositionList;

    /** The set position list. */
    public SortedSet&lt;String&gt; setPositionList;

    /** The intersecting list. */
    public SortedSet&lt;String&gt; intersectingList;

    /**
     * Instantiates a new component prefix.
     *
     * @param key the key
     */
<span class="fc" id="L218">    public ComponentPrefix(String key) {</span>
<span class="fc" id="L219">      this.key = key;</span>
<span class="fc" id="L220">      singlePositionList = new TreeSet&lt;&gt;();</span>
<span class="fc" id="L221">      multiplePositionList = new TreeSet&lt;&gt;();</span>
<span class="fc" id="L222">      setPositionList = new TreeSet&lt;&gt;();</span>
<span class="fc" id="L223">      intersectingList = new TreeSet&lt;&gt;();</span>
<span class="fc" id="L224">    }</span>

    /**
     * Adds the single position.
     *
     * @param prefix the prefix
     */
    public void addSinglePosition(String prefix) {
<span class="pc bpc" id="L232" title="1 of 4 branches missed.">      if (!prefix.trim().isEmpty() &amp;&amp; !singlePositionList.contains(prefix)</span>
<span class="pc bpc" id="L233" title="1 of 2 branches missed.">          &amp;&amp; !multiplePositionList.contains(prefix)) {</span>
<span class="fc" id="L234">        singlePositionList.add(prefix);</span>
      }
<span class="fc" id="L236">    }</span>

    /**
     * Adds the multiple position.
     *
     * @param prefix the prefix
     */
    public void addMultiplePosition(String prefix) {
<span class="pc bpc" id="L244" title="1 of 2 branches missed.">      if (!prefix.trim().isEmpty()) {</span>
<span class="pc bpc" id="L245" title="1 of 2 branches missed.">        if (!singlePositionList.contains(prefix)) {</span>
<span class="fc bfc" id="L246" title="All 2 branches covered.">          if (!multiplePositionList.contains(prefix)) {</span>
<span class="fc" id="L247">            multiplePositionList.add(prefix);</span>
          }
        } else {
<span class="nc" id="L250">          singlePositionList.remove(prefix);</span>
<span class="nc" id="L251">          multiplePositionList.add(prefix);</span>
        }
      }
<span class="fc" id="L254">    }</span>

    /**
     * Adds the set position.
     *
     * @param prefix the prefix
     */
    public void addSetPosition(String prefix) {
<span class="pc bpc" id="L262" title="1 of 2 branches missed.">      if (!prefix.trim().isEmpty()) {</span>
<span class="pc bpc" id="L263" title="1 of 2 branches missed.">        if (!singlePositionList.contains(prefix)) {</span>
<span class="fc bfc" id="L264" title="All 2 branches covered.">          if (!setPositionList.contains(prefix)) {</span>
<span class="fc" id="L265">            setPositionList.add(prefix);</span>
          }
        } else {
<span class="nc" id="L268">          singlePositionList.remove(prefix);</span>
<span class="nc" id="L269">          setPositionList.add(prefix);</span>
        }
      }
<span class="fc" id="L272">    }</span>

    /**
     * Adds the intersecting.
     *
     * @param prefix the prefix
     */
    public void addIntersecting(String prefix) {
<span class="pc bpc" id="L280" title="1 of 2 branches missed.">      if (!prefix.trim().isEmpty()) {</span>
<span class="fc" id="L281">        intersectingList.add(prefix);</span>
      }
<span class="fc" id="L283">    }</span>

  }

  /**
   * The Class ComponentDocument.
   */
  public static class ComponentDocument implements BasicComponent {

    /** The key. */
    public String key;

    /** The prefix. */
    public String prefix;

    /** The regexp. */
    public String regexp;

    /** The ignore regexp. */
    public String ignoreRegexp;

    /** The list. */
    public Set&lt;String&gt; list;

    /** The ignore list. */
    public Set&lt;String&gt; ignoreList;

    /** The list regexp. */
    public boolean listRegexp;

    /** The list expand. */
    public boolean listExpand;

    /** The ignore list regexp. */
    public boolean ignoreListRegexp;

    /** The list expand number. */
    public int listExpandNumber;

    /** The data type. */
    public String dataType;

    /** The stats type. */
    public String statsType;

    /** The stats items. */
    public SortedSet&lt;String&gt; statsItems;

    /** The list number. */
    public int listNumber;

    /** The unique key. */
    public Map&lt;Integer, String&gt; uniqueKey;

    /** The stats data. */
    public Map&lt;Integer, MtasDataCollector&lt;?, ?&gt;&gt; statsData;

    /** The stats list. */
    public Map&lt;Integer, MtasDataCollector&lt;?, ?&gt;&gt; statsList;

    /**
     * Instantiates a new component document.
     *
     * @param key the key
     * @param prefix the prefix
     * @param statsType the stats type
     * @param regexp the regexp
     * @param list the list
     * @param listNumber the list number
     * @param listRegexp the list regexp
     * @param listExpand the list expand
     * @param listExpandNumber the list expand number
     * @param ignoreRegexp the ignore regexp
     * @param ignoreList the ignore list
     * @param ignoreListRegexp the ignore list regexp
     * @throws IOException Signals that an I/O exception has occurred.
     */
    public ComponentDocument(String key, String prefix, String statsType,
        String regexp, String[] list, int listNumber, Boolean listRegexp,
        Boolean listExpand, int listExpandNumber, String ignoreRegexp,
<span class="nc" id="L363">        String[] ignoreList, Boolean ignoreListRegexp) throws IOException {</span>
<span class="nc" id="L364">      this.key = key;</span>
<span class="nc" id="L365">      this.prefix = prefix;</span>
<span class="nc" id="L366">      this.regexp = regexp;</span>
<span class="nc bnc" id="L367" title="All 4 branches missed.">      if (list != null &amp;&amp; list.length &gt; 0) {</span>
<span class="nc" id="L368">        this.list = new HashSet&lt;&gt;(Arrays.asList(list));</span>
<span class="nc bnc" id="L369" title="All 2 branches missed.">        this.listRegexp = listRegexp != null ? listRegexp : false;</span>
<span class="nc bnc" id="L370" title="All 4 branches missed.">        this.listExpand = (listExpand != null &amp;&amp; listExpandNumber &gt; 0)</span>
<span class="nc" id="L371">            ? listExpand : false;</span>
<span class="nc bnc" id="L372" title="All 2 branches missed.">        if (this.listExpand) {</span>
<span class="nc" id="L373">          this.listExpandNumber = listExpandNumber;</span>
        } else {
<span class="nc" id="L375">          this.listExpandNumber = 0;</span>
        }
      } else {
<span class="nc" id="L378">        this.list = null;</span>
<span class="nc" id="L379">        this.listRegexp = false;</span>
<span class="nc" id="L380">        this.listExpand = false;</span>
<span class="nc" id="L381">        this.listExpandNumber = 0;</span>
      }
<span class="nc" id="L383">      this.ignoreRegexp = ignoreRegexp;</span>
<span class="nc bnc" id="L384" title="All 4 branches missed.">      if (ignoreList != null &amp;&amp; ignoreList.length &gt; 0) {</span>
<span class="nc" id="L385">        this.ignoreList = new HashSet&lt;&gt;(Arrays.asList(ignoreList));</span>
<span class="nc bnc" id="L386" title="All 2 branches missed.">        this.ignoreListRegexp = ignoreListRegexp != null ? ignoreListRegexp</span>
            : false;
      } else {
<span class="nc" id="L389">        this.ignoreList = null;</span>
<span class="nc" id="L390">        this.ignoreListRegexp = false;</span>
      }
<span class="nc" id="L392">      this.listNumber = listNumber;</span>
<span class="nc" id="L393">      uniqueKey = new HashMap&lt;&gt;();</span>
<span class="nc" id="L394">      dataType = CodecUtil.DATA_TYPE_LONG;</span>
<span class="nc" id="L395">      statsItems = CodecUtil.createStatsItems(statsType);</span>
<span class="nc" id="L396">      this.statsType = CodecUtil.createStatsType(statsItems, null, null);</span>
<span class="nc" id="L397">      this.statsData = new HashMap&lt;&gt;();</span>
<span class="nc bnc" id="L398" title="All 2 branches missed.">      if (this.listNumber &gt; 0) {</span>
<span class="nc" id="L399">        this.statsList = new HashMap&lt;&gt;();</span>
      } else {
<span class="nc" id="L401">        this.statsList = null;</span>
      }
<span class="nc" id="L403">    }</span>
  }

  /**
   * The Class ComponentKwic.
   */
  public static class ComponentKwic implements BasicComponent {

    /** The query. */
    public MtasSpanQuery query;

    /** The key. */
    public String key;

    /** The tokens. */
    public Map&lt;Integer, List&lt;KwicToken&gt;&gt; tokens;

    /** The hits. */
    public Map&lt;Integer, List&lt;KwicHit&gt;&gt; hits;

    /** The unique key. */
    public Map&lt;Integer, String&gt; uniqueKey;

    /** The sub total. */
    public Map&lt;Integer, Integer&gt; subTotal;

    /** The min position. */
    public Map&lt;Integer, Integer&gt; minPosition;

    /** The max position. */
    public Map&lt;Integer, Integer&gt; maxPosition;

    /** The prefixes. */
    public List&lt;String&gt; prefixes;

    /** The left. */
    public int left;

    /** The right. */
    public int right;

    /** The start. */
    public int start;

    /** The number. */
    public Integer number;

    /** The output. */
    public String output;

    /** The Constant KWIC_OUTPUT_TOKEN. */
    public static final String KWIC_OUTPUT_TOKEN = &quot;token&quot;;

    /** The Constant KWIC_OUTPUT_HIT. */
    public static final String KWIC_OUTPUT_HIT = &quot;hit&quot;;

    /**
     * Instantiates a new component kwic.
     *
     * @param query the query
     * @param key the key
     * @param prefixes the prefixes
     * @param number the number
     * @param start the start
     * @param left the left
     * @param right the right
     * @param output the output
     * @throws IOException Signals that an I/O exception has occurred.
     */
    public ComponentKwic(MtasSpanQuery query, String key, String prefixes,
        Integer number, int start, int left, int right, String output)
<span class="nc" id="L474">        throws IOException {</span>
<span class="nc" id="L475">      this.query = query;</span>
<span class="nc" id="L476">      this.key = key;</span>
<span class="nc bnc" id="L477" title="All 2 branches missed.">      this.left = (left &gt; 0) ? left : 0;</span>
<span class="nc bnc" id="L478" title="All 2 branches missed.">      this.right = (right &gt; 0) ? right : 0;</span>
<span class="nc bnc" id="L479" title="All 2 branches missed.">      this.start = (start &gt; 0) ? start : 0;</span>
<span class="nc bnc" id="L480" title="All 4 branches missed.">      this.number = (number != null &amp;&amp; number &gt;= 0) ? number : null;</span>
<span class="nc" id="L481">      this.output = output;</span>
<span class="nc" id="L482">      tokens = new HashMap&lt;&gt;();</span>
<span class="nc" id="L483">      hits = new HashMap&lt;&gt;();</span>
<span class="nc" id="L484">      uniqueKey = new HashMap&lt;&gt;();</span>
<span class="nc" id="L485">      subTotal = new HashMap&lt;&gt;();</span>
<span class="nc" id="L486">      minPosition = new HashMap&lt;&gt;();</span>
<span class="nc" id="L487">      maxPosition = new HashMap&lt;&gt;();</span>
<span class="nc" id="L488">      this.prefixes = new ArrayList&lt;&gt;();</span>
<span class="nc bnc" id="L489" title="All 4 branches missed.">      if ((prefixes != null) &amp;&amp; (prefixes.trim().length() &gt; 0)) {</span>
<span class="nc" id="L490">        List&lt;String&gt; l = Arrays.asList(prefixes.split(Pattern.quote(&quot;,&quot;)));</span>
<span class="nc bnc" id="L491" title="All 2 branches missed.">        for (String ls : l) {</span>
<span class="nc bnc" id="L492" title="All 2 branches missed.">          if (ls.trim().length() &gt; 0) {</span>
<span class="nc" id="L493">            this.prefixes.add(ls.trim());</span>
          }
<span class="nc" id="L495">        }</span>
      }
<span class="nc bnc" id="L497" title="All 2 branches missed.">      if (this.output == null) {</span>
<span class="nc bnc" id="L498" title="All 2 branches missed.">        if (!this.prefixes.isEmpty()) {</span>
<span class="nc" id="L499">          this.output = ComponentKwic.KWIC_OUTPUT_HIT;</span>
        } else {
<span class="nc" id="L501">          this.output = ComponentKwic.KWIC_OUTPUT_TOKEN;</span>
        }
<span class="nc bnc" id="L503" title="All 2 branches missed.">      } else if (!this.output.equals(ComponentKwic.KWIC_OUTPUT_HIT)</span>
<span class="nc bnc" id="L504" title="All 2 branches missed.">          &amp;&amp; !this.output.equals(ComponentKwic.KWIC_OUTPUT_TOKEN)) {</span>
<span class="nc" id="L505">        throw new IOException(&quot;unrecognized output '&quot; + this.output + &quot;'&quot;);</span>
      }
<span class="nc" id="L507">    }</span>
  }

  /**
   * The Class ComponentList.
   */
  public static class ComponentList implements BasicComponent {

    /** The span query. */
    public MtasSpanQuery spanQuery;

    /** The field. */
    public String field;

    /** The query value. */
    public String queryValue;

    /** The query type. */
    public String queryType;

    /** The query prefix. */
    public String queryPrefix;

    /** The query ignore. */
    public String queryIgnore;

    /** The query maximum ignore length. */
    public String queryMaximumIgnoreLength;

    /** The key. */
    public String key;

    /** The query variables. */
    public Map&lt;String, String[]&gt; queryVariables;

    /** The tokens. */
    public List&lt;ListToken&gt; tokens;

    /** The hits. */
    public List&lt;ListHit&gt; hits;

    /** The unique key. */
    public Map&lt;Integer, String&gt; uniqueKey;

    /** The sub total. */
    public Map&lt;Integer, Integer&gt; subTotal;

    /** The min position. */
    public Map&lt;Integer, Integer&gt; minPosition;

    /** The max position. */
    public Map&lt;Integer, Integer&gt; maxPosition;

    /** The prefixes. */
    public List&lt;String&gt; prefixes;

    /** The left. */
    public int left;

    /** The right. */
    public int right;

    /** The total. */
    public int total;

    /** The position. */
    public int position;

    /** The start. */
    public int start;

    /** The number. */
    public int number;

    /** The prefix. */
    public String prefix;

    /** The output. */
    public String output;

    /** The Constant LIST_OUTPUT_TOKEN. */
    public static final String LIST_OUTPUT_TOKEN = &quot;token&quot;;

    /** The Constant LIST_OUTPUT_HIT. */
    public static final String LIST_OUTPUT_HIT = &quot;hit&quot;;

    /**
     * Instantiates a new component list.
     *
     * @param spanQuery the span query
     * @param field the field
     * @param queryValue the query value
     * @param queryType the query type
     * @param queryPrefix the query prefix
     * @param queryVariables the query variables
     * @param queryIgnore the query ignore
     * @param queryMaximumIgnoreLength the query maximum ignore length
     * @param key the key
     * @param prefix the prefix
     * @param start the start
     * @param number the number
     * @param left the left
     * @param right the right
     * @param output the output
     * @throws IOException Signals that an I/O exception has occurred.
     */
    public ComponentList(MtasSpanQuery spanQuery, String field,
        String queryValue, String queryType, String queryPrefix,
        Map&lt;String, String[]&gt; queryVariables, String queryIgnore,
        String queryMaximumIgnoreLength, String key, String prefix, int start,
<span class="nc" id="L617">        int number, int left, int right, String output) throws IOException {</span>
<span class="nc" id="L618">      this.spanQuery = spanQuery;</span>
<span class="nc" id="L619">      this.field = field;</span>
<span class="nc" id="L620">      this.queryValue = queryValue;</span>
<span class="nc" id="L621">      this.queryType = queryType;</span>
<span class="nc" id="L622">      this.queryPrefix = queryPrefix;</span>
<span class="nc" id="L623">      this.queryIgnore = queryIgnore;</span>
<span class="nc" id="L624">      this.queryMaximumIgnoreLength = queryMaximumIgnoreLength;</span>
<span class="nc" id="L625">      this.queryVariables = queryVariables;</span>
<span class="nc" id="L626">      this.key = key;</span>
<span class="nc" id="L627">      this.left = left;</span>
<span class="nc" id="L628">      this.right = right;</span>
<span class="nc" id="L629">      this.start = start;</span>
<span class="nc" id="L630">      this.number = number;</span>
<span class="nc" id="L631">      this.output = output;</span>
<span class="nc" id="L632">      this.prefix = prefix;</span>
<span class="nc" id="L633">      total = 0;</span>
<span class="nc" id="L634">      position = 0;</span>
<span class="nc" id="L635">      tokens = new ArrayList&lt;&gt;();</span>
<span class="nc" id="L636">      hits = new ArrayList&lt;&gt;();</span>
<span class="nc" id="L637">      uniqueKey = new HashMap&lt;&gt;();</span>
<span class="nc" id="L638">      subTotal = new HashMap&lt;&gt;();</span>
<span class="nc" id="L639">      minPosition = new HashMap&lt;&gt;();</span>
<span class="nc" id="L640">      maxPosition = new HashMap&lt;&gt;();</span>
<span class="nc" id="L641">      this.prefixes = new ArrayList&lt;&gt;();</span>
<span class="nc bnc" id="L642" title="All 4 branches missed.">      if ((prefix != null) &amp;&amp; (prefix.trim().length() &gt; 0)) {</span>
<span class="nc" id="L643">        List&lt;String&gt; l = Arrays.asList(prefix.split(Pattern.quote(&quot;,&quot;)));</span>
<span class="nc bnc" id="L644" title="All 2 branches missed.">        for (String ls : l) {</span>
<span class="nc bnc" id="L645" title="All 2 branches missed.">          if (ls.trim().length() &gt; 0) {</span>
<span class="nc" id="L646">            this.prefixes.add(ls.trim());</span>
          }
<span class="nc" id="L648">        }</span>
      }
      // check output
<span class="nc bnc" id="L651" title="All 2 branches missed.">      if (this.output == null) {</span>
<span class="nc bnc" id="L652" title="All 2 branches missed.">        if (!this.prefixes.isEmpty()) {</span>
<span class="nc" id="L653">          this.output = ComponentList.LIST_OUTPUT_HIT;</span>
        } else {
<span class="nc" id="L655">          this.output = ComponentList.LIST_OUTPUT_TOKEN;</span>
        }
<span class="nc bnc" id="L657" title="All 2 branches missed.">      } else if (!this.output.equals(ComponentList.LIST_OUTPUT_HIT)</span>
<span class="nc bnc" id="L658" title="All 2 branches missed.">          &amp;&amp; !this.output.equals(ComponentList.LIST_OUTPUT_TOKEN)) {</span>
<span class="nc" id="L659">        throw new IOException(&quot;unrecognized output '&quot; + this.output + &quot;'&quot;);</span>
      }
<span class="nc" id="L661">    }</span>
  }

  /**
   * The Class ComponentGroup.
   */
  public static class ComponentGroup implements BasicComponent {

    /** The span query. */
    public MtasSpanQuery spanQuery;

    /** The data type. */
    public String dataType;

    /** The stats type. */
    public String statsType;

    /** The sort type. */
    public String sortType;

    /** The sort direction. */
    public String sortDirection;

    /** The stats items. */
    public SortedSet&lt;String&gt; statsItems;

    /** The start. */
    public Integer start;

    /** The number. */
    public Integer number;

    /** The key. */
    public String key;

    /** The data collector. */
    public MtasDataCollector&lt;?, ?&gt; dataCollector;

    /** The prefixes. */
    ArrayList&lt;String&gt; prefixes;

    /** The hit inside. */
    HashSet&lt;String&gt; hitInside;

    /** The hit inside left. */
    HashSet&lt;String&gt;[] hitInsideLeft;

    /** The hit inside right. */
    HashSet&lt;String&gt;[] hitInsideRight;

    /** The hit left. */
    HashSet&lt;String&gt;[] hitLeft;

    /** The hit right. */
    HashSet&lt;String&gt;[] hitRight;

    /** The left. */
    HashSet&lt;String&gt;[] left;

    /** The right. */
    HashSet&lt;String&gt;[] right;

    /**
     * Instantiates a new component group.
     *
     * @param spanQuery the span query
     * @param key the key
     * @param number the number
     * @param groupingHitInsidePrefixes the grouping hit inside prefixes
     * @param groupingHitInsideLeftPosition the grouping hit inside left position
     * @param groupingHitInsideLeftPrefixes the grouping hit inside left prefixes
     * @param groupingHitInsideRightPosition the grouping hit inside right position
     * @param groupingHitInsideRightPrefixes the grouping hit inside right prefixes
     * @param groupingHitLeftPosition the grouping hit left position
     * @param groupingHitLeftPrefixes the grouping hit left prefixes
     * @param groupingHitRightPosition the grouping hit right position
     * @param groupingHitRightPrefixes the grouping hit right prefixes
     * @param groupingLeftPosition the grouping left position
     * @param groupingLeftPrefixes the grouping left prefixes
     * @param groupingRightPosition the grouping right position
     * @param groupingRightPrefixes the grouping right prefixes
     * @throws IOException Signals that an I/O exception has occurred.
     */
    public ComponentGroup(MtasSpanQuery spanQuery, String key, int number,
        String groupingHitInsidePrefixes,
        String[] groupingHitInsideLeftPosition,
        String[] groupingHitInsideLeftPrefixes,
        String[] groupingHitInsideRightPosition,
        String[] groupingHitInsideRightPrefixes,
        String[] groupingHitLeftPosition, String[] groupingHitLeftPrefixes,
        String[] groupingHitRightPosition, String[] groupingHitRightPrefixes,
        String[] groupingLeftPosition, String[] groupingLeftPrefixes,
        String[] groupingRightPosition, String[] groupingRightPrefixes)
<span class="fc" id="L754">        throws IOException {</span>
<span class="fc" id="L755">      this.spanQuery = spanQuery;</span>
<span class="fc" id="L756">      this.key = key;</span>
<span class="fc" id="L757">      this.dataType = CodecUtil.DATA_TYPE_LONG;</span>
<span class="fc" id="L758">      this.sortType = CodecUtil.STATS_TYPE_SUM;</span>
<span class="fc" id="L759">      this.sortDirection = CodecUtil.SORT_DESC;</span>
<span class="fc" id="L760">      this.statsItems = CodecUtil.createStatsItems(&quot;n,sum,mean&quot;);</span>
<span class="fc" id="L761">      this.statsType = CodecUtil.createStatsType(this.statsItems, this.sortType,</span>
          null);
<span class="fc" id="L763">      this.start = 0;</span>
<span class="fc" id="L764">      this.number = number;</span>
<span class="fc" id="L765">      HashSet&lt;String&gt; tmpPrefixes = new HashSet&lt;&gt;();</span>
      // analyze grouping condition
<span class="pc bpc" id="L767" title="1 of 2 branches missed.">      if (groupingHitInsidePrefixes != null) {</span>
<span class="fc" id="L768">        hitInside = new HashSet&lt;&gt;();</span>
<span class="fc" id="L769">        String[] tmpList = groupingHitInsidePrefixes.split(&quot;,&quot;);</span>
<span class="fc bfc" id="L770" title="All 2 branches covered.">        for (String tmpItem : tmpList) {</span>
<span class="pc bpc" id="L771" title="1 of 2 branches missed.">          if (!tmpItem.trim().isEmpty()) {</span>
<span class="fc" id="L772">            hitInside.add(tmpItem.trim());</span>
          }
        }
<span class="fc" id="L775">        tmpPrefixes.addAll(hitInside);</span>
<span class="fc" id="L776">      } else {</span>
<span class="nc" id="L777">        hitInside = null;</span>
      }
<span class="fc" id="L779">      hitInsideLeft = createPositionedPrefixes(tmpPrefixes,</span>
          groupingHitInsideLeftPosition, groupingHitInsideLeftPrefixes);
<span class="fc" id="L781">      hitInsideRight = createPositionedPrefixes(tmpPrefixes,</span>
          groupingHitInsideRightPosition, groupingHitInsideRightPrefixes);
<span class="fc" id="L783">      hitLeft = createPositionedPrefixes(tmpPrefixes, groupingHitLeftPosition,</span>
          groupingHitLeftPrefixes);
<span class="fc" id="L785">      hitRight = createPositionedPrefixes(tmpPrefixes, groupingHitRightPosition,</span>
          groupingHitRightPrefixes);
<span class="fc" id="L787">      left = createPositionedPrefixes(tmpPrefixes, groupingLeftPosition,</span>
          groupingLeftPrefixes);
<span class="fc" id="L789">      right = createPositionedPrefixes(tmpPrefixes, groupingRightPosition,</span>
          groupingRightPrefixes);
<span class="fc" id="L791">      prefixes = new ArrayList&lt;&gt;(tmpPrefixes);</span>
      // datacollector
<span class="fc" id="L793">      dataCollector = DataCollector.getCollector(</span>
          DataCollector.COLLECTOR_TYPE_LIST, this.dataType, this.statsType,
          this.statsItems, this.sortType, this.sortDirection, this.start,
          this.number, null, null);
<span class="fc" id="L797">    }</span>

    /**
     * Creates the positioned prefixes.
     *
     * @param prefixList the prefix list
     * @param position the position
     * @param prefixes the prefixes
     * @return the hash set[]
     * @throws IOException Signals that an I/O exception has occurred.
     */
    private static HashSet&lt;String&gt;[] createPositionedPrefixes(
        HashSet&lt;String&gt; prefixList, String[] position, String[] prefixes)
        throws IOException {
<span class="fc" id="L811">      Pattern p = Pattern.compile(&quot;^([0-9]+)(\\-([0-9]+))?$&quot;);</span>
      Matcher m;
<span class="pc bpc" id="L813" title="2 of 4 branches missed.">      if (position == null &amp;&amp; prefixes == null) {</span>
<span class="fc" id="L814">        return null;</span>
<span class="nc bnc" id="L815" title="All 6 branches missed.">      } else if (prefixes == null || position == null</span>
          || position.length != prefixes.length) {
<span class="nc" id="L817">        throw new IOException(&quot;incorrect position/prefixes&quot;);</span>
<span class="nc bnc" id="L818" title="All 2 branches missed.">      } else if (position.length == 0) {</span>
<span class="nc" id="L819">        return null;</span>
      } else {
        // analyze positions
<span class="nc" id="L822">        int[][] tmpPosition = new int[position.length][];</span>
<span class="nc" id="L823">        int maxPosition = -1;</span>
<span class="nc bnc" id="L824" title="All 2 branches missed.">        for (int i = 0; i &lt; position.length; i++) {</span>
<span class="nc" id="L825">          m = p.matcher(position[i]);</span>
<span class="nc bnc" id="L826" title="All 2 branches missed.">          if (m.find()) {</span>
<span class="nc bnc" id="L827" title="All 2 branches missed.">            if (m.group(3) == null) {</span>
<span class="nc" id="L828">              int start = Integer.parseInt(m.group(1));</span>
<span class="nc" id="L829">              tmpPosition[i] = new int[] { start };</span>
<span class="nc" id="L830">              maxPosition = Math.max(maxPosition, start);</span>
<span class="nc" id="L831">            } else {</span>
<span class="nc" id="L832">              int start = Integer.parseInt(m.group(1));</span>
<span class="nc" id="L833">              int end = Integer.parseInt(m.group(3));</span>
<span class="nc bnc" id="L834" title="All 2 branches missed.">              if (start &gt; end) {</span>
<span class="nc" id="L835">                throw new IOException(&quot;incorrect position &quot; + position[i]);</span>
              } else {
<span class="nc" id="L837">                tmpPosition[i] = new int[end - start + 1];</span>
<span class="nc bnc" id="L838" title="All 2 branches missed.">                for (int t = start; t &lt;= end; t++)</span>
<span class="nc" id="L839">                  tmpPosition[i][t - start] = t;</span>
<span class="nc" id="L840">                maxPosition = Math.max(maxPosition, end);</span>
              }
<span class="nc" id="L842">            }</span>
          } else {
<span class="nc" id="L844">            throw new IOException(&quot;incorrect position &quot; + position[i]);</span>
          }
        }
        @SuppressWarnings(&quot;unchecked&quot;)
<span class="nc" id="L848">        HashSet&lt;String&gt;[] result = new HashSet[maxPosition + 1];</span>
<span class="nc" id="L849">        Arrays.fill(result, null);</span>
        List&lt;String&gt; tmpPrefixList;
        String[] tmpList;
<span class="nc bnc" id="L852" title="All 2 branches missed.">        for (int i = 0; i &lt; tmpPosition.length; i++) {</span>
<span class="nc" id="L853">          tmpList = prefixes[i].split(&quot;,&quot;);</span>
<span class="nc" id="L854">          tmpPrefixList = new ArrayList&lt;&gt;();</span>
<span class="nc bnc" id="L855" title="All 2 branches missed.">          for (String tmpItem : tmpList) {</span>
<span class="nc bnc" id="L856" title="All 2 branches missed.">            if (!tmpItem.trim().isEmpty()) {</span>
<span class="nc" id="L857">              tmpPrefixList.add(tmpItem.trim());</span>
            }
          }
<span class="nc bnc" id="L860" title="All 2 branches missed.">          if (tmpPrefixList.isEmpty()) {</span>
<span class="nc" id="L861">            throw new IOException(&quot;incorrect prefixes &quot; + prefixes[i]);</span>
          }
<span class="nc bnc" id="L863" title="All 2 branches missed.">          for (int t = 0; t &lt; tmpPosition[i].length; t++) {</span>
<span class="nc bnc" id="L864" title="All 2 branches missed.">            if (result[tmpPosition[i][t]] == null) {</span>
<span class="nc" id="L865">              result[tmpPosition[i][t]] = new HashSet&lt;&gt;();</span>
            }
<span class="nc" id="L867">            result[tmpPosition[i][t]].addAll(tmpPrefixList);</span>
          }
<span class="nc" id="L869">          prefixList.addAll(tmpPrefixList);</span>
        }
<span class="nc" id="L871">        return result;</span>
      }
    }

  }

  /**
   * The Class ComponentFacet.
   */
  public static class ComponentFacet implements BasicComponent {

    /** The span queries. */
    public MtasSpanQuery[] spanQueries;

    /** The base fields. */
    public String[] baseFields;

    /** The base field types. */
    public String[] baseFieldTypes;

    /** The base types. */
    public String[] baseTypes;

    /** The base sort types. */
    public String[] baseSortTypes;

    /** The base sort directions. */
    public String[] baseSortDirections;

    /** The base range sizes. */
    public Double[] baseRangeSizes;

    /** The base range bases. */
    public Double[] baseRangeBases;

    /** The base collector types. */
    public String[] baseCollectorTypes;

    /** The base data types. */
    public String[] baseDataTypes;

    /** The base stats types. */
    public String[] baseStatsTypes;

    /** The base stats items. */
    public SortedSet&lt;String&gt;[] baseStatsItems;

    /** The key. */
    public String key;

    /** The data collector. */
    public MtasDataCollector&lt;?, ?&gt; dataCollector;

    /** The base function list. */
    public HashMap&lt;MtasDataCollector&lt;?, ?&gt;, SubComponentFunction[]&gt;[] baseFunctionList;

    /** The base numbers. */
    public Integer[] baseNumbers;

    /** The base minimum longs. */
    public Long[] baseMinimumLongs;

    /** The base maximum longs. */
    public Long[] baseMaximumLongs;

    /** The base parsers. */
    public MtasFunctionParserFunction[] baseParsers;

    /** The base function keys. */
    public String[][] baseFunctionKeys;

    /** The base function expressions. */
    public String[][] baseFunctionExpressions;

    /** The base function types. */
    public String[][] baseFunctionTypes;

    /** The base function parser functions. */
    public MtasFunctionParserFunction[][] baseFunctionParserFunctions;

    /** The Constant TYPE_INTEGER. */
    public static final String TYPE_INTEGER = &quot;integer&quot;;

    /** The Constant TYPE_DOUBLE. */
    public static final String TYPE_DOUBLE = &quot;double&quot;;

    /** The Constant TYPE_LONG. */
    public static final String TYPE_LONG = &quot;long&quot;;

    /** The Constant TYPE_FLOAT. */
    public static final String TYPE_FLOAT = &quot;float&quot;;

    /** The Constant TYPE_STRING. */
    public static final String TYPE_STRING = &quot;string&quot;;

    /**
     * Instantiates a new component facet.
     *
     * @param spanQueries the span queries
     * @param field the field
     * @param key the key
     * @param baseFields the base fields
     * @param baseFieldTypes the base field types
     * @param baseTypes the base types
     * @param baseRangeSizes the base range sizes
     * @param baseRangeBases the base range bases
     * @param baseSortTypes the base sort types
     * @param baseSortDirections the base sort directions
     * @param baseNumbers the base numbers
     * @param baseMinimumDoubles the base minimum doubles
     * @param baseMaximumDoubles the base maximum doubles
     * @param baseFunctionKeys the base function keys
     * @param baseFunctionExpressions the base function expressions
     * @param baseFunctionTypes the base function types
     * @throws IOException Signals that an I/O exception has occurred.
     * @throws ParseException the parse exception
     */
    @SuppressWarnings(&quot;unchecked&quot;)
    public ComponentFacet(MtasSpanQuery[] spanQueries, String field, String key,
        String[] baseFields, String[] baseFieldTypes, String[] baseTypes,
        Double[] baseRangeSizes, Double[] baseRangeBases,
        String[] baseSortTypes, String[] baseSortDirections,
        Integer[] baseNumbers, Double[] baseMinimumDoubles,
        Double[] baseMaximumDoubles, String[][] baseFunctionKeys,
        String[][] baseFunctionExpressions, String[][] baseFunctionTypes)
<span class="nc" id="L996">        throws IOException, ParseException {</span>
<span class="nc" id="L997">      this.spanQueries = (MtasSpanQuery[]) spanQueries.clone();</span>
<span class="nc" id="L998">      this.key = key;</span>
<span class="nc" id="L999">      this.baseFields = (String[]) baseFields.clone();</span>
<span class="nc" id="L1000">      this.baseFieldTypes = (String[]) baseFieldTypes.clone();</span>
<span class="nc" id="L1001">      this.baseTypes = (String[]) baseTypes.clone();</span>
<span class="nc" id="L1002">      this.baseRangeSizes = (Double[]) baseRangeSizes.clone();</span>
<span class="nc" id="L1003">      this.baseRangeBases = (Double[]) baseRangeBases.clone();</span>
<span class="nc" id="L1004">      this.baseSortTypes = (String[]) baseSortTypes.clone();</span>
<span class="nc" id="L1005">      this.baseSortDirections = (String[]) baseSortDirections.clone();</span>
<span class="nc" id="L1006">      this.baseNumbers = (Integer[]) baseNumbers.clone();</span>
      // compute types
<span class="nc" id="L1008">      this.baseMinimumLongs = new Long[baseFields.length];</span>
<span class="nc" id="L1009">      this.baseMaximumLongs = new Long[baseFields.length];</span>
<span class="nc" id="L1010">      this.baseCollectorTypes = new String[baseFields.length];</span>
<span class="nc" id="L1011">      this.baseStatsItems = new SortedSet[baseFields.length];</span>
<span class="nc" id="L1012">      this.baseStatsTypes = new String[baseFields.length];</span>
<span class="nc" id="L1013">      this.baseDataTypes = new String[baseFields.length];</span>
<span class="nc" id="L1014">      this.baseParsers = new MtasFunctionParserFunction[baseFields.length];</span>
<span class="nc" id="L1015">      this.baseFunctionList = new HashMap[baseFields.length];</span>
<span class="nc" id="L1016">      this.baseFunctionParserFunctions = new MtasFunctionParserFunction[baseFields.length][];</span>
<span class="nc bnc" id="L1017" title="All 2 branches missed.">      for (int i = 0; i &lt; baseFields.length; i++) {</span>
<span class="nc bnc" id="L1018" title="All 2 branches missed.">        if (baseMinimumDoubles[i] != null) {</span>
<span class="nc" id="L1019">          this.baseMinimumLongs[i] = baseMinimumDoubles[i].longValue();</span>
        } else {
<span class="nc" id="L1021">          this.baseMinimumLongs[i] = null;</span>
        }
<span class="nc bnc" id="L1023" title="All 2 branches missed.">        if (baseMaximumDoubles[i] != null) {</span>
<span class="nc" id="L1024">          this.baseMaximumLongs[i] = baseMaximumDoubles[i].longValue();</span>
        } else {
<span class="nc" id="L1026">          this.baseMaximumLongs[i] = null;</span>
        }
<span class="nc" id="L1028">        baseDataTypes[i] = CodecUtil.DATA_TYPE_LONG;</span>
<span class="nc" id="L1029">        baseFunctionList[i] = new HashMap&lt;&gt;();</span>
<span class="nc" id="L1030">        baseFunctionParserFunctions[i] = null;</span>
<span class="nc" id="L1031">        baseParsers[i] = new MtasFunctionParserFunctionDefault(</span>
            this.spanQueries.length);
<span class="nc bnc" id="L1033" title="All 2 branches missed.">        if (this.baseSortDirections[i] == null) {</span>
<span class="nc" id="L1034">          this.baseSortDirections[i] = CodecUtil.SORT_ASC;</span>
<span class="nc bnc" id="L1035" title="All 2 branches missed.">        } else if (!this.baseSortDirections[i].equals(CodecUtil.SORT_ASC)</span>
<span class="nc bnc" id="L1036" title="All 2 branches missed.">            &amp;&amp; !this.baseSortDirections[i].equals(CodecUtil.SORT_DESC)) {</span>
<span class="nc" id="L1037">          throw new IOException(</span>
              &quot;unrecognized sortDirection &quot; + this.baseSortDirections[i]);
        }
<span class="nc bnc" id="L1040" title="All 2 branches missed.">        if (this.baseSortTypes[i] == null) {</span>
<span class="nc" id="L1041">          this.baseSortTypes[i] = CodecUtil.SORT_TERM;</span>
<span class="nc bnc" id="L1042" title="All 2 branches missed.">        } else if (!this.baseSortTypes[i].equals(CodecUtil.SORT_TERM)</span>
<span class="nc bnc" id="L1043" title="All 2 branches missed.">            &amp;&amp; !CodecUtil.isStatsType(this.baseSortTypes[i])) {</span>
<span class="nc" id="L1044">          throw new IOException(</span>
              &quot;unrecognized sortType &quot; + this.baseSortTypes[i]);
        }
<span class="nc" id="L1047">        this.baseCollectorTypes[i] = DataCollector.COLLECTOR_TYPE_LIST;</span>
<span class="nc" id="L1048">        this.baseStatsItems[i] = CodecUtil.createStatsItems(this.baseTypes[i]);</span>
<span class="nc" id="L1049">        this.baseStatsTypes[i] = CodecUtil.createStatsType(baseStatsItems[i],</span>
            this.baseSortTypes[i], new MtasFunctionParserFunctionDefault(1));
      }
      boolean doFunctions;
<span class="nc bnc" id="L1053" title="All 6 branches missed.">      doFunctions = baseFunctionKeys != null &amp;&amp; baseFunctionExpressions != null</span>
          &amp;&amp; baseFunctionTypes != null;
<span class="nc bnc" id="L1055" title="All 4 branches missed.">      doFunctions = doFunctions ? baseFunctionKeys.length == baseFields.length</span>
          : false;
<span class="nc bnc" id="L1057" title="All 4 branches missed.">      doFunctions = doFunctions ? baseFunctionTypes.length == baseFields.length</span>
          : false;
<span class="nc bnc" id="L1059" title="All 2 branches missed.">      if (doFunctions) {</span>
<span class="nc" id="L1060">        this.baseFunctionKeys = new String[baseFields.length][];</span>
<span class="nc" id="L1061">        this.baseFunctionExpressions = new String[baseFields.length][];</span>
<span class="nc" id="L1062">        this.baseFunctionTypes = new String[baseFields.length][];</span>
<span class="nc bnc" id="L1063" title="All 2 branches missed.">        for (int i = 0; i &lt; baseFields.length; i++) {</span>
<span class="nc bnc" id="L1064" title="All 4 branches missed.">          if (baseFunctionKeys[i].length == baseFunctionExpressions[i].length</span>
              &amp;&amp; baseFunctionKeys[i].length == baseFunctionTypes[i].length) {
<span class="nc" id="L1066">            this.baseFunctionKeys[i] = new String[baseFunctionKeys[i].length];</span>
<span class="nc" id="L1067">            this.baseFunctionExpressions[i] = new String[baseFunctionExpressions[i].length];</span>
<span class="nc" id="L1068">            this.baseFunctionTypes[i] = new String[baseFunctionTypes[i].length];</span>
<span class="nc" id="L1069">            baseFunctionParserFunctions[i] = new MtasFunctionParserFunction[baseFunctionExpressions[i].length];</span>
<span class="nc bnc" id="L1070" title="All 2 branches missed.">            for (int j = 0; j &lt; baseFunctionKeys[i].length; j++) {</span>
<span class="nc" id="L1071">              this.baseFunctionKeys[i][j] = baseFunctionKeys[i][j];</span>
<span class="nc" id="L1072">              this.baseFunctionExpressions[i][j] = baseFunctionExpressions[i][j];</span>
<span class="nc" id="L1073">              this.baseFunctionTypes[i][j] = baseFunctionTypes[i][j];</span>
<span class="nc" id="L1074">              baseFunctionParserFunctions[i][j] = new MtasFunctionParser(</span>
                  new BufferedReader(
<span class="nc" id="L1076">                      new StringReader(baseFunctionExpressions[i][j]))).parse();</span>
            }
          } else {
<span class="nc" id="L1079">            this.baseFunctionKeys[i] = new String[0];</span>
<span class="nc" id="L1080">            this.baseFunctionExpressions[i] = new String[0];</span>
<span class="nc" id="L1081">            this.baseFunctionTypes[i] = new String[0];</span>
<span class="nc" id="L1082">            baseFunctionParserFunctions[i] = new MtasFunctionParserFunction[0];</span>
          }
        }
      }
<span class="nc bnc" id="L1086" title="All 2 branches missed.">      if (baseFields.length &gt; 0) {</span>
<span class="nc bnc" id="L1087" title="All 2 branches missed.">        if (baseFields.length == 1) {</span>
<span class="nc" id="L1088">          dataCollector = DataCollector.getCollector(this.baseCollectorTypes[0],</span>
              this.baseDataTypes[0], this.baseStatsTypes[0],
              this.baseStatsItems[0], this.baseSortTypes[0],
<span class="nc" id="L1091">              this.baseSortDirections[0], 0, this.baseNumbers[0], null, null);</span>
        } else {
<span class="nc" id="L1093">          String[] subBaseCollectorTypes = Arrays</span>
<span class="nc" id="L1094">              .copyOfRange(baseCollectorTypes, 1, baseDataTypes.length);</span>
<span class="nc" id="L1095">          String[] subBaseDataTypes = Arrays.copyOfRange(baseDataTypes, 1,</span>
              baseDataTypes.length);
<span class="nc" id="L1097">          String[] subBaseStatsTypes = Arrays.copyOfRange(baseStatsTypes, 1,</span>
              baseStatsTypes.length);
<span class="nc" id="L1099">          SortedSet&lt;String&gt;[] subBaseStatsItems = Arrays</span>
<span class="nc" id="L1100">              .copyOfRange(baseStatsItems, 1, baseStatsItems.length);</span>
<span class="nc" id="L1101">          String[] subBaseSortTypes = Arrays.copyOfRange(baseSortTypes, 1,</span>
              baseSortTypes.length);
<span class="nc" id="L1103">          String[] subBaseSortDirections = Arrays</span>
<span class="nc" id="L1104">              .copyOfRange(baseSortDirections, 1, baseSortDirections.length);</span>
<span class="nc" id="L1105">          Integer[] subNumbers = Arrays.copyOfRange(baseNumbers, 1,</span>
              baseNumbers.length);
<span class="nc" id="L1107">          Integer[] subStarts = ArrayUtils.toObject(new int[subNumbers.length]);</span>
<span class="nc" id="L1108">          dataCollector = DataCollector.getCollector(this.baseCollectorTypes[0],</span>
              this.baseDataTypes[0], this.baseStatsTypes[0],
              this.baseStatsItems[0], this.baseSortTypes[0],
<span class="nc" id="L1111">              this.baseSortDirections[0], 0, this.baseNumbers[0],</span>
              subBaseCollectorTypes, subBaseDataTypes, subBaseStatsTypes,
              subBaseStatsItems, subBaseSortTypes, subBaseSortDirections,
              subStarts, subNumbers, null, null);
<span class="nc" id="L1115">        }</span>
      } else {
<span class="nc" id="L1117">        throw new IOException(&quot;no baseFields&quot;);</span>
      }
<span class="nc" id="L1119">    }</span>

    /**
     * Function sum rule.
     *
     * @return true, if successful
     */
    public boolean functionSumRule() {
<span class="nc bnc" id="L1127" title="All 2 branches missed.">      if (baseFunctionParserFunctions != null) {</span>
<span class="nc bnc" id="L1128" title="All 2 branches missed.">        for (int i = 0; i &lt; baseFields.length; i++) {</span>
<span class="nc bnc" id="L1129" title="All 2 branches missed.">          for (MtasFunctionParserFunction function : baseFunctionParserFunctions[i]) {</span>
<span class="nc bnc" id="L1130" title="All 2 branches missed.">            if (!function.sumRule()) {</span>
<span class="nc" id="L1131">              return false;</span>
            }
          }
        }
      }
<span class="nc" id="L1136">      return true;</span>
    }

    /**
     * Function need positions.
     *
     * @return true, if successful
     */
    public boolean functionNeedPositions() {
<span class="nc bnc" id="L1145" title="All 2 branches missed.">      if (baseFunctionParserFunctions != null) {</span>
<span class="nc bnc" id="L1146" title="All 2 branches missed.">        for (int i = 0; i &lt; baseFields.length; i++) {</span>
<span class="nc bnc" id="L1147" title="All 2 branches missed.">          for (MtasFunctionParserFunction function : baseFunctionParserFunctions[i]) {</span>
<span class="nc bnc" id="L1148" title="All 2 branches missed.">            if (function.needPositions()) {</span>
<span class="nc" id="L1149">              return true;</span>
            }
          }
        }
      }
<span class="nc" id="L1154">      return false;</span>
    }

    /**
     * Base parser sum rule.
     *
     * @return true, if successful
     */
    public boolean baseParserSumRule() {
<span class="nc bnc" id="L1163" title="All 2 branches missed.">      for (int i = 0; i &lt; baseFields.length; i++) {</span>
<span class="nc bnc" id="L1164" title="All 2 branches missed.">        if (!baseParsers[i].sumRule()) {</span>
<span class="nc" id="L1165">          return false;</span>
        }
      }
<span class="nc" id="L1168">      return true;</span>
    }

    /**
     * Base parser need positions.
     *
     * @return true, if successful
     */
    public boolean baseParserNeedPositions() {
<span class="nc bnc" id="L1177" title="All 2 branches missed.">      for (int i = 0; i &lt; baseFields.length; i++) {</span>
<span class="nc bnc" id="L1178" title="All 2 branches missed.">        if (baseParsers[i].needPositions()) {</span>
<span class="nc" id="L1179">          return true;</span>
        }
      }
<span class="nc" id="L1182">      return false;</span>
    }

  }

  /**
   * The Class ComponentTermVector.
   */
  public static class ComponentTermVector implements BasicComponent {

    /** The key. */
    public String key;

    /** The prefix. */
    public String prefix;

    /** The regexp. */
    public String regexp;

    /** The ignore regexp. */
    public String ignoreRegexp;

    /** The boundary. */
    public String boundary;

    /** The full. */
    public boolean full;

    /** The list. */
    public Set&lt;String&gt; list;

    /** The ignore list. */
    public Set&lt;String&gt; ignoreList;

    /** The list regexp. */
    public boolean listRegexp;

    /** The ignore list regexp. */
    public boolean ignoreListRegexp;

    /** The functions. */
    public List&lt;SubComponentFunction&gt; functions;

    /** The number. */
    public int number;

    /** The start value. */
    public BytesRef startValue;

    /** The sub component function. */
    public SubComponentFunction subComponentFunction;

    /** The boundary registration. */
    public boolean boundaryRegistration;

    /** The sort type. */
    public String sortType;

    /** The sort direction. */
    public String sortDirection;

    /**
     * Instantiates a new component term vector.
     *
     * @param key the key
     * @param prefix the prefix
     * @param regexp the regexp
     * @param full the full
     * @param type the type
     * @param sortType the sort type
     * @param sortDirection the sort direction
     * @param startValue the start value
     * @param number the number
     * @param functionKey the function key
     * @param functionExpression the function expression
     * @param functionType the function type
     * @param boundary the boundary
     * @param list the list
     * @param listRegexp the list regexp
     * @param ignoreRegexp the ignore regexp
     * @param ignoreList the ignore list
     * @param ignoreListRegexp the ignore list regexp
     * @throws IOException Signals that an I/O exception has occurred.
     * @throws ParseException the parse exception
     */
    @SuppressWarnings({ &quot;unchecked&quot;, &quot;rawtypes&quot; })
    public ComponentTermVector(String key, String prefix, String regexp,
        Boolean full, String type, String sortType, String sortDirection,
        String startValue, int number, String[] functionKey,
        String[] functionExpression, String[] functionType, String boundary,
        String[] list, Boolean listRegexp, String ignoreRegexp,
        String[] ignoreList, Boolean ignoreListRegexp)
<span class="fc" id="L1274">        throws IOException, ParseException {</span>
<span class="fc" id="L1275">      this.key = key;</span>
<span class="fc" id="L1276">      this.prefix = prefix;</span>
<span class="fc" id="L1277">      this.regexp = regexp;</span>
<span class="pc bpc" id="L1278" title="1 of 4 branches missed.">      this.full = (full != null &amp;&amp; full) ? true : false;</span>
<span class="fc bfc" id="L1279" title="All 2 branches covered.">      if (sortType == null) {</span>
<span class="fc" id="L1280">        this.sortType = CodecUtil.SORT_TERM;</span>
      } else {
<span class="fc" id="L1282">        this.sortType = sortType;</span>
      }
<span class="fc bfc" id="L1284" title="All 2 branches covered.">      if (sortDirection == null) {</span>
<span class="pc bpc" id="L1285" title="1 of 2 branches missed.">        if (this.sortType.equals(CodecUtil.SORT_TERM)) {</span>
<span class="fc" id="L1286">          this.sortDirection = CodecUtil.SORT_ASC;</span>
        } else {
<span class="nc" id="L1288">          this.sortDirection = CodecUtil.SORT_DESC;</span>
        }
      } else {
<span class="fc" id="L1291">        this.sortDirection = sortDirection;</span>
      }
<span class="pc bpc" id="L1293" title="1 of 4 branches missed.">      if (list != null &amp;&amp; list.length &gt; 0) {</span>
<span class="fc" id="L1294">        this.list = new HashSet(Arrays.asList(list));</span>
<span class="pc bpc" id="L1295" title="1 of 2 branches missed.">        this.listRegexp = listRegexp != null ? listRegexp : false;</span>
<span class="fc" id="L1296">        this.boundary = null;</span>
<span class="fc" id="L1297">        this.number = Integer.MAX_VALUE;</span>
<span class="pc bpc" id="L1298" title="1 of 2 branches missed.">        if (!this.full) {</span>
<span class="fc" id="L1299">          this.sortType = CodecUtil.SORT_TERM;</span>
<span class="fc" id="L1300">          this.sortDirection = CodecUtil.SORT_ASC;</span>
        }
      } else {
<span class="fc" id="L1303">        this.list = null;</span>
<span class="fc" id="L1304">        this.listRegexp = false;</span>
<span class="pc bpc" id="L1305" title="1 of 2 branches missed.">        this.startValue = (startValue != null)</span>
            ? new BytesRef(prefix + MtasToken.DELIMITER + startValue) : null;
<span class="pc bpc" id="L1307" title="1 of 2 branches missed.">        if (boundary == null) {</span>
<span class="fc" id="L1308">          this.boundary = null;</span>
<span class="pc bpc" id="L1309" title="1 of 2 branches missed.">          if (number &lt; -1) {</span>
<span class="nc" id="L1310">            throw new IOException(&quot;number should not be &quot; + number);</span>
<span class="fc bfc" id="L1311" title="All 2 branches covered.">          } else if (number &gt;= 0) {</span>
<span class="fc" id="L1312">            this.number = number;</span>
          } else {
<span class="pc bpc" id="L1314" title="1 of 2 branches missed.">            if (!full) {</span>
<span class="nc" id="L1315">              throw new IOException(</span>
                  &quot;number &quot; + number + &quot; only supported for full termvector&quot;);
            } else {
<span class="fc" id="L1318">              this.number = Integer.MAX_VALUE;</span>
            }
          }
        } else {
<span class="nc" id="L1322">          this.boundary = boundary;</span>
<span class="nc" id="L1323">          this.number = Integer.MAX_VALUE;</span>
        }
      }
<span class="fc" id="L1326">      this.ignoreRegexp = ignoreRegexp;</span>
<span class="pc bpc" id="L1327" title="1 of 4 branches missed.">      if (ignoreList != null &amp;&amp; ignoreList.length &gt; 0) {</span>
<span class="fc" id="L1328">        this.ignoreList = new HashSet(Arrays.asList(ignoreList));</span>
<span class="pc bpc" id="L1329" title="1 of 2 branches missed.">        this.ignoreListRegexp = ignoreListRegexp != null ? ignoreListRegexp</span>
            : false;
      } else {
<span class="fc" id="L1332">        this.ignoreList = null;</span>
<span class="fc" id="L1333">        this.ignoreListRegexp = false;</span>
      }
<span class="fc" id="L1335">      functions = new ArrayList&lt;&gt;();</span>
<span class="pc bpc" id="L1336" title="2 of 6 branches missed.">      if (functionKey != null &amp;&amp; functionExpression != null</span>
          &amp;&amp; functionType != null) {
<span class="pc bpc" id="L1338" title="2 of 4 branches missed.">        if (functionKey.length == functionExpression.length</span>
            &amp;&amp; functionKey.length == functionType.length) {
<span class="pc bpc" id="L1340" title="1 of 2 branches missed.">          for (int i = 0; i &lt; functionKey.length; i++) {</span>
<span class="nc" id="L1341">            functions</span>
<span class="nc" id="L1342">                .add(new SubComponentFunction(DataCollector.COLLECTOR_TYPE_LIST,</span>
                    functionKey[i], functionExpression[i], functionType[i]));
          }
        }
      }
<span class="fc bfc" id="L1347" title="All 2 branches covered.">      if (!this.sortType.equals(CodecUtil.SORT_TERM)</span>
<span class="pc bpc" id="L1348" title="1 of 2 branches missed.">          &amp;&amp; !CodecUtil.isStatsType(this.sortType)) {</span>
<span class="nc" id="L1349">        throw new IOException(&quot;unknown sortType '&quot; + this.sortType + &quot;'&quot;);</span>
<span class="fc bfc" id="L1350" title="All 4 branches covered.">      } else if (!full &amp;&amp; !this.sortType.equals(CodecUtil.SORT_TERM)) {</span>
<span class="pc bpc" id="L1351" title="1 of 2 branches missed.">        if (!(this.sortType.equals(CodecUtil.STATS_TYPE_SUM)</span>
<span class="nc bnc" id="L1352" title="All 2 branches missed.">            || this.sortType.equals(CodecUtil.STATS_TYPE_N))) {</span>
<span class="nc" id="L1353">          throw new IOException(&quot;sortType '&quot; + this.sortType</span>
              + &quot;' only supported with full termVector&quot;);
        }
      }
<span class="fc bfc" id="L1357" title="All 2 branches covered.">      if (!this.sortType.equals(CodecUtil.SORT_TERM)) {</span>
<span class="pc bpc" id="L1358" title="1 of 2 branches missed.">        if (startValue != null) {</span>
<span class="nc" id="L1359">          throw new IOException(&quot;startValue '&quot; + startValue</span>
              + &quot;' only supported with termVector sorted on &quot;
              + CodecUtil.SORT_TERM);
        }
      }
<span class="fc bfc" id="L1364" title="All 2 branches covered.">      if (!this.sortDirection.equals(CodecUtil.SORT_ASC)</span>
<span class="pc bpc" id="L1365" title="1 of 2 branches missed.">          &amp;&amp; !this.sortDirection.equals(CodecUtil.SORT_DESC)) {</span>
<span class="nc" id="L1366">        throw new IOException(</span>
            &quot;unrecognized sortDirection '&quot; + this.sortDirection + &quot;'&quot;);
      }
<span class="pc bpc" id="L1369" title="1 of 2 branches missed.">      boundaryRegistration = this.boundary != null;</span>
<span class="fc" id="L1370">      String segmentRegistration = null;</span>
<span class="fc bfc" id="L1371" title="All 2 branches covered.">      if (this.full) {</span>
<span class="fc" id="L1372">        this.boundary = null;</span>
<span class="fc" id="L1373">        segmentRegistration = null;</span>
<span class="pc bpc" id="L1374" title="1 of 2 branches missed.">      } else if (this.boundary != null) {</span>
<span class="nc bnc" id="L1375" title="All 2 branches missed.">        if (this.sortDirection.equals(CodecUtil.SORT_ASC)) {</span>
<span class="nc" id="L1376">          segmentRegistration = MtasDataCollector.SEGMENT_BOUNDARY_ASC;</span>
<span class="nc bnc" id="L1377" title="All 2 branches missed.">        } else if (this.sortDirection.equals(CodecUtil.SORT_DESC)) {</span>
<span class="nc" id="L1378">          segmentRegistration = MtasDataCollector.SEGMENT_BOUNDARY_DESC;</span>
        }
<span class="fc bfc" id="L1380" title="All 2 branches covered.">      } else if (!this.sortType.equals(CodecUtil.SORT_TERM)) {</span>
<span class="fc bfc" id="L1381" title="All 2 branches covered.">        if (this.sortDirection.equals(CodecUtil.SORT_ASC)) {</span>
<span class="fc" id="L1382">          segmentRegistration = MtasDataCollector.SEGMENT_SORT_ASC;</span>
<span class="pc bpc" id="L1383" title="1 of 2 branches missed.">        } else if (this.sortDirection.equals(CodecUtil.SORT_DESC)) {</span>
<span class="fc" id="L1384">          segmentRegistration = MtasDataCollector.SEGMENT_SORT_DESC;</span>
        }
      }
      // create main subComponentFunction
<span class="fc" id="L1388">      this.subComponentFunction = new SubComponentFunction(</span>
          DataCollector.COLLECTOR_TYPE_LIST, key, type,
          new MtasFunctionParserFunctionDefault(1), this.sortType,
<span class="fc" id="L1391">          this.sortDirection, 0, this.number, segmentRegistration, boundary);</span>
<span class="fc" id="L1392">    }</span>

    /**
     * Function sum rule.
     *
     * @return true, if successful
     */
    public boolean functionSumRule() {
<span class="nc bnc" id="L1400" title="All 2 branches missed.">      if (functions != null) {</span>
<span class="nc bnc" id="L1401" title="All 2 branches missed.">        for (SubComponentFunction function : functions) {</span>
<span class="nc bnc" id="L1402" title="All 2 branches missed.">          if (!function.parserFunction.sumRule()) {</span>
<span class="nc" id="L1403">            return false;</span>
          }
<span class="nc" id="L1405">        }</span>
      }
<span class="nc" id="L1407">      return true;</span>
    }

    /**
     * Function need positions.
     *
     * @return true, if successful
     */
    public boolean functionNeedPositions() {
<span class="pc bpc" id="L1416" title="1 of 2 branches missed.">      if (functions != null) {</span>
<span class="pc bpc" id="L1417" title="1 of 2 branches missed.">        for (SubComponentFunction function : functions) {</span>
<span class="nc bnc" id="L1418" title="All 2 branches missed.">          if (function.parserFunction.needPositions()) {</span>
<span class="nc" id="L1419">            return true;</span>
          }
<span class="nc" id="L1421">        }</span>
      }
<span class="fc" id="L1423">      return false;</span>
    }

  }

  /**
   * The Interface ComponentStats.
   */
  public abstract static interface ComponentStats extends BasicComponent {
  }

  /**
   * The Class ComponentSpan.
   */
  public static class ComponentSpan implements ComponentStats {

    /** The queries. */
    public MtasSpanQuery[] queries;

    /** The key. */
    public String key;

    /** The data type. */
    public String dataType;

    /** The stats type. */
    public String statsType;

    /** The stats items. */
    public SortedSet&lt;String&gt; statsItems;

    /** The minimum long. */
    public Long minimumLong;

    /** The maximum long. */
    public Long maximumLong;

    /** The data collector. */
    public MtasDataCollector&lt;?, ?&gt; dataCollector;

    /** The functions. */
    public List&lt;SubComponentFunction&gt; functions;

    /** The parser. */
    public MtasFunctionParserFunction parser;

    /**
     * Instantiates a new component span.
     *
     * @param queries the queries
     * @param key the key
     * @param minimumDouble the minimum double
     * @param maximumDouble the maximum double
     * @param type the type
     * @param functionKey the function key
     * @param functionExpression the function expression
     * @param functionType the function type
     * @throws IOException Signals that an I/O exception has occurred.
     * @throws ParseException the parse exception
     */
    public ComponentSpan(MtasSpanQuery[] queries, String key,
        Double minimumDouble, Double maximumDouble, String type,
        String[] functionKey, String[] functionExpression,
<span class="fc" id="L1486">        String[] functionType) throws IOException, ParseException {</span>
<span class="fc" id="L1487">      this.queries = (MtasSpanQuery[]) queries.clone();</span>
<span class="fc" id="L1488">      this.key = key;</span>
<span class="fc" id="L1489">      functions = new ArrayList&lt;&gt;();</span>
<span class="pc bpc" id="L1490" title="2 of 6 branches missed.">      if (functionKey != null &amp;&amp; functionExpression != null</span>
          &amp;&amp; functionType != null) {
<span class="pc bpc" id="L1492" title="2 of 4 branches missed.">        if (functionKey.length == functionExpression.length</span>
            &amp;&amp; functionKey.length == functionType.length) {
<span class="fc bfc" id="L1494" title="All 2 branches covered.">          for (int i = 0; i &lt; functionKey.length; i++) {</span>
<span class="fc" id="L1495">            functions</span>
<span class="fc" id="L1496">                .add(new SubComponentFunction(DataCollector.COLLECTOR_TYPE_DATA,</span>
                    functionKey[i], functionExpression[i], functionType[i]));
          }
        }
      }
<span class="fc" id="L1501">      parser = new MtasFunctionParserFunctionDefault(queries.length);</span>
<span class="fc" id="L1502">      dataType = parser.getType();</span>
<span class="fc" id="L1503">      statsItems = CodecUtil.createStatsItems(type);</span>
<span class="fc" id="L1504">      statsType = CodecUtil.createStatsType(this.statsItems, null, parser);</span>
<span class="fc bfc" id="L1505" title="All 2 branches covered.">      if (minimumDouble != null) {</span>
<span class="fc" id="L1506">        this.minimumLong = minimumDouble.longValue();</span>
      } else {
<span class="fc" id="L1508">        this.minimumLong = null;</span>
      }
<span class="fc bfc" id="L1510" title="All 2 branches covered.">      if (maximumDouble != null) {</span>
<span class="fc" id="L1511">        this.maximumLong = maximumDouble.longValue();</span>
      } else {
<span class="fc" id="L1513">        this.maximumLong = null;</span>
      }
<span class="fc" id="L1515">      dataCollector = DataCollector.getCollector(</span>
          DataCollector.COLLECTOR_TYPE_DATA, dataType, this.statsType,
          this.statsItems, null, null, null, null, null, null);
<span class="fc" id="L1518">    }</span>

    /**
     * Function sum rule.
     *
     * @return true, if successful
     */
    public boolean functionSumRule() {
<span class="pc bpc" id="L1526" title="1 of 2 branches missed.">      if (functions != null) {</span>
<span class="fc bfc" id="L1527" title="All 2 branches covered.">        for (SubComponentFunction function : functions) {</span>
<span class="pc bpc" id="L1528" title="1 of 2 branches missed.">          if (!function.parserFunction.sumRule()) {</span>
<span class="nc" id="L1529">            return false;</span>
          }
<span class="fc" id="L1531">        }</span>
      }
<span class="fc" id="L1533">      return true;</span>
    }

    /**
     * Function basic.
     *
     * @return true, if successful
     */
    public boolean functionBasic() {
<span class="pc bpc" id="L1542" title="1 of 2 branches missed.">      if (functions != null) {</span>
<span class="fc bfc" id="L1543" title="All 2 branches covered.">        for (SubComponentFunction function : functions) {</span>
<span class="pc bpc" id="L1544" title="1 of 2 branches missed.">          if (!function.statsType.equals(CodecUtil.STATS_BASIC)) {</span>
<span class="nc" id="L1545">            return false;</span>
          }
<span class="fc" id="L1547">        }</span>
      }
<span class="fc" id="L1549">      return true;</span>
    }

    /**
     * Function need positions.
     *
     * @return true, if successful
     */
    public boolean functionNeedPositions() {
<span class="pc bpc" id="L1558" title="1 of 2 branches missed.">      if (functions != null) {</span>
<span class="fc bfc" id="L1559" title="All 2 branches covered.">        for (SubComponentFunction function : functions) {</span>
<span class="pc bpc" id="L1560" title="1 of 2 branches missed.">          if (function.parserFunction.needPositions()) {</span>
<span class="nc" id="L1561">            return true;</span>
          }
<span class="fc" id="L1563">        }</span>
      }
<span class="fc" id="L1565">      return false;</span>
    }

    /**
     * Function need arguments.
     *
     * @return the sets the
     */
    public Set&lt;Integer&gt; functionNeedArguments() {
<span class="fc" id="L1574">      Set&lt;Integer&gt; list = new HashSet&lt;&gt;();</span>
<span class="pc bpc" id="L1575" title="1 of 2 branches missed.">      if (functions != null) {</span>
<span class="fc bfc" id="L1576" title="All 2 branches covered.">        for (SubComponentFunction function : functions) {</span>
<span class="fc" id="L1577">          list.addAll(function.parserFunction.needArgument());</span>
<span class="fc" id="L1578">        }</span>
      }
<span class="fc" id="L1580">      return list;</span>
    }

  }

  /**
   * The Class ComponentPosition.
   */
  public static class ComponentPosition implements ComponentStats {

    /** The key. */
    public String key;

    /** The data type. */
    public String dataType;

    /** The stats type. */
    public String statsType;

    /** The stats items. */
    public SortedSet&lt;String&gt; statsItems;

    /** The minimum long. */
    public Long minimumLong;

    /** The maximum long. */
    public Long maximumLong;

    /** The data collector. */
    public MtasDataCollector&lt;?, ?&gt; dataCollector;

    /**
     * Instantiates a new component position.
     *
     * @param key the key
     * @param minimumDouble the minimum double
     * @param maximumDouble the maximum double
     * @param statsType the stats type
     * @throws IOException Signals that an I/O exception has occurred.
     * @throws ParseException the parse exception
     */
    public ComponentPosition(String key, Double minimumDouble,
        Double maximumDouble, String statsType)
<span class="fc" id="L1623">        throws IOException, ParseException {</span>
<span class="fc" id="L1624">      this.key = key;</span>
<span class="fc" id="L1625">      dataType = CodecUtil.DATA_TYPE_LONG;</span>
<span class="fc" id="L1626">      this.statsItems = CodecUtil.createStatsItems(statsType);</span>
<span class="fc" id="L1627">      this.statsType = CodecUtil.createStatsType(this.statsItems, null, null);</span>
<span class="fc bfc" id="L1628" title="All 2 branches covered.">      if (minimumDouble != null) {</span>
<span class="fc" id="L1629">        this.minimumLong = minimumDouble.longValue();</span>
      } else {
<span class="fc" id="L1631">        this.minimumLong = null;</span>
      }
<span class="fc bfc" id="L1633" title="All 2 branches covered.">      if (maximumDouble != null) {</span>
<span class="fc" id="L1634">        this.maximumLong = maximumDouble.longValue();</span>
      } else {
<span class="fc" id="L1636">        this.maximumLong = null;</span>
      }
<span class="fc" id="L1638">      dataCollector = DataCollector.getCollector(</span>
          DataCollector.COLLECTOR_TYPE_DATA, dataType, this.statsType,
          this.statsItems, null, null, null, null, null, null);
<span class="fc" id="L1641">    }</span>
  }

  /**
   * The Class ComponentToken.
   */
  public static class ComponentToken implements ComponentStats {

    /** The key. */
    public String key;

    /** The data type. */
    public String dataType;

    /** The stats type. */
    public String statsType;

    /** The stats items. */
    public SortedSet&lt;String&gt; statsItems;

    /** The minimum long. */
    public Long minimumLong;

    /** The maximum long. */
    public Long maximumLong;

    /** The data collector. */
    public MtasDataCollector&lt;?, ?&gt; dataCollector;

    /**
     * Instantiates a new component token.
     *
     * @param key the key
     * @param minimumDouble the minimum double
     * @param maximumDouble the maximum double
     * @param statsType the stats type
     * @throws IOException Signals that an I/O exception has occurred.
     * @throws ParseException the parse exception
     */
    public ComponentToken(String key, Double minimumDouble,
        Double maximumDouble, String statsType)
<span class="fc" id="L1682">        throws IOException, ParseException {</span>
<span class="fc" id="L1683">      this.key = key;</span>
<span class="fc" id="L1684">      dataType = CodecUtil.DATA_TYPE_LONG;</span>
<span class="fc" id="L1685">      this.statsItems = CodecUtil.createStatsItems(statsType);</span>
<span class="fc" id="L1686">      this.statsType = CodecUtil.createStatsType(this.statsItems, null, null);</span>
<span class="fc bfc" id="L1687" title="All 2 branches covered.">      if (minimumDouble != null) {</span>
<span class="fc" id="L1688">        this.minimumLong = minimumDouble.longValue();</span>
      } else {
<span class="fc" id="L1690">        this.minimumLong = null;</span>
      }
<span class="fc bfc" id="L1692" title="All 2 branches covered.">      if (maximumDouble != null) {</span>
<span class="fc" id="L1693">        this.maximumLong = maximumDouble.longValue();</span>
      } else {
<span class="fc" id="L1695">        this.maximumLong = null;</span>
      }
<span class="fc" id="L1697">      dataCollector = DataCollector.getCollector(</span>
          DataCollector.COLLECTOR_TYPE_DATA, dataType, this.statsType,
          this.statsItems, null, null, null, null, null, null);
<span class="fc" id="L1700">    }</span>
  }

  /**
   * The Class ComponentCollection.
   */
  public static class ComponentCollection implements BasicComponent {

    /** The Constant ACTION_CREATE. */
    public static final String ACTION_CREATE = &quot;create&quot;;

    /** The Constant ACTION_CHECK. */
    public static final String ACTION_CHECK = &quot;check&quot;;

    /** The Constant ACTION_LIST. */
    public static final String ACTION_LIST = &quot;list&quot;;

    /** The Constant ACTION_POST. */
    public static final String ACTION_POST = &quot;post&quot;;

    /** The Constant ACTION_IMPORT. */
    public static final String ACTION_IMPORT = &quot;import&quot;;

    /** The Constant ACTION_DELETE. */
    public static final String ACTION_DELETE = &quot;delete&quot;;

    /** The Constant ACTION_EMPTY. */
    public static final String ACTION_EMPTY = &quot;empty&quot;;

    /** The Constant ACTION_GET. */
    public static final String ACTION_GET = &quot;get&quot;;

    /** The key. */
    public String key;

    /** The version. */
    public String version;

    /** The id. */
    public String id;

    /** The action. */
    private String action;

    /** The fields. */
    private Set&lt;String&gt; fields;

    /** The values. */
    private HashSet&lt;String&gt; values;

    /**
     * Instantiates a new component collection.
     *
     * @param key the key
     * @param action the action
     */
<span class="fc" id="L1756">    public ComponentCollection(String key, String action) {</span>
<span class="fc" id="L1757">      this.key = key;</span>
<span class="fc" id="L1758">      this.action = action;</span>
<span class="fc" id="L1759">      this.version = null;</span>
<span class="fc" id="L1760">      values = new HashSet&lt;&gt;();</span>
<span class="fc" id="L1761">    }</span>

    /**
     * Sets the list variables.
     *
     * @throws IOException Signals that an I/O exception has occurred.
     */
    public void setListVariables() throws IOException {
<span class="pc bpc" id="L1769" title="1 of 2 branches missed.">      if (action.equals(ACTION_LIST)) {</span>
        // do nothing
      } else {
<span class="nc" id="L1772">        throw new IOException(&quot;not allowed with action &quot; + action);</span>
      }
<span class="fc" id="L1774">    }</span>

    /**
     * Sets the create variables.
     *
     * @param id the id
     * @param fields the fields
     * @throws IOException Signals that an I/O exception has occurred.
     */
    public void setCreateVariables(String id, Set&lt;String&gt; fields)
        throws IOException {
<span class="pc bpc" id="L1785" title="1 of 2 branches missed.">      if (action.equals(ACTION_CREATE)) {</span>
<span class="fc" id="L1786">        this.id = id;</span>
<span class="fc" id="L1787">        this.fields = fields;</span>
      } else {
<span class="nc" id="L1789">        throw new IOException(&quot;not allowed with action &quot; + action);</span>
      }
<span class="fc" id="L1791">    }</span>

    /**
     * Sets the check variables.
     *
     * @param id the new check variables
     * @throws IOException Signals that an I/O exception has occurred.
     */
    public void setCheckVariables(String id) throws IOException {
<span class="pc bpc" id="L1800" title="1 of 2 branches missed.">      if (action.equals(ACTION_CHECK)) {</span>
<span class="fc" id="L1801">        this.id = id;</span>
      } else {
<span class="nc" id="L1803">        throw new IOException(&quot;not allowed with action &quot; + action);</span>
      }
<span class="fc" id="L1805">    }</span>

    /**
     * Sets the gets the variables.
     *
     * @param id the new gets the variables
     * @throws IOException Signals that an I/O exception has occurred.
     */
    public void setGetVariables(String id) throws IOException {
<span class="nc bnc" id="L1814" title="All 2 branches missed.">      if (action.equals(ACTION_GET)) {</span>
<span class="nc" id="L1815">        this.id = id;</span>
      } else {
<span class="nc" id="L1817">        throw new IOException(&quot;not allowed with action &quot; + action);</span>
      }
<span class="nc" id="L1819">    }</span>

    /**
     * Sets the post variables.
     *
     * @param id the id
     * @param values the values
     * @throws IOException Signals that an I/O exception has occurred.
     */
    public void setPostVariables(String id, HashSet&lt;String&gt; values)
        throws IOException {
<span class="pc bpc" id="L1830" title="1 of 2 branches missed.">      if (action.equals(ACTION_POST)) {</span>
<span class="fc" id="L1831">        this.id = id;</span>
<span class="fc" id="L1832">        this.values = values;</span>
      } else {
<span class="nc" id="L1834">        throw new IOException(&quot;not allowed with action &quot; + action);</span>
      }
<span class="fc" id="L1836">    }</span>

    /**
     * Sets the import variables.
     *
     * @param id the id
     * @param url the url
     * @param collection the collection
     * @throws IOException Signals that an I/O exception has occurred.
     */
    public void setImportVariables(String id, String url, String collection)
        throws IOException {
<span class="nc bnc" id="L1848" title="All 2 branches missed.">      if (action.equals(ACTION_IMPORT)) {</span>
<span class="nc" id="L1849">        this.id = id;</span>
<span class="nc" id="L1850">        StringBuilder importUrlBuffer = new StringBuilder(url);</span>
<span class="nc" id="L1851">        importUrlBuffer.append(&quot;select&quot;);</span>
<span class="nc" id="L1852">        importUrlBuffer.append(&quot;?q=*:*&amp;rows=0&amp;wt=json&quot;);</span>
<span class="nc" id="L1853">        importUrlBuffer.append(&quot;&amp;mtas=true&amp;mtas.collection=true&quot;);</span>
<span class="nc" id="L1854">        importUrlBuffer.append(&quot;&amp;mtas.collection.0.key=0&quot;);</span>
<span class="nc" id="L1855">        importUrlBuffer.append(&quot;&amp;mtas.collection.0.action=get&quot;);</span>
<span class="nc" id="L1856">        importUrlBuffer.append(</span>
<span class="nc" id="L1857">            &quot;&amp;mtas.collection.0.id=&quot; + URLEncoder.encode(collection, &quot;UTF-8&quot;));</span>
<span class="nc" id="L1858">        Map&lt;String, Object&gt; params = getImport(importUrlBuffer.toString());</span>
        try {
<span class="nc bnc" id="L1860" title="All 4 branches missed.">          if (params.containsKey(&quot;mtas&quot;) &amp;&amp; params.get(&quot;mtas&quot;) instanceof Map) {</span>
<span class="nc" id="L1861">            Map&lt;String, Object&gt; mtasParams = (Map&lt;String, Object&gt;) params</span>
<span class="nc" id="L1862">                .get(&quot;mtas&quot;);</span>
<span class="nc bnc" id="L1863" title="All 2 branches missed.">            if (mtasParams.containsKey(&quot;collection&quot;)</span>
<span class="nc bnc" id="L1864" title="All 2 branches missed.">                &amp;&amp; mtasParams.get(&quot;collection&quot;) instanceof List) {</span>
<span class="nc" id="L1865">              List&lt;Object&gt; mtasCollectionList = (List&lt;Object&gt;) mtasParams</span>
<span class="nc" id="L1866">                  .get(&quot;collection&quot;);</span>
<span class="nc bnc" id="L1867" title="All 2 branches missed.">              if (mtasCollectionList.size() == 1</span>
<span class="nc bnc" id="L1868" title="All 2 branches missed.">                  &amp;&amp; mtasCollectionList.get(0) instanceof Map) {</span>
<span class="nc" id="L1869">                Map&lt;String, Object&gt; collectionData = (Map&lt;String, Object&gt;) mtasCollectionList</span>
<span class="nc" id="L1870">                    .get(0);</span>
<span class="nc bnc" id="L1871" title="All 2 branches missed.">                if (collectionData.containsKey(&quot;values&quot;)</span>
<span class="nc bnc" id="L1872" title="All 2 branches missed.">                    &amp;&amp; collectionData.get(&quot;values&quot;) instanceof List) {</span>
<span class="nc" id="L1873">                  List&lt;String&gt; valuesList = (List&lt;String&gt;) collectionData</span>
<span class="nc" id="L1874">                      .get(&quot;values&quot;);</span>
<span class="nc bnc" id="L1875" title="All 2 branches missed.">                  for (String valueItem : valuesList) {</span>
<span class="nc" id="L1876">                    values.add(valueItem);</span>
<span class="nc" id="L1877">                  }</span>
<span class="nc" id="L1878">                } else {</span>
<span class="nc" id="L1879">                  throw new IOException(&quot;no values in response&quot;);</span>
                }
<span class="nc" id="L1881">              } else {</span>
<span class="nc" id="L1882">                throw new IOException(</span>
                    &quot;no valid mtas collection item in response&quot;);
              }
<span class="nc" id="L1885">            } else {</span>
<span class="nc" id="L1886">              throw new IOException(&quot;no valid mtas collection in response&quot;);</span>
            }
<span class="nc" id="L1888">          } else {</span>
<span class="nc" id="L1889">            throw new IOException(&quot;no valid mtas in response&quot;);</span>
          }
<span class="nc" id="L1891">        } catch (ClassCastException e) {</span>
<span class="nc" id="L1892">          throw new IOException(&quot;unexpected response&quot;, e);</span>
<span class="nc" id="L1893">        }</span>
<span class="nc" id="L1894">      } else {</span>
<span class="nc" id="L1895">        throw new IOException(&quot;not allowed with action &quot; + action);</span>
      }
<span class="nc" id="L1897">    }</span>

    /**
     * Gets the import.
     *
     * @param collectionGetUrl the collection get url
     * @return the import
     * @throws IOException Signals that an I/O exception has occurred.
     */
    private Map&lt;String, Object&gt; getImport(String collectionGetUrl)
        throws IOException {
      // get data
<span class="nc" id="L1909">      URL url = new URL(collectionGetUrl);</span>
<span class="nc" id="L1910">      HttpURLConnection connection = (HttpURLConnection) url.openConnection();</span>
<span class="nc" id="L1911">      connection.setDoOutput(false);</span>
<span class="nc" id="L1912">      connection.setDoInput(true);</span>
<span class="nc" id="L1913">      connection.setInstanceFollowRedirects(false);</span>
<span class="nc" id="L1914">      connection.setRequestMethod(&quot;GET&quot;);</span>
<span class="nc" id="L1915">      connection.setRequestProperty(&quot;Content-Type&quot;,</span>
          &quot;application/json; charset=UTF-8&quot;);
<span class="nc" id="L1917">      connection.setRequestProperty(&quot;charset&quot;, &quot;utf-8&quot;);</span>
<span class="nc" id="L1918">      connection.setUseCaches(false);</span>
      // process response
<span class="nc" id="L1920">      InputStream is = null;</span>
      try {
<span class="nc" id="L1922">        is = connection.getInputStream();</span>
<span class="nc" id="L1923">      } catch (IOException ioe) {</span>
<span class="nc" id="L1924">        throw new IOException(&quot;Couldn't get data from url&quot;);</span>
<span class="nc" id="L1925">      }</span>
<span class="nc" id="L1926">      InputStreamReader in = new InputStreamReader((InputStream) is, &quot;UTF8&quot;);</span>
<span class="nc" id="L1927">      Map&lt;String, Object&gt; params = new HashMap&lt;&gt;();</span>
<span class="nc" id="L1928">      getParamsFromJSON(params, IOUtils.toString(in));</span>
<span class="nc" id="L1929">      connection.disconnect();</span>
<span class="nc" id="L1930">      return params;</span>
    }

    /**
     * Sets the delete variables.
     *
     * @param id the new delete variables
     * @throws IOException Signals that an I/O exception has occurred.
     */
    public void setDeleteVariables(String id) throws IOException {
<span class="pc bpc" id="L1940" title="1 of 2 branches missed.">      if (action.equals(ACTION_DELETE)) {</span>
<span class="fc" id="L1941">        this.id = id;</span>
      } else {
<span class="nc" id="L1943">        throw new IOException(&quot;not allowed with action &quot; + action);</span>
      }
<span class="fc" id="L1945">    }</span>

    /**
     * Action.
     *
     * @return the string
     */
    public String action() {
<span class="fc" id="L1953">      return action;</span>
    }

    /**
     * Values.
     *
     * @return the hash set
     */
    public HashSet&lt;String&gt; values() {
<span class="fc" id="L1962">      return values;</span>
    }

    /**
     * Fields.
     *
     * @return the sets the
     */
    public Set&lt;String&gt; fields() {
<span class="fc" id="L1971">      return fields;</span>
    }

    /**
     * Adds the value.
     *
     * @param value the value
     * @throws IOException Signals that an I/O exception has occurred.
     */
    public void addValue(String value) throws IOException {
<span class="pc bpc" id="L1981" title="1 of 2 branches missed.">      if (action.equals(ACTION_CREATE)) {</span>
<span class="pc bpc" id="L1982" title="1 of 2 branches missed.">        if (version == null) {</span>
<span class="fc" id="L1983">          values.add(value);</span>
        } else {
<span class="nc" id="L1985">          throw new IOException(&quot;version already set&quot;);</span>
        }
      } else {
<span class="nc" id="L1988">        throw new IOException(&quot;not allowed for action '&quot; + action + &quot;'&quot;);</span>
      }
<span class="fc" id="L1990">    }</span>

    /**
     * Gets the params from JSON.
     *
     * @param params the params
     * @param json the json
     * @return the params from JSON
     */
    private static void getParamsFromJSON(Map&lt;String, Object&gt; params,
        String json) {
<span class="nc" id="L2001">      JSONParser parser = new JSONParser(json);</span>
      try {
<span class="nc" id="L2003">        Object o = ObjectBuilder.getVal(parser);</span>
<span class="nc bnc" id="L2004" title="All 2 branches missed.">        if (!(o instanceof Map))</span>
<span class="nc" id="L2005">          return;</span>
<span class="nc" id="L2006">        Map&lt;String, Object&gt; map = (Map&lt;String, Object&gt;) o;</span>
        // To make consistent with json.param handling, we should make query
        // params come after json params (i.e. query params should
        // appear to overwrite json params.

        // Solr params are based on String though, so we need to convert
<span class="nc bnc" id="L2012" title="All 2 branches missed.">        for (Map.Entry&lt;String, Object&gt; entry : map.entrySet()) {</span>
<span class="nc" id="L2013">          String key = entry.getKey();</span>
<span class="nc" id="L2014">          Object val = entry.getValue();</span>
<span class="nc bnc" id="L2015" title="All 2 branches missed.">          if (params.get(key) != null) {</span>
<span class="nc" id="L2016">            continue;</span>
          }

<span class="nc bnc" id="L2019" title="All 2 branches missed.">          if (val == null) {</span>
<span class="nc" id="L2020">            params.remove(key);</span>
          } else {
<span class="nc" id="L2022">            params.put(key, val);</span>
          }
<span class="nc" id="L2024">        }</span>

<span class="nc" id="L2026">      } catch (Exception e) {</span>
        // ignore parse exceptions at this stage, they may be caused by
        // incomplete
        // macro expansions
<span class="nc" id="L2030">        return;</span>
<span class="nc" id="L2031">      }</span>

<span class="nc" id="L2033">    }</span>

  }

  /**
   * The Class SubComponentFunction.
   */
  public static class SubComponentFunction {

    /** The key. */
    public String key;

    /** The expression. */
    public String expression;

    /** The type. */
    public String type;

    /** The parser function. */
    public MtasFunctionParserFunction parserFunction;

    /** The stats type. */
    public String statsType;

    /** The data type. */
    public String dataType;

    /** The sort type. */
    public String sortType;

    /** The sort direction. */
    public String sortDirection;

    /** The stats items. */
    public SortedSet&lt;String&gt; statsItems;

    /** The data collector. */
    public MtasDataCollector&lt;?, ?&gt; dataCollector;

    /**
     * Instantiates a new sub component function.
     *
     * @param collectorType the collector type
     * @param key the key
     * @param type the type
     * @param parserFunction the parser function
     * @param sortType the sort type
     * @param sortDirection the sort direction
     * @param start the start
     * @param number the number
     * @param segmentRegistration the segment registration
     * @param boundary the boundary
     * @throws ParseException the parse exception
     * @throws IOException Signals that an I/O exception has occurred.
     */
    public SubComponentFunction(String collectorType, String key, String type,
        MtasFunctionParserFunction parserFunction, String sortType,
        String sortDirection, Integer start, Integer number,
        String segmentRegistration, String boundary)
<span class="fc" id="L2092">        throws ParseException, IOException {</span>
<span class="fc" id="L2093">      this.key = key;</span>
<span class="fc" id="L2094">      this.expression = null;</span>
<span class="fc" id="L2095">      this.type = type;</span>
<span class="fc" id="L2096">      this.parserFunction = parserFunction;</span>
<span class="fc" id="L2097">      this.sortType = sortType;</span>
<span class="fc" id="L2098">      this.sortDirection = sortDirection;</span>
<span class="fc" id="L2099">      this.dataType = parserFunction.getType();</span>
<span class="fc" id="L2100">      this.statsItems = CodecUtil.createStatsItems(this.type);</span>
<span class="fc" id="L2101">      this.statsType = CodecUtil.createStatsType(statsItems, sortType,</span>
          parserFunction);
<span class="pc bpc" id="L2103" title="1 of 2 branches missed.">      if (collectorType.equals(DataCollector.COLLECTOR_TYPE_LIST)) {</span>
<span class="fc" id="L2104">        dataCollector = DataCollector.getCollector(</span>
            DataCollector.COLLECTOR_TYPE_LIST, dataType, statsType, statsItems,
            sortType, sortDirection, start, number, null, null, null, null,
            null, null, null, null, segmentRegistration, boundary);
<span class="nc bnc" id="L2108" title="All 2 branches missed.">      } else if (collectorType.equals(DataCollector.COLLECTOR_TYPE_DATA)) {</span>
<span class="nc" id="L2109">        dataCollector = DataCollector.getCollector(</span>
            DataCollector.COLLECTOR_TYPE_DATA, dataType, statsType, statsItems,
            sortType, sortDirection, start, number, segmentRegistration,
            boundary);
      }
<span class="fc" id="L2114">    }</span>

    /**
     * Instantiates a new sub component function.
     *
     * @param collectorType the collector type
     * @param key the key
     * @param expression the expression
     * @param type the type
     * @throws ParseException the parse exception
     * @throws IOException Signals that an I/O exception has occurred.
     */
    public SubComponentFunction(String collectorType, String key,
<span class="fc" id="L2127">        String expression, String type) throws ParseException, IOException {</span>
<span class="fc" id="L2128">      this.key = key;</span>
<span class="fc" id="L2129">      this.expression = expression;</span>
<span class="fc" id="L2130">      this.type = type;</span>
<span class="fc" id="L2131">      this.sortType = null;</span>
<span class="fc" id="L2132">      this.sortDirection = null;</span>
<span class="fc" id="L2133">      parserFunction = new MtasFunctionParser(</span>
<span class="fc" id="L2134">          new BufferedReader(new StringReader(this.expression))).parse();</span>
<span class="fc" id="L2135">      dataType = parserFunction.getType();</span>
<span class="fc" id="L2136">      statsItems = CodecUtil.createStatsItems(this.type);</span>
<span class="fc" id="L2137">      statsType = CodecUtil.createStatsType(statsItems, null, parserFunction);</span>
<span class="pc bpc" id="L2138" title="1 of 2 branches missed.">      if (collectorType.equals(DataCollector.COLLECTOR_TYPE_LIST)) {</span>
<span class="nc" id="L2139">        dataCollector = DataCollector.getCollector(</span>
            DataCollector.COLLECTOR_TYPE_LIST, dataType, statsType, statsItems,
<span class="nc" id="L2141">            sortType, sortDirection, 0, Integer.MAX_VALUE, null, null);</span>
<span class="pc bpc" id="L2142" title="1 of 2 branches missed.">      } else if (collectorType.equals(DataCollector.COLLECTOR_TYPE_DATA)) {</span>
<span class="fc" id="L2143">        dataCollector = DataCollector.getCollector(</span>
            DataCollector.COLLECTOR_TYPE_DATA, dataType, statsType, statsItems,
            sortType, sortDirection, null, null, null, null);
      }
<span class="fc" id="L2147">    }</span>
  }

  /**
   * The Class KwicToken.
   */
  public static class KwicToken {

    /** The start position. */
    public int startPosition;

    /** The end position. */
    public int endPosition;

    /** The tokens. */
    public List&lt;MtasTokenString&gt; tokens;

    /**
     * Instantiates a new kwic token.
     *
     * @param match the match
     * @param tokens the tokens
     */
<span class="nc" id="L2170">    public KwicToken(Match match, List&lt;MtasTokenString&gt; tokens) {</span>
<span class="nc" id="L2171">      startPosition = match.startPosition;</span>
<span class="nc" id="L2172">      endPosition = match.endPosition - 1;</span>
<span class="nc" id="L2173">      this.tokens = tokens;</span>
<span class="nc" id="L2174">    }</span>

  }

  /**
   * The Class KwicHit.
   */
  public static class KwicHit {

    /** The start position. */
    public int startPosition;

    /** The end position. */
    public int endPosition;

    /** The hits. */
    public Map&lt;Integer, List&lt;String&gt;&gt; hits;

    /**
     * Instantiates a new kwic hit.
     *
     * @param match the match
     * @param hits the hits
     */
<span class="nc" id="L2198">    public KwicHit(Match match, Map&lt;Integer, List&lt;String&gt;&gt; hits) {</span>
<span class="nc" id="L2199">      startPosition = match.startPosition;</span>
<span class="nc" id="L2200">      endPosition = match.endPosition - 1;</span>
<span class="nc" id="L2201">      this.hits = hits;</span>
<span class="nc" id="L2202">    }</span>
  }

  /**
   * The Class GroupHit.
   */
  public static class GroupHit {

    /** The hash. */
    private int hash;

    /** The hash left. */
    private int hashLeft;

    /** The hash hit. */
    private int hashHit;

    /** The hash right. */
    private int hashRight;

    /** The key. */
    private String key;

    /** The key left. */
    private String keyLeft;

    /** The key hit. */
    private String keyHit;

    /** The key right. */
    private String keyRight;

    /** The data hit. */
    public List&lt;String&gt;[] dataHit;

    /** The data left. */
    public List&lt;String&gt;[] dataLeft;

    /** The data right. */
    public List&lt;String&gt;[] dataRight;

    /** The missing hit. */
    public Set&lt;String&gt;[] missingHit;

    /** The missing left. */
    public Set&lt;String&gt;[] missingLeft;

    /** The missing right. */
    public Set&lt;String&gt;[] missingRight;

    /** The unknown hit. */
    public Set&lt;String&gt;[] unknownHit;

    /** The unknown left. */
    public Set&lt;String&gt;[] unknownLeft;

    /** The unknown right. */
    public Set&lt;String&gt;[] unknownRight;

    /** The Constant KEY_START. */
    public static final String KEY_START = MtasToken.DELIMITER + &quot;grouphit&quot;
        + MtasToken.DELIMITER;

    /**
     * Sort.
     *
     * @param data the data
     * @return the list
     */
    private List&lt;MtasTreeHit&lt;String&gt;&gt; sort(List&lt;MtasTreeHit&lt;String&gt;&gt; data) {
<span class="fc" id="L2272">      Collections.sort(data, new Comparator&lt;MtasTreeHit&lt;String&gt;&gt;() {</span>
        @Override
        public int compare(MtasTreeHit&lt;String&gt; hit1, MtasTreeHit&lt;String&gt; hit2) {
<span class="nc" id="L2275">          int compare = Integer.compare(hit1.additionalId, hit2.additionalId);</span>
<span class="nc bnc" id="L2276" title="All 2 branches missed.">          compare = (compare == 0)</span>
<span class="nc" id="L2277">              ? Long.compare(hit1.additionalRef, hit2.additionalRef) : compare;</span>
<span class="nc" id="L2278">          return compare;</span>
        }
      });
<span class="fc" id="L2281">      return data;</span>
    }

    /**
     * Instantiates a new group hit.
     *
     * @param list the list
     * @param start the start
     * @param end the end
     * @param hitStart the hit start
     * @param hitEnd the hit end
     * @param group the group
     * @param knownPrefixes the known prefixes
     * @throws UnsupportedEncodingException the unsupported encoding exception
     */
    @SuppressWarnings(&quot;unchecked&quot;)
    public GroupHit(List&lt;MtasTreeHit&lt;String&gt;&gt; list, int start, int end,
        int hitStart, int hitEnd, ComponentGroup group,
<span class="fc" id="L2299">        Set&lt;String&gt; knownPrefixes) throws UnsupportedEncodingException {</span>
      // compute dimensions
<span class="fc" id="L2301">      int leftRangeStart = start;</span>
<span class="fc" id="L2302">      int leftRangeEnd = Math.min(end, hitStart - 1);</span>
<span class="fc" id="L2303">      int leftRangeLength = Math.max(0, 1 + leftRangeEnd - leftRangeStart);</span>
<span class="fc" id="L2304">      int hitLength = 1 + hitEnd - hitStart;</span>
<span class="fc" id="L2305">      int rightRangeStart = Math.max(start, hitEnd + 1);</span>
<span class="fc" id="L2306">      int rightRangeEnd = end;</span>
<span class="fc" id="L2307">      int rightRangeLength = Math.max(0, 1 + rightRangeEnd - rightRangeStart);</span>
      // create initial arrays
<span class="pc bpc" id="L2309" title="1 of 2 branches missed.">      if (leftRangeLength &gt; 0) {</span>
<span class="nc" id="L2310">        keyLeft = &quot;&quot;;</span>
<span class="nc" id="L2311">        dataLeft = (ArrayList&lt;String&gt;[]) new ArrayList[leftRangeLength];</span>
<span class="nc" id="L2312">        missingLeft = (HashSet&lt;String&gt;[]) new HashSet[leftRangeLength];</span>
<span class="nc" id="L2313">        unknownLeft = (HashSet&lt;String&gt;[]) new HashSet[leftRangeLength];</span>
<span class="nc bnc" id="L2314" title="All 2 branches missed.">        for (int p = 0; p &lt; leftRangeLength; p++) {</span>
<span class="nc" id="L2315">          dataLeft[p] = new ArrayList&lt;&gt;();</span>
<span class="nc" id="L2316">          missingLeft[p] = new HashSet&lt;&gt;();</span>
<span class="nc" id="L2317">          unknownLeft[p] = new HashSet&lt;&gt;();</span>
        }
      } else {
<span class="fc" id="L2320">        keyLeft = null;</span>
<span class="fc" id="L2321">        dataLeft = null;</span>
<span class="fc" id="L2322">        missingLeft = null;</span>
<span class="fc" id="L2323">        unknownLeft = null;</span>
      }
<span class="pc bpc" id="L2325" title="1 of 2 branches missed.">      if (hitLength &gt; 0) {</span>
<span class="fc" id="L2326">        keyHit = &quot;&quot;;</span>
<span class="fc" id="L2327">        dataHit = (ArrayList&lt;String&gt;[]) new ArrayList[hitLength];</span>
<span class="fc" id="L2328">        missingHit = (HashSet&lt;String&gt;[]) new HashSet[hitLength];</span>
<span class="fc" id="L2329">        unknownHit = (HashSet&lt;String&gt;[]) new HashSet[hitLength];</span>
<span class="fc bfc" id="L2330" title="All 2 branches covered.">        for (int p = 0; p &lt; hitLength; p++) {</span>
<span class="fc" id="L2331">          dataHit[p] = new ArrayList&lt;&gt;();</span>
<span class="fc" id="L2332">          missingHit[p] = new HashSet&lt;&gt;();</span>
<span class="fc" id="L2333">          unknownHit[p] = new HashSet&lt;&gt;();</span>
        }
      } else {
<span class="nc" id="L2336">        keyHit = null;</span>
<span class="nc" id="L2337">        dataHit = null;</span>
<span class="nc" id="L2338">        missingHit = null;</span>
<span class="nc" id="L2339">        unknownHit = null;</span>
      }
<span class="pc bpc" id="L2341" title="1 of 2 branches missed.">      if (rightRangeLength &gt; 0) {</span>
<span class="nc" id="L2342">        keyRight = &quot;&quot;;</span>
<span class="nc" id="L2343">        dataRight = (ArrayList&lt;String&gt;[]) new ArrayList[rightRangeLength];</span>
<span class="nc" id="L2344">        missingRight = (HashSet&lt;String&gt;[]) new HashSet[rightRangeLength];</span>
<span class="nc" id="L2345">        unknownRight = (HashSet&lt;String&gt;[]) new HashSet[rightRangeLength];</span>
<span class="nc bnc" id="L2346" title="All 2 branches missed.">        for (int p = 0; p &lt; rightRangeLength; p++) {</span>
<span class="nc" id="L2347">          dataRight[p] = new ArrayList&lt;&gt;();</span>
<span class="nc" id="L2348">          missingRight[p] = new HashSet&lt;&gt;();</span>
<span class="nc" id="L2349">          unknownRight[p] = new HashSet&lt;&gt;();</span>
        }
      } else {
<span class="fc" id="L2352">        keyRight = null;</span>
<span class="fc" id="L2353">        dataRight = null;</span>
<span class="fc" id="L2354">        missingRight = null;</span>
<span class="fc" id="L2355">        unknownRight = null;</span>
      }

      // construct missing sets
<span class="pc bpc" id="L2359" title="1 of 2 branches missed.">      if (group.hitInside != null) {</span>
<span class="fc bfc" id="L2360" title="All 2 branches covered.">        for (int p = hitStart; p &lt;= hitEnd; p++) {</span>
<span class="fc" id="L2361">          missingHit[p - hitStart].addAll(group.hitInside);</span>
        }
      }
<span class="pc bpc" id="L2364" title="1 of 2 branches missed.">      if (group.hitInsideLeft != null) {</span>
<span class="nc bnc" id="L2365" title="All 2 branches missed.">        for (int p = hitStart; p &lt;= Math.min(hitEnd,</span>
<span class="nc" id="L2366">            hitStart + group.hitInsideLeft.length - 1); p++) {</span>
<span class="nc bnc" id="L2367" title="All 2 branches missed.">          if (group.hitInsideLeft[p - hitStart] != null) {</span>
<span class="nc" id="L2368">            missingHit[p - hitStart].addAll(group.hitInsideLeft[p - hitStart]);</span>
          }
        }
      }
<span class="pc bpc" id="L2372" title="1 of 2 branches missed.">      if (group.hitLeft != null) {</span>
<span class="nc bnc" id="L2373" title="All 2 branches missed.">        for (int p = hitStart; p &lt;= Math.min(hitEnd,</span>
<span class="nc" id="L2374">            hitStart + group.hitLeft.length - 1); p++) {</span>
<span class="nc bnc" id="L2375" title="All 2 branches missed.">          if (group.hitLeft[p - hitStart] != null) {</span>
<span class="nc" id="L2376">            missingHit[p - hitStart].addAll(group.hitLeft[p - hitStart]);</span>
          }
        }
      }
<span class="pc bpc" id="L2380" title="1 of 2 branches missed.">      if (group.hitInsideRight != null) {</span>
<span class="nc" id="L2381">        for (int p = Math.max(hitStart,</span>
<span class="nc bnc" id="L2382" title="All 2 branches missed.">            hitEnd - group.hitInsideRight.length + 1); p &lt;= hitEnd; p++) {</span>
<span class="nc bnc" id="L2383" title="All 2 branches missed.">          if (group.hitInsideRight[hitEnd - p] != null) {</span>
<span class="nc" id="L2384">            missingHit[p - hitStart].addAll(group.hitInsideRight[hitEnd - p]);</span>
          }
        }
      }
<span class="pc bpc" id="L2388" title="1 of 2 branches missed.">      if (group.hitRight != null) {</span>
<span class="nc bnc" id="L2389" title="All 2 branches missed.">        for (int p = hitStart; p &lt;= Math.min(hitEnd,</span>
<span class="nc" id="L2390">            hitStart + group.hitRight.length - 1); p++) {</span>
<span class="nc bnc" id="L2391" title="All 2 branches missed.">          if (group.hitRight[p - hitStart] != null) {</span>
<span class="nc" id="L2392">            missingHit[p - hitStart].addAll(group.hitRight[p - hitStart]);</span>
          }
        }
      }
<span class="pc bpc" id="L2396" title="1 of 2 branches missed.">      if (group.left != null) {</span>
<span class="nc bnc" id="L2397" title="All 2 branches missed.">        for (int p = 0; p &lt; Math.min(leftRangeLength, group.left.length); p++) {</span>
<span class="nc bnc" id="L2398" title="All 2 branches missed.">          if (group.left[p] != null) {</span>
<span class="nc" id="L2399">            missingLeft[p].addAll(group.left[p]);</span>
          }
        }
      }
<span class="pc bpc" id="L2403" title="1 of 2 branches missed.">      if (group.hitRight != null) {</span>
<span class="nc bnc" id="L2404" title="All 2 branches missed.">        for (int p = 0; p &lt; Math.min(leftRangeLength,</span>
<span class="nc" id="L2405">            group.hitRight.length - dataHit.length); p++) {</span>
<span class="nc bnc" id="L2406" title="All 2 branches missed.">          if (group.hitRight[p + dataHit.length] != null) {</span>
<span class="nc" id="L2407">            missingLeft[p].addAll(group.hitRight[p + dataHit.length]);</span>
          }
        }
      }
<span class="pc bpc" id="L2411" title="1 of 2 branches missed.">      if (group.right != null) {</span>
<span class="nc bnc" id="L2412" title="All 2 branches missed.">        for (int p = 0; p &lt; Math.min(rightRangeLength,</span>
<span class="nc" id="L2413">            group.right.length); p++) {</span>
<span class="nc bnc" id="L2414" title="All 2 branches missed.">          if (group.right[p] != null) {</span>
<span class="nc" id="L2415">            missingRight[p].addAll(group.right[p]);</span>
          }
        }
      }
<span class="pc bpc" id="L2419" title="1 of 2 branches missed.">      if (group.hitLeft != null) {</span>
<span class="nc bnc" id="L2420" title="All 2 branches missed.">        for (int p = 0; p &lt; Math.min(rightRangeLength,</span>
<span class="nc" id="L2421">            group.hitLeft.length - dataHit.length); p++) {</span>
<span class="nc bnc" id="L2422" title="All 2 branches missed.">          if (group.hitLeft[p + dataHit.length] != null) {</span>
<span class="nc" id="L2423">            missingRight[p].addAll(group.hitLeft[p + dataHit.length]);</span>
          }
        }
      }

      // fill arrays and update missing administration
<span class="fc" id="L2429">      List&lt;MtasTreeHit&lt;String&gt;&gt; sortedList = sort(list);</span>
<span class="fc bfc" id="L2430" title="All 2 branches covered.">      for (MtasTreeHit&lt;String&gt; hit : sortedList) {</span>
        // inside hit
<span class="pc bpc" id="L2432" title="2 of 4 branches missed.">        if (group.hitInside != null &amp;&amp; hit.idData != null</span>
<span class="pc bpc" id="L2433" title="1 of 2 branches missed.">            &amp;&amp; group.hitInside.contains(hit.idData)) {</span>
<span class="fc" id="L2434">          for (int p = Math.max(hitStart, hit.startPosition); p &lt;= Math</span>
<span class="fc bfc" id="L2435" title="All 2 branches covered.">              .min(hitEnd, hit.endPosition); p++) {</span>
<span class="fc" id="L2436">            dataHit[p - hitStart].add(hit.refData);</span>
<span class="fc" id="L2437">            missingHit[p - hitStart]</span>
<span class="fc" id="L2438">                .remove(MtasToken.getPrefixFromValue(hit.refData));</span>
          }
<span class="nc bnc" id="L2440" title="All 10 branches missed.">        } else if ((group.hitInsideLeft != null || group.hitLeft != null</span>
            || group.hitInsideRight != null || group.hitRight != null)
            &amp;&amp; hit.idData != null) {
<span class="nc" id="L2443">          for (int p = Math.max(hitStart, hit.startPosition); p &lt;= Math</span>
<span class="nc bnc" id="L2444" title="All 2 branches missed.">              .min(hitEnd, hit.endPosition); p++) {</span>
<span class="nc" id="L2445">            int pHitLeft = p - hitStart;</span>
<span class="nc" id="L2446">            int pHitRight = hitEnd - p;</span>
<span class="nc bnc" id="L2447" title="All 6 branches missed.">            if (group.hitInsideLeft != null</span>
                &amp;&amp; pHitLeft &lt;= (group.hitInsideLeft.length - 1)
                &amp;&amp; group.hitInsideLeft[pHitLeft] != null
<span class="nc bnc" id="L2450" title="All 2 branches missed.">                &amp;&amp; group.hitInsideLeft[pHitLeft].contains(hit.idData)) {</span>
              // keyHit += hit.refData;
<span class="nc" id="L2452">              dataHit[p - hitStart].add(hit.refData);</span>
<span class="nc" id="L2453">              missingHit[p - hitStart]</span>
<span class="nc" id="L2454">                  .remove(MtasToken.getPrefixFromValue(hit.refData));</span>
<span class="nc bnc" id="L2455" title="All 6 branches missed.">            } else if (group.hitLeft != null</span>
                &amp;&amp; pHitLeft &lt;= (group.hitLeft.length - 1)
                &amp;&amp; group.hitLeft[pHitLeft] != null
<span class="nc bnc" id="L2458" title="All 2 branches missed.">                &amp;&amp; group.hitLeft[pHitLeft].contains(hit.idData)) {</span>
              // keyHit += hit.refData;
<span class="nc" id="L2460">              dataHit[p - hitStart].add(hit.refData);</span>
<span class="nc" id="L2461">              missingHit[p - hitStart]</span>
<span class="nc" id="L2462">                  .remove(MtasToken.getPrefixFromValue(hit.refData));</span>
<span class="nc bnc" id="L2463" title="All 6 branches missed.">            } else if (group.hitInsideRight != null</span>
                &amp;&amp; pHitRight &lt;= (group.hitInsideRight.length - 1)
                &amp;&amp; group.hitInsideRight[pHitRight] != null
<span class="nc bnc" id="L2466" title="All 2 branches missed.">                &amp;&amp; group.hitInsideRight[pHitRight].contains(hit.idData)) {</span>
<span class="nc" id="L2467">              dataHit[p - hitStart].add(hit.refData);</span>
<span class="nc" id="L2468">              missingHit[p - hitStart]</span>
<span class="nc" id="L2469">                  .remove(MtasToken.getPrefixFromValue(hit.refData));</span>
<span class="nc bnc" id="L2470" title="All 6 branches missed.">            } else if (group.hitRight != null</span>
                &amp;&amp; pHitRight &lt;= (group.hitRight.length - 1)
                &amp;&amp; group.hitRight[pHitRight] != null
<span class="nc bnc" id="L2473" title="All 2 branches missed.">                &amp;&amp; group.hitRight[pHitRight].contains(hit.idData)) {</span>
<span class="nc" id="L2474">              dataHit[p - hitStart].add(hit.refData);</span>
<span class="nc" id="L2475">              missingHit[p - hitStart]</span>
<span class="nc" id="L2476">                  .remove(MtasToken.getPrefixFromValue(hit.refData));</span>
            }
          }
        }
        // left
<span class="pc bpc" id="L2481" title="1 of 2 branches missed.">        if (hit.startPosition &lt; hitStart) {</span>
<span class="nc bnc" id="L2482" title="All 8 branches missed.">          if ((group.left != null || (group.hitRight != null</span>
              &amp;&amp; group.hitRight.length &gt; (1 + hitEnd - hitStart)))
              &amp;&amp; hit.idData != null) {
<span class="nc" id="L2485">            for (int p = Math.min(hit.endPosition,</span>
<span class="nc bnc" id="L2486" title="All 2 branches missed.">                hitStart - 1); p &gt;= hit.startPosition; p--) {</span>
<span class="nc" id="L2487">              int pLeft = hitStart - 1 - p;</span>
<span class="nc" id="L2488">              int pHitRight = hitEnd - p;</span>
<span class="nc bnc" id="L2489" title="All 6 branches missed.">              if (group.left != null &amp;&amp; pLeft &lt;= (group.left.length - 1)</span>
                  &amp;&amp; group.left[pLeft] != null
<span class="nc bnc" id="L2491" title="All 2 branches missed.">                  &amp;&amp; group.left[pLeft].contains(hit.idData)) {</span>
<span class="nc" id="L2492">                dataLeft[hitStart - 1 - p].add(hit.refData);</span>
<span class="nc" id="L2493">                missingLeft[hitStart - 1 - p]</span>
<span class="nc" id="L2494">                    .remove(MtasToken.getPrefixFromValue(hit.refData));</span>
<span class="nc bnc" id="L2495" title="All 6 branches missed.">              } else if (group.hitRight != null</span>
                  &amp;&amp; pHitRight &lt;= (group.hitRight.length - 1)
                  &amp;&amp; group.hitRight[pHitRight] != null
<span class="nc bnc" id="L2498" title="All 2 branches missed.">                  &amp;&amp; group.hitRight[pHitRight].contains(hit.idData)) {</span>
<span class="nc" id="L2499">                dataLeft[hitStart - 1 - p].add(hit.refData);</span>
<span class="nc" id="L2500">                missingLeft[hitStart - 1 - p]</span>
<span class="nc" id="L2501">                    .remove(MtasToken.getPrefixFromValue(hit.refData));</span>
              }
            }
          }
        }
        // right
<span class="pc bpc" id="L2507" title="1 of 2 branches missed.">        if (hit.endPosition &gt; hitEnd) {</span>
<span class="nc bnc" id="L2508" title="All 8 branches missed.">          if ((group.right != null || (group.hitLeft != null</span>
              &amp;&amp; group.hitLeft.length &gt; (1 + hitEnd - hitStart)))
              &amp;&amp; hit.idData != null) {
<span class="nc" id="L2511">            for (int p = Math.max(hit.startPosition,</span>
<span class="nc bnc" id="L2512" title="All 2 branches missed.">                hitEnd + 1); p &lt;= hit.endPosition; p++) {</span>
<span class="nc" id="L2513">              int pRight = p - hitEnd - 1;</span>
<span class="nc" id="L2514">              int pHitLeft = p - hitStart;</span>
<span class="nc bnc" id="L2515" title="All 6 branches missed.">              if (group.right != null &amp;&amp; pRight &lt;= (group.right.length - 1)</span>
                  &amp;&amp; group.right[pRight] != null
<span class="nc bnc" id="L2517" title="All 2 branches missed.">                  &amp;&amp; group.right[pRight].contains(hit.idData)) {</span>
<span class="nc" id="L2518">                dataRight[p - rightRangeStart].add(hit.refData);</span>
<span class="nc" id="L2519">                missingRight[p - rightRangeStart]</span>
<span class="nc" id="L2520">                    .remove(MtasToken.getPrefixFromValue(hit.refData));</span>
<span class="nc bnc" id="L2521" title="All 6 branches missed.">              } else if (group.hitLeft != null</span>
                  &amp;&amp; pHitLeft &lt;= (group.hitLeft.length - 1)
                  &amp;&amp; group.hitLeft[pHitLeft] != null
<span class="nc bnc" id="L2524" title="All 2 branches missed.">                  &amp;&amp; group.hitLeft[pHitLeft].contains(hit.idData)) {</span>
<span class="nc" id="L2525">                dataRight[p - rightRangeStart].add(hit.refData);</span>
<span class="nc" id="L2526">                missingRight[p - rightRangeStart]</span>
<span class="nc" id="L2527">                    .remove(MtasToken.getPrefixFromValue(hit.refData));</span>
              }
            }
          }
        }
<span class="fc" id="L2532">      }</span>
      // register unknown
<span class="pc bpc" id="L2534" title="1 of 2 branches missed.">      if (missingLeft != null) {</span>
<span class="nc bnc" id="L2535" title="All 2 branches missed.">        for (int i = 0; i &lt; missingLeft.length; i++) {</span>
<span class="nc bnc" id="L2536" title="All 2 branches missed.">          for (String prefix : missingLeft[i]) {</span>
<span class="nc bnc" id="L2537" title="All 2 branches missed.">            if (!knownPrefixes.contains(prefix)) {</span>
<span class="nc" id="L2538">              unknownLeft[i].add(prefix);</span>
            }
<span class="nc" id="L2540">          }</span>
        }
      }
<span class="pc bpc" id="L2543" title="1 of 2 branches missed.">      if (missingHit != null) {</span>
<span class="fc bfc" id="L2544" title="All 2 branches covered.">        for (int i = 0; i &lt; missingHit.length; i++) {</span>
<span class="pc bpc" id="L2545" title="1 of 2 branches missed.">          for (String prefix : missingHit[i]) {</span>
<span class="nc bnc" id="L2546" title="All 2 branches missed.">            if (!knownPrefixes.contains(prefix)) {</span>
<span class="nc" id="L2547">              unknownHit[i].add(prefix);</span>
            }
<span class="nc" id="L2549">          }</span>
        }
      }
<span class="pc bpc" id="L2552" title="1 of 2 branches missed.">      if (missingRight != null) {</span>
<span class="nc bnc" id="L2553" title="All 2 branches missed.">        for (int i = 0; i &lt; missingRight.length; i++) {</span>
<span class="nc bnc" id="L2554" title="All 2 branches missed.">          for (String prefix : missingRight[i]) {</span>
<span class="nc bnc" id="L2555" title="All 2 branches missed.">            if (!knownPrefixes.contains(prefix)) {</span>
<span class="nc" id="L2556">              unknownRight[i].add(prefix);</span>
            }
<span class="nc" id="L2558">          }</span>
        }
      }
      // construct keys
<span class="fc" id="L2562">      keyLeft = dataToString(dataLeft, missingLeft);</span>
<span class="fc" id="L2563">      keyHit = dataToString(dataHit, missingHit);</span>
<span class="fc" id="L2564">      keyRight = dataToString(dataRight, missingRight);</span>
<span class="fc" id="L2565">      key = KEY_START;</span>
<span class="pc bpc" id="L2566" title="1 of 2 branches missed.">      if (keyLeft != null) {</span>
<span class="nc" id="L2567">        key += keyLeft;</span>
<span class="nc" id="L2568">        hashLeft = keyLeft.hashCode();</span>
      } else {
<span class="fc" id="L2570">        hashLeft = 1;</span>
      }
<span class="fc" id="L2572">      key += &quot;|&quot;;</span>
<span class="pc bpc" id="L2573" title="1 of 2 branches missed.">      if (keyHit != null) {</span>
<span class="fc" id="L2574">        key += keyHit;</span>
<span class="fc" id="L2575">        hashHit = keyHit.hashCode();</span>
      } else {
<span class="nc" id="L2577">        hashHit = 1;</span>
      }
<span class="fc" id="L2579">      key += &quot;|&quot;;</span>
<span class="pc bpc" id="L2580" title="1 of 2 branches missed.">      if (keyRight != null) {</span>
<span class="nc" id="L2581">        key += keyRight;</span>
<span class="nc" id="L2582">        hashRight = keyRight.hashCode();</span>
      } else {
<span class="fc" id="L2584">        hashRight = 1;</span>
      }
      // compute hash
<span class="fc" id="L2587">      hash = hashHit * (hashLeft ^ 3) * (hashRight ^ 5);</span>
<span class="fc" id="L2588">    }</span>

    /*
     * (non-Javadoc)
     * 
     * @see java.lang.Object#hashCode()
     */
    @Override
    public int hashCode() {
<span class="fc" id="L2597">      return hash;</span>
    }

    /**
     * Data equals.
     *
     * @param d1 the d 1
     * @param d2 the d 2
     * @return true, if successful
     */
    private boolean dataEquals(List&lt;String&gt;[] d1, List&lt;String&gt;[] d2) {
      List&lt;String&gt; a1;
      List&lt;String&gt; a2;
<span class="pc bpc" id="L2610" title="1 of 4 branches missed.">      if (d1 == null &amp;&amp; d2 == null) {</span>
<span class="fc" id="L2611">        return true;</span>
<span class="pc bpc" id="L2612" title="2 of 4 branches missed.">      } else if (d1 == null || d2 == null) {</span>
<span class="nc" id="L2613">        return false;</span>
      } else {
<span class="pc bpc" id="L2615" title="1 of 2 branches missed.">        if (d1.length == d2.length) {</span>
<span class="fc bfc" id="L2616" title="All 2 branches covered.">          for (int i = 0; i &lt; d1.length; i++) {</span>
<span class="fc" id="L2617">            a1 = d1[i];</span>
<span class="fc" id="L2618">            a2 = d2[i];</span>
<span class="pc bpc" id="L2619" title="3 of 6 branches missed.">            if (a1 != null &amp;&amp; a2 != null &amp;&amp; a1.size() == a2.size()) {</span>
<span class="fc bfc" id="L2620" title="All 2 branches covered.">              for (int j = 0; j &lt; a1.size(); j++) {</span>
<span class="pc bpc" id="L2621" title="1 of 2 branches missed.">                if (!a1.get(j).equals(a2.get(j))) {</span>
<span class="nc" id="L2622">                  return false;</span>
                }
              }
            } else {
<span class="nc" id="L2626">              return false;</span>
            }
          }
<span class="fc" id="L2629">          return true;</span>
        } else {
<span class="nc" id="L2631">          return false;</span>
        }
      }
    }

    /*
     * (non-Javadoc)
     * 
     * @see java.lang.Object#equals(java.lang.Object)
     */
    @Override
    public boolean equals(Object obj) {
<span class="pc bpc" id="L2643" title="1 of 2 branches missed.">      if (this == obj)</span>
<span class="nc" id="L2644">        return true;</span>
<span class="pc bpc" id="L2645" title="1 of 2 branches missed.">      if (obj == null)</span>
<span class="nc" id="L2646">        return false;</span>
<span class="pc bpc" id="L2647" title="1 of 2 branches missed.">      if (getClass() != obj.getClass())</span>
<span class="nc" id="L2648">        return false;</span>
<span class="fc" id="L2649">      GroupHit other = (GroupHit) obj;</span>
<span class="fc bfc" id="L2650" title="All 2 branches covered.">      if (hashCode() != other.hashCode())</span>
<span class="fc" id="L2651">        return false;</span>
<span class="pc bpc" id="L2652" title="1 of 2 branches missed.">      if (!dataEquals(dataHit, other.dataHit))</span>
<span class="nc" id="L2653">        return false;</span>
<span class="pc bpc" id="L2654" title="1 of 2 branches missed.">      if (!dataEquals(dataLeft, other.dataLeft))</span>
<span class="nc" id="L2655">        return false;</span>
<span class="pc bpc" id="L2656" title="1 of 2 branches missed.">      if (!dataEquals(dataRight, other.dataRight))</span>
<span class="nc" id="L2657">        return false;</span>
<span class="fc" id="L2658">      return true;</span>
    }

    /**
     * Data to string.
     *
     * @param data the data
     * @param missing the missing
     * @return the string
     * @throws UnsupportedEncodingException the unsupported encoding exception
     */
    private String dataToString(List&lt;String&gt;[] data, Set&lt;String&gt;[] missing)
        throws UnsupportedEncodingException {
<span class="fc" id="L2671">      StringBuilder text = null;</span>
<span class="fc" id="L2672">      Encoder encoder = Base64.getEncoder();</span>
      String prefix;
      String postfix;
<span class="pc bpc" id="L2675" title="2 of 6 branches missed.">      if (data != null &amp;&amp; missing != null &amp;&amp; data.length == missing.length) {</span>
<span class="fc bfc" id="L2676" title="All 2 branches covered.">        for (int i = 0; i &lt; data.length; i++) {</span>
<span class="pc bpc" id="L2677" title="1 of 2 branches missed.">          if (i &gt; 0) {</span>
<span class="nc" id="L2678">            text.append(&quot;,&quot;);</span>
          } else {
<span class="fc" id="L2680">            text = new StringBuilder();</span>
          }
<span class="fc bfc" id="L2682" title="All 2 branches covered.">          for (int j = 0; j &lt; data[i].size(); j++) {</span>
<span class="pc bpc" id="L2683" title="1 of 2 branches missed.">            if (j &gt; 0) {</span>
<span class="nc" id="L2684">              text.append(&quot;&amp;&quot;);</span>
            }
<span class="fc" id="L2686">            prefix = MtasToken.getPrefixFromValue(data[i].get(j));</span>
<span class="fc" id="L2687">            postfix = MtasToken.getPostfixFromValue(data[i].get(j));</span>
<span class="fc" id="L2688">            text.append(encoder</span>
<span class="fc" id="L2689">                .encodeToString(prefix.getBytes(StandardCharsets.UTF_8)));</span>
<span class="pc bpc" id="L2690" title="1 of 2 branches missed.">            if (!postfix.isEmpty()) {</span>
<span class="fc" id="L2691">              text.append(&quot;.&quot;);</span>
<span class="fc" id="L2692">              text.append(encoder</span>
<span class="fc" id="L2693">                  .encodeToString(postfix.getBytes(StandardCharsets.UTF_8)));</span>
            }
          }
<span class="pc bpc" id="L2696" title="1 of 2 branches missed.">          if (missing[i] != null) {</span>
<span class="fc" id="L2697">            String[] tmpMissing = missing[i]</span>
<span class="fc" id="L2698">                .toArray(new String[missing[i].size()]);</span>
<span class="pc bpc" id="L2699" title="1 of 2 branches missed.">            for (int j = 0; j &lt; tmpMissing.length; j++) {</span>
<span class="nc bnc" id="L2700" title="All 4 branches missed.">              if (j &gt; 0 || !data[i].isEmpty()) {</span>
<span class="nc" id="L2701">                text.append(&quot;&amp;&quot;);</span>
              }
<span class="nc" id="L2703">              text.append(encoder.encodeToString(</span>
<span class="nc" id="L2704">                  (&quot;!&quot; + tmpMissing[j]).getBytes(StandardCharsets.UTF_8)));</span>
            }
          }
        }
      }
<span class="fc bfc" id="L2709" title="All 2 branches covered.">      return text != null ? text.toString() : null;</span>
    }

    /*
     * (non-Javadoc)
     * 
     * @see java.lang.Object#toString()
     */
    public String toString() {
<span class="fc" id="L2718">      return key;</span>
    }

    /**
     * Key to sub sub object.
     *
     * @param key the key
     * @param newKey the new key
     * @return the map[]
     */
    private static Map&lt;String, String&gt;[] keyToSubSubObject(String key,
        StringBuilder newKey) {
<span class="pc bpc" id="L2730" title="1 of 2 branches missed.">      if (!key.isEmpty()) {</span>
<span class="fc" id="L2731">        newKey.append(&quot; [&quot;);</span>
        String prefix;
        String postfix;
<span class="fc" id="L2734">        String[] parts = key.split(Pattern.quote(&quot;&amp;&quot;));</span>
<span class="fc" id="L2735">        Map&lt;String, String&gt;[] result = new HashMap[parts.length];</span>
<span class="fc" id="L2736">        Pattern pattern = Pattern.compile(&quot;^([^\\.]*)\\.([^\\.]*)$&quot;);</span>
<span class="fc" id="L2737">        Decoder decoder = Base64.getDecoder();</span>
        Matcher matcher;
<span class="fc" id="L2739">        StringBuilder tmpNewKey = null;</span>
<span class="fc bfc" id="L2740" title="All 2 branches covered.">        for (int i = 0; i &lt; parts.length; i++) {</span>
<span class="pc bpc" id="L2741" title="1 of 2 branches missed.">          if (parts[i].isEmpty()) {</span>
<span class="nc" id="L2742">            result[i] = null;</span>
          } else {
<span class="fc" id="L2744">            HashMap&lt;String, String&gt; subResult = new HashMap&lt;&gt;();</span>
<span class="fc" id="L2745">            matcher = pattern.matcher(parts[i]);</span>
<span class="pc bpc" id="L2746" title="1 of 2 branches missed.">            if (tmpNewKey != null) {</span>
<span class="nc" id="L2747">              tmpNewKey.append(&quot; &amp; &quot;);</span>
            } else {
<span class="fc" id="L2749">              tmpNewKey = new StringBuilder();</span>
            }
<span class="pc bpc" id="L2751" title="1 of 2 branches missed.">            if (matcher.matches()) {</span>
<span class="fc" id="L2752">              prefix = new String(</span>
<span class="fc" id="L2753">                  decoder.decode(</span>
<span class="fc" id="L2754">                      matcher.group(1).getBytes(StandardCharsets.UTF_8)),</span>
                  StandardCharsets.UTF_8);
<span class="fc" id="L2756">              postfix = new String(</span>
<span class="fc" id="L2757">                  decoder.decode(</span>
<span class="fc" id="L2758">                      matcher.group(2).getBytes(StandardCharsets.UTF_8)),</span>
                  StandardCharsets.UTF_8);
<span class="fc" id="L2760">              tmpNewKey.append(prefix.replace(&quot;=&quot;, &quot;\\=&quot;));</span>
<span class="fc" id="L2761">              tmpNewKey.append(&quot;=\&quot;&quot; + postfix.replace(&quot;\&quot;&quot;, &quot;\\\&quot;&quot;) + &quot;\&quot;&quot;);</span>
<span class="fc" id="L2762">              subResult.put(&quot;prefix&quot;, prefix);</span>
<span class="fc" id="L2763">              subResult.put(&quot;value&quot;, postfix);</span>
            } else {
<span class="nc" id="L2765">              prefix = new String(</span>
<span class="nc" id="L2766">                  decoder.decode(parts[i].getBytes(StandardCharsets.UTF_8)),</span>
                  StandardCharsets.UTF_8);
<span class="nc" id="L2768">              tmpNewKey.append(prefix.replace(&quot;=&quot;, &quot;\\=&quot;));</span>
<span class="nc bnc" id="L2769" title="All 2 branches missed.">              if (prefix.startsWith(&quot;!&quot;)) {</span>
<span class="nc" id="L2770">                subResult.put(&quot;missing&quot;, prefix.substring(1));</span>
              } else {
<span class="nc" id="L2772">                subResult.put(&quot;prefix&quot;, prefix);</span>
              }
            }
<span class="fc" id="L2775">            result[i] = subResult;</span>
          }
        }
<span class="pc bpc" id="L2778" title="1 of 2 branches missed.">        if (tmpNewKey != null) {</span>
<span class="fc" id="L2779">          newKey.append(tmpNewKey);</span>
        }
<span class="fc" id="L2781">        newKey.append(&quot;]&quot;);</span>
<span class="fc" id="L2782">        return result;</span>
      } else {
<span class="nc" id="L2784">        newKey.append(&quot; []&quot;);</span>
<span class="nc" id="L2785">        return null;</span>
      }
    }

    /**
     * Key to sub object.
     *
     * @param key the key
     * @param newKey the new key
     * @return the map
     */
    private static Map&lt;Integer, Map&lt;String, String&gt;[]&gt; keyToSubObject(
        String key, StringBuilder newKey) {
<span class="fc" id="L2798">      Map&lt;Integer, Map&lt;String, String&gt;[]&gt; result = new HashMap&lt;&gt;();</span>
<span class="pc bpc" id="L2799" title="1 of 4 branches missed.">      if (key == null || key.trim().isEmpty()) {</span>
<span class="fc" id="L2800">        return null;</span>
      } else {
<span class="fc" id="L2802">        String[] parts = key.split(Pattern.quote(&quot;,&quot;), -1);</span>
<span class="pc bpc" id="L2803" title="1 of 2 branches missed.">        if (parts.length &gt; 0) {</span>
<span class="fc bfc" id="L2804" title="All 2 branches covered.">          for (int i = 0; i &lt; parts.length; i++) {</span>
<span class="fc" id="L2805">            result.put(i, keyToSubSubObject(parts[i].trim(), newKey));</span>
          }
<span class="fc" id="L2807">          return result;</span>
        } else {
<span class="nc" id="L2809">          return null;</span>
        }
      }
    }

    /**
     * Key to object.
     *
     * @param key the key
     * @param newKey the new key
     * @return the map
     */
    public static Map&lt;String, Map&lt;Integer, Map&lt;String, String&gt;[]&gt;&gt; keyToObject(
        String key, StringBuilder newKey) {
<span class="pc bpc" id="L2823" title="1 of 2 branches missed.">      if (key.startsWith(KEY_START)) {</span>
<span class="fc" id="L2824">        String content = key.substring(KEY_START.length());</span>
<span class="fc" id="L2825">        StringBuilder keyLeft = new StringBuilder(&quot;&quot;);</span>
<span class="fc" id="L2826">        StringBuilder keyHit = new StringBuilder(&quot;&quot;);</span>
<span class="fc" id="L2827">        StringBuilder keyRight = new StringBuilder(&quot;&quot;);</span>
<span class="fc" id="L2828">        Map&lt;String, Map&lt;Integer, Map&lt;String, String&gt;[]&gt;&gt; result = new HashMap&lt;&gt;();</span>
<span class="fc" id="L2829">        Map&lt;Integer, Map&lt;String, String&gt;[]&gt; resultLeft = null;</span>
<span class="fc" id="L2830">        Map&lt;Integer, Map&lt;String, String&gt;[]&gt; resultHit = null;</span>
<span class="fc" id="L2831">        Map&lt;Integer, Map&lt;String, String&gt;[]&gt; resultRight = null;</span>
<span class="fc" id="L2832">        String[] parts = content.split(Pattern.quote(&quot;|&quot;), -1);</span>
<span class="pc bpc" id="L2833" title="1 of 2 branches missed.">        if (parts.length == 3) {</span>
<span class="fc" id="L2834">          resultLeft = keyToSubObject(parts[0].trim(), keyLeft);</span>
<span class="fc" id="L2835">          resultHit = keyToSubObject(parts[1].trim(), keyHit);</span>
<span class="fc" id="L2836">          resultRight = keyToSubObject(parts[2].trim(), keyRight);</span>
<span class="nc bnc" id="L2837" title="All 2 branches missed.">        } else if (parts.length == 1) {</span>
<span class="nc" id="L2838">          resultHit = keyToSubObject(parts[0].trim(), keyHit);</span>
        }
<span class="pc bpc" id="L2840" title="1 of 2 branches missed.">        if (resultLeft != null) {</span>
<span class="nc" id="L2841">          result.put(&quot;left&quot;, resultLeft);</span>
        }
<span class="fc" id="L2843">        result.put(&quot;hit&quot;, resultHit);</span>
<span class="pc bpc" id="L2844" title="1 of 2 branches missed.">        if (resultRight != null) {</span>
<span class="nc" id="L2845">          result.put(&quot;right&quot;, resultRight);</span>
        }
<span class="fc" id="L2847">        newKey.append(keyLeft);</span>
<span class="fc" id="L2848">        newKey.append(&quot; |&quot;);</span>
<span class="fc" id="L2849">        newKey.append(keyHit);</span>
<span class="fc" id="L2850">        newKey.append(&quot; |&quot;);</span>
<span class="fc" id="L2851">        newKey.append(keyRight);</span>
<span class="fc" id="L2852">        return result;</span>
      } else {
<span class="nc" id="L2854">        return null;</span>
      }
    }

  }

  /**
   * The Class ListToken.
   */
  public static class ListToken {

    /** The doc id. */
    public Integer docId;

    /** The doc position. */
    public Integer docPosition;

    /** The start position. */
    public int startPosition;

    /** The end position. */
    public int endPosition;

    /** The tokens. */
    public List&lt;MtasTokenString&gt; tokens;

    /**
     * Instantiates a new list token.
     *
     * @param docId the doc id
     * @param docPosition the doc position
     * @param match the match
     * @param tokens the tokens
     */
    public ListToken(Integer docId, Integer docPosition, Match match,
<span class="nc" id="L2889">        List&lt;MtasTokenString&gt; tokens) {</span>
<span class="nc" id="L2890">      this.docId = docId;</span>
<span class="nc" id="L2891">      this.docPosition = docPosition;</span>
<span class="nc" id="L2892">      startPosition = match.startPosition;</span>
<span class="nc" id="L2893">      endPosition = match.endPosition - 1;</span>
<span class="nc" id="L2894">      this.tokens = tokens;</span>
<span class="nc" id="L2895">    }</span>
  }

  /**
   * The Class ListHit.
   */
  public static class ListHit {

    /** The doc id. */
    public Integer docId;

    /** The doc position. */
    public Integer docPosition;

    /** The start position. */
    public int startPosition;

    /** The end position. */
    public int endPosition;

    /** The hits. */
    public Map&lt;Integer, List&lt;String&gt;&gt; hits;

    /**
     * Instantiates a new list hit.
     *
     * @param docId the doc id
     * @param docPosition the doc position
     * @param match the match
     * @param hits the hits
     */
    public ListHit(Integer docId, Integer docPosition, Match match,
<span class="nc" id="L2927">        Map&lt;Integer, List&lt;String&gt;&gt; hits) {</span>
<span class="nc" id="L2928">      this.docId = docId;</span>
<span class="nc" id="L2929">      this.docPosition = docPosition;</span>
<span class="nc" id="L2930">      startPosition = match.startPosition;</span>
<span class="nc" id="L2931">      endPosition = match.endPosition - 1;</span>
<span class="nc" id="L2932">      this.hits = hits;</span>
<span class="nc" id="L2933">    }</span>
  }

  /**
   * The Class Match.
   */
  public static class Match {

    /** The start position. */
    public int startPosition;

    /** The end position. */
    public int endPosition;

    /**
     * Instantiates a new match.
     *
     * @param startPosition the start position
     * @param endPosition the end position
     */
<span class="fc" id="L2953">    public Match(int startPosition, int endPosition) {</span>
<span class="fc" id="L2954">      this.startPosition = startPosition;</span>
<span class="fc" id="L2955">      this.endPosition = endPosition;</span>
<span class="fc" id="L2956">    }</span>

    /*
     * (non-Javadoc)
     * 
     * @see java.lang.Object#equals(java.lang.Object)
     */
    @Override
    public boolean equals(Object obj) {
<span class="nc bnc" id="L2965" title="All 2 branches missed.">      if (this == obj)</span>
<span class="nc" id="L2966">        return true;</span>
<span class="nc bnc" id="L2967" title="All 2 branches missed.">      if (obj == null)</span>
<span class="nc" id="L2968">        return false;</span>
<span class="nc bnc" id="L2969" title="All 2 branches missed.">      if (getClass() != obj.getClass())</span>
<span class="nc" id="L2970">        return false;</span>
<span class="nc" id="L2971">      final Match that = (Match) obj;</span>
<span class="nc bnc" id="L2972" title="All 4 branches missed.">      return startPosition == that.startPosition</span>
          &amp;&amp; endPosition == that.endPosition;
    }

    /*
     * (non-Javadoc)
     * 
     * @see java.lang.Object#hashCode()
     */
    @Override
    public int hashCode() {
<span class="nc" id="L2983">      int h = this.getClass().getSimpleName().hashCode();</span>
<span class="nc" id="L2984">      h = (h * 5) ^ startPosition;</span>
<span class="nc" id="L2985">      h = (h * 7) ^ endPosition;</span>
<span class="nc" id="L2986">      return h;</span>
    }

  }

}
</pre><div class="footer"><span class="right">Created with <a href="http://www.jacoco.org/jacoco">JaCoCo</a> 0.7.9.201702052155</span></div></body></html>