1/*
2 * Copyright (C) 2015 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.ahat;
18
19import com.android.tools.perflib.heap.Heap;
20import java.util.ArrayList;
21import java.util.HashMap;
22import java.util.List;
23import java.util.Map;
24
25/**
26 * Class for rendering a table that includes sizes of some kind for each heap.
27 */
28class HeapTable {
29  /**
30   * Configuration for a value column of a heap table.
31   */
32  public interface ValueConfig<T> {
33    String getDescription();
34    DocString render(T element);
35  }
36
37  /**
38   * Configuration for the HeapTable.
39   */
40  public interface TableConfig<T> {
41    String getHeapsDescription();
42    long getSize(T element, Heap heap);
43    List<ValueConfig<T>> getValueConfigs();
44  }
45
46  /**
47   * Render the table to the given document.
48   * @param query - The page query.
49   * @param id - A unique identifier for the table on the page.
50   */
51  public static <T> void render(Doc doc, Query query, String id,
52      TableConfig<T> config, AhatSnapshot snapshot, List<T> elements) {
53    // Only show the heaps that have non-zero entries.
54    List<Heap> heaps = new ArrayList<Heap>();
55    for (Heap heap : snapshot.getHeaps()) {
56      if (hasNonZeroEntry(snapshot, heap, config, elements)) {
57        heaps.add(heap);
58      }
59    }
60
61    List<ValueConfig<T>> values = config.getValueConfigs();
62
63    // Print the heap and values descriptions.
64    boolean showTotal = heaps.size() > 1;
65    List<Column> subcols = new ArrayList<Column>();
66    for (Heap heap : heaps) {
67      subcols.add(new Column(heap.getName(), Column.Align.RIGHT));
68    }
69    if (showTotal) {
70      subcols.add(new Column("Total", Column.Align.RIGHT));
71    }
72    List<Column> cols = new ArrayList<Column>();
73    for (ValueConfig value : values) {
74      cols.add(new Column(value.getDescription()));
75    }
76    doc.table(DocString.text(config.getHeapsDescription()), subcols, cols);
77
78    // Print the entries up to the selected limit.
79    SubsetSelector<T> selector = new SubsetSelector(query, id, elements);
80    ArrayList<DocString> vals = new ArrayList<DocString>();
81    for (T elem : selector.selected()) {
82      vals.clear();
83      long total = 0;
84      for (Heap heap : heaps) {
85        long size = config.getSize(elem, heap);
86        total += size;
87        vals.add(DocString.format("%,14d", size));
88      }
89      if (showTotal) {
90        vals.add(DocString.format("%,14d", total));
91      }
92
93      for (ValueConfig<T> value : values) {
94        vals.add(value.render(elem));
95      }
96      doc.row(vals.toArray(new DocString[0]));
97    }
98
99    // Print a summary of the remaining entries if there are any.
100    List<T> remaining = selector.remaining();
101    if (!remaining.isEmpty()) {
102      Map<Heap, Long> summary = new HashMap<Heap, Long>();
103      for (Heap heap : heaps) {
104        summary.put(heap, 0L);
105      }
106
107      for (T elem : remaining) {
108        for (Heap heap : heaps) {
109          summary.put(heap, summary.get(heap) + config.getSize(elem, heap));
110        }
111      }
112
113      vals.clear();
114      long total = 0;
115      for (Heap heap : heaps) {
116        long size = summary.get(heap);
117        total += size;
118        vals.add(DocString.format("%,14d", size));
119      }
120      if (showTotal) {
121        vals.add(DocString.format("%,14d", total));
122      }
123
124      for (ValueConfig<T> value : values) {
125        vals.add(DocString.text("..."));
126      }
127      doc.row(vals.toArray(new DocString[0]));
128    }
129    doc.end();
130    selector.render(doc);
131  }
132
133  // Returns true if the given heap has a non-zero size entry.
134  public static <T> boolean hasNonZeroEntry(AhatSnapshot snapshot, Heap heap,
135      TableConfig<T> config, List<T> elements) {
136    if (snapshot.getHeapSize(heap) > 0) {
137      for (T element : elements) {
138        if (config.getSize(element, heap) > 0) {
139          return true;
140        }
141      }
142    }
143    return false;
144  }
145}
146
147