/******************************************************************************* * Copyright (c) 2014 Obeo. * 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: * Obeo - initial API and implementation *******************************************************************************/ package org.eclipse.emf.compare.uml2.internal.provider.decorator; import com.google.common.base.Function; import com.google.common.base.Joiner; import com.google.common.collect.Iterables; import java.io.IOException; import java.net.URL; import java.util.ArrayList; import java.util.Collections; import java.util.Iterator; import java.util.List; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Status; import org.eclipse.emf.common.util.EList; import org.eclipse.emf.common.util.ResourceLocator; import org.eclipse.emf.common.util.URI; import org.eclipse.emf.compare.provider.ExtendedItemProviderDecorator; import org.eclipse.emf.compare.uml2.internal.provider.UMLCompareEditPlugin; import org.eclipse.emf.ecore.EObject; import org.eclipse.emf.ecore.resource.Resource; import org.eclipse.emf.ecore.resource.ResourceSet; import org.eclipse.emf.ecore.resource.URIConverter; import org.eclipse.emf.edit.provider.ComposeableAdapterFactory; import org.eclipse.emf.edit.provider.ComposedImage; import org.eclipse.emf.edit.provider.IChangeNotifier; import org.eclipse.emf.edit.provider.IChildCreationExtender; import org.eclipse.emf.edit.provider.IEditingDomainItemProvider; import org.eclipse.emf.edit.provider.IItemColorProvider; import org.eclipse.emf.edit.provider.IItemFontProvider; import org.eclipse.emf.edit.provider.IItemLabelProvider; import org.eclipse.emf.edit.provider.IItemPropertySource; import org.eclipse.emf.edit.provider.IItemProviderDecorator; import org.eclipse.emf.edit.provider.ITreeItemContentProvider; import org.eclipse.emf.edit.provider.ReflectiveItemProvider; import org.eclipse.uml2.common.util.UML2Util; import org.eclipse.uml2.uml.Element; import org.eclipse.uml2.uml.NamedElement; import org.eclipse.uml2.uml.Stereotype; /** * Item provider decorator for stereotyped {@link Element}s. * * @author <a href="mailto:arthur.daussy@obeo.fr">Arthur Daussy</a> */ public class StereotypedElementItemProviderDecorator extends ExtendedItemProviderDecorator implements IEditingDomainItemProvider, ITreeItemContentProvider, IItemLabelProvider, IItemPropertySource, IItemColorProvider, IItemFontProvider { /** * Constructor. * * @param adapterFactory * {@link ComposeableAdapterFactory}. */ public StereotypedElementItemProviderDecorator(ComposeableAdapterFactory adapterFactory) { super(adapterFactory); } @Override public String getText(Object object) { if (object instanceof Element) { Element elem = (Element)object; EList<Stereotype> appliedStereotypes = elem.getAppliedStereotypes(); if (!appliedStereotypes.isEmpty()) { return getStereotypedElementLabel((Element)object, appliedStereotypes); } } return super.getText(object); } /** * Gets a label for a stereotyped {@link Element}. * * @param element * stereotyped {@link Element}. * @param appliedStereotypes * List of stereotypes to display. * @return the label of the stereotyped element. */ private String getStereotypedElementLabel(Element element, EList<Stereotype> appliedStereotypes) { StringBuilder labelBuilder = new StringBuilder(); String stereotypes = Joiner.on(',') .join(Iterables.transform(appliedStereotypes, new Function<Stereotype, String>() { public String apply(Stereotype input) { return input.getName(); } })); labelBuilder.append('<').append(stereotypes).append("> "); //$NON-NLS-1$ if (element instanceof NamedElement) { NamedElement namedElement = (NamedElement)element; labelBuilder.append(namedElement.getName()); } return labelBuilder.toString(); } @Override public Object getImage(Object object) { if (object instanceof Element && !((Element)object).getAppliedStereotypes().isEmpty()) { return new ComposedImage(getStereotypeIcons((Element)object, super.getImage(object))); } return super.getImage(object); } /** * Gets all icons from the stereotypes applied on the element. * <p> * The icons for a stereotype are computed this way: * </p> * <ol> * <li>IF:There is {@link org.eclipse.uml2.uml.Image} in the profile model then computes the icon from its * location. (warning: currently the feature "content" of a {@link org.eclipse.uml2.uml.Image} is not used * to get a icon)</li> * <li>ELSEIF: There is ItemProvider registered into the platform for the stereotype application use it. * </li> * <li>ELSE: Uses the base element icon.</li> * </ol> * <p> * This method has been inspired from the implementation of * {@link org.eclipse.uml2.uml.edit.providers.ElementItemProvider#overlayImage(Object object, Object image)} * </p> * * @param element * base {@link Element}. * @param baseElementIcon * Base element icon. * @return List of icon to use */ protected List<Object> getStereotypeIcons(Element element, Object baseElementIcon) { List<Object> images = null; Iterator<Stereotype> stereotypeIterator = element.getAppliedStereotypes().iterator(); while (stereotypeIterator.hasNext() && images == null) { Stereotype appliedStereotype = stereotypeIterator.next(); if (!appliedStereotype.getIcons().isEmpty()) { images = getStereotypeIconsFromProfile(appliedStereotype); } else { Object img = getStereotypeIconFromItemProvider(appliedStereotype, element); if (img != null) { images = Collections.singletonList(img); } } } if (images == null) { // If there is no icon for any stereotypes then uses the base element icon. images = Collections.singletonList(baseElementIcon); } return images; } /** * Get the icon of a stereotype using registered item providers. * * @param appliedStereotype * Applied stereotype from which you want to retrieve an icon. * @param element * Base UML element. * @return The icon of the stereotype or <code>null</code> if none. */ private Object getStereotypeIconFromItemProvider(Stereotype appliedStereotype, Element element) { EObject steretotypeApplication = element.getStereotypeApplication(appliedStereotype); return getStereotypeIconFromItemProvider(steretotypeApplication); } /** * Gets the icons for a stereotype using the UML profile model. * * @param appliedStereotype * Applied stereotyped from which you want to retrieve icons. * @return List of icons to use for this stereotype or <code>null</code> if no icon has been found. * @see Stereotype#getIcons() */ protected List<Object> getStereotypeIconsFromProfile(Stereotype appliedStereotype) { List<Object> images = new ArrayList<Object>(); for (org.eclipse.uml2.uml.Image icon : appliedStereotype.getIcons()) { String location = icon.getLocation(); if (!UML2Util.isEmpty(location)) { Object img = getIconFromLocation(icon.eResource(), location); if (img != null) { images.add(img); } } else { // TODO handle icons defined by the "content" feature... } } if (images.isEmpty()) { images = null; } return images; } /** * Gets an icon from its location. * * @param eResource * Resource holding the {@link org.eclipse.uml2.uml.Image} element. * @param location * Location of the icon * @return an icon or <code>null</code> otherwise. */ protected Object getIconFromLocation(Resource eResource, String location) { Object img = null; if (eResource != null) { ResourceSet resourceSet = eResource.getResourceSet(); if (resourceSet != null) { URIConverter uriConverter = resourceSet.getURIConverter(); URI normalizedURI = uriConverter.normalize(eResource.getURI()); URI uri = URI.createURI(location).resolve(normalizedURI); URL url; try { url = new URL(uriConverter.normalize(uri).toString()); url.openStream().close(); img = url; } catch (IOException e) { UMLCompareEditPlugin.getPlugin().getLog() .log(new Status(IStatus.WARNING, "org.eclipse.emf.compare.uml2.edit", //$NON-NLS-1$ UMLCompareEditPlugin.INSTANCE.getString( "Unable_To_Retreive_Icon_Error_Message", //$NON-NLS-1$ new Object[] {location }), e)); } } } return img; } /** * Retrieves the icon from registered item providers. * <p> * This implementation remove the {@link ReflectiveItemProvider} to prevent getting the basic EMF icon. * </p> * </p> * * @param object * Input. * @return The icon of the input object or <code>null</code> if no icon has been found. */ public Object getStereotypeIconFromItemProvider(Object object) { /* * Use the root adapter factory to retrieve the adapter factory of the static profiles. This only * works if the EMF.edit code has been generated. */ ComposeableAdapterFactory rootAdapterFactory = ((ComposeableAdapterFactory)adapterFactory) .getRootAdapterFactory(); IItemLabelProvider itemLabelProvider = (IItemLabelProvider)rootAdapterFactory.adapt(object, IItemLabelProvider.class); if (itemLabelProvider instanceof IItemProviderDecorator) { IChangeNotifier cNotifier = ((IItemProviderDecorator)itemLabelProvider) .getDecoratedItemProvider(); if (cNotifier instanceof IItemLabelProvider) { itemLabelProvider = (IItemLabelProvider)cNotifier; } } if (itemLabelProvider == null || itemLabelProvider instanceof ReflectiveItemProvider) { return null; } else { return itemLabelProvider.getImage(object); } } public ResourceLocator getResourceLocator() { return ((IChildCreationExtender)adapterFactory).getResourceLocator(); } }