MtasSolrMtasResult.java

package mtas.solr.handler.component.util;

import java.io.IOException;
import java.io.Serializable;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;

import org.apache.solr.common.util.NamedList;
import org.apache.solr.common.util.SimpleOrderedMap;

import mtas.codec.util.CodecComponent.SubComponentDistance;
import mtas.codec.util.DataCollector;
import mtas.codec.util.collector.MtasDataCollector;
import mtas.codec.util.collector.MtasDataCollectorResult;
import mtas.codec.util.collector.MtasDataItem;
import mtas.codec.util.distance.Distance;

/**
 * The Class MtasSolrMtasResult.
 */
public class MtasSolrMtasResult implements Serializable {

  /** The Constant serialVersionUID. */
  private static final long serialVersionUID = 1L;

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

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

  /** The distances. */
  public List<SubComponentDistance> distances;

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

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

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

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

  /** The data collector. */
  public MtasDataCollector<?, ?> dataCollector = null;

  /** The function data. */
  public Map<MtasDataCollector<?, ?>, HashMap<String, MtasSolrMtasResult>> functionData;

  /** The sub data type. */
  private String[] subDataType;

  /** The sub stats type. */
  private String[] subStatsType;

  /** The sub stats items. */
  private SortedSet<String>[] subStatsItems;

  /** The sub distances. */
  private List<SubComponentDistance>[] subDistances;

  /** The sub sort type. */
  private String[] subSortType;

  /** The sub sort direction. */
  private String[] subSortDirection;

  /** The sub start. */
  private Integer[] subStart;

  /** The sub number. */
  private Integer[] subNumber;

  /**
   * Instantiates a new mtas solr mtas result.
   *
   * @param dataCollector the data collector
   * @param dataType the data type
   * @param statsType the stats type
   * @param statsItems the stats items
   * @param distances the distances
   * @param sortType the sort type
   * @param sortDirection the sort direction
   * @param start the start
   * @param number the number
   * @param functionData the function data
   */
  @SuppressWarnings("unchecked")
  public MtasSolrMtasResult(MtasDataCollector<?, ?> dataCollector,
      String[] dataType, String[] statsType, SortedSet<String>[] statsItems,
      List<SubComponentDistance>[] distances, String[] sortType,
      String[] sortDirection, Integer[] start, Integer[] number,
      Map<MtasDataCollector<?, ?>, HashMap<String, MtasSolrMtasResult>> functionData) {
    this.dataCollector = dataCollector;
    this.functionData = functionData;
    this.dataType = (dataType == null) ? null : dataType[0];
    this.statsType = (statsType == null) ? null : statsType[0];
    this.distances = (distances == null) ? null : distances[0];
    this.sortType = (sortType == null) ? null : sortType[0];
    this.sortDirection = (sortDirection == null) ? null : sortDirection[0];
    this.start = (start == null) ? null : start[0];
    this.number = (number == null) ? null : number[0];
    this.subStart = null;
    this.subNumber = null;
    if ((dataType != null) && (dataType.length > 1)) {
      subDataType = new String[dataType.length - 1];
      subStatsType = new String[dataType.length - 1];
      subStatsItems = new TreeSet[dataType.length - 1];      
      subSortType = new String[dataType.length - 1];
      subSortDirection = new String[dataType.length - 1];
      System.arraycopy(dataType, 1, subDataType, 0, dataType.length - 1);
      System.arraycopy(statsType, 1, subStatsType, 0, dataType.length - 1);
      System.arraycopy(statsItems, 1, subStatsItems, 0, dataType.length - 1);      
      System.arraycopy(sortType, 1, subSortType, 0, dataType.length - 1);
      System.arraycopy(sortDirection, 1, subSortDirection, 0,
          dataType.length - 1);
      if(distances!=null) {
        subDistances = new List[dataType.length - 1];
        System.arraycopy(distances, 1, subDistances, 0, dataType.length - 1);
      } else {
        subDistances = null;
      }      
    } else {
      subDataType = null;
      subStatsType = null;
      subStatsItems = null;
      subDistances = null;
      subSortType = null;
      subSortDirection = null;
    }
  }

  /**
   * Instantiates a new mtas solr mtas result.
   *
   * @param dataCollector the data collector
   * @param dataType the data type
   * @param statsType the stats type
   * @param statsItems the stats items
   * @param distance the distance
   * @param functionData the function data
   */
  @SuppressWarnings("unchecked")
  public MtasSolrMtasResult(MtasDataCollector<?, ?> dataCollector,
      String dataType, String statsType, SortedSet<String> statsItems,
      List<SubComponentDistance> distance,
      Map<MtasDataCollector<?, ?>, HashMap<String, MtasSolrMtasResult>> functionData) {
    this(dataCollector, new String[] { dataType }, new String[] { statsType },
        new SortedSet[] { statsItems }, new List[] { distance },
        new String[] { null }, new String[] { null }, new Integer[] { 0 },
        new Integer[] { 1 }, functionData);
  }

