/*
* Copyright 2005-2016 Sixth and Red River Software, Bas Leijdekkers
*
* 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.sixrr.metrics.ui.metricdisplay;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.PsiElement;
import com.sixrr.metrics.Metric;
import com.sixrr.metrics.profile.MetricInstance;
import com.sixrr.metrics.metricModel.MetricInstanceAbbreviationComparator;
import com.sixrr.metrics.metricModel.MetricsResult;
import com.sixrr.metrics.profile.MetricTableSpecification;
import com.sixrr.metrics.profile.MetricsProfile;
import com.sixrr.metrics.profile.MetricsProfileRepository;
import com.sixrr.metrics.utils.MetricsReloadedBundle;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.table.AbstractTableModel;
import java.util.*;
class MetricTableModel extends AbstractTableModel {
private final int[] columnPermutation;
private final MetricTableSpecification tableSpecification;
private final String type;
private String[] measuredObjects;
private MetricInstance[] metricsInstances;
private MetricsResult prevResults;
private MetricsResult results;
private int[] rowPermutation;
MetricTableModel(@NotNull MetricsResult results, @NotNull String type,
@NotNull MetricTableSpecification tableSpecification) {
this.results = results;
this.type = type;
this.tableSpecification = tableSpecification;
prevResults = null;
measuredObjects = results.getMeasuredObjects();
metricsInstances = findMetricInstances(results.getMetrics());
Arrays.sort(metricsInstances, new MetricInstanceAbbreviationComparator());
final Map<MetricInstance, Integer> remainingMetrics = new LinkedHashMap<MetricInstance, Integer>();
for (int i = 0; i < metricsInstances.length; i++) {
final MetricInstance metric = metricsInstances[i];
remainingMetrics.put(metric, Integer.valueOf(i + 1));
}
columnPermutation = new int[metricsInstances.length + 1];
final List<String> columnOrder = tableSpecification.getColumnOrder();
final Set<String> strippedColumnOrder = new LinkedHashSet<String>();
for (final String columnName : columnOrder) {
boolean found = false;
if (columnName.equals(type)) {
found = true;
} else {
for (final MetricInstance metricInstance : metricsInstances) {
if (metricInstance.getMetric().getAbbreviation().equals(columnName)) {
found = true;
break;
}
}
}
if (found) {
strippedColumnOrder.add(columnName);
}
}
int columnCount = 0;
int leftOverCount = strippedColumnOrder.size();
for (final String columnName : strippedColumnOrder) {
if (columnName.equals(type)) {
columnPermutation[columnCount] = 0;
} else {
boolean found = false;
for (int i = 0; i < metricsInstances.length; i++) {
final MetricInstance metricInstance = metricsInstances[i];
if (metricInstance.getMetric().getAbbreviation().equals(columnName)) {
if (columnCount < columnPermutation.length) {
columnPermutation[columnCount] = i + 1;
remainingMetrics.remove(metricInstance);
}
found = true;
break;
}
}
if (!found) {
columnPermutation[columnCount] = leftOverCount;
leftOverCount++;
}
}
columnCount++;
}
if (columnCount == 0) {
columnCount = 1;
}
final Collection<Integer> remainingMetricSlots = remainingMetrics.values();
for (final Integer position : remainingMetricSlots) {
columnPermutation[columnCount] = position;
columnCount++;
}
rowPermutation = new int[measuredObjects.length];
sort();
}
public void changeSort(int column, boolean ascending) {
tableSpecification.setSortColumn(column);
tableSpecification.setAscending(ascending);
sort();
fireTableDataChanged();
MetricsProfileRepository.getInstance().persistCurrentProfile();
}
private MetricInstance[] findMetricInstances(@NotNull Metric[] metrics) {
final MetricsProfile profile = MetricsProfileRepository.getInstance().getCurrentProfile();
final MetricInstance[] metricInstances = new MetricInstance[metrics.length];
for (int i = 0; i < metrics.length; i++) {
final MetricInstance metricInstance = profile.getMetricInstance(metrics[i]);
assert metricInstance != null;
metricInstances[i] = metricInstance;
}
return metricInstances;
}
@Override
public int getColumnCount() {
return metricsInstances.length + 1;
}
@Override
public String getColumnName(int column) {
final int permutedColumn = columnPermutation[column];
if (permutedColumn == 0) {
return type;
} else {
final MetricInstance metricInstance = metricsInstances[permutedColumn - 1];
final Metric metric = metricInstance.getMetric();
return metric.getAbbreviation();
}
}
@Nullable
public PsiElement getElementAtRow(int row) {
if (row >= rowPermutation.length) {
return null;
}
final String measuredObject = measuredObjects[rowPermutation[row]];
return results.getElementForMeasuredObject(measuredObject);
}
public MetricInstance getMetricForColumn(int column) {
final int permutedColumn = columnPermutation[column];
return metricsInstances[permutedColumn - 1];
}
public MetricInstance[] getMetricsInstances() {
return metricsInstances.clone();
}
public MetricsResult getResults() {
return results;
}
public void setResults(MetricsResult newResults) {
results = newResults;
tabulateMetrics();
tabulateMeasuredObjects();
rowPermutation = new int[measuredObjects.length];
sort();
fireTableStructureChanged();
fireTableDataChanged();
}
@Override
public int getRowCount() {
return hasSummaryRows() ? measuredObjects.length + 2 : measuredObjects.length;
}
public int getSortColumn() {
return tableSpecification.getSortColumn();
}
@Override
@Nullable
public Object getValueAt(int rowIndex, int columnIndex) {
final int permutedColumn = columnPermutation[columnIndex];
if (hasSummaryRows()) {
if (rowIndex == measuredObjects.length) {
if (permutedColumn == 0) {
return MetricsReloadedBundle.message("total");
} else if (prevResults == null) {
final MetricInstance metricInstance = metricsInstances[permutedColumn - 1];
return results.getTotalForMetric(metricInstance.getMetric());
} else {
final MetricInstance metricInstance = metricsInstances[permutedColumn - 1];
final Double value = results.getTotalForMetric(metricInstance.getMetric());
final Double prevValue = prevResults.getTotalForMetric(metricInstance.getMetric());
return Pair.create(value, prevValue);
}
}
if (rowIndex == measuredObjects.length + 1) {
if (permutedColumn == 0) {
return MetricsReloadedBundle.message("average");
} else if (prevResults == null) {
final MetricInstance metricInstance = metricsInstances[permutedColumn - 1];
return results.getAverageForMetric(metricInstance.getMetric());
} else {
final MetricInstance metricInstance = metricsInstances[permutedColumn - 1];
final Double value = results.getAverageForMetric(metricInstance.getMetric());
final Double prevValue = prevResults.getAverageForMetric(metricInstance.getMetric());
return Pair.create(value, prevValue);
}
}
}
final String measuredObject = measuredObjects[rowPermutation[rowIndex]];
if (permutedColumn == 0) {
return measuredObject;
} else if (prevResults == null) {
final MetricInstance metricInstance = metricsInstances[permutedColumn - 1];
return results.getValueForMetric(metricInstance.getMetric(), measuredObject);
} else {
final MetricInstance metric = metricsInstances[permutedColumn - 1];
final Double value = results.getValueForMetric(metric.getMetric(), measuredObject);
final Double prevValue = prevResults.getValueForMetric(metric.getMetric(), measuredObject);
return Pair.create(value, prevValue);
}
}
public boolean hasDiff() {
return prevResults != null;
}
public boolean hasSummaryRows() {
return measuredObjects.length > 1;
}
public boolean isAscending() {
return tableSpecification.isAscending();
}
public void setPrevResults(MetricsResult newResults) {
prevResults = newResults;
tabulateMetrics();
tabulateMeasuredObjects();
rowPermutation = new int[measuredObjects.length];
sort();
fireTableStructureChanged();
fireTableDataChanged();
}
private void sort() {
// change the rowPermutation
int sortColumn = tableSpecification.getSortColumn();
if (sortColumn >= columnPermutation.length) {
tableSpecification.setAscending(true);
tableSpecification.setSortColumn(0);
sortColumn = 0;
}
final Pair<Integer, ? extends Comparable<?>>[] tempArray = new Pair[rowPermutation.length];
final int permutedColumn = columnPermutation[sortColumn];
if (permutedColumn == 0) {
for (int i = 0; i < rowPermutation.length; i++) {
final String name = measuredObjects[i];
tempArray[i] = Pair.create(Integer.valueOf(i), name);
}
} else {
for (int i = 0; i < rowPermutation.length; i++) {
final String name = measuredObjects[i];
final MetricInstance metricInstance = metricsInstances[permutedColumn - 1];
final Double value = results.getValueForMetric(metricInstance.getMetric(), name);
tempArray[i] = Pair.create(Integer.valueOf(i), value);
}
}
Arrays.sort(tempArray, new PairComparator(tableSpecification.isAscending()));
for (int i = 0; i < tempArray.length; i++) {
rowPermutation[i] = tempArray[i].getFirst().intValue();
}
}
private void tabulateMeasuredObjects() {
final String[] resultObjects = results.getMeasuredObjects();
final Set<String> allObjects = new HashSet<String>(resultObjects.length);
if (prevResults != null) {
final String[] prevResultObjects = prevResults.getMeasuredObjects();
Collections.addAll(allObjects, prevResultObjects);
}
Collections.addAll(allObjects, resultObjects);
measuredObjects = allObjects.toArray(new String[allObjects.size()]);
}
private void tabulateMetrics() {
final Metric[] currentMetrics = results.getMetrics();
final MetricInstance[] resultMetrics = findMetricInstances(currentMetrics);
final Set<MetricInstance> allMetrics = new HashSet<MetricInstance>(resultMetrics.length);
Collections.addAll(allMetrics, resultMetrics);
if (prevResults != null) {
final MetricInstance[] prevResultMetrics = findMetricInstances(prevResults.getMetrics());
Collections.addAll(allMetrics, prevResultMetrics);
}
metricsInstances = allMetrics.toArray(new MetricInstance[allMetrics.size()]);
Arrays.sort(metricsInstances, new MetricInstanceAbbreviationComparator());
}
private static class PairComparator implements Comparator<Pair> {
private final boolean ascending;
PairComparator(boolean ascending) {
this.ascending = ascending;
}
@Override
public int compare(Pair pair1, Pair pair2) {
final Object value1 = pair1.getSecond();
final Object value2 = pair2.getSecond();
if (value1 == null && value2 == null) {
return 0;
}
final int comparison;
if (value1 == null) {
comparison = -1;
} else if (value2 == null) {
comparison = 1;
} else {
if (value1 instanceof String && value2 instanceof String) {
final String s1 = (String) value1;
final String s2 = (String) value2;
comparison = StringUtil.naturalCompare(s1, s2);
} else {
comparison = ((Comparable) value1).compareTo(value2);
}
}
return ascending ? comparison : -comparison;
}
}
}