package rocks.inspectit.ui.rcp.editor.table.input; import java.util.ArrayList; import java.util.EnumSet; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Set; import org.apache.commons.collections.CollectionUtils; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.jface.resource.JFaceResources; import org.eclipse.jface.resource.LocalResourceManager; import org.eclipse.jface.viewers.IBaseLabelProvider; import org.eclipse.jface.viewers.IContentProvider; import org.eclipse.jface.viewers.IStructuredContentProvider; 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.TableColumn; import rocks.inspectit.shared.all.cmr.model.MethodIdent; import rocks.inspectit.shared.all.cmr.service.ICachedDataService; import rocks.inspectit.shared.all.communication.ExceptionEvent; import rocks.inspectit.shared.all.communication.data.AggregatedExceptionSensorData; import rocks.inspectit.shared.all.communication.data.ExceptionSensorData; import rocks.inspectit.shared.all.communication.data.InvocationSequenceData; import rocks.inspectit.shared.all.util.ObjectUtils; import rocks.inspectit.shared.cs.communication.comparator.AggregatedExceptionSensorDataComparatorEnum; import rocks.inspectit.shared.cs.communication.comparator.DefaultDataComparatorEnum; import rocks.inspectit.shared.cs.communication.comparator.ExceptionSensorDataComparatorEnum; import rocks.inspectit.shared.cs.communication.comparator.IDataComparator; import rocks.inspectit.shared.cs.communication.comparator.MethodSensorDataComparatorEnum; import rocks.inspectit.shared.cs.indexing.aggregation.impl.AggregationPerformer; import rocks.inspectit.shared.cs.indexing.aggregation.impl.ExceptionDataAggregator; import rocks.inspectit.shared.cs.indexing.aggregation.impl.ExceptionDataAggregator.ExceptionAggregationType; import rocks.inspectit.ui.rcp.InspectIT; import rocks.inspectit.ui.rcp.InspectITImages; import rocks.inspectit.ui.rcp.editor.inputdefinition.InputDefinition; import rocks.inspectit.ui.rcp.editor.preferences.IPreferenceGroup; import rocks.inspectit.ui.rcp.editor.preferences.PreferenceEventCallback.PreferenceEvent; import rocks.inspectit.ui.rcp.editor.preferences.PreferenceId; import rocks.inspectit.ui.rcp.editor.table.TableViewerComparator; import rocks.inspectit.ui.rcp.editor.tree.util.TraceTreeData; import rocks.inspectit.ui.rcp.editor.viewers.RawAggregatedResultComparator; 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.handlers.ShowHideColumnsHandler; import rocks.inspectit.ui.rcp.model.ExceptionImageFactory; import rocks.inspectit.ui.rcp.model.ModifiersImageFactory; /** * This input controller displays the contents of {@link ExceptionSensorData} objects in an * invocation sequence. * * @author Eduard Tudenhoefner * */ public class ExceptionSensorInvocInputController extends AbstractTableInputController { /** * The ID of this subview / controller. */ public static final String ID = "inspectit.subview.table.exceptionsensorinvoc"; /** * The resource manager is used for the images etc. */ private LocalResourceManager resourceManager = new LocalResourceManager(JFaceResources.getResources()); /** * The private inner enumeration used to define the used IDs which are mapped into the columns. * The order in this enumeration represents the order of the columns. If it is reordered, * nothing else has to be changed. * * @author Eduard Tudenhoefner * */ private static enum Column { /** The timestamp column. */ TIMESTAMP("Timestamp", 150, InspectITImages.IMG_TIMESTAMP, false, true, DefaultDataComparatorEnum.TIMESTAMP), /** The fqn column. */ FQN("Fully-Qualified Name", 400, InspectITImages.IMG_CLASS, true, true, ExceptionSensorDataComparatorEnum.FQN), /** The count column. */ CREATED("Created", 60, null, true, false, AggregatedExceptionSensorDataComparatorEnum.CREATED), /** The RETHROWN column. */ RETHROWN("Rethrown", 60, null, true, false, AggregatedExceptionSensorDataComparatorEnum.RETHROWN), /** The HANDLED column. */ HANDLED("Handled", 60, null, true, false, AggregatedExceptionSensorDataComparatorEnum.HANDLED), /** The constructor column. */ CONSTRUCTOR("Constructor", 250, InspectITImages.IMG_METHOD_PUBLIC, false, true, MethodSensorDataComparatorEnum.METHOD), /** The error message column. */ ERROR_MESSAGE("Error Message", 250, null, false, true, ExceptionSensorDataComparatorEnum.ERROR_MESSAGE); /** The name. */ private String name; /** The width of the column. */ private int width; /** The image descriptor. Can be <code>null</code> */ private Image image; /** If the column should be shown in aggregated mode. */ private boolean showInAggregatedMode; /** If the column should be shown in raw mode. */ private boolean showInRawMode; /** Comparator for the column. */ private IDataComparator<? super AggregatedExceptionSensorData> 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 showInAggregatedMode * If the column should be shown in aggregated mode. * @param showInRawMode * If the column should be shown in raw mode. * @param dataComparator * Comparator for the column. * */ private Column(String name, int width, String imageName, boolean showInAggregatedMode, boolean showInRawMode, IDataComparator<? super AggregatedExceptionSensorData> dataComparator) { this.name = name; this.width = width; this.image = InspectIT.getDefault().getImage(imageName); this.showInAggregatedMode = showInAggregatedMode; this.showInRawMode = showInRawMode; 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]; } } /** * This data access service is needed because of the ID mappings. */ private ICachedDataService cachedDataService; /** * List that is displayed after processing the invocation. */ private List<ExceptionSensorData> exceptionSensorDataList; /** * Empty styled string. */ private final StyledString emptyStyledString = new StyledString(); /** * Should view display raw mode or not. */ private boolean rawMode = false; /** * {@inheritDoc} */ @Override public void setInputDefinition(InputDefinition inputDefinition) { super.setInputDefinition(inputDefinition); cachedDataService = inputDefinition.getRepositoryDefinition().getCachedDataService(); } /** * {@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); if (column.showInAggregatedMode) { viewerColumn.getColumn().setWidth(column.width); } else { viewerColumn.getColumn().setWidth(0); } if (null != column.image) { viewerColumn.getColumn().setImage(column.image); } super.mapTableViewerColumn(column, viewerColumn); } } /** * {@inheritDoc} */ @Override public boolean canAlterColumnWidth(TableColumn tableColumn) { for (Column column : Column.values()) { if (Objects.equals(getMappedTableViewerColumn(column).getColumn(), tableColumn)) { return (column.showInRawMode && rawMode) || (column.showInAggregatedMode && !rawMode); } } return true; } /** * {@inheritDoc} */ @Override public Set<PreferenceId> getPreferenceIds() { Set<PreferenceId> preferences = EnumSet.noneOf(PreferenceId.class); preferences.add(PreferenceId.INVOCATION_SUBVIEW_MODE); return preferences; } /** * {@inheritDoc} */ @Override public void preferenceEventFired(PreferenceEvent preferenceEvent) { if (PreferenceId.INVOCATION_SUBVIEW_MODE.equals(preferenceEvent.getPreferenceId())) { Map<IPreferenceGroup, Object> preferenceMap = preferenceEvent.getPreferenceMap(); if ((null != preferenceMap) && preferenceMap.containsKey(PreferenceId.InvocationSubviewMode.RAW)) { Boolean isRawMode = (Boolean) preferenceMap.get(PreferenceId.InvocationSubviewMode.RAW); // first show/hide columns and then change the rawMode value handleRawAggregatedColumnVisibility(isRawMode.booleanValue()); rawMode = isRawMode.booleanValue(); } } } /** * Handles the raw and aggregated columns hiding/showing. * * @param rawMode * Is raw mode active. */ private void handleRawAggregatedColumnVisibility(boolean rawMode) { for (Column column : Column.values()) { if (rawMode) { if (column.showInRawMode && !column.showInAggregatedMode && !ShowHideColumnsHandler.isColumnHidden(this.getClass(), column.name)) { Integer width = ShowHideColumnsHandler.getRememberedColumnWidth(this.getClass(), column.name); getMappedTableViewerColumn(column).getColumn().setWidth((null != width) ? width.intValue() : column.width); } else if (!column.showInRawMode && column.showInAggregatedMode) { getMappedTableViewerColumn(column).getColumn().setWidth(0); } } else { if (!column.showInRawMode && column.showInAggregatedMode && !ShowHideColumnsHandler.isColumnHidden(this.getClass(), column.name)) { Integer width = ShowHideColumnsHandler.getRememberedColumnWidth(this.getClass(), column.name); getMappedTableViewerColumn(column).getColumn().setWidth((null != width) ? width.intValue() : column.width); } else if (column.showInRawMode && !column.showInAggregatedMode) { getMappedTableViewerColumn(column).getColumn().setWidth(0); } } } } /** * {@inheritDoc} */ @Override public IContentProvider getContentProvider() { return new ExceptionSensorInvocContentProvider(); } /** * {@inheritDoc} */ @Override public ViewerComparator getComparator() { TableViewerComparator<AggregatedExceptionSensorData> exceptionSensorInputViewerComparator = new TableViewerComparator<>(); for (Column column : Column.values()) { RawAggregatedResultComparator<AggregatedExceptionSensorData> comparator = new RawAggregatedResultComparator<AggregatedExceptionSensorData>(column.dataComparator, cachedDataService, column.showInRawMode, column.showInAggregatedMode) { @Override protected boolean isRawMode() { return rawMode; } }; exceptionSensorInputViewerComparator.addColumn(getMappedTableViewerColumn(column).getColumn(), comparator); } return exceptionSensorInputViewerComparator; } /** * {@inheritDoc} */ @Override public IBaseLabelProvider getLabelProvider() { return new ExceptionSensorInvocLabelProvider(); } /** * {@inheritDoc} */ @Override public boolean canOpenInput(List<? extends Object> data) { if (null == data) { return false; } if (data.isEmpty()) { return true; } // we accept invocation sequences if (data.get(0) instanceof InvocationSequenceData) { return true; } // or one trace data if (data.get(0) instanceof TraceTreeData) { return true; } return false; } /** * {@inheritDoc} */ @Override public void setLimit(int limit) { } /** * {@inheritDoc} */ public void update(IProgressMonitor monitor) { } /** * The content provider for this view. * * @author Eduard Tudenhoefner * */ private final class ExceptionSensorInvocContentProvider implements IStructuredContentProvider { /** * {@inheritDoc} */ @Override @SuppressWarnings("unchecked") public Object[] getElements(Object inputElement) { List<? extends Object> input = (List<? extends Object>) inputElement; if (CollectionUtils.isEmpty(input)) { return new Object[0]; } List<InvocationSequenceData> invocationSequenceDataList; if (input.get(0) instanceof TraceTreeData) { invocationSequenceDataList = TraceTreeData.collectInvocations((TraceTreeData) input.get(0), new ArrayList<InvocationSequenceData>()); } else { invocationSequenceDataList = (List<InvocationSequenceData>) inputElement; } exceptionSensorDataList = getRawExceptionSensorDataList(invocationSequenceDataList, new ArrayList<ExceptionSensorData>()); if (!rawMode) { AggregationPerformer<ExceptionSensorData> aggregationPerformer = new AggregationPerformer<>(new ExceptionDataAggregator(ExceptionAggregationType.THROWABLE_TYPE)); aggregationPerformer.processCollection(exceptionSensorDataList); exceptionSensorDataList = aggregationPerformer.getResultList(); } return exceptionSensorDataList.toArray(); } /** * Returns raw list of exceptions. * * @param invocationSequenceDataList * Invocations. * @param exceptionSensorDataList * Result list. * @return List of exceptions for raw display. */ private List<ExceptionSensorData> getRawExceptionSensorDataList(List<InvocationSequenceData> invocationSequenceDataList, List<ExceptionSensorData> exceptionSensorDataList) { for (InvocationSequenceData invocationSequenceData : invocationSequenceDataList) { if ((null != invocationSequenceData.getExceptionSensorDataObjects()) && !invocationSequenceData.getExceptionSensorDataObjects().isEmpty()) { for (ExceptionSensorData object : invocationSequenceData.getExceptionSensorDataObjects()) { if (ObjectUtils.equals(object.getExceptionEvent(), ExceptionEvent.CREATED)) { exceptionSensorDataList.add(object); } } } if ((null != invocationSequenceData.getNestedSequences()) && !invocationSequenceData.getNestedSequences().isEmpty()) { getRawExceptionSensorDataList(invocationSequenceData.getNestedSequences(), exceptionSensorDataList); } } return exceptionSensorDataList; } /** * {@inheritDoc} */ @Override public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { } /** * {@inheritDoc} */ @Override public void dispose() { } } /** * The exception sensor label provider used by this view. * * @author Eduard Tudenhoefner * */ private final class ExceptionSensorInvocLabelProvider 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) { ExceptionSensorData data = (ExceptionSensorData) element; Column enumId = Column.fromOrd(index); MethodIdent methodIdent = null; if (0 != data.getMethodIdent()) { methodIdent = cachedDataService.getMethodIdentForId(data.getMethodIdent()); } return getStyledTextForColumn(data, methodIdent, enumId); } /** * Returns the column image for the given element at the given index. * * @param element * The element. * @param index * The index. * @return Returns the Image. */ @Override public Image getColumnImage(Object element, int index) { if (rawMode) { Column enumId = Column.fromOrd(index); switch (enumId) { case CONSTRUCTOR: ExceptionSensorData data = (ExceptionSensorData) element; MethodIdent methodIdent = cachedDataService.getMethodIdentForId(data.getMethodIdent()); Image image = ModifiersImageFactory.getImage(methodIdent.getModifiers()); image = ExceptionImageFactory.decorateImageWithException(image, data, resourceManager); return image; default: return null; } } else { return null; } } } /** * Returns the styled text for a specific column. * * @param data * The data object to extract the information from. * @param methodIdent * The method ident object where to retrieve information from. * @param enumId * The enumeration ID. * @return The styled string containing the information from the data object. */ private StyledString getStyledTextForColumn(ExceptionSensorData data, MethodIdent methodIdent, Column enumId) { if (null != data) { switch (enumId) { case TIMESTAMP: if (rawMode) { return new StyledString(NumberFormatter.formatTimeWithMillis(data.getTimeStamp())); } else { return emptyStyledString; } case FQN: return new StyledString(data.getThrowableType()); case CREATED: if (!rawMode && (data instanceof AggregatedExceptionSensorData)) { return new StyledString(NumberFormatter.formatLong(((AggregatedExceptionSensorData) data).getCreated())); } else if (ExceptionEvent.CREATED.equals(data.getExceptionEvent())) { return new StyledString("Yes"); } else { return emptyStyledString; } case RETHROWN: if (!rawMode && (data instanceof AggregatedExceptionSensorData)) { return new StyledString(NumberFormatter.formatLong(((AggregatedExceptionSensorData) data).getPassed())); } else if (ExceptionEvent.PASSED.equals(data.getExceptionEvent())) { return new StyledString("Yes"); } else { return emptyStyledString; } case HANDLED: if (!rawMode && (data instanceof AggregatedExceptionSensorData)) { return new StyledString(NumberFormatter.formatLong(((AggregatedExceptionSensorData) data).getHandled())); } else if (ExceptionEvent.HANDLED.equals(data.getExceptionEvent())) { return new StyledString("Yes"); } else { return emptyStyledString; } case CONSTRUCTOR: if (rawMode) { return new StyledString(TextFormatter.getMethodWithParameters(methodIdent)); } else { return emptyStyledString; } case ERROR_MESSAGE: if (rawMode) { StyledString styledString = new StyledString(); if (null != data.getErrorMessage()) { styledString.append(data.getErrorMessage()); } return styledString; } else { return emptyStyledString; } default: return new StyledString("error"); } } return new StyledString("error"); } /** * {@inheritDoc} */ @Override public String getReadableString(Object object) { if (object instanceof ExceptionSensorData) { ExceptionSensorData data = (ExceptionSensorData) object; StringBuilder sb = new StringBuilder(); MethodIdent methodIdent = cachedDataService.getMethodIdentForId(data.getMethodIdent()); for (Column column : Column.values()) { sb.append(getStyledTextForColumn(data, methodIdent, column).toString()); sb.append('\t'); } return sb.toString(); } throw new RuntimeException("Could not create the human readable string!"); } /** * {@inheritDoc} */ @Override public List<String> getColumnValues(Object object) { if (object instanceof ExceptionSensorData) { ExceptionSensorData data = (ExceptionSensorData) object; MethodIdent methodIdent = cachedDataService.getMethodIdentForId(data.getMethodIdent()); List<String> values = new ArrayList<>(); for (Column column : Column.values()) { values.add(getStyledTextForColumn(data, methodIdent, column).toString()); } return values; } throw new RuntimeException("Could not create the column values!"); } /** * {@inheritDoc} */ @Override public Object[] getObjectsToSearch(Object tableInput) { return exceptionSensorDataList.toArray(); } /** * {@inheritDoc} */ @Override public void dispose() { resourceManager.dispose(); } /** * {@inheritDoc} */ @Override public SubViewClassification getSubViewClassification() { return SubViewClassification.SLAVE; } }