  /**
   * Merge.
   *
   * @param newItem the new item
   * @throws IOException Signals that an I/O exception has occurred.
   */
  void merge(MtasSolrMtasResult newItem) throws IOException {
    HashMap<MtasDataCollector<?, ?>, MtasDataCollector<?, ?>> map = new HashMap<>();
    if (newItem.dataCollector.withTotal()) {
      dataCollector.setWithTotal();
    }
    dataCollector.merge(newItem.dataCollector, map, true);
    if (newItem.functionData != null) {
      if (functionData == null) {
        functionData = new HashMap<>();
      }
      for (MtasDataCollector<?, ?> keyCollector : newItem.functionData
          .keySet()) {
        if (map.containsKey(keyCollector)) {
          // compute mapped key
          MtasDataCollector<?, ?> newKeyCollector = keyCollector;
          while (map.containsKey(newKeyCollector)) {
            newKeyCollector = map.get(keyCollector);
          }
          if (functionData.containsKey(newKeyCollector)) {
            HashMap<String, MtasSolrMtasResult> tmpList = functionData
                .get(newKeyCollector);
            for (String functionKey : newItem.functionData.get(keyCollector)
                .keySet()) {
              if (tmpList.containsKey(functionKey)) {
                tmpList.get(functionKey).merge(
                    newItem.functionData.get(keyCollector).get(functionKey));
              } else {
                tmpList.put(functionKey,
                    newItem.functionData.get(keyCollector).get(functionKey));
              }
            }
          } else {
            functionData.put(newKeyCollector,
                newItem.functionData.get(keyCollector));
          }
        }
      }
    }
  }

  /**
   * Gets the data.
   *
   * @param showDebugInfo the show debug info
   * @return the data
   * @throws IOException Signals that an I/O exception has occurred.
   */
  NamedList<Object> getData(boolean showDebugInfo) throws IOException {
    if (dataCollector.getCollectorType()
        .equals(DataCollector.COLLECTOR_TYPE_DATA)) {
      NamedList<Object> mtasResponse = new SimpleOrderedMap<>();
      // functions
      Map<String, NamedList<Object>> functionList = new HashMap<>();
      if (functionData != null && functionData.containsKey(dataCollector)) {
        HashMap<String, MtasSolrMtasResult> functionDataItem = functionData
            .get(dataCollector);
        for (Entry<String, MtasSolrMtasResult> entry : functionDataItem
            .entrySet()) {
          MtasSolrMtasResult functionResult = entry.getValue();
          if (functionResult.dataCollector.getCollectorType()
              .equals(DataCollector.COLLECTOR_TYPE_DATA)) {
            NamedList<Object> functionData = functionResult
                .getData(showDebugInfo);
            functionList.put(entry.getKey(), functionData);
          } else {
            throw new IOException("unexpected function collectorType "
                + functionResult.dataCollector.getCollectorType());
          }
        }
      }
      // main result
      MtasDataItem<?, ?> dataItem = dataCollector.getResult().getData();
      if (dataItem != null) {
        mtasResponse.addAll(dataItem.rewrite(showDebugInfo));
        if (functionList.size() > 0) {
          mtasResponse.add("functions", functionList);
        }
        if ((subDataType != null) && (dataItem.getSub() != null)) {
          MtasSolrMtasResult css = new MtasSolrMtasResult(dataItem.getSub(),
              subDataType, subStatsType, subStatsItems, subDistances,
              subSortType, subSortDirection, subStart, subNumber, functionData);
          if (dataItem.getSub().getCollectorType()
              .equals(DataCollector.COLLECTOR_TYPE_LIST)) {
            mtasResponse.add(dataItem.getSub().getCollectorType(),
                css.getNamedList(showDebugInfo));
          } else if (dataItem.getSub().getCollectorType()
              .equals(DataCollector.COLLECTOR_TYPE_DATA)) {
            mtasResponse.add(dataItem.getSub().getCollectorType(),
                css.getData(showDebugInfo));
          }
        }
      }
      return mtasResponse;
    } else {
      throw new IOException(
          "only allowed for " + DataCollector.COLLECTOR_TYPE_DATA);
    }
  }

  /**
   * Gets the key list.
   *
   * @return the key list
   * @throws IOException Signals that an I/O exception has occurred.
   */
  public Set<String> getKeyList() throws IOException {
    if (dataCollector.getCollectorType()
        .equals(DataCollector.COLLECTOR_TYPE_LIST)) {
      return dataCollector.getResult().getComparatorList().keySet();
    } else {
      throw new IOException(
          "only allowed for " + DataCollector.COLLECTOR_TYPE_LIST);
    }
  }

  /**
   * Gets the full key list.
   *
   * @return the full key list
   * @throws IOException Signals that an I/O exception has occurred.
   */
  public Set<String> getFullKeyList() throws IOException {
    if (dataCollector.getCollectorType()
        .equals(DataCollector.COLLECTOR_TYPE_LIST)) {
      return dataCollector.getKeyList();
    } else {
      throw new IOException(
          "only allowed for " + DataCollector.COLLECTOR_TYPE_LIST);
    }
  }

