package rocks.inspectit.ui.rcp.editor.table.input; import java.util.ArrayList; import java.util.Collections; import java.util.EnumSet; import java.util.List; import java.util.Objects; import java.util.Set; import org.eclipse.jface.viewers.DoubleClickEvent; import org.eclipse.jface.viewers.IBaseLabelProvider; import org.eclipse.jface.viewers.IContentProvider; import org.eclipse.jface.viewers.IStructuredContentProvider; import org.eclipse.jface.viewers.StructuredSelection; import org.eclipse.jface.viewers.StyledString; import org.eclipse.jface.viewers.TableViewer; import org.eclipse.jface.viewers.TableViewerColumn; import org.eclipse.jface.viewers.Viewer; import org.eclipse.jface.viewers.ViewerComparator; import org.eclipse.swt.SWT; import org.eclipse.swt.graphics.Image; import org.eclipse.swt.widgets.Display; import org.eclipse.ui.IWorkbenchPage; import org.eclipse.ui.IWorkbenchWindow; import org.eclipse.ui.PlatformUI; import rocks.inspectit.shared.all.cmr.service.ICachedDataService; import rocks.inspectit.shared.all.communication.data.SqlStatementData; import rocks.inspectit.shared.cs.communication.comparator.IDataComparator; import rocks.inspectit.shared.cs.communication.comparator.InvocationAwareDataComparatorEnum; import rocks.inspectit.shared.cs.communication.comparator.ResultComparator; import rocks.inspectit.shared.cs.communication.comparator.SqlStatementDataComparatorEnum; import rocks.inspectit.shared.cs.communication.comparator.TimerDataComparatorEnum; import rocks.inspectit.ui.rcp.InspectIT; import rocks.inspectit.ui.rcp.InspectITImages; import rocks.inspectit.ui.rcp.editor.preferences.PreferenceEventCallback.PreferenceEvent; import rocks.inspectit.ui.rcp.editor.preferences.PreferenceId; import rocks.inspectit.ui.rcp.editor.root.IRootEditor; import rocks.inspectit.ui.rcp.editor.table.TableViewerComparator; import rocks.inspectit.ui.rcp.editor.text.input.SqlStatementTextInputController.SqlHolderHelper; import rocks.inspectit.ui.rcp.editor.viewers.StyledCellIndexLabelProvider; import rocks.inspectit.ui.rcp.formatter.NumberFormatter; import rocks.inspectit.ui.rcp.formatter.TextFormatter; import rocks.inspectit.ui.rcp.preferences.PreferencesConstants; import rocks.inspectit.ui.rcp.preferences.PreferencesUtils; import rocks.inspectit.ui.rcp.repository.CmrRepositoryDefinition; /** * Input controller for the table view that is displayed below the aggregated SQL table. * <p> * Shows one statement aggregated on parameter basis. * * @author Ivan Senic * */ public class SqlParameterAggregationInputControler extends AbstractTableInputController { /** * The ID of this subview / controller. */ public static final String ID = "inspectit.subview.table.sqlparameteraggregation"; /** * @author Ivan Senic * */ private static enum Column { /** The Parameters column. */ PARAMETERS("Parameters", 600, null, SqlStatementDataComparatorEnum.PARAMETERS), /** Invocation Affiliation. */ INVOCATION_AFFILLIATION("In Invocations", 120, InspectITImages.IMG_INVOCATION, InvocationAwareDataComparatorEnum.INVOCATION_AFFILIATION), /** The count column. */ COUNT("Count", 80, null, TimerDataComparatorEnum.COUNT), /** The average column. */ AVERAGE("Avg (ms)", 80, null, TimerDataComparatorEnum.AVERAGE), /** The min column. */ MIN("Min (ms)", 80, null, TimerDataComparatorEnum.MIN), /** The max column. */ MAX("Max (ms)", 80, null, TimerDataComparatorEnum.MAX), /** The duration column. */ DURATION("Duration (ms)", 80, null, TimerDataComparatorEnum.DURATION); /** The name. */ private String name; /** The width of the column. */ private int width; /** The image descriptor. Can be <code>null</code> */ private Image image; /** Comparator for the column. */ private IDataComparator<? super SqlStatementData> dataComparator; /** * Default constructor which creates a column enumeration object. * * @param name * The name of the column. * @param width * The width of the column. * @param imageName * The name of the image. Names are defined in {@link InspectITImages}. * @param dataComparator * Comparator for the column. */ private Column(String name, int width, String imageName, IDataComparator<? super SqlStatementData> dataComparator) { this.name = name; this.width = width; this.image = InspectIT.getDefault().getImage(imageName); this.dataComparator = dataComparator; } /** * Converts an ordinal into a column. * * @param i * The ordinal. * @return The appropriate column. */ public static Column fromOrd(int i) { if ((i < 0) || (i >= Column.values().length)) { throw new IndexOutOfBoundsException("Invalid ordinal"); } return Column.values()[i]; } } /** * Decimal places. */ private int timeDecimalPlaces = PreferencesUtils.getIntValue(PreferencesConstants.DECIMAL_PLACES); /** * {@inheritDoc} */ @Override public void createColumns(TableViewer tableViewer) { for (Column column : Column.values()) { TableViewerColumn viewerColumn = new TableViewerColumn(tableViewer, SWT.NONE); viewerColumn.getColumn().setMoveable(true); viewerColumn.getColumn().setResizable(true); viewerColumn.getColumn().setText(column.name); viewerColumn.getColumn().setWidth(column.width); if (null != column.image) { viewerColumn.getColumn().setImage(column.image); } mapTableViewerColumn(column, viewerColumn); } } /** * {@inheritDoc} */ @Override public Set<PreferenceId> getPreferenceIds() { Set<PreferenceId> preferences = EnumSet.noneOf(PreferenceId.class); if (getInputDefinition().getRepositoryDefinition() instanceof CmrRepositoryDefinition) { preferences.add(PreferenceId.CLEAR_BUFFER); } preferences.add(PreferenceId.TIME_RESOLUTION); return preferences; } /** * {@inheritDoc} */ @Override public void preferenceEventFired(PreferenceEvent preferenceEvent) { switch (preferenceEvent.getPreferenceId()) { case TIME_RESOLUTION: if (preferenceEvent.getPreferenceMap().containsKey(PreferenceId.TimeResolution.TIME_DECIMAL_PLACES_ID)) { timeDecimalPlaces = (Integer) preferenceEvent.getPreferenceMap().get(PreferenceId.TimeResolution.TIME_DECIMAL_PLACES_ID); } break; default: break; } } /** * {@inheritDoc} */ @Override public Object getTableInput() { return null; } /** * {@inheritDoc} */ @Override public boolean canOpenInput(List<? extends Object> data) { if (data != null) { for (Object object : data) { if (!(object instanceof SqlHolderHelper)) { return false; } else if (!((SqlHolderHelper) object).isMaster()) { return false; } } } return true; } /** * {@inheritDoc} */ @Override public IContentProvider getContentProvider() { return new SqlParameterContentProvider(); } /** * {@inheritDoc} */ @Override public IBaseLabelProvider getLabelProvider() { return new SqlLabelProvider(); } /** * {@inheritDoc} */ @Override public ViewerComparator getComparator() { ICachedDataService cachedDataService = getInputDefinition().getRepositoryDefinition().getCachedDataService(); TableViewerComparator<SqlStatementData> sqlViewerComparator = new TableViewerComparator<>(); for (Column column : Column.values()) { ResultComparator<SqlStatementData> resultComparator = new ResultComparator<>(column.dataComparator, cachedDataService); sqlViewerComparator.addColumn(getMappedTableViewerColumn(column).getColumn(), resultComparator); } return sqlViewerComparator; } /** * {@inheritDoc} */ @Override public String getReadableString(Object object) { if (object instanceof SqlStatementData) { SqlStatementData data = (SqlStatementData) object; StringBuilder sb = new StringBuilder(); for (Column column : Column.values()) { sb.append(getStyledTextForColumn(data, column).toString()); sb.append('\t'); } return sb.toString(); } throw new RuntimeException("Could not create the human readable string!"); } /** * {@inheritDoc} * <p> * We don't search the input directly. */ @SuppressWarnings("unchecked") @Override public Object[] getObjectsToSearch(Object tableInput) { if (null != tableInput) { List<SqlStatementData> sqlStatementDatas = new ArrayList<>(); // we can cast here safely cause only this can be input List<SqlHolderHelper> helpers = (List<SqlHolderHelper>) tableInput; for (SqlHolderHelper helper : helpers) { sqlStatementDatas.addAll(helper.getSqlStatementDataList()); } return sqlStatementDatas.toArray(new SqlStatementData[sqlStatementDatas.size()]); } return new Object[0]; } /** * {@inheritDoc} */ @Override public List<String> getColumnValues(Object object) { if (object instanceof SqlStatementData) { SqlStatementData data = (SqlStatementData) object; List<String> values = new ArrayList<>(); for (Column column : Column.values()) { values.add(getStyledTextForColumn(data, column).toString()); } return values; } throw new RuntimeException("Could not create the column values!"); } /** * {@inheritDoc} */ @Override public void doubleClick(DoubleClickEvent event) { final StructuredSelection selection = (StructuredSelection) event.getSelection(); if (!selection.isEmpty()) { Object selected = selection.getFirstElement(); if (selected instanceof SqlStatementData) { List<SqlStatementData> sqlList = new ArrayList<>(); sqlList.add((SqlStatementData) selected); passSqlWithParameters(sqlList); } } } /** * Returns styled string for the column. * * @param data * Data to return string for. * @param enumId * Enumerated column. * @return {@link StyledString}. */ private StyledString getStyledTextForColumn(SqlStatementData data, Column enumId) { switch (enumId) { case PARAMETERS: return new StyledString(TextFormatter.getSqlParametersText(data.getParameterValues())); case INVOCATION_AFFILLIATION: int percentage = (int) (data.getInvocationAffiliationPercentage() * 100); int invocations = 0; if (null != data.getInvocationParentsIdSet()) { invocations = data.getInvocationParentsIdSet().size(); } return TextFormatter.getInvocationAffilliationPercentageString(percentage, invocations); case COUNT: return new StyledString(Long.toString(data.getCount())); case AVERAGE: return new StyledString(NumberFormatter.formatDouble(data.getAverage(), timeDecimalPlaces)); case MIN: return new StyledString(NumberFormatter.formatDouble(data.getMin(), timeDecimalPlaces)); case MAX: return new StyledString(NumberFormatter.formatDouble(data.getMax(), timeDecimalPlaces)); case DURATION: return new StyledString(NumberFormatter.formatDouble(data.getDuration(), timeDecimalPlaces)); default: return new StyledString("error"); } } /** * Passes the {@link SqlStatementData} to the bottom view that displays the SQL string. * * @param sqlStatementDataList * List to pass. */ private void passSqlWithParameters(final List<SqlStatementData> sqlStatementDataList) { final SqlHolderHelper sqlHolderHelper = new SqlHolderHelper(sqlStatementDataList, false); Display.getDefault().asyncExec(new Runnable() { @Override public void run() { IWorkbenchWindow window = PlatformUI.getWorkbench().getActiveWorkbenchWindow(); IWorkbenchPage page = window.getActivePage(); IRootEditor rootEditor = (IRootEditor) page.getActiveEditor(); if (null != rootEditor) { rootEditor.setDataInput(Collections.singletonList(sqlHolderHelper)); } } }); } /** * The sql label provider used by this view. * * @author Ivan Senic * */ private final class SqlLabelProvider extends StyledCellIndexLabelProvider { /** * Creates the styled text. * * @param element * The element to create the styled text for. * @param index * The index in the column. * @return The created styled string. */ @Override public StyledString getStyledText(Object element, int index) { SqlStatementData data = (SqlStatementData) element; Column enumId = Column.fromOrd(index); return getStyledTextForColumn(data, enumId); } } /** * @author Ivan Senic * */ private final class SqlParameterContentProvider implements IStructuredContentProvider { /** * {@inheritDoc} */ @SuppressWarnings("unchecked") @Override public void inputChanged(final Viewer viewer, Object oldInput, Object newInput) { if ((null == newInput) || Objects.equals(newInput, Collections.emptyList())) { viewer.getControl().setEnabled(false); } else { List<SqlHolderHelper> helperList = (List<SqlHolderHelper>) newInput; final List<SqlStatementData> list = helperList.get(0).getSqlStatementDataList(); if ((null == list) || Objects.equals(list, Collections.emptyList())) { viewer.getControl().setEnabled(false); } else { viewer.getControl().setEnabled(true); SqlParameterAggregationInputControler.this.passSqlWithParameters(list); StructuredSelection structuredSelection = new StructuredSelection(list.get(0)); viewer.setSelection(structuredSelection, true); } } } /** * {@inheritDoc} */ @Override public void dispose() { } /** * {@inheritDoc} */ @Override public Object[] getElements(Object inputElement) { if ((inputElement instanceof List) && !((List<?>) inputElement).isEmpty()) { Object first = ((List<?>) inputElement).get(0); if (first instanceof SqlHolderHelper) { return ((SqlHolderHelper) first).getSqlStatementDataList().toArray(); } } return new Object[0]; } } }