| /** |
| * Copyright (c) 2015 Codetrails GmbH. |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License v1.0 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/epl-v10.html |
| */ |
| package org.eclipse.epp.internal.logging.aeri.ide.dialogs; |
| |
| import static com.google.common.base.Preconditions.checkNotNull; |
| import static java.text.MessageFormat.format; |
| import static org.apache.commons.lang3.StringUtils.*; |
| import static org.eclipse.e4.ui.services.IServiceConstants.ACTIVE_SHELL; |
| import static org.eclipse.emf.databinding.EMFProperties.value; |
| import static org.eclipse.emf.databinding.FeaturePath.fromList; |
| import static org.eclipse.epp.internal.logging.aeri.ide.IDEWorkflow.*; |
| import static org.eclipse.epp.internal.logging.aeri.ide.IIdePackage.Literals.LOG_EVENT__OPTIONS; |
| import static org.eclipse.epp.internal.logging.aeri.ide.di.ImageRegistryCreationFunction.ICO_INFO; |
| import static org.eclipse.epp.internal.logging.aeri.ide.dialogs.UI.*; |
| import static org.eclipse.epp.internal.logging.aeri.ide.utils.IDEConstants.BUNDLE_ID; |
| import static org.eclipse.epp.logging.aeri.core.IModelPackage.Literals.*; |
| import static org.eclipse.jface.databinding.swt.WidgetProperties.text; |
| |
| import java.text.MessageFormat; |
| import java.util.Collections; |
| import java.util.Comparator; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Set; |
| import java.util.stream.Collectors; |
| |
| import javax.inject.Inject; |
| import javax.inject.Named; |
| |
| import org.eclipse.core.databinding.Binding; |
| import org.eclipse.core.databinding.DataBindingContext; |
| import org.eclipse.core.databinding.observable.ChangeEvent; |
| import org.eclipse.core.databinding.observable.IChangeListener; |
| import org.eclipse.core.databinding.observable.list.IObservableList; |
| import org.eclipse.core.databinding.observable.list.ListDiffVisitor; |
| import org.eclipse.core.databinding.observable.value.IObservableValue; |
| import org.eclipse.e4.core.contexts.Active; |
| import org.eclipse.e4.core.contexts.IEclipseContext; |
| import org.eclipse.e4.core.di.annotations.Optional; |
| import org.eclipse.e4.core.services.events.IEventBroker; |
| import org.eclipse.e4.ui.di.UISynchronize; |
| import org.eclipse.emf.common.notify.Notification; |
| import org.eclipse.emf.common.notify.impl.AdapterImpl; |
| import org.eclipse.emf.common.util.EList; |
| import org.eclipse.emf.databinding.EMFProperties; |
| import org.eclipse.emf.ecore.EObject; |
| import org.eclipse.emf.ecore.util.EcoreUtil; |
| import org.eclipse.epp.internal.logging.aeri.ide.IDEWorkflow; |
| import org.eclipse.epp.internal.logging.aeri.ide.IIdeFactory; |
| import org.eclipse.epp.internal.logging.aeri.ide.IIdePackage; |
| import org.eclipse.epp.internal.logging.aeri.ide.IInternalInput; |
| import org.eclipse.epp.internal.logging.aeri.ide.ILogEvent; |
| import org.eclipse.epp.internal.logging.aeri.ide.ILogEventGroup; |
| import org.eclipse.epp.internal.logging.aeri.ide.ILogEventsQueue; |
| import org.eclipse.epp.internal.logging.aeri.ide.IProcessorDescriptor; |
| import org.eclipse.epp.internal.logging.aeri.ide.IServerDescriptor; |
| import org.eclipse.epp.internal.logging.aeri.ide.l10n.Messages; |
| import org.eclipse.epp.internal.logging.aeri.ide.processors.AnonymizeMessagesProcessor; |
| import org.eclipse.epp.internal.logging.aeri.ide.processors.AnonymizeStackTracesProcessor; |
| import org.eclipse.epp.internal.logging.aeri.ide.processors.Processors; |
| import org.eclipse.epp.internal.logging.aeri.ide.processors.StepsToReproduceProcessor; |
| import org.eclipse.epp.internal.logging.aeri.ide.util.IdeSwitch; |
| import org.eclipse.epp.internal.logging.aeri.ide.utils.IDEConstants; |
| import org.eclipse.epp.logging.aeri.core.IModelPackage.Literals; |
| import org.eclipse.epp.logging.aeri.ide.processors.IEditableReportProcessor; |
| import org.eclipse.epp.logging.aeri.ide.processors.IEditableReportProcessor.EditResult; |
| import org.eclipse.epp.logging.aeri.core.IReport; |
| import org.eclipse.epp.logging.aeri.core.IReportProcessor; |
| import org.eclipse.epp.logging.aeri.core.Severity; |
| import org.eclipse.jdt.annotation.Nullable; |
| import org.eclipse.jface.databinding.swt.ISWTObservableValue; |
| import org.eclipse.jface.databinding.viewers.IViewerObservableValue; |
| import org.eclipse.jface.databinding.viewers.ObservableListContentProvider; |
| import org.eclipse.jface.databinding.viewers.ViewerProperties; |
| import org.eclipse.jface.dialogs.IDialogConstants; |
| import org.eclipse.jface.dialogs.MessageDialog; |
| import org.eclipse.jface.layout.GridDataFactory; |
| import org.eclipse.jface.preference.PreferenceDialog; |
| import org.eclipse.jface.resource.FontDescriptor; |
| import org.eclipse.jface.resource.ImageRegistry; |
| import org.eclipse.jface.resource.JFaceResources; |
| import org.eclipse.jface.viewers.ArrayContentProvider; |
| import org.eclipse.jface.viewers.ColumnLabelProvider; |
| import org.eclipse.jface.viewers.ColumnViewer; |
| import org.eclipse.jface.viewers.ComboViewer; |
| import org.eclipse.jface.viewers.IStructuredSelection; |
| import org.eclipse.jface.viewers.ITableFontProvider; |
| import org.eclipse.jface.viewers.LabelProvider; |
| import org.eclipse.jface.viewers.TableViewer; |
| import org.eclipse.swt.SWT; |
| import org.eclipse.swt.custom.SashForm; |
| import org.eclipse.swt.custom.StyledText; |
| import org.eclipse.swt.events.ControlAdapter; |
| import org.eclipse.swt.events.ControlEvent; |
| import org.eclipse.swt.events.KeyAdapter; |
| import org.eclipse.swt.events.KeyEvent; |
| import org.eclipse.swt.events.SelectionAdapter; |
| import org.eclipse.swt.events.SelectionEvent; |
| import org.eclipse.swt.graphics.Font; |
| import org.eclipse.swt.graphics.Image; |
| import org.eclipse.swt.graphics.Point; |
| import org.eclipse.swt.layout.GridLayout; |
| import org.eclipse.swt.widgets.Composite; |
| import org.eclipse.swt.widgets.Control; |
| import org.eclipse.swt.widgets.Label; |
| import org.eclipse.swt.widgets.Link; |
| import org.eclipse.swt.widgets.Shell; |
| import org.eclipse.swt.widgets.ToolBar; |
| import org.eclipse.swt.widgets.ToolItem; |
| import org.eclipse.ui.dialogs.PreferencesUtil; |
| import org.eclipse.ui.forms.events.ExpansionAdapter; |
| import org.eclipse.ui.forms.events.ExpansionEvent; |
| import org.eclipse.ui.forms.widgets.ExpandableComposite; |
| |
| import com.google.common.collect.ArrayListMultimap; |
| import com.google.common.collect.Lists; |
| import com.google.common.collect.Multimap; |
| import com.google.common.collect.Sets; |
| |
| public class ReviewDialog extends MessageDialog { |
| |
| public static final String CTX_STATE_REVIEW_IN_PROGRESS = BUNDLE_ID + ".di.review-in-progress"; //$NON-NLS-1$ |
| |
| private static final int DEFAULT_DIALOG_WIDTH = 600; |
| private static final int DEFAULT_DIALOG_HEIGHT = 320; |
| |
| private SashForm reportsAndDetailsSash; |
| private TableViewer reportsViewer; |
| private SashForm commentAndPreviewSash; |
| private ComboViewer severity; |
| private StyledText comments; |
| private Composite previewArea; |
| private ReportPreview preview; |
| |
| private Composite detailsArea; |
| private ExpandableComposite previewTwistie; |
| private ToolBar processorsBar; |
| private ExpandableComposite commentTwistie; |
| private ShowPreviewListener showPreviewListener; |
| |
| private IEclipseContext context; |
| private IEventBroker broker; |
| private ImageRegistry registry; |
| |
| @Nullable |
| private ILogEventGroup active; |
| private ILogEventsQueue queue; |
| |
| private DataBindingContext dbContext; |
| private IViewerObservableValue selectedLogEvent; |
| private IObservableList<ILogEvent> events; |
| private IncomingEventsCopier incomingEventsCopier; |
| |
| private UISynchronize uiSynchronize; |
| |
| private List<IProcessorDescriptor> reportProcessorDescriptors; |
| private Map<String, String> directivesToReadable; |
| |
| @Inject |
| public ReviewDialog(@Active @Optional ILogEventGroup active, ILogEventsQueue queue, ImageRegistry registry, |
| @Named(ACTIVE_SHELL) @Optional Shell parentShell, UISynchronize uiSynchronize, IEclipseContext context, IEventBroker broker, |
| @Named(IDEWorkflow.CTX_REPORT_PROCESSORS) List<IProcessorDescriptor> descriptors) { |
| super(parentShell, Messages.DIALOG_TITLE_REVIEW, null, |
| MessageFormat.format( |
| "Eclipse encountered {0,choice,1#an error|1<{0,number,integer} errors}. Errors may reveal severe issues in the code and thus we kindly ask you to send them to the affected projects.", |
| queue.getGroups().size()), |
| MessageDialog.WARNING, new String[] { Messages.BUTTON_TEXT_SEND, Messages.BUTTON_TEXT_DONT_SEND }, 0); |
| this.active = active; |
| this.queue = queue; |
| this.uiSynchronize = uiSynchronize; |
| this.reportProcessorDescriptors = Lists.newArrayList(descriptors); |
| sortProcessorDescriptors(); |
| this.directivesToReadable = descriptors.stream() |
| .collect(Collectors.toMap(IProcessorDescriptor::getDirective, IProcessorDescriptor::getName)); |
| this.registry = checkNotNull(registry); |
| this.context = context; |
| this.broker = broker; |
| setShellStyle(SWT.MODELESS | SWT.DIALOG_TRIM | SWT.RESIZE | SWT.MAX); |
| setBlockOnOpen(false); |
| setupObservableList(); |
| context.modify(CTX_STATE_REVIEW_IN_PROGRESS, true); |
| } |
| |
| protected void sortProcessorDescriptors() { |
| // special handling for the anonymize and steps to reproduce processors, they should always be the last elements |
| Comparator<IProcessorDescriptor> comparator = Comparator |
| .comparing(IProcessorDescriptor::getProcessor, |
| (a, b) -> a.getWrapped() instanceof AnonymizeStackTracesProcessor ? 1 |
| : b.getWrapped() instanceof AnonymizeStackTracesProcessor ? -1 : 0) |
| .thenComparing(IProcessorDescriptor::getProcessor, |
| (a, b) -> a.getWrapped() instanceof AnonymizeMessagesProcessor ? 1 |
| : b.getWrapped() instanceof AnonymizeMessagesProcessor ? -1 : 0) |
| .thenComparing(IProcessorDescriptor::getProcessor, |
| (a, b) -> a.getWrapped() instanceof StepsToReproduceProcessor ? 1 |
| : b.getWrapped() instanceof StepsToReproduceProcessor ? -1 : 0) |
| .thenComparing(IProcessorDescriptor::getName, Comparator.naturalOrder()); |
| Collections.sort(reportProcessorDescriptors, comparator); |
| } |
| |
| @SuppressWarnings("unchecked") |
| private void setupObservableList() { |
| final IInternalInput input = IIdeFactory.eINSTANCE.createInternalInput(); |
| this.events = EMFProperties.list(IIdePackage.Literals.INTERNAL_INPUT__INPUT).observe(input); |
| |
| for (ILogEventGroup group : queue.getGroups()) { |
| input.getInput().addAll(group.getEvents()); |
| } |
| |
| incomingEventsCopier = new IncomingEventsCopier(input); |
| queue.eAdapters().add(incomingEventsCopier); |
| } |
| |
| @Override |
| protected void configureShell(Shell shell) { |
| super.configureShell(shell); |
| shell.setSize(DEFAULT_DIALOG_WIDTH, DEFAULT_DIALOG_HEIGHT); |
| } |
| |
| @Override |
| public void create() { |
| super.create(); |
| } |
| |
| @Override |
| protected boolean customShouldTakeFocus() { |
| return false; |
| } |
| |
| @Override |
| protected Control createMessageArea(Composite composite) { |
| Image image = getImage(); |
| if (image != null) { |
| imageLabel = new Label(composite, SWT.NULL); |
| image.setBackground(imageLabel.getBackground()); |
| imageLabel.setImage(image); |
| GridDataFactory.fillDefaults().align(SWT.CENTER, SWT.BEGINNING).applyTo(imageLabel); |
| } |
| Composite messageLink = new Composite(composite, SWT.NONE); |
| GridDataFactory.fillDefaults().align(SWT.FILL, SWT.BEGINNING).grab(true, false).applyTo(messageLink); |
| messageLink.setLayout(new GridLayout()); |
| Control processLink = createMessage(messageLink); |
| if (processLink != null) { |
| GridDataFactory.fillDefaults().align(SWT.FILL, SWT.BEGINNING).grab(true, false) |
| .hint(convertHorizontalDLUsToPixels(IDialogConstants.MINIMUM_MESSAGE_AREA_WIDTH), SWT.DEFAULT).applyTo(processLink); |
| } |
| return composite; |
| } |
| |
| @Override |
| public Composite createCustomArea(final Composite parent) { |
| createReportsAndDetailsSash(parent); |
| createReportsViewer(reportsAndDetailsSash); |
| createDetailsArea(reportsAndDetailsSash); |
| reportsAndDetailsSash.setWeights(new int[] { 1, 3 }); |
| |
| createPreferencesLink(parent); |
| |
| createDatabindings(); |
| if (active != null) { |
| EList<ILogEvent> events = active.getEvents(); |
| if (!events.isEmpty()) { |
| // TODO not sure why/when this could become null - but it was |
| ILogEvent event = events.get(0); |
| selectedLogEvent.setValue(event); |
| } |
| } |
| |
| return reportsAndDetailsSash; |
| } |
| |
| @Nullable |
| private Control createMessage(final Composite parent) { |
| final Multimap<IProcessorDescriptor, ILogEvent> eventsByRequestedProcessors = ArrayListMultimap.create(); |
| Set<IServerDescriptor> interestedServers = Sets.newHashSet(); |
| for (ILogEventGroup group : queue.getGroups()) { |
| for (ILogEvent event : group.getEvents()) { |
| reportProcessorDescriptors.stream().filter(descriptor -> Processors.shouldProcess(descriptor, event)) |
| .forEach(descriptor -> { |
| eventsByRequestedProcessors.put(descriptor, event); |
| interestedServers.add(event.getServer()); |
| }); |
| } |
| } |
| Label additionalInformationRequestedMessage = new Label(parent, SWT.WRAP); |
| if (!eventsByRequestedProcessors.isEmpty()) { |
| int countEvents = Sets.newHashSet(eventsByRequestedProcessors.values()).size(); |
| this.message += MessageFormat.format( |
| " {0,choice,1#One project|1<{0,number,integer} projects} requested additional information for {1,choice,1#this error|1<{1,number,integer} errors}. Please provide the requested information to {1,choice,1#this report|1<the highlighted reports}.", |
| interestedServers.size(), countEvents); |
| } |
| additionalInformationRequestedMessage.setText(this.message); |
| return additionalInformationRequestedMessage; |
| } |
| |
| private void createDetailsArea(Composite parent) { |
| detailsArea = new Composite(reportsAndDetailsSash, SWT.NONE); |
| { |
| commentTwistie = new ExpandableComposite(detailsArea, SWT.NONE); |
| commentTwistie.setText(Messages.TWISTIE_TEXT_COMMENT); |
| commentTwistie.setExpanded(true); |
| commentTwistie.addExpansionListener(new ExpansionAdapter() { |
| |
| @Override |
| public void expansionStateChanged(ExpansionEvent e) { |
| if (e.getState()) { |
| commentAndPreviewSash.setMaximizedControl(null); |
| } else { |
| commentAndPreviewSash.setMaximizedControl(previewArea); |
| if (!previewTwistie.isExpanded()) { |
| previewTwistie.setExpanded(true); |
| showPreviewListener.showPreview(); |
| } |
| } |
| } |
| }); |
| } |
| { |
| severity = new ComboViewer(detailsArea); |
| severity.setContentProvider(ArrayContentProvider.getInstance()); |
| severity.setInput(Severity.VALUES); |
| severity.setLabelProvider(new LabelProvider() { |
| |
| @Override |
| public String getText(Object element) { |
| return element == Severity.UNKNOWN ? Messages.COMBO_TEXT_SEVERITY_UNKNOWN |
| : capitalize(lowerCase(replaceChars(element.toString(), '_', ' '))); |
| } |
| }); |
| } |
| { |
| commentAndPreviewSash = new SashForm(detailsArea, SWT.VERTICAL); |
| commentAndPreviewSash.setSashWidth(10); |
| |
| createCommentArea(commentAndPreviewSash); |
| createPreviewArea(commentAndPreviewSash); |
| |
| final int lineHeight = Math.max(16 + 3, parent.getFont().getFontData()[0].getHeight()); |
| |
| commentAndPreviewSash.addControlListener(new ControlAdapter() { |
| |
| private int preferredCommentHeight = -1; |
| private int autoExpandPoint; |
| |
| @Override |
| public void controlResized(ControlEvent e) { |
| int sashHeight = commentAndPreviewSash.getClientArea().height; |
| |
| if (preferredCommentHeight < 0) { |
| // Calculate weights and expand point based on initial window |
| preferredCommentHeight = sashHeight - lineHeight - commentAndPreviewSash.getSashWidth(); |
| autoExpandPoint = (int) (sashHeight * 1.35); |
| } |
| |
| if (preview.getStyledText().isVisible()) { |
| if (sashHeight < autoExpandPoint && commentTwistie.isExpanded()) { |
| previewTwistie.setExpanded(false); |
| preview.getStyledText().setVisible(false); |
| } |
| } else if (sashHeight >= autoExpandPoint) { |
| previewTwistie.setExpanded(true); |
| preview.getStyledText().setVisible(true); |
| } |
| |
| int preferredPreviewHeight = sashHeight - preferredCommentHeight; |
| // never accept negative values |
| if (preferredPreviewHeight < lineHeight || preferredPreviewHeight < 0 || preferredCommentHeight < 0) { |
| commentAndPreviewSash.setWeights(new int[] { 3, 1 }); |
| } else { |
| commentAndPreviewSash.setWeights(new int[] { preferredCommentHeight, preferredPreviewHeight }); |
| } |
| } |
| }); |
| } |
| |
| gl().margins(0, 0).spacing(0, 0).numColumns(2).applyTo(detailsArea); |
| gdGrabHV().span(2, 1).applyTo(commentAndPreviewSash); |
| GridDataFactory.fillDefaults().align(SWT.END, SWT.TOP).applyTo(commentTwistie); |
| GridDataFactory.fillDefaults().align(SWT.END, SWT.TOP).applyTo(severity.getControl()); |
| } |
| |
| private void createReportsAndDetailsSash(final Composite parent) { |
| reportsAndDetailsSash = new SashForm(parent, SWT.HORIZONTAL); |
| reportsAndDetailsSash.setSashWidth(10); |
| gdGrabHV().indent(0, 10).applyTo(reportsAndDetailsSash); |
| } |
| |
| private void createReportsViewer(Composite parent) { |
| Composite container = new Composite(parent, SWT.NONE); |
| Label label = new Label(container, SWT.NONE); |
| label.setText(Messages.LABEL_TEXT_EVENTS); |
| reportsViewer = new TableViewer(container); |
| reportsViewer.setContentProvider(new ObservableListContentProvider()); |
| reportsViewer.setLabelProvider(new ReportsViewerLabelProvider(reportsViewer)); |
| reportsViewer.getControl().addKeyListener(new ReportsViewerDeleteListener()); |
| gl().applyTo(container); |
| gdGrabH().align(SWT.BEGINNING, SWT.TOP).applyTo(label); |
| gdGrabHV().applyTo(container); |
| gdGrabHV().applyTo(reportsViewer.getControl()); |
| reportsViewer.setInput(events); |
| } |
| |
| private void createCommentArea(Composite parent) { |
| comments = new StyledText(parent, SWT.BORDER | SWT.MULTI | SWT.WRAP | SWT.V_SCROLL); |
| comments.setFont(JFaceResources.getFont(JFaceResources.TEXT_FONT)); |
| comments.setToolTipText(Messages.TOOLTIP_COMMENTS); |
| comments.setAlwaysShowScrollBars(false); |
| gdGrabHV().applyTo(comments); |
| } |
| |
| private void createPreviewArea(Composite parent) { |
| previewArea = new Composite(parent, SWT.NONE); |
| { |
| previewTwistie = new ExpandableComposite(previewArea, SWT.NONE); |
| previewTwistie.setText(Messages.TWISTIE_TEXT_PREVIEW); |
| showPreviewListener = new ShowPreviewListener(); |
| previewTwistie.addExpansionListener(showPreviewListener); |
| } |
| { |
| processorsBar = new ToolBar(previewArea, SWT.FLAT); |
| ToolItem showResponseToolItem = new ToolItem(processorsBar, SWT.PUSH); |
| showResponseToolItem.setImage(registry.get(ICO_INFO)); |
| showResponseToolItem.setToolTipText("Show problem state"); |
| showResponseToolItem.addSelectionListener(new SelectionAdapter() { |
| |
| @Override |
| public void widgetSelected(SelectionEvent e) { |
| ILogEvent event = (ILogEvent) selectedLogEvent.getValue(); |
| if (event == null) { |
| return; |
| } |
| new ProblemStatusDialog(event, reportProcessorDescriptors, getShell()).open(); |
| } |
| }); |
| new ToolItem(processorsBar, SWT.SEPARATOR); |
| for (IProcessorDescriptor descriptor : reportProcessorDescriptors) { |
| ToolItem processorToolItem = new ToolItem(processorsBar, SWT.CHECK); |
| processorToolItem.setData(descriptor); |
| processorToolItem.setImage(descriptor.getImage16()); |
| } |
| |
| } |
| { |
| |
| preview = new ReportPreview(previewArea); |
| preview.setEditListener(isReset -> previewReport()); |
| } |
| gl().numColumns(2).applyTo(previewArea); |
| gdGrabH().align(SWT.END, SWT.CENTER).applyTo(processorsBar); |
| gdGrabHV().span(2, 1).hint(SWT.DEFAULT, DEFAULT_DIALOG_HEIGHT).applyTo(preview.getStyledText()); |
| } |
| |
| protected void createPreferencesLink(final Composite parent) { |
| Link link = new Link(parent, SWT.NONE); |
| link.setText(Messages.LINK_TEXT_SENDING_PREFERENCES); |
| link.addSelectionListener(new SelectionAdapter() { |
| @Override |
| public void widgetSelected(SelectionEvent e) { |
| uiSynchronize.asyncExec(new Runnable() { |
| |
| @Override |
| public void run() { |
| PreferenceDialog dialog = PreferencesUtil.createPreferenceDialogOn(null, IDEConstants.PREFERENCE_PAGE_ID, null, |
| null); |
| dialog.open(); |
| } |
| }); |
| } |
| }); |
| gdGrabH().indent(0, 10).minSize(SWT.DEFAULT, 20).applyTo(link); |
| } |
| |
| @SuppressWarnings("unchecked") |
| private void createDatabindings() { |
| dbContext = new DataBindingContext(); |
| { |
| selectedLogEvent = ViewerProperties.singlePostSelection().observe(reportsViewer); |
| selectedLogEvent.addChangeListener(x -> { |
| ILogEvent logEvent = (ILogEvent) selectedLogEvent.getValue(); |
| if (logEvent != null) { |
| for (ToolItem processorToolItem : processorsBar.getItems()) { |
| IProcessorDescriptor descriptor = (IProcessorDescriptor) processorToolItem.getData(); |
| if (descriptor != null) { |
| IReportProcessor processor = descriptor.getProcessor(); |
| boolean canContribute = processor.canContribute(logEvent.getStatus(), logEvent.getContext()); |
| processorToolItem.setEnabled(canContribute); |
| String tooltipText = format("{0}:\n{1}", descriptor.getName(), descriptor.getDescription()); |
| if (!canContribute) { |
| tooltipText = format("The processor ''{0}'' cannot contribute to the report.\n\n{1}", descriptor.getName(), |
| tooltipText); |
| } |
| processorToolItem.setToolTipText(tooltipText); |
| } |
| } |
| } |
| }); |
| } |
| { |
| IObservableList<IReportProcessor> enabledProcessors = EMFProperties |
| .list(fromList(LOG_EVENT__OPTIONS, Literals.SEND_OPTIONS__ENABLED_PROCESSORS)).observeDetail(selectedLogEvent); |
| enabledProcessors.addListChangeListener(event -> { |
| event.diff.accept(new ActivatedProcessorsChangeVisitor()); |
| }); |
| for (ToolItem processorToolItem : processorsBar.getItems()) { |
| IProcessorDescriptor descriptor = (IProcessorDescriptor) processorToolItem.getData(); |
| if (descriptor == null) { |
| continue; |
| } |
| IReportProcessor processor = descriptor.getProcessor(); |
| processorToolItem.addSelectionListener(new SelectionAdapter() { |
| |
| @Override |
| public void widgetSelected(SelectionEvent e) { |
| ILogEvent event = (ILogEvent) selectedLogEvent.getValue(); |
| if (event == null) { |
| return; |
| } |
| EList<IReportProcessor> activeProcessors = event.getOptions().getEnabledProcessors(); |
| if (processorToolItem.getSelection()) { |
| if (!activeProcessors.contains(processor)) { |
| activeProcessors.add(processor); |
| } |
| } else { |
| activeProcessors.remove(processor); |
| } |
| // update the labels (we may remove the bold notification if all requested processors are activated or vice-versa) |
| reportsViewer.refresh(true, false); |
| } |
| |
| }); |
| } |
| } |
| { |
| IObservableValue<String> emf = value(fromList(LOG_EVENT__OPTIONS, SEND_OPTIONS__COMMENT)).observeDetail(selectedLogEvent); |
| ISWTObservableValue swt = text(SWT.Modify).observeDelayed(300, comments); |
| dbContext.bindValue(swt, emf); |
| } |
| { |
| IObservableValue<Severity> emf = value(fromList(LOG_EVENT__OPTIONS, SEND_OPTIONS__SEVERITY)).observeDetail(selectedLogEvent); |
| IObservableValue<Severity> jface = ViewerProperties.singleSelection().observe(severity); |
| dbContext.bindValue(jface, emf); |
| } |
| addChangeListenerToBindings(dbContext); |
| |
| selectedLogEvent.addChangeListener(new IChangeListener() { |
| |
| @Override |
| public void handleChange(ChangeEvent e) { |
| ILogEvent event = (ILogEvent) selectedLogEvent.getValue(); |
| if (event != null) { |
| for (ToolItem processorToolItem : processorsBar.getItems()) { |
| IProcessorDescriptor descriptor = (IProcessorDescriptor) processorToolItem.getData(); |
| if (descriptor == null) { |
| continue; |
| } |
| // TODO special handling for the processors bound to preferences, this should be generalized and put somewhere else |
| // in the next change |
| if (descriptor.getProcessor().getWrapped() instanceof AnonymizeStackTracesProcessor |
| && event.getOptions().isAnonymizeStackTraces() |
| || descriptor.getProcessor().getWrapped() instanceof AnonymizeMessagesProcessor |
| && event.getOptions().isAnonymizeMessages()) { |
| event.getOptions().getEnabledProcessors().add(descriptor.getProcessor()); |
| } |
| boolean shouldProcess = Processors.shouldProcess(descriptor, event); |
| processorToolItem.setSelection(event.getOptions().getEnabledProcessors().contains(descriptor.getProcessor())); |
| processorToolItem.setImage(UIUtils.decorate(descriptor, registry, shouldProcess)); |
| } |
| } |
| } |
| |
| }); |
| } |
| |
| private void editAndPreviewReport(IEditableReportProcessor processor) { |
| ILogEvent logEvent = (ILogEvent) selectedLogEvent.getValue(); |
| if (logEvent != null) { |
| IServerDescriptor server = logEvent.getServer(); |
| IReport report = server.getConnection().transform(logEvent.getStatus(), logEvent.getContext()); |
| |
| IProcessorDescriptor descriptor = null; |
| // TODO is there a better way to obtain the descriptor from the processor? |
| for (IProcessorDescriptor availableDescriptor : reportProcessorDescriptors) { |
| if (availableDescriptor.getProcessor() == processor) { |
| descriptor = availableDescriptor; |
| break; |
| } |
| } |
| |
| if (descriptor == null) { |
| return; |
| } |
| |
| processor.process(report, logEvent.getStatus(), logEvent.getContext()); |
| EditResult editResult = processor.edit(logEvent.getStatus(), logEvent.getContext(), getShell()); |
| switch (editResult) { |
| case MODIFIED: |
| preview.getEditedDescriptors().add(descriptor); |
| //$FALL-THROUGH$ |
| case UNMODIFIED: |
| // Ensure report has the updated information |
| processor.process(report, logEvent.getStatus(), logEvent.getContext()); |
| break; |
| case CANCELED: |
| default: |
| EList<IReportProcessor> activeProcessors = logEvent.getOptions().getEnabledProcessors(); |
| activeProcessors.remove(processor); |
| report = server.getConnection().transform(logEvent.getStatus(), logEvent.getContext()); |
| for (ToolItem processorToolItem : processorsBar.getItems()) { |
| IProcessorDescriptor toolItemDescriptor = (IProcessorDescriptor) processorToolItem.getData(); |
| if (toolItemDescriptor == descriptor) { |
| processorToolItem.setSelection(false); |
| break; |
| } |
| } |
| break; |
| } |
| preview.preview(report, logEvent.getStatus(), server.getName(), reportProcessorDescriptors, logEvent.getContext(), getShell()); |
| reportsViewer.refresh(true, false); |
| } |
| } |
| |
| private void previewReport() { |
| ILogEvent logEvent = (ILogEvent) selectedLogEvent.getValue(); |
| if (logEvent != null) { |
| IServerDescriptor server = logEvent.getServer(); |
| IReport report = server.getConnection().transform(logEvent.getStatus(), logEvent.getContext()); |
| preview.preview(report, logEvent.getStatus(), server.getName(), reportProcessorDescriptors, logEvent.getContext(), getShell()); |
| } |
| } |
| |
| private void addChangeListenerToBindings(DataBindingContext context) { |
| for (Object o : context.getBindings()) { |
| Binding b = (Binding) o; |
| b.getModel().addChangeListener(new UpdatePreviewChangeListener()); |
| } |
| } |
| |
| @Override |
| protected void buttonPressed(int buttonId) { |
| // MessageDialogs have a null implementation of this method. Need to re-implement it. |
| if (IDialogConstants.OK_ID == buttonId) { |
| okPressed(); |
| } else if (IDialogConstants.CANCEL_ID == buttonId) { |
| cancelPressed(); |
| } |
| } |
| |
| @Override |
| public boolean close() { |
| context.modify(CTX_STATE_REVIEW_IN_PROGRESS, false); |
| dbContext.dispose(); |
| queue.eAdapters().remove(incomingEventsCopier); |
| return super.close(); |
| } |
| |
| @Override |
| protected void okPressed() { |
| super.okPressed(); |
| broker.post(TOPIC_USER_REQUESTS_SEND_ALL_GROUPS, queue); |
| } |
| |
| @Override |
| protected void cancelPressed() { |
| super.cancelPressed(); |
| broker.post(TOPIC_USER_REQUESTS_CLEAR_QUEUE, queue); |
| } |
| |
| private final class ActivatedProcessorsChangeVisitor extends ListDiffVisitor { |
| |
| @Override |
| public void handleAdd(int index, Object processor) { |
| if (processor instanceof IEditableReportProcessor) { |
| editAndPreviewReport((IEditableReportProcessor) processor); |
| } else { |
| previewReport(); |
| } |
| } |
| |
| @Override |
| public void handleRemove(int index, Object processor) { |
| previewReport(); |
| } |
| } |
| |
| private final class UpdatePreviewChangeListener implements IChangeListener { |
| @Override |
| public void handleChange(ChangeEvent event) { |
| previewReport(); |
| } |
| } |
| |
| private final class IncomingEventsCopier extends AdapterImpl { |
| private final IInternalInput input; |
| |
| private IncomingEventsCopier(IInternalInput input) { |
| this.input = input; |
| } |
| |
| @Override |
| public void notifyChanged(Notification msg) { |
| EList<ILogEvent> input2 = input.getInput(); |
| switch (msg.getEventType()) { |
| case Notification.ADD: { |
| ILogEventGroup group = (ILogEventGroup) msg.getNewValue(); |
| for (ILogEvent event : group.getEvents()) { |
| // TODO me need a better solution than this for final version: |
| IServerDescriptor server = event.getServer(); |
| if (server.isConfigured()) { |
| input2.add(event); |
| } |
| } |
| break; |
| } |
| case Notification.REMOVE: |
| ILogEventGroup group = (ILogEventGroup) msg.getOldValue(); |
| input2.removeAll(group.getEvents()); |
| break; |
| } |
| } |
| } |
| |
| private final class ReportsViewerDeleteListener extends KeyAdapter { |
| @Override |
| public void keyPressed(KeyEvent e) { |
| if (e.keyCode == SWT.DEL || e.keyCode == SWT.BS) { |
| deleteSelection(); |
| } |
| } |
| |
| private void deleteSelection() { |
| IStructuredSelection selection = (IStructuredSelection) reportsViewer.getSelection(); |
| @SuppressWarnings("unchecked") |
| List<ILogEvent> elements = selection.toList(); |
| events.removeAll(elements); |
| for (ILogEvent event : elements) { |
| ILogEventGroup group = (ILogEventGroup) event.eContainer(); |
| EcoreUtil.delete(event); |
| if (group.getEvents().isEmpty()) { |
| EcoreUtil.remove(group); |
| } |
| } |
| } |
| } |
| |
| private final class ShowPreviewListener extends ExpansionAdapter { |
| private Point collapsedShellSize; |
| |
| @Override |
| public void expansionStateChanged(ExpansionEvent e) { |
| if (e.getState()) { |
| showPreview(); |
| } else { |
| hidePreview(); |
| } |
| } |
| |
| public void hidePreview() { |
| Shell shell = getShell(); |
| Point current = shell.getSize(); |
| preview.getStyledText().setVisible(false); |
| shell.setSize(current.x, DEFAULT_DIALOG_HEIGHT); |
| } |
| |
| public void showPreview() { |
| Shell shell = getShell(); |
| preview.getStyledText().setVisible(true); |
| collapsedShellSize = shell.getSize(); |
| shell.setSize(collapsedShellSize.x, collapsedShellSize.y + 300); |
| } |
| } |
| |
| private final class ReportsViewerLabelProvider extends ColumnLabelProvider implements ITableFontProvider { |
| |
| private Font defaultFont; |
| private Font boldFont; |
| |
| ReportsViewerLabelProvider(ColumnViewer viewer) { |
| defaultFont = viewer.getControl().getFont(); |
| FontDescriptor boldDescriptor = FontDescriptor.createFrom(defaultFont).setStyle(SWT.BOLD); |
| boldFont = boldDescriptor.createFont(viewer.getControl().getDisplay()); |
| } |
| |
| @Override |
| public String getText(Object element) { |
| return new IdeSwitch<String>() { |
| @Override |
| public String caseLogEvent(ILogEvent event) { |
| if (isActivationConfirmRequired(event)) { |
| return "* " + event.getLabel(); |
| } |
| return event.getLabel(); |
| } |
| }.doSwitch((EObject) element); |
| } |
| |
| @Override |
| public int getToolTipDisplayDelayTime(Object object) { |
| return 100; // msec |
| } |
| |
| @Override |
| public int getToolTipTimeDisplayed(Object object) { |
| return 5000; // msec |
| } |
| |
| @Override |
| public Image getImage(Object element) { |
| return new IdeSwitch<Image>() { |
| @Override |
| public Image caseLogEvent(ILogEvent event) { |
| boolean shouldProcess = Processors.shouldProcess(reportProcessorDescriptors, event); |
| Image image = UIUtils.decorate(event.getServer(), event.getStatus(), registry, shouldProcess); |
| return image; |
| } |
| }.doSwitch((EObject) element); |
| } |
| |
| @Override |
| public Font getFont(Object element, int columnIndex) { |
| return new IdeSwitch<Font>() { |
| @Override |
| public Font caseLogEvent(ILogEvent event) { |
| return isActivationConfirmRequired(event) ? boldFont : defaultFont; |
| } |
| |
| }.doSwitch((EObject) element); |
| } |
| |
| private boolean isActivationConfirmRequired(ILogEvent event) { |
| return reportProcessorDescriptors.stream().filter(descriptor -> Processors.shouldProcess(descriptor, event) |
| && !event.getOptions().getEnabledProcessors().contains(descriptor.getProcessor())).findFirst().isPresent(); |
| } |
| } |
| } |