  /**
   * Gets the named list.
   *
   * @param showDebugInfo the show debug info
   * @return the named list
   * @throws IOException Signals that an I/O exception has occurred.
   */
  NamedList<Object> getNamedList(boolean showDebugInfo) throws IOException {
    if (dataCollector.getCollectorType()
        .equals(DataCollector.COLLECTOR_TYPE_LIST)) {
      SimpleOrderedMap<Object> mtasResponseList = new SimpleOrderedMap<>();
      // functions
      Map<String, SimpleOrderedMap<Object>> functionList = new HashMap<>();
      if (functionData != null && functionData.containsKey(dataCollector)) {
        HashMap<String, MtasSolrMtasResult> functionDataItem = functionData
            .get(dataCollector);
        for (Entry<String, MtasSolrMtasResult> entry : functionDataItem
            .entrySet()) {
          MtasSolrMtasResult functionResult = entry.getValue();
          if (functionResult.dataCollector.getCollectorType()
              .equals(DataCollector.COLLECTOR_TYPE_LIST)) {
            NamedList<Object> functionNamedList = functionResult
                .getNamedList(showDebugInfo);
            for (int i = 0; i < functionNamedList.size(); i++) {
              if (functionList.containsKey(functionNamedList.getName(i))) {
                SimpleOrderedMap<Object> tmpMap = functionList
                    .get(functionNamedList.getName(i));
                tmpMap.add(entry.getKey(), functionNamedList.getVal(i));
              } else {
                SimpleOrderedMap<Object> tmpMap = new SimpleOrderedMap<>();
                tmpMap.add(entry.getKey(), functionNamedList.getVal(i));
                functionList.put(functionNamedList.getName(i), tmpMap);
              }
            }
          } else {
            throw new IOException("unexpected function collectorType "
                + functionResult.dataCollector.getCollectorType());
          }
        }
      }
      // main result
      Map<String, ?> dataList = dataCollector.getResult().getList();
      for (Entry<String, ?> entry : dataList.entrySet()) {
        SimpleOrderedMap<Object> mtasResponseListItem = new SimpleOrderedMap<>();
        MtasDataItem<?, ?> dataItem = (MtasDataItem<?, ?>) entry.getValue();
        if (this.distances != null && !this.distances.isEmpty()) {
          SimpleOrderedMap<Object> mtasResponseListItemDistance = new SimpleOrderedMap<>();
          for (SubComponentDistance item : this.distances) {
            mtasResponseListItemDistance.add(item.key,
                item.getDistance().compute(entry.getKey()));
          }
          mtasResponseListItem.add(Distance.NAME, mtasResponseListItemDistance);
        }
        mtasResponseListItem.addAll(dataItem.rewrite(showDebugInfo));
        if (functionList.containsKey(entry.getKey())) {
          mtasResponseListItem.add("functions",
              functionList.get(entry.getKey()));
        }
        if ((subDataType != null) && (dataItem.getSub() != null)) {
          MtasSolrMtasResult css = new MtasSolrMtasResult(dataItem.getSub(),
              subDataType, subStatsType, subStatsItems, subDistances,
              subSortType, subSortDirection, subStart, subNumber, functionData);
          if (dataItem.getSub().getCollectorType()
              .equals(DataCollector.COLLECTOR_TYPE_LIST)) {
            if (css.dataCollector.withTotal()) {
              mtasResponseListItem.add(
                  DataCollector.COLLECTOR_TYPE_LIST + "Total",
                  css.dataCollector.getSize());
            }
            mtasResponseListItem.add(DataCollector.COLLECTOR_TYPE_LIST,
                css.getNamedList(showDebugInfo));
          } else if (dataItem.getSub().getCollectorType()
              .equals(DataCollector.COLLECTOR_TYPE_DATA)) {
            mtasResponseListItem.add(DataCollector.COLLECTOR_TYPE_DATA,
                css.getData(showDebugInfo));
          }
        }
        mtasResponseList.add(entry.getKey(), mtasResponseListItem);
      }
      return mtasResponseList;
    } else {
      throw new IOException(
          "only allowed for " + DataCollector.COLLECTOR_TYPE_LIST);
    }
  }

  /*
   * (non-Javadoc)
   * 
   * @see java.lang.Object#toString()
   */
  @Override
  public String toString() {
    if (dataCollector.getCollectorType()
        .equals(DataCollector.COLLECTOR_TYPE_DATA)) {
      return this.getClass().getSimpleName() + "(data-" + hashCode() + ")";
    }
    if (dataCollector.getCollectorType()
        .equals(DataCollector.COLLECTOR_TYPE_LIST)) {
      return this.getClass().getSimpleName() + "(list("
          + dataCollector.getSize() + ")-" + hashCode() + ")";
    } else {
      return this.getClass().getSimpleName() + ": unknown";
    }
  }

  /**
   * Gets the result.
   *
   * @return the result
   * @throws IOException Signals that an I/O exception has occurred.
   */
  public MtasDataCollectorResult getResult() throws IOException {
    return dataCollector.getResult();
  }

}