Java tutorial
/******************************************************************************* * Copyright (c) 2006-2013 The RCP Company 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 * * Contributors: * The RCP Company - initial API and implementation *******************************************************************************/ package com.rcpcompany.uibindings.bindingMessages; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import org.eclipse.core.runtime.IAdaptable; import org.eclipse.core.runtime.IConfigurationElement; import org.eclipse.core.runtime.IExecutableExtension; import org.eclipse.core.runtime.Platform; import org.eclipse.emf.ecore.EObject; import org.eclipse.jface.dialogs.IMessageProvider; import org.eclipse.jface.fieldassist.FieldDecorationRegistry; import org.eclipse.jface.resource.ImageDescriptor; import org.eclipse.jface.viewers.IDecoration; import org.eclipse.jface.viewers.ILabelProviderListener; import org.eclipse.jface.viewers.ILightweightLabelDecorator; import org.eclipse.jface.viewers.LabelProviderChangedEvent; import org.eclipse.ui.model.IWorkbenchAdapter; import com.rcpcompany.uibindings.internal.Activator; import com.rcpcompany.uibindings.model.utils.BasicUtils; import com.rcpcompany.uibindings.validators.IValidationAdapterManagerChangeEvent; import com.rcpcompany.uibindings.validators.IValidationAdapterManagerChangeListener; import com.rcpcompany.uibindings.validators.IValidatorAdapterManager; import com.rcpcompany.utils.logging.LogUtils; /** * Label decotator that adds an error or warning overlay to objects with outstanding errors. * <p> * The decorator can also propagate the state of child objects to the parents (recursively). This is * done either by specifying the string "propagate" as a argument to the class as shown below. * <p> * To use this decorator, specify the following extension: * * <pre> * <extension point="org.eclipse.ui.decorators"> * <decorator * class="com.rcpcompany.uibindings.bindingMessages.ValidationLabelDecorator:propagate" * id="com.rcpcompany.uibindings.decorator" * label="UI Bindings" * lightweight="true" * location="TOP_LEFT" * state="true"> * <enablement> * <objectClass * name="org.eclipse.emf.ecore.EObject"> * </objectClass> * </enablement> * </decorator> * </extension> * </pre> * * @author Tonny Madsen, The RCP Company */ public class ValidationLabelDecorator implements ILightweightLabelDecorator, IExecutableExtension { /** * This interface is used when the validation status must be propagated to parent objects in a * tree. * * @author Tonny Madsen, The RCP Company */ public interface IPropagationAdapter { /** * Returns the parent object of the specified child. * * @param object the child object * @return the parent of the child or <code>null</code> for root elements */ Object getParent(Object object); } /** * The keyword used to specify whether the label decorator will propagate the state of child * objects are propagated to the parents (recursively). */ public static final String PROPAGATE = "propagate"; //$NON-NLS-1$ /** * The propagation adapter used to find the parent. */ private IPropagationAdapter myPropagationAdapter = null; /** * Returns the current propagation adapter. * * @return the current adapter */ public IPropagationAdapter getPropagationAdapter() { return myPropagationAdapter; } /** * Set the new propagation adapter to used for this label decorator. * * @param propagationAdapter the new adapter */ public void setPropagationAdapter(IPropagationAdapter propagationAdapter) { myPropagationAdapter = propagationAdapter; } /** * The validation manager... */ private final IValidatorAdapterManager myValidatorManager = IValidatorAdapterManager.Factory.getManager(); /** * Constructs and returns a new decorator. */ public ValidationLabelDecorator() { myValidatorManager.addValidationAdapterManagerChangeListener(myVAMListener); } @Override public void dispose() { myValidatorManager.removeValidationAdapterManagerChangeListener(myVAMListener); } /** * The current severities for the affected objects. Calculated in */ private final Map<Object, Integer> myObjectSeverities = new HashMap<Object, Integer>(); /** * Returns the max severity for the specified element. * <p> * Includes the severity of any popagated severity * * @param element the element to test * @return the severity */ public int getElementSeverity(Object element) { final Integer severity = myObjectSeverities.get(element); if (severity == null) return IMessageProvider.NONE; return severity; } @Override public void decorate(Object element, IDecoration decoration) { final Integer severity = myObjectSeverities.get(element); if (severity == null) return; if (Activator.getDefault() != null && Activator.getDefault().TRACE_LABEL_DECORATOR) { LogUtils.debug(this, hashCode() + ": " + element + ": severity: " + severity); //$NON-NLS-1$ //$NON-NLS-2$ } switch (severity) { case IMessageProvider.NONE: break; case IMessageProvider.INFORMATION: break; case IMessageProvider.WARNING: decoration.addOverlay(WARNING_IMAGE); break; case IMessageProvider.ERROR: decoration.addOverlay(ERROR_IMAGE); break; default: break; } } /** * Image used to indicate that the object has an error. */ public static final ImageDescriptor ERROR_IMAGE = ImageDescriptor.createFromImage( FieldDecorationRegistry.getDefault().getFieldDecoration(FieldDecorationRegistry.DEC_ERROR).getImage()); /** * Image used to indicate that the object has a warning. */ public static final ImageDescriptor WARNING_IMAGE = ImageDescriptor.createFromImage(FieldDecorationRegistry .getDefault().getFieldDecoration(FieldDecorationRegistry.DEC_WARNING).getImage()); /** * Listeners on this label decorator. * * @see #addListener(ILabelProviderListener) * @see #removeListener(ILabelProviderListener) */ /* package */final List<ILabelProviderListener> myListeners = new ArrayList<ILabelProviderListener>(); /** * Whether this decorator has been fully initialized. */ private boolean inited; @Override public void addListener(ILabelProviderListener listener) { if (!myListeners.contains(listener)) { myListeners.add(listener); } if (!inited) { inited = true; calculateSeverities(); } } @Override public void removeListener(ILabelProviderListener listener) { myListeners.remove(listener); } private final IValidationAdapterManagerChangeListener myVAMListener = new IValidationAdapterManagerChangeListener() { @Override public void affectedObjectsChanged(IValidationAdapterManagerChangeEvent event) { calculateSeverities(); } }; @Override public boolean isLabelProperty(Object element, String property) { return true; } /** * Calculates the severities for all the affected objects covered by this decorator. */ protected void calculateSeverities() { // Calculate the new severities for all objects with a message final Map<Object, Integer> newSeverities = new HashMap<Object, Integer>(); for (final EObject o : myValidatorManager.getCurrentObjects()) { final int severity = myValidatorManager.getObjectSeverity(o); if (severity == IMessageProvider.NONE) { continue; } updateSeverity(newSeverities, o, severity); } // Update myObjectSeverities and changedObjects final Set<Object> changedObjects = new HashSet<Object>(); final Set<Object> deletedObjects = new HashSet<Object>(); for (final Map.Entry<Object, Integer> e : newSeverities.entrySet()) { final Object o = e.getKey(); if (BasicUtils.equals(myObjectSeverities.get(o), e.getValue())) { continue; } if (Activator.getDefault().TRACE_LABEL_DECORATOR) { LogUtils.debug(this, hashCode() + ": " + o + ": NEW severity: " + e.getValue()); //$NON-NLS-1$ //$NON-NLS-2$ } myObjectSeverities.put(o, e.getValue()); changedObjects.add(o); } for (final Map.Entry<Object, Integer> e : myObjectSeverities.entrySet()) { final Object o = e.getKey(); if (newSeverities.get(o) != null) { continue; } deletedObjects.add(o); changedObjects.add(o); } for (final Object o : deletedObjects) { myObjectSeverities.remove(o); } if (changedObjects.size() == 0) return; final Object[] array = changedObjects.toArray(); final LabelProviderChangedEvent event = new LabelProviderChangedEvent(ValidationLabelDecorator.this, array); if (Activator.getDefault().TRACE_LABEL_DECORATOR) { LogUtils.debug(this, hashCode() + ": " + Arrays.toString(array)); //$NON-NLS-1$ } for (final ILabelProviderListener l : myListeners) { try { l.labelProviderChanged(event); } catch (final Exception ex) { LogUtils.error(l, ex); } } } /** * A static translation from integer values to the corresponding integer objects. * <p> * Used to avoid creating too many Integer objects in {@link #updateSeverity(Map, Object, int)}. */ private static final Integer[] SEVERITY_OBJECTS = { IMessageProvider.NONE, IMessageProvider.INFORMATION, IMessageProvider.WARNING, IMessageProvider.ERROR }; /** * Updates the specified map with the specified severity for the specified object if the new * severity is more grave than any old severity stored for the same object. * <p> * If the map is updated and a propagation adapter is defined, the parent object is also * updated. * * @param map the map with severities * @param o the object to update * @param severity the new severity */ private void updateSeverity(Map<Object, Integer> map, Object o, int severity) { final Integer oldSeverity = map.get(o); if (oldSeverity != null && oldSeverity >= severity) return; if (oldSeverity == null) { int oSeverity = IMessageProvider.NONE; if (o instanceof EObject) { oSeverity = myValidatorManager.getObjectSeverity((EObject) o); } if (severity < oSeverity) { severity = oSeverity; } } if (Activator.getDefault().TRACE_LABEL_DECORATOR) { LogUtils.debug(this, hashCode() + ": update " + severity + ": " + o); //$NON-NLS-1$ //$NON-NLS-2$ } map.put(o, SEVERITY_OBJECTS[severity]); if (myPropagationAdapter == null) return; final Object parent = myPropagationAdapter.getParent(o); if (parent != null) { updateSeverity(map, parent, severity); } } @Override public void setInitializationData(IConfigurationElement config, String propertyName, Object data) { if (PROPAGATE.equals(data) || ((data instanceof Map<?, ?>) && ((Map<String, ?>) data).get(PROPAGATE) == Boolean.TRUE)) { myPropagationAdapter = new WorkbenchAdapterPropagationAdapter(); } } /** * {@link IPropagationAdapter} implementation that is based on {@link IWorkbenchAdapter}. */ protected static class WorkbenchAdapterPropagationAdapter implements IPropagationAdapter { @Override public Object getParent(Object object) { IWorkbenchAdapter adapter = null; if (adapter == null && object instanceof IAdaptable) { adapter = (IWorkbenchAdapter) ((IAdaptable) object).getAdapter(IWorkbenchAdapter.class); } if (adapter == null) { adapter = (IWorkbenchAdapter) Platform.getAdapterManager().getAdapter(object, IWorkbenchAdapter.class); } if (adapter == null) return object; return adapter.getParent(object); } } }