| /** |
| * Copyright (c) 2012 Eclipse contributors and others. |
| * 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.emf.edit.ui.provider; |
| |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.Collection; |
| import java.util.Collections; |
| import java.util.EventObject; |
| import java.util.HashMap; |
| import java.util.HashSet; |
| import java.util.Iterator; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Set; |
| import java.util.regex.Matcher; |
| import java.util.regex.Pattern; |
| |
| import org.eclipse.core.runtime.IProgressMonitor; |
| import org.eclipse.core.runtime.IStatus; |
| import org.eclipse.core.runtime.Status; |
| import org.eclipse.core.runtime.jobs.Job; |
| import org.eclipse.emf.common.command.AbstractCommand; |
| import org.eclipse.emf.common.command.Command; |
| import org.eclipse.emf.common.command.CommandStack; |
| import org.eclipse.emf.common.command.CommandStackListener; |
| import org.eclipse.emf.common.notify.Adapter; |
| import org.eclipse.emf.common.notify.AdapterFactory; |
| import org.eclipse.emf.common.notify.Notification; |
| import org.eclipse.emf.common.notify.Notifier; |
| import org.eclipse.emf.common.ui.ImageURIRegistry; |
| import org.eclipse.emf.common.ui.MarkerHelper; |
| import org.eclipse.emf.common.ui.viewer.ColumnViewerInformationControlToolTipSupport; |
| import org.eclipse.emf.common.ui.viewer.IStyledLabelDecorator; |
| import org.eclipse.emf.common.ui.viewer.IUndecoratingLabelProvider; |
| import org.eclipse.emf.common.util.BasicDiagnostic; |
| import org.eclipse.emf.common.util.Diagnostic; |
| import org.eclipse.emf.common.util.DiagnosticChain; |
| import org.eclipse.emf.common.util.EList; |
| import org.eclipse.emf.common.util.URI; |
| import org.eclipse.emf.common.util.UniqueEList; |
| import org.eclipse.emf.ecore.EClass; |
| import org.eclipse.emf.ecore.EObject; |
| import org.eclipse.emf.ecore.InternalEObject; |
| import org.eclipse.emf.ecore.resource.Resource; |
| import org.eclipse.emf.ecore.resource.ResourceSet; |
| import org.eclipse.emf.ecore.util.Diagnostician; |
| import org.eclipse.emf.ecore.util.EContentAdapter; |
| import org.eclipse.emf.ecore.util.EObjectValidator; |
| import org.eclipse.emf.ecore.util.EcoreUtil; |
| import org.eclipse.emf.edit.domain.AdapterFactoryEditingDomain; |
| import org.eclipse.emf.edit.domain.EditingDomain; |
| import org.eclipse.emf.edit.domain.IEditingDomainProvider; |
| import org.eclipse.emf.edit.provider.ComposedImage; |
| import org.eclipse.emf.edit.ui.EMFEditUIPlugin; |
| import org.eclipse.emf.edit.ui.action.ValidateAction; |
| import org.eclipse.emf.edit.ui.util.EditUIMarkerHelper; |
| import org.eclipse.emf.edit.ui.view.ExtendedPropertySheetPage; |
| import org.eclipse.jface.action.Action; |
| import org.eclipse.jface.dialogs.IDialogSettings; |
| import org.eclipse.jface.preference.JFacePreferences; |
| import org.eclipse.jface.resource.ImageDescriptor; |
| import org.eclipse.jface.resource.JFaceResources; |
| import org.eclipse.jface.viewers.CellLabelProvider; |
| import org.eclipse.jface.viewers.ColumnViewer; |
| import org.eclipse.jface.viewers.IContentProvider; |
| import org.eclipse.jface.viewers.ILabelDecorator; |
| import org.eclipse.jface.viewers.ILabelProvider; |
| import org.eclipse.jface.viewers.ILabelProviderListener; |
| import org.eclipse.jface.viewers.IStructuredContentProvider; |
| import org.eclipse.jface.viewers.ITreeContentProvider; |
| import org.eclipse.jface.viewers.StructuredViewer; |
| import org.eclipse.jface.viewers.StyledString; |
| import org.eclipse.jface.viewers.StyledString.Styler; |
| import org.eclipse.jface.viewers.ViewerCell; |
| import org.eclipse.swt.SWT; |
| import org.eclipse.swt.browser.LocationEvent; |
| import org.eclipse.swt.custom.StyleRange; |
| import org.eclipse.swt.graphics.Image; |
| import org.eclipse.swt.graphics.TextStyle; |
| import org.eclipse.swt.widgets.Control; |
| import org.eclipse.swt.widgets.Display; |
| import org.eclipse.ui.IWorkbenchPart; |
| |
| /** |
| * A {@link ILabelDecorator label decorator} for associating {@link Diagnostic diagnostic} decorations with a {@link StructuredViewer structured viewer}'s content labels. |
| * |
| * @since 2.9 |
| */ |
| public class DiagnosticDecorator extends CellLabelProvider implements ILabelDecorator |
| { |
| /** |
| * Converts special characters to entities except those in {@link #enquote(String) enquoted ranges}. |
| */ |
| public static String escapeContent(String content) |
| { |
| StringBuilder result = new StringBuilder(); |
| boolean escape = true; |
| for (int i = 0, length = content.length(); i < length; ++i) |
| { |
| char character = content.charAt(i); |
| if (escape) |
| { |
| switch (character) |
| { |
| case '\002': |
| { |
| escape = false; |
| break; |
| } |
| case '\003': |
| { |
| escape = true; |
| break; |
| } |
| case '&': |
| { |
| result.append("&"); |
| break; |
| } |
| case '<': |
| { |
| result.append("<"); |
| break; |
| } |
| default: |
| { |
| result.append(character); |
| break; |
| } |
| } |
| } |
| else |
| { |
| result.append(character); |
| } |
| } |
| return result.toString(); |
| } |
| |
| /** |
| * Marks the string such that the content will not be subsequently {@link #escapeContent(String) escaped}. |
| */ |
| public static String enquote(String content) |
| { |
| return '\002' + content + '\003'; |
| } |
| |
| private static final Pattern ENQUOTED_SEGMENT_PATTERN = Pattern.compile("\002<img src='[^']*'/> (?:<i>)?([^\003]*)(?:</i>)?\003"); |
| |
| /** |
| * Cleans up the escaping and HTML tags inserted by the {@link LiveValidator live validator}. |
| * It can be safely called on text not produces by the live validator because it only transformed {@link #enquote(String) enquoted} content. |
| */ |
| public static String strip(String content) |
| { |
| Matcher matcher = ENQUOTED_SEGMENT_PATTERN.matcher(content); |
| if (matcher.find()) |
| { |
| StringBuilder result = new StringBuilder(); |
| int start = 0; |
| do |
| { |
| result.append(content.substring(start, matcher.start())); |
| String label = matcher.group(1); |
| result.append(label.replace("<", "<").replace("&", "&")); |
| start = matcher.end(); |
| } |
| while (matcher.find()); |
| result.append(content.substring(start)); |
| return result.toString(); |
| } |
| else |
| { |
| return content; |
| } |
| } |
| |
| /** |
| * A content adapter for monitoring a {@link ResourceSet resource set}'s resources. |
| */ |
| public static abstract class DiagnosticAdapter extends EContentAdapter |
| { |
| /** |
| * Inform all diagnostic adapters associated with the notifier of new diagnostic information. |
| */ |
| public static void update(Notifier notifier, Diagnostic diagnostic) |
| { |
| for (Adapter adapter : notifier.eAdapters()) |
| { |
| if (adapter instanceof DiagnosticAdapter) |
| { |
| ((DiagnosticAdapter)adapter).updateDiagnostic(diagnostic); |
| } |
| } |
| } |
| |
| protected abstract void updateDiagnostic(Diagnostic diagnostic); |
| |
| /** |
| * This will typically compute a diagnostic for the resource and call {@link #updateDiagnostic(Diagnostic)}. |
| */ |
| protected abstract void handleResourceDiagnostics(List<Resource> resources); |
| |
| @Override |
| public void notifyChanged(Notification notification) |
| { |
| Object notifier = notification.getNotifier(); |
| if (notifier instanceof Resource) |
| { |
| switch (notification.getFeatureID(Resource.class)) |
| { |
| case Resource.RESOURCE__IS_LOADED: |
| case Resource.RESOURCE__ERRORS: |
| case Resource.RESOURCE__WARNINGS: |
| { |
| Resource resource = (Resource)notifier; |
| handleResourceDiagnostics(Collections.singletonList(resource)); |
| break; |
| } |
| } |
| } |
| else |
| { |
| // Be careful because we're adding the adapter on a background thread. |
| // |
| synchronized (notifier) |
| { |
| super.notifyChanged(notification); |
| } |
| } |
| } |
| |
| @Override |
| protected void setTarget(ResourceSet target) |
| { |
| super.setTarget(target); |
| |
| handleResourceDiagnostics(new ArrayList<Resource>(target.getResources())); |
| } |
| |
| @Override |
| protected void setTarget(Resource target) |
| { |
| basicSetTarget(target); |
| } |
| |
| @Override |
| protected void unsetTarget(Resource target) |
| { |
| basicUnsetTarget(target); |
| } |
| } |
| |
| /** |
| * A listener that interprets links as navigable references to objects in the resource set. |
| */ |
| public static class EditingDomainLocationListener extends ColumnViewerInformationControlToolTipSupport.PathLocationListener |
| { |
| protected EditingDomain editingDomain; |
| |
| public EditingDomainLocationListener(EditingDomain editingDomain, StructuredViewer viewer) |
| { |
| super(viewer); |
| this.editingDomain = editingDomain; |
| } |
| |
| @Override |
| public void changing(LocationEvent event) |
| { |
| EObject eObject = null; |
| try |
| { |
| URI uri = URI.createURI(event.location); |
| if (uri.hasFragment()) |
| { |
| eObject = editingDomain.getResourceSet().getEObject(uri, false); |
| } |
| } |
| catch (Throwable throwable) |
| { |
| // Ignore when we can't find the object. |
| } |
| if (eObject != null) |
| { |
| event.doit = false; |
| setSelection(eObject); |
| } |
| else |
| { |
| super.changing(event); |
| } |
| } |
| } |
| |
| public static class LiveValidator |
| { |
| public static class LiveValidationAction extends Action |
| { |
| public static final String LIVE_VALIDATOR_DIALOG_SETTINGS_KEY = "liveValidator"; |
| |
| protected EditingDomain domain; |
| protected IDialogSettings dialogSettings; |
| |
| public LiveValidationAction(EditingDomain domain, IDialogSettings dialogSettings) |
| { |
| this(dialogSettings); |
| this.domain = domain; |
| update(); |
| } |
| |
| public LiveValidationAction(IDialogSettings dialogSettings) |
| { |
| super(EMFEditUIPlugin.INSTANCE.getString("_UI_LiveValidation_menu_item")); |
| setDescription(EMFEditUIPlugin.INSTANCE.getString("_UI_LiveValidation_simple_description")); |
| this.dialogSettings = dialogSettings; |
| } |
| |
| public EditingDomain getEditingDomain() |
| { |
| return domain; |
| } |
| |
| public void setEditingDomain(EditingDomain domain) |
| { |
| this.domain = domain; |
| } |
| |
| @Override |
| public void run() |
| { |
| boolean checked = isChecked(); |
| if (dialogSettings != null) |
| { |
| dialogSettings.put(LIVE_VALIDATOR_DIALOG_SETTINGS_KEY, checked); |
| } |
| update(); |
| LiveValidator liveValidator = LIVE_VALIDATORS.get(domain); |
| if (liveValidator != null) |
| { |
| ResourceSet resourceSet = domain.getResourceSet(); |
| EList<Resource> resources = resourceSet.getResources(); |
| if (checked) |
| { |
| liveValidator.scheduledResources.addAll(resources); |
| liveValidator.scheduleValidation(); |
| } |
| else |
| { |
| for (Adapter adapter : resourceSet.eAdapters()) |
| { |
| if (adapter instanceof DiagnosticDecoratorAdapter) |
| { |
| DiagnosticDecoratorAdapter diagnosticDecoratorAdapter = (DiagnosticDecoratorAdapter)adapter; |
| diagnosticDecoratorAdapter.refreshResourceDiagnostics(resources); |
| } |
| } |
| } |
| } |
| } |
| |
| public void update() |
| { |
| setEnabled(domain != null); |
| if (dialogSettings != null) |
| { |
| setChecked(dialogSettings.getBoolean(LIVE_VALIDATOR_DIALOG_SETTINGS_KEY)); |
| } |
| } |
| |
| public void setActiveWorkbenchPart(IWorkbenchPart workbenchPart) |
| { |
| setEditingDomain(workbenchPart instanceof IEditingDomainProvider ? ((IEditingDomainProvider)workbenchPart).getEditingDomain() : null); |
| } |
| } |
| |
| private final List<DiagnosticDecorator> diagnosticDecorators = new ArrayList<DiagnosticDecorator>(); |
| |
| protected EditingDomain editingDomain; |
| protected IDialogSettings dialogSettings; |
| protected AdapterFactory adapterFactory; |
| protected ILabelProvider labelProvider; |
| protected Job validationJob; |
| protected List<Resource> scheduledResources = Collections.synchronizedList(new UniqueEList<Resource>()); |
| |
| public LiveValidator(final EditingDomain editingDomain, IDialogSettings dialogSettings) |
| { |
| this.editingDomain = editingDomain; |
| this.dialogSettings = dialogSettings; |
| adapterFactory = editingDomain instanceof AdapterFactoryEditingDomain ? ((AdapterFactoryEditingDomain)editingDomain).getAdapterFactory() : null; |
| labelProvider = adapterFactory == null ? null : new AdapterFactoryLabelProvider(adapterFactory); |
| |
| editingDomain.getCommandStack().addCommandStackListener |
| (new CommandStackListener() |
| { |
| public void commandStackChanged(EventObject event) |
| { |
| Command mostRecentCommand = ((CommandStack)event.getSource()).getMostRecentCommand(); |
| if (!(mostRecentCommand instanceof AbstractCommand.NonDirtying)) |
| { |
| scheduledResources.addAll(editingDomain.getResourceSet().getResources()); |
| scheduleValidation(); |
| } |
| } |
| }); |
| } |
| |
| public void scheduleValidation(Resource resource) |
| { |
| scheduledResources.add(resource); |
| scheduleValidation(); |
| } |
| |
| public void scheduleValidation() |
| { |
| if (validationJob == null && (dialogSettings == null || dialogSettings.getBoolean(LiveValidationAction.LIVE_VALIDATOR_DIALOG_SETTINGS_KEY))) |
| { |
| validationJob = |
| new Job("Validation Job") |
| { |
| @Override |
| protected IStatus run(final IProgressMonitor monitor) |
| { |
| Diagnostician diagnostician = |
| new Diagnostician() |
| { |
| @Override |
| public String getObjectLabel(EObject eObject) |
| { |
| String text = labelProvider != null && eObject.eIsProxy() ? ((InternalEObject)eObject).eProxyURI().toString() : labelProvider.getText(eObject); |
| if (text == null || text.length() == 0) |
| { |
| text = "<i>null</i>"; |
| } |
| else |
| { |
| text = escapeContent(text); |
| } |
| Image image = labelProvider != null ? labelProvider.getImage(eObject) : null; |
| if (image != null) |
| { |
| URI imageURI = ImageURIRegistry.INSTANCE.getImageURI(image); |
| return enquote("<img src='" + imageURI + "'/> " + text); |
| } |
| else |
| { |
| return escapeContent(text); |
| } |
| } |
| |
| @Override |
| public boolean validate(EClass eClass, EObject eObject, DiagnosticChain diagnostics, Map<Object, Object> context) |
| { |
| monitor.worked(1); |
| return super.validate(eClass, eObject, diagnostics, context); |
| } |
| }; |
| |
| final ResourceSet resourceSet = editingDomain.getResourceSet(); |
| |
| List<Resource> resources = new UniqueEList<Resource>(Arrays.asList(scheduledResources.toArray(new Resource[0]))); |
| scheduledResources.removeAll(resources); |
| |
| // Count all the objects we need to validate and resolve proxies. |
| // |
| int count = 0; |
| for (Resource resource : resources) |
| { |
| synchronized (resource) |
| { |
| synchronized (resourceSet) |
| { |
| for (Iterator<EObject> i = resource.getAllContents(); i.hasNext(); ) |
| { |
| ++count; |
| EObject eObject = i.next(); |
| for (@SuppressWarnings("unused") EObject eCrossReference : eObject.eCrossReferences()) |
| { |
| // Just resolve proxies. |
| } |
| } |
| } |
| } |
| } |
| |
| // Also count all the objects we need to validate from resources loaded because of proxy resolution. |
| // |
| for (List<Resource> moreResources = Arrays.asList(scheduledResources.toArray(new Resource[0])); |
| !moreResources.isEmpty(); |
| moreResources = Arrays.asList(scheduledResources.toArray(new Resource[0]))) |
| { |
| resources.addAll(moreResources); |
| scheduledResources.removeAll(moreResources); |
| for (Resource resource : moreResources) |
| { |
| synchronized (resource) |
| { |
| synchronized (resourceSet) |
| { |
| for (Iterator<EObject> i = resource.getAllContents(); i.hasNext(); ) |
| { |
| ++count; |
| EObject eObject = i.next(); |
| for (@SuppressWarnings("unused") EObject eCrossReference : eObject.eCrossReferences()) |
| { |
| // Just resolve proxies. |
| } |
| } |
| } |
| } |
| } |
| } |
| |
| monitor.beginTask("", count); |
| final BasicDiagnostic diagnostic = |
| new BasicDiagnostic |
| (EObjectValidator.DIAGNOSTIC_SOURCE, |
| 0, |
| EMFEditUIPlugin.INSTANCE.getString("_UI_DiagnosisOfNObjects_message", new String[] { "" + resources.size() }), |
| new Object [] { resourceSet } ); |
| Map<Object, Object> context = diagnostician.createDefaultContext(); |
| for (int i = 0; i < resources.size(); ++i) |
| { |
| Resource resource = resources.get(i); |
| monitor.setTaskName(EMFEditUIPlugin.INSTANCE.getString("_UI_Validating_message", new Object [] { resource.getURI() })); |
| |
| final BasicDiagnostic resourceDiagnostic = |
| new BasicDiagnostic |
| (EObjectValidator.DIAGNOSTIC_SOURCE, |
| 0, |
| EMFEditUIPlugin.INSTANCE.getString("_UI_DiagnosisOfNObjects_message", new String[] { "1" }), |
| new Object [] { resource } ); |
| |
| synchronized (resource) |
| { |
| synchronized (resourceSet) |
| { |
| for (EObject eObject : resource.getContents()) |
| { |
| diagnostician.validate(eObject, resourceDiagnostic, context); |
| context.remove(EObjectValidator.ROOT_OBJECT); |
| } |
| } |
| } |
| |
| for (Resource.Diagnostic warning : resource.getWarnings()) |
| { |
| resourceDiagnostic.add(new BasicDiagnostic(Diagnostic.WARNING, null, 0, warning.getMessage(), new Object [] { resource })); |
| } |
| |
| for (Resource.Diagnostic error : resource.getErrors()) |
| { |
| resourceDiagnostic.add(new BasicDiagnostic(Diagnostic.ERROR, null, 0, error.getMessage(), new Object[] { resource})); |
| } |
| |
| diagnostic.add(resourceDiagnostic); |
| |
| if (monitor.isCanceled()) |
| { |
| validationJob = null; |
| return Status.CANCEL_STATUS; |
| } |
| monitor.worked(1); |
| } |
| |
| DiagnosticAdapter.update(resourceSet, diagnostic); |
| |
| Display.getDefault().asyncExec |
| (new Runnable() |
| { |
| public void run() |
| { |
| validationJob = null; |
| if (!scheduledResources.isEmpty()) |
| { |
| LiveValidator.this.scheduleValidation(); |
| } |
| } |
| }); |
| |
| return Status.OK_STATUS; |
| } |
| }; |
| validationJob.setPriority(Job.DECORATE); |
| validationJob.schedule(500); |
| } |
| } |
| |
| public void register(DiagnosticDecorator diagnosticDecorator) |
| { |
| diagnosticDecorators.add(diagnosticDecorator); |
| } |
| |
| public void deregister(DiagnosticDecorator diagnosticDecorator) |
| { |
| if (diagnosticDecorators.remove(diagnosticDecorator) && diagnosticDecorators.isEmpty()) |
| { |
| dispose(); |
| } |
| } |
| |
| public void dispose() |
| { |
| LIVE_VALIDATORS.remove(editingDomain); |
| if (validationJob != null) |
| { |
| validationJob.cancel(); |
| } |
| } |
| } |
| |
| /** |
| * An extended {@link DiagnosticDecorator} that handle style strings decoration. |
| * @since 2.10 |
| */ |
| public static class Styled extends DiagnosticDecorator implements IStyledLabelDecorator |
| { |
| /** |
| * Creates an instance that supports {@link LiveValidator live validation} and supports |
| * {@link DiagnosticDecorator.LiveValidator.LiveValidationAction#LIVE_VALIDATOR_DIALOG_SETTINGS_KEY enablement} |
| * via {@link IDialogSettings dialog setting}. |
| */ |
| public Styled(EditingDomain editingDomain, ExtendedPropertySheetPage propertySheetPage, IDialogSettings dialogSettings) |
| { |
| super(editingDomain, propertySheetPage, dialogSettings); |
| } |
| |
| /** |
| * Creates an instance that supports {@link LiveValidator live validation}. |
| */ |
| public Styled(EditingDomain editingDomain, ExtendedPropertySheetPage propertySheetPage) |
| { |
| super(editingDomain, propertySheetPage); |
| } |
| |
| /** |
| * Creates an instance that supports {@link LiveValidator live validation} and supports |
| * {@link DiagnosticDecorator.LiveValidator.LiveValidationAction#LIVE_VALIDATOR_DIALOG_SETTINGS_KEY enablement} |
| * via {@link IDialogSettings dialog setting}. |
| */ |
| public Styled(EditingDomain editingDomain, StructuredViewer viewer, IDialogSettings dialogSettings) |
| { |
| super(editingDomain, viewer, dialogSettings); |
| } |
| |
| /** |
| * Creates an instance that supports {@link LiveValidator live validation}. |
| */ |
| public Styled(EditingDomain editingDomain, StructuredViewer viewer) |
| { |
| super(editingDomain, viewer); |
| } |
| |
| /** |
| * Creates an instance that doesn't support {@link LiveValidator live validation}. |
| * Only decorations explicitly produced from {@link ValidateAction} or those |
| * {@link EditUIMarkerHelper#getMarkerDiagnostics(Object, org.eclipse.core.resources.IFile) |
| * derived from markers} are displayed. |
| */ |
| public Styled(ResourceSet resourceSet, ExtendedPropertySheetPage propertySheetPage) |
| { |
| super(resourceSet, propertySheetPage); |
| } |
| |
| /** |
| * Creates an instance that doesn't support {@link LiveValidator live validation}. |
| * Only decorations explicitly produced from {@link ValidateAction} or those |
| * {@link EditUIMarkerHelper#getMarkerDiagnostics(Object, org.eclipse.core.resources.IFile) |
| * derived from markers} are displayed. |
| */ |
| public Styled(ResourceSet resourceSet, StructuredViewer viewer) |
| { |
| super(resourceSet, viewer); |
| } |
| |
| public StyledString decorateStyledText(StyledString styledString, Object object) |
| { |
| return styledString; |
| } |
| } |
| |
| /** |
| * A styled diagnostic decorator that will decorate the given styled string by underlying it |
| * with a {@link SWT#UNDERLINE_ERROR} underline style colored in {@link JFacePreferences#ERROR_COLOR}. |
| * @author mbarbero |
| * |
| */ |
| public static class StyledError extends DiagnosticDecorator.Styled implements IStyledLabelDecorator |
| { |
| |
| public StyledError(EditingDomain editingDomain, |
| ExtendedPropertySheetPage propertySheetPage, |
| IDialogSettings dialogSettings) |
| { |
| super(editingDomain, propertySheetPage, dialogSettings); |
| } |
| |
| public StyledError(EditingDomain editingDomain, |
| ExtendedPropertySheetPage propertySheetPage) |
| { |
| super(editingDomain, propertySheetPage); |
| } |
| |
| public StyledError(EditingDomain editingDomain, StructuredViewer viewer, |
| IDialogSettings dialogSettings) |
| { |
| super(editingDomain, viewer, dialogSettings); |
| } |
| |
| public StyledError(EditingDomain editingDomain, StructuredViewer viewer) |
| { |
| super(editingDomain, viewer); |
| } |
| |
| public StyledError(ResourceSet resourceSet, |
| ExtendedPropertySheetPage propertySheetPage) |
| { |
| super(resourceSet, propertySheetPage); |
| } |
| |
| public StyledError(ResourceSet resourceSet, StructuredViewer viewer) |
| { |
| super(resourceSet, viewer); |
| } |
| |
| /** |
| * Decorate the given {@code styledString} by underlying the whole given {@code styledString} |
| * with a {@link SWT#UNDERLINE_ERROR} underline style colored in {@link JFacePreferences#ERROR_COLOR}. |
| */ |
| @Override |
| public StyledString decorateStyledText(StyledString styledString, Object object) |
| { |
| if (styledString == null || object == null) |
| { |
| throw new NullPointerException(); |
| } |
| Diagnostic diagnostic = getDecorations().get(object); |
| if (diagnostic != null && diagnostic.getSeverity() >= Diagnostic.WARNING) |
| { |
| StyledString result = new StyledString(); |
| StyleRange[] styleRanges = styledString.getStyleRanges(); |
| String string = styledString.getString(); |
| if (styleRanges.length == 0) |
| { |
| result.append(string, ErrorStyler.INSTANCE); |
| } |
| else |
| { |
| int start = 0; |
| for (StyleRange range : styleRanges) |
| { |
| if (start < range.start) |
| { |
| result.append(string.substring(start, (range.start - start)), ErrorStyler.INSTANCE); |
| } |
| start = range.start + range.length; |
| result.append(string.substring(range.start, start), new ComposedStyler(range, ErrorStyler.INSTANCE)); |
| } |
| if (start < styledString.length()) |
| { |
| result.append(string.substring(start, string.length()), ErrorStyler.INSTANCE); |
| } |
| } |
| |
| return result; |
| } |
| return styledString; |
| } |
| |
| /** |
| * A styler underlying the given text with a {@link SWT#UNDERLINE_ERROR} underline style colored |
| * in {@link JFacePreferences#ERROR_COLOR}. |
| * @since 2.10 |
| */ |
| public static final class ErrorStyler extends Styler |
| { |
| public static final Styler INSTANCE = new ErrorStyler(); |
| |
| @Override |
| public void applyStyles(TextStyle textStyle) |
| { |
| textStyle.underline = true; |
| textStyle.underlineStyle = SWT.UNDERLINE_ERROR; |
| textStyle.underlineColor = JFaceResources.getColorRegistry().get(JFacePreferences.ERROR_COLOR); |
| } |
| } |
| |
| /** |
| * An extended styler applying a wrapped base style first, and successively apply other |
| * stylers to the given TextStyle, overriding any previous style. |
| * |
| * @since 2.10 |
| */ |
| public static class ComposedStyler extends Styler |
| { |
| protected TextStyle baseStyle; |
| protected Styler[] stylers; |
| |
| public ComposedStyler(TextStyle baseStyle, Styler... stylers) |
| { |
| this.baseStyle = baseStyle; |
| this.stylers = stylers; |
| } |
| |
| @Override |
| public void applyStyles(TextStyle textStyle) |
| { |
| textStyle.font = baseStyle.font; |
| textStyle.metrics = baseStyle.metrics; |
| textStyle.rise = baseStyle.rise; |
| |
| textStyle.background = baseStyle.background; |
| textStyle.foreground = baseStyle.foreground; |
| |
| textStyle.borderColor = baseStyle.borderColor; |
| textStyle.borderStyle = baseStyle.borderStyle; |
| |
| textStyle.strikeout = baseStyle.strikeout; |
| textStyle.strikeoutColor = baseStyle.strikeoutColor; |
| |
| textStyle.underline = baseStyle.underline; |
| textStyle.underlineStyle = baseStyle.underlineStyle; |
| textStyle.underlineColor = baseStyle.underlineColor; |
| |
| textStyle.data = baseStyle.data; |
| |
| for (Styler styler : stylers) |
| { |
| styler.applyStyles(textStyle); |
| } |
| } |
| |
| } |
| } |
| |
| public class DiagnosticDecoratorAdapter extends DiagnosticAdapter |
| { |
| protected class Dispatcher implements Runnable |
| { |
| protected Display display = Display.getDefault(); |
| |
| protected List<Diagnostic> diagnostics; |
| |
| protected int expectedSize; |
| |
| public void dispatch(Diagnostic diagnostic) |
| { |
| List<Diagnostic> currentDiagnostics = null; |
| synchronized (this) |
| { |
| if (diagnostics == null) |
| { |
| diagnostics = new ArrayList<Diagnostic>(); |
| } |
| |
| // Merge in the new diagnostics with the ones we have cached for dispatching. |
| // |
| updateDiagnotics(diagnostics, diagnostic); |
| |
| // If we didn't delay dispatching, then prepare to dispatch this to the decorator. |
| // |
| if (!dispatch()) |
| { |
| currentDiagnostics = diagnostics; |
| diagnostics = null; |
| } |
| } |
| |
| // If we have are dispatching the diagnostics immediately... |
| // |
| if (currentDiagnostics != null) |
| { |
| dispatch(currentDiagnostics); |
| } |
| } |
| |
| /** |
| * Returns <code>true</code> when a delayed dispatch has been scheduled. |
| */ |
| protected boolean dispatch() |
| { |
| // If we're not in the display thread... |
| // |
| if (Display.getCurrent() != display) |
| { |
| // Set up the expected size and do a delayed asynchronous call. |
| // |
| expectedSize = diagnostics.size(); |
| display.asyncExec |
| (new Runnable() |
| { |
| public void run() |
| { |
| display.timerExec(1000, Dispatcher.this); |
| } |
| }); |
| return true; |
| } |
| else |
| { |
| return false; |
| } |
| } |
| |
| /** |
| * Dispatches the diagnostics to the decorator. |
| */ |
| protected void dispatch(List<Diagnostic> diagnostics) |
| { |
| BasicDiagnostic resourceSetDiagnostic = |
| new BasicDiagnostic |
| (EObjectValidator.DIAGNOSTIC_SOURCE, |
| 0, |
| EMFEditUIPlugin.INSTANCE.getString("_UI_DiagnosisOfNObjects_message", new String[] { "" + diagnostics.size() }), |
| new Object [] { resourceSet } ); |
| for (Diagnostic diagnostic : diagnostics) |
| { |
| resourceSetDiagnostic.add(diagnostic); |
| } |
| handleDiagnostic(resourceSetDiagnostic); |
| } |
| |
| public void run() |
| { |
| List<Diagnostic> currentDiagnostics; |
| synchronized (this) |
| { |
| // If the diagnostics have already been dispatched, don't do anything else. |
| // |
| if (diagnostics == null) |
| { |
| return; |
| } |
| |
| // If we've added more diagnostics since we started a delayed dispatch, delay a little longer. |
| // |
| if (diagnostics.size() != expectedSize) |
| { |
| expectedSize = diagnostics.size(); |
| display.asyncExec |
| (new Runnable() |
| { |
| public void run() |
| { |
| display.timerExec(1000, Dispatcher.this); |
| } |
| }); |
| return; |
| } |
| currentDiagnostics = diagnostics; |
| diagnostics = null; |
| } |
| |
| // Dispatch the diagnostics immediately. |
| // |
| dispatch(currentDiagnostics); |
| } |
| } |
| |
| protected Dispatcher dispatcher; |
| |
| protected Dispatcher getDispatcher() |
| { |
| if (dispatcher == null) |
| { |
| dispatcher = new Dispatcher(); |
| } |
| return dispatcher; |
| } |
| |
| @Override |
| public void updateDiagnostic(Diagnostic diagnostic) |
| { |
| getDispatcher().dispatch(diagnostic); |
| } |
| |
| @Override |
| protected void handleResourceDiagnostics(List<Resource> resources) |
| { |
| final BasicDiagnostic diagnostic = |
| new BasicDiagnostic |
| (EObjectValidator.DIAGNOSTIC_SOURCE, |
| 0, |
| EMFEditUIPlugin.INSTANCE.getString("_UI_DiagnosisOfNObjects_message", new String[] { "" + resources.size() }), |
| new Object [] { resourceSet } ); |
| LiveValidator liveValidator = getLiveValidator(); |
| if (liveValidator != null) |
| { |
| for (Resource resource : resources) |
| { |
| diagnostic.add(markerHelper.getMarkerDiagnostics(resource, null)); |
| liveValidator.scheduleValidation(resource); |
| } |
| } |
| updateDiagnostic(diagnostic); |
| } |
| |
| protected void refreshResourceDiagnostics(List<Resource> resources) |
| { |
| final BasicDiagnostic diagnostic = |
| new BasicDiagnostic |
| (EObjectValidator.DIAGNOSTIC_SOURCE, |
| 0, |
| EMFEditUIPlugin.INSTANCE.getString("_UI_DiagnosisOfNObjects_message", new String[] { "" + resources.size() }), |
| new Object [] { resourceSet } ); |
| for (Resource resource : resources) |
| { |
| diagnostic.add(markerHelper.getMarkerDiagnostics(resource, null)); |
| liveValidator.scheduleValidation(resource); |
| } |
| diagnostics.clear(); |
| updateDiagnostic(diagnostic); |
| } |
| } |
| |
| private static final Map<EditingDomain, LiveValidator> LIVE_VALIDATORS = new HashMap<EditingDomain, LiveValidator>(); |
| |
| protected DiagnosticAdapter diagnosticAdapter; |
| protected EditingDomain editingDomain; |
| protected LiveValidator liveValidator; |
| protected ResourceSet resourceSet; |
| protected StructuredViewer viewer; |
| protected ExtendedPropertySheetPage propertySheetPage; |
| protected IDialogSettings dialogSettings; |
| protected Map<Object, BasicDiagnostic> decorations = new HashMap<Object, BasicDiagnostic>(); |
| protected MarkerHelper markerHelper = new EditUIMarkerHelper(); |
| protected Object input; |
| protected IContentProvider contentProvider; |
| protected List<Diagnostic> diagnostics = new ArrayList<Diagnostic>(); |
| |
| /** |
| * Creates an instance that doesn't support {@link LiveValidator live validation}. |
| * Only decorations explicitly produced from {@link ValidateAction} or those {@link EditUIMarkerHelper#getMarkerDiagnostics(Object, org.eclipse.core.resources.IFile) derived from markers} are displayed. |
| */ |
| public DiagnosticDecorator(ResourceSet resourceSet, StructuredViewer viewer) |
| { |
| this.viewer = viewer; |
| this.resourceSet = resourceSet; |
| |
| diagnosticAdapter = new DiagnosticDecoratorAdapter(); |
| resourceSet.eAdapters().add(diagnosticAdapter); |
| |
| input = viewer.getInput(); |
| contentProvider = viewer.getContentProvider(); |
| } |
| |
| /** |
| * Creates an instance that supports {@link LiveValidator live validation}. |
| */ |
| public DiagnosticDecorator(EditingDomain editingDomain, StructuredViewer viewer) |
| { |
| this(editingDomain, viewer, null); |
| } |
| |
| /** |
| * Creates an instance that supports {@link LiveValidator live validation} and supports {@link DiagnosticDecorator.LiveValidator.LiveValidationAction#LIVE_VALIDATOR_DIALOG_SETTINGS_KEY enablement} via {@link IDialogSettings dialog setting}. |
| */ |
| public DiagnosticDecorator(EditingDomain editingDomain, StructuredViewer viewer, IDialogSettings dialogSettings) |
| { |
| this.editingDomain = editingDomain; |
| this.resourceSet = editingDomain.getResourceSet(); |
| this.viewer = viewer; |
| this.dialogSettings = dialogSettings; |
| |
| diagnosticAdapter = new DiagnosticDecoratorAdapter(); |
| resourceSet.eAdapters().add(diagnosticAdapter); |
| |
| input = viewer.getInput(); |
| contentProvider = viewer.getContentProvider(); |
| } |
| |
| /** |
| * Creates an instance that doesn't support {@link LiveValidator live validation}. |
| * Only decorations explicitly produced from {@link ValidateAction} or those {@link EditUIMarkerHelper#getMarkerDiagnostics(Object, org.eclipse.core.resources.IFile) derived from markers} are displayed. |
| */ |
| public DiagnosticDecorator(ResourceSet resourceSet, ExtendedPropertySheetPage propertySheetPage) |
| { |
| this.resourceSet = resourceSet; |
| this.propertySheetPage = propertySheetPage; |
| |
| diagnosticAdapter = new DiagnosticDecoratorAdapter(); |
| resourceSet.eAdapters().add(diagnosticAdapter); |
| |
| input = propertySheetPage.getInput(); |
| } |
| |
| /** |
| * Creates an instance that supports {@link LiveValidator live validation}. |
| */ |
| public DiagnosticDecorator(EditingDomain editingDomain, ExtendedPropertySheetPage propertySheetPage) |
| { |
| this(editingDomain, propertySheetPage, null); |
| } |
| |
| /** |
| * Creates an instance that supports {@link LiveValidator live validation} and supports {@link DiagnosticDecorator.LiveValidator.LiveValidationAction#LIVE_VALIDATOR_DIALOG_SETTINGS_KEY enablement} via {@link IDialogSettings dialog setting}. |
| */ |
| public DiagnosticDecorator(EditingDomain editingDomain, ExtendedPropertySheetPage propertySheetPage, IDialogSettings dialogSettings) |
| { |
| this.editingDomain = editingDomain; |
| this.resourceSet = editingDomain.getResourceSet(); |
| this.propertySheetPage = propertySheetPage; |
| this.dialogSettings = dialogSettings; |
| |
| diagnosticAdapter = new DiagnosticDecoratorAdapter(); |
| resourceSet.eAdapters().add(diagnosticAdapter); |
| |
| input = propertySheetPage.getInput(); |
| } |
| |
| protected LiveValidator getLiveValidator() |
| { |
| if (liveValidator == null && editingDomain != null) |
| { |
| liveValidator = LIVE_VALIDATORS.get(editingDomain); |
| if (liveValidator == null) |
| { |
| liveValidator = new LiveValidator(editingDomain, dialogSettings); |
| LIVE_VALIDATORS.put(editingDomain, liveValidator); |
| } |
| liveValidator.register(this); |
| } |
| return liveValidator; |
| } |
| |
| public String decorateText(String text, Object object) |
| { |
| return text; |
| } |
| |
| public Image decorateImage(Image image, Object object) |
| { |
| Diagnostic diagnostic = getDecorations().get(object); |
| if (diagnostic != null && diagnostic.getSeverity() >= Diagnostic.WARNING) |
| { |
| return decorate(image, diagnostic); |
| } |
| else |
| { |
| return image; |
| } |
| } |
| |
| public Image decorate(Image image, Diagnostic diagnostic) |
| { |
| if (image == null) |
| { |
| return ExtendedImageRegistry.INSTANCE.getImage(EMFEditUIPlugin.INSTANCE.getImage(diagnostic.getSeverity() == Diagnostic.WARNING ? "full/ovr16/warning_ovr.gif" : "full/ovr16/error_ovr.gif")); |
| } |
| else |
| { |
| List<Object> images = new ArrayList<Object>(2); |
| images.add(image); |
| images.add(EMFEditUIPlugin.INSTANCE.getImage(diagnostic.getSeverity() == Diagnostic.WARNING ? "full/ovr16/warning_ovr.gif" : "full/ovr16/error_ovr.gif")); |
| ComposedImage composedImage = new DecoratedComposedImage(images); |
| return ExtendedImageRegistry.INSTANCE.getImage(composedImage); |
| } |
| } |
| |
| private static final class DecoratedComposedImage extends ComposedImage |
| { |
| private DecoratedComposedImage(Collection<?> images) |
| { |
| super(images); |
| } |
| |
| @Override |
| public List<Point> getDrawPoints(Size size) |
| { |
| List<Point> result = new ArrayList<Point>(); |
| result.add(new Point()); |
| Point overlay = new Point(); |
| overlay.y = 7; |
| result.add(overlay); |
| return result; |
| } |
| } |
| |
| public Map<Object, ? extends Diagnostic> getDecorations() |
| { |
| // Check that the current decorations are up-to-date with respect to the viewer input and content provider. |
| // |
| if (propertySheetPage != null) |
| { |
| List<?> input = propertySheetPage.getInput(); |
| if (!input.equals(this.input)) |
| { |
| redecorate(); |
| } |
| this.input = input; |
| } |
| else |
| { |
| Object input = viewer.getInput(); |
| IContentProvider contentProvider = viewer.getContentProvider(); |
| if (input != null && !input.equals(this.input) || contentProvider != null && !contentProvider.equals(this.contentProvider)) |
| { |
| redecorate(); |
| } |
| this.input = input; |
| this.contentProvider = contentProvider; |
| } |
| |
| return decorations; |
| } |
| |
| protected void updateDiagnotics(List<Diagnostic> diagnostics, Diagnostic diagnostic) |
| { |
| String markerSource = markerHelper.getDiagnosticSource(); |
| String source = diagnostic.getSource(); |
| if (markerSource.equals(source)) |
| { |
| // Diagnostics produced from markers are expected to have a root diagnostic with the resource as the data, |
| // so we clean up old diagnostics that have the same source and for that resource. |
| // |
| for (Iterator<Diagnostic> i = diagnostics.iterator(); i.hasNext(); ) |
| { |
| Diagnostic oldDiagnostic = i.next(); |
| if (markerSource.equals(oldDiagnostic.getSource()) && diagnostic.getData().equals(oldDiagnostic.getData())) |
| { |
| i.remove(); |
| } |
| } |
| diagnostics.add(diagnostic); |
| } |
| else |
| { |
| // If the root diagnostic is for a resource set, we expect it to hold per-resource children. |
| // |
| Object object = diagnostic.getData().get(0); |
| if (object instanceof ResourceSet) |
| { |
| for (Diagnostic child : diagnostic.getChildren()) |
| { |
| updateDiagnotics(diagnostics, child); |
| } |
| } |
| else |
| { |
| // Generally these will be per resource diagnostics. |
| // We clean up old diagnostics with a marker source or the same source and with the same data. |
| // |
| for (Iterator<Diagnostic> i = diagnostics.iterator(); i.hasNext(); ) |
| { |
| Diagnostic oldDiagnostic = i.next(); |
| String oldSource = oldDiagnostic.getSource(); |
| if ((source.equals(oldSource) || markerSource.equals(oldSource)) && diagnostic.getData().equals(oldDiagnostic.getData())) |
| { |
| i.remove(); |
| } |
| } |
| diagnostics.add(diagnostic); |
| } |
| } |
| } |
| |
| protected void handleDiagnostic(Diagnostic rootDiagnostic) |
| { |
| // Update the decorative diagnostics. |
| // |
| updateDiagnotics(diagnostics, rootDiagnostic); |
| |
| // Redecorate with the current diagnostics. |
| // |
| redecorate(); |
| } |
| |
| protected void redecorate() |
| { |
| // If the viewer has input and is ready to receive decorations... |
| // |
| if (propertySheetPage != null ? propertySheetPage.getInput() != null : viewer.getInput() != null && viewer.getContentProvider() != null) |
| { |
| // Build up the map for root decorations. |
| // |
| Map<Object, BasicDiagnostic> objects = new HashMap<Object, BasicDiagnostic>(); |
| for (Diagnostic diagnostic : diagnostics) |
| { |
| for (Diagnostic child : diagnostic.getChildren()) |
| { |
| List<?> data = child.getData(); |
| if (!data.isEmpty()) |
| { |
| decorate(objects, data.get(0), child, null); |
| } |
| } |
| } |
| |
| // Keep track of the old decorations and start building fresh new ones. |
| // |
| Map<Object, BasicDiagnostic> oldDecorations = decorations; |
| decorations = new HashMap<Object, BasicDiagnostic>(); |
| |
| // Decorate based on the root decorations. |
| // |
| decorate(objects); |
| |
| if (propertySheetPage != null) |
| { |
| // If this is for the property sheet and there are decorations that need cleaning or there are new decorations to show, refresh the view. |
| // |
| if (!decorations.isEmpty() || !oldDecorations.isEmpty()) |
| { |
| final Control control = propertySheetPage.getControl(); |
| if (!control.isDisposed()) |
| { |
| control.getDisplay().asyncExec |
| (new Runnable() |
| { |
| public void run() |
| { |
| if (!control.isDisposed()) |
| { |
| propertySheetPage.refresh(); |
| } |
| } |
| }); |
| } |
| } |
| } |
| else |
| { |
| // Compute the objects that need to be refreshed. |
| // |
| final Set<Object> objectsToRefresh = new HashSet<Object>(); |
| for (Map.Entry<Object, BasicDiagnostic> entry : decorations.entrySet()) |
| { |
| Object decoratedObject = entry.getKey(); |
| BasicDiagnostic oldDiagnostic = oldDecorations.get(decoratedObject); |
| if (oldDiagnostic == null || entry.getValue().getSeverity() != oldDiagnostic.getSeverity()) |
| { |
| // Each object that's newly decorated or has a different decoration needs to be refreshed. |
| // |
| objectsToRefresh.add(decoratedObject); |
| } |
| |
| // Forget about this old decoration; either the object is already added for refreshing or it doesn't need refreshing because the severity is the same. |
| // |
| if (oldDiagnostic != null) |
| { |
| oldDecorations.remove(decoratedObject); |
| } |
| } |
| |
| // Also refresh all the object with old decorations that no longer have decorations. |
| // |
| objectsToRefresh.addAll(oldDecorations.keySet()); |
| |
| if (!objectsToRefresh.isEmpty()) |
| { |
| // Be careful to refresh asynchronously if the viewer is busy. |
| // |
| if (!(viewer instanceof ColumnViewer) || !((ColumnViewer)viewer).isBusy()) |
| { |
| viewer.update(objectsToRefresh.toArray(), null); |
| } |
| else |
| { |
| final Control control = viewer.getControl(); |
| if (!control.isDisposed()) |
| { |
| control.getDisplay().asyncExec |
| (new Runnable() |
| { |
| public void run() |
| { |
| if (!control.isDisposed()) |
| { |
| viewer.update(objectsToRefresh.toArray(), null); |
| } |
| } |
| }); |
| } |
| } |
| } |
| } |
| } |
| } |
| |
| protected BasicDiagnostic decorate(Map<Object, BasicDiagnostic> decorations, Object object, Diagnostic diagnostic, List<Integer> path) |
| { |
| BasicDiagnostic oldDiagnostic = decorations.get(object); |
| if (diagnostic != null) |
| { |
| if (oldDiagnostic == null) |
| { |
| oldDiagnostic = new BasicDiagnostic(null, 0, null, path == null ? new Object [] { object } : new Object [] { object, path.toArray(new Integer[path.size()]) }); |
| decorations.put(object, oldDiagnostic); |
| } |
| oldDiagnostic.add(diagnostic); |
| } |
| return oldDiagnostic; |
| } |
| |
| protected void decorate(Map<Object, BasicDiagnostic> objects) |
| { |
| if (propertySheetPage != null) |
| { |
| for (Object object : propertySheetPage.getInput()) |
| { |
| decorate(decorations, object, objects.get(object), null); |
| } |
| } |
| else |
| { |
| Object input = viewer.getInput(); |
| IContentProvider contentProvider = viewer.getContentProvider(); |
| if (contentProvider instanceof IStructuredContentProvider) |
| { |
| ITreeContentProvider treeContentProvider = contentProvider instanceof ITreeContentProvider ? (ITreeContentProvider)contentProvider : null; |
| |
| int index = 0; |
| List<Integer> path = new ArrayList<Integer>(); |
| path.add(-1); |
| for (Object object : ((IStructuredContentProvider)contentProvider).getElements(input)) |
| { |
| path.set(0, index++); |
| BasicDiagnostic childDiagnostic = getDiagnostic(objects, object, path); |
| Diagnostic objectDiagnostic = decorate(decorations, object, childDiagnostic, path); |
| if (treeContentProvider != null) |
| { |
| Set<Object> visited = new HashSet<Object>(); |
| objectDiagnostic = decorate(objects, treeContentProvider, visited, object, path); |
| } |
| decorate(decorations, input, objectDiagnostic, null); |
| } |
| } |
| } |
| } |
| |
| /** |
| * @since 2.10 |
| */ |
| protected BasicDiagnostic getDiagnostic(Map<Object, BasicDiagnostic> objects, Object object, List<Integer> path) |
| { |
| BasicDiagnostic childDiagnostic = objects.get(object); |
| if (childDiagnostic == null) |
| { |
| // Check if the unwrapped child is different and has diagnostics... |
| // |
| Object unwrappedChild = AdapterFactoryEditingDomain.unwrap(object); |
| if (object != unwrappedChild) |
| { |
| childDiagnostic = objects.get(unwrappedChild); |
| if (childDiagnostic != null) |
| { |
| // Create a new diagnostic wrapper for it. |
| // |
| childDiagnostic = |
| new BasicDiagnostic |
| (childDiagnostic.getSource(), |
| childDiagnostic.getCode(), |
| childDiagnostic.getChildren(), |
| childDiagnostic.getMessage(), |
| new Object[] { object, path.toArray(new Integer[path.size()]) }); |
| objects.put(object, childDiagnostic); |
| } |
| } |
| } |
| return childDiagnostic; |
| } |
| |
| protected BasicDiagnostic decorate(Map<Object, BasicDiagnostic> objects, ITreeContentProvider treeContentProvider, Set<Object> visited, Object object, List<Integer> path) |
| { |
| BasicDiagnostic result = decorations.get(object); |
| if (visited.add(object)) |
| { |
| int index = 0; |
| int last = path.size(); |
| for (Object child : treeContentProvider.getChildren(object)) |
| { |
| path.add(index++); |
| BasicDiagnostic childDiagnostic = getDiagnostic(objects, child, path); |
| BasicDiagnostic childResult = decorate(decorations, child, childDiagnostic, path); |
| childResult = decorate(objects, treeContentProvider, visited, child, path); |
| path.remove(last); |
| result = decorate(decorations, object, childResult, path); |
| } |
| } |
| return result; |
| } |
| |
| @Override |
| public String getToolTipText(Object object) |
| { |
| BasicDiagnostic diagnostic = decorations.get(object); |
| if (diagnostic != null) |
| { |
| ILabelProvider labelProvider = (ILabelProvider)viewer.getLabelProvider(); |
| if (labelProvider instanceof IUndecoratingLabelProvider) |
| { |
| final IUndecoratingLabelProvider undecoratingLabelProvider = (IUndecoratingLabelProvider)labelProvider; |
| labelProvider = |
| new ILabelProvider() |
| { |
| public void removeListener(ILabelProviderListener listener) |
| { |
| undecoratingLabelProvider.removeListener(listener); |
| } |
| |
| public boolean isLabelProperty(Object element, String property) |
| { |
| return undecoratingLabelProvider.isLabelProperty(element, property); |
| } |
| |
| public void dispose() |
| { |
| undecoratingLabelProvider.dispose(); |
| } |
| |
| public void addListener(ILabelProviderListener listener) |
| { |
| undecoratingLabelProvider.addListener(listener); |
| } |
| |
| public String getText(Object element) |
| { |
| return undecoratingLabelProvider.getUndecoratedText(element); |
| } |
| |
| public Image getImage(Object element) |
| { |
| return undecoratingLabelProvider.getUndecoratedImage(element); |
| } |
| }; |
| } |
| StringBuilder result = new StringBuilder(); |
| buildToolTipText(result, labelProvider, diagnostic, object); |
| return result.length() == 0 ? null : result.toString(); |
| } |
| else |
| { |
| return null; |
| } |
| } |
| |
| protected void buildToolTipText(StringBuilder result, ILabelProvider labelProvider, Diagnostic diagnostic, Object object) |
| { |
| List<Diagnostic> children = diagnostic.getChildren(); |
| Diagnostic child = children.get(0); |
| int index = 0; |
| if (child.getData().contains(object)) |
| { |
| ++index; |
| for (Diagnostic grandChild : child.getChildren()) |
| { |
| buildToolTipMessage(result, labelProvider, object, grandChild, 0); |
| } |
| } |
| |
| StringBuilder moreResults = new StringBuilder(); |
| for (int size = children.size(); index < size; ++index) |
| { |
| child = children.get(index); |
| buildMoreToolTipText(moreResults, labelProvider, child); |
| } |
| if (moreResults.length() != 0) |
| { |
| result.append("<h1>Problems on Children</h1>\n"); |
| result.append(moreResults); |
| } |
| } |
| |
| protected void buildToolTipMessage(StringBuilder result, ILabelProvider labelProvider, Object object, Diagnostic diagnostic, int indentation) |
| { |
| String message = diagnostic.getMessage(); |
| ImageDescriptor imageDescriptor = |
| ExtendedImageRegistry.INSTANCE.getImageDescriptor |
| (EMFEditUIPlugin.INSTANCE.getImage(diagnostic.getSeverity() == Diagnostic.WARNING ? "full/ovr16/warning_ovr.gif" : "full/ovr16/error_ovr.gif")); |
| URI severityURI = ImageURIRegistry.INSTANCE.getImageURI(imageDescriptor); |
| result.append("<div>"); |
| for (int i = 0; i < indentation; ++i) |
| { |
| result.append("   "); |
| } |
| result.append("<img src='"); |
| result.append(severityURI); |
| result.append("'/> "); |
| result.append(escapeContent(message)); |
| result.append("</div>\n"); |
| List<?> excludedObjects = object instanceof EObject ? ((EObject)object).eClass().getEAllStructuralFeatures() : Collections.emptyList(); |
| for (Object data : diagnostic.getData()) |
| { |
| if (data != object && !excludedObjects.contains(data) && data instanceof EObject) |
| { |
| EObject eObject = (EObject)data; |
| if (eObject.eResource() != null && eObject.eResource().getResourceSet() == resourceSet) |
| { |
| result.append("<div>"); |
| for (int i = 0; i <= indentation; ++i) |
| { |
| result.append("   "); |
| } |
| String text = escapeContent(labelProvider.getText(data)); |
| Image image = labelProvider.getImage(data); |
| result.append("<img src='"); |
| result.append(ImageURIRegistry.INSTANCE.getImageURI(image)); |
| result.append("'/> <a href='"); |
| result.append(EcoreUtil.getURI((EObject)data)); |
| result.append("'>"); |
| result.append(text); |
| result.append("</a></div>\n"); |
| } |
| } |
| } |
| |
| for (Diagnostic child : diagnostic.getChildren()) |
| { |
| buildToolTipMessage(result, labelProvider, child.getData().get(0), child, indentation + 1); |
| } |
| } |
| |
| protected void buildMoreToolTipText(StringBuilder result, ILabelProvider labelProvider, Diagnostic diagnostic) |
| { |
| List<?> data = diagnostic.getData(); |
| Object object = data.get(0); |
| |
| List<Diagnostic> children = diagnostic.getChildren(); |
| Diagnostic child = children.get(0); |
| int index = 0; |
| if (child.getData().contains(object)) |
| { |
| ++index; |
| Image image = labelProvider.getImage(object); |
| if (image != null) |
| { |
| URI imageURI = ImageURIRegistry.INSTANCE.getImageURI(image); |
| result.append("<div><img src='"); |
| result.append(imageURI); |
| result.append("'/> "); |
| } |
| result.append("<a href='"); |
| result.append("path:"); |
| Integer[] path = (Integer[])data.get(1); |
| for (Integer segment : path) |
| { |
| result.append('/'); |
| result.append(segment); |
| } |
| String text = escapeContent(labelProvider.getText(object)); |
| if (text == null || text.length() == 0) |
| { |
| text = "<i>null</i>"; |
| } |
| result.append("'>"); |
| result.append(text); |
| result.append("</a></div>\n"); |
| for (Diagnostic grandChild : child.getChildren()) |
| { |
| buildToolTipMessage(result, labelProvider, object, grandChild, 1); |
| } |
| } |
| |
| for (int size = children.size(); index < size; ++index) |
| { |
| child = children.get(index); |
| buildMoreToolTipText(result, labelProvider, child); |
| } |
| } |
| |
| @Override |
| public void update(ViewerCell cell) |
| { |
| // Do nothing. |
| } |
| |
| @Override |
| public void dispose() |
| { |
| if (liveValidator != null) |
| { |
| liveValidator.deregister(this); |
| } |
| |
| if (diagnosticAdapter != null) |
| { |
| resourceSet.eAdapters().remove(diagnosticAdapter); |
| } |
| |
| super.dispose(); |
| } |
| } |