content: /*
   * Copyright (C) 2015 The Android Open Source Project
   *
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
   * You may obtain a copy of the License at
   *
   *      http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   */
  
  package com.android.ahat;
  
  import com.android.tools.perflib.heap.Heap;
  import java.util.ArrayList;
  import java.util.HashMap;
  import java.util.List;
  import java.util.Map;
  
  /**
   * Class for rendering a table that includes sizes of some kind for each heap.
   */
  class HeapTable {
    /**
     * Configuration for a value column of a heap table.
     */
    public interface ValueConfig<T> {
      String getDescription();
      DocString render(T element);
    }
  
    /**
     * Configuration for the HeapTable.
     */
    public interface TableConfig<T> {
      String getHeapsDescription();
      long getSize(T element, Heap heap);
      List<ValueConfig<T>> getValueConfigs();
    }
  
    /**
     * Render the table to the given document.
     * @param query - The page query.
     * @param id - A unique identifier for the table on the page.
     */
    public static <T> void render(Doc doc, Query query, String id,
        TableConfig<T> config, AhatSnapshot snapshot, List<T> elements) {
      // Only show the heaps that have non-zero entries.
      List<Heap> heaps = new ArrayList<Heap>();
      for (Heap heap : snapshot.getHeaps()) {
        if (hasNonZeroEntry(snapshot, heap, config, elements)) {
          heaps.add(heap);
        }
      }
  
      List<ValueConfig<T>> values = config.getValueConfigs();
  
      // Print the heap and values descriptions.
      boolean showTotal = heaps.size() > 1;
      List<Column> subcols = new ArrayList<Column>();
      for (Heap heap : heaps) {
        subcols.add(new Column(heap.getName(), Column.Align.RIGHT));
      }
      if (showTotal) {
        subcols.add(new Column("Total", Column.Align.RIGHT));
      }
      List<Column> cols = new ArrayList<Column>();
      for (ValueConfig value : values) {
        cols.add(new Column(value.getDescription()));
      }
      doc.table(DocString.text(config.getHeapsDescription()), subcols, cols);
  
      // Print the entries up to the selected limit.
      SubsetSelector<T> selector = new SubsetSelector(query, id, elements);
      ArrayList<DocString> vals = new ArrayList<DocString>();
      for (T elem : selector.selected()) {
        vals.clear();
        long total = 0;
        for (Heap heap : heaps) {
          long size = config.getSize(elem, heap);
          total += size;
          vals.add(DocString.format("%,14d", size));
        }
        if (showTotal) {
          vals.add(DocString.format("%,14d", total));
        }
  
        for (ValueConfig<T> value : values) {
          vals.add(value.render(elem));
        }
        doc.row(vals.toArray(new DocString[0]));
      }
  
      // Print a summary of the remaining entries if there are any.
      List<T> remaining = selector.remaining();
      if (!remaining.isEmpty()) {
        Map<Heap, Long> summary = new HashMap<Heap, Long>();
        for (Heap heap : heaps) {
          summary.put(heap, 0L);
        }
  
        for (T elem : remaining) {
          for (Heap heap : heaps) {
            summary.put(heap, summary.get(heap) + config.getSize(elem, heap));
          }
        }
  
        vals.clear();
        long total = 0;
        for (Heap heap : heaps) {
          long size = summary.get(heap);
          total += size;
          vals.add(DocString.format("%,14d", size));
        }
        if (showTotal) {
          vals.add(DocString.format("%,14d", total));
        }
  
        for (ValueConfig<T> value : values) {
          vals.add(DocString.text("..."));
        }
        doc.row(vals.toArray(new DocString[0]));
      }
      doc.end();
      selector.render(doc);
    }
  
    // Returns true if the given heap has a non-zero size entry.
    public static <T> boolean hasNonZeroEntry(AhatSnapshot snapshot, Heap heap,
        TableConfig<T> config, List<T> elements) {
      if (snapshot.getHeapSize(heap) > 0) {
        for (T element : elements) {
          if (config.getSize(element, heap) > 0) {
            return true;
          }
        }
      }
      return false;
    }
  }
  
  