/*****************************************************************************
* Copyright (c) 2010 CEA LIST.
*
* 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:
* Remi Schnekenburger (CEA LIST) remi.schnekenburger@cea.fr - Initial API and implementation
*****************************************************************************/
package org.eclipse.papyrus.uml.diagram.paletteconfiguration.provider;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.eclipse.core.runtime.IConfigurationElement;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.Platform;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.resource.ResourceSet;
import org.eclipse.emf.ecore.resource.impl.ResourceSetImpl;
import org.eclipse.emf.ecore.util.EcoreUtil;
import org.eclipse.gef.Tool;
import org.eclipse.gef.palette.CombinedTemplateCreationEntry;
import org.eclipse.gef.palette.PaletteContainer;
import org.eclipse.gef.palette.PaletteDrawer;
import org.eclipse.gef.palette.PaletteEntry;
import org.eclipse.gef.palette.PaletteRoot;
import org.eclipse.gef.palette.PaletteSeparator;
import org.eclipse.gmf.runtime.common.core.service.AbstractProvider;
import org.eclipse.gmf.runtime.common.core.service.IOperation;
import org.eclipse.gmf.runtime.diagram.ui.internal.services.palette.PaletteToolEntry;
import org.eclipse.gmf.runtime.diagram.ui.services.palette.IPaletteProvider;
import org.eclipse.gmf.runtime.diagram.ui.services.palette.PaletteFactory;
import org.eclipse.gmf.runtime.gef.ui.internal.palette.PaletteStack;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.papyrus.uml.diagram.common.part.PapyrusPalettePreferences;
import org.eclipse.papyrus.uml.diagram.common.service.AspectUnspecifiedTypeConnectionTool;
import org.eclipse.papyrus.uml.diagram.common.service.AspectUnspecifiedTypeCreationTool;
import org.eclipse.papyrus.uml.diagram.paletteconfiguration.Activator;
import org.eclipse.papyrus.uml.diagram.paletteconfiguration.ChildConfiguration;
import org.eclipse.papyrus.uml.diagram.paletteconfiguration.Configuration;
import org.eclipse.papyrus.uml.diagram.paletteconfiguration.DrawerConfiguration;
import org.eclipse.papyrus.uml.diagram.paletteconfiguration.IconDescriptor;
import org.eclipse.papyrus.uml.diagram.paletteconfiguration.LeafConfiguration;
import org.eclipse.papyrus.uml.diagram.paletteconfiguration.PaletteConfiguration;
import org.eclipse.papyrus.uml.diagram.paletteconfiguration.PaletteconfigurationPackage;
import org.eclipse.papyrus.uml.diagram.paletteconfiguration.SeparatorConfiguration;
import org.eclipse.papyrus.uml.diagram.paletteconfiguration.StackConfiguration;
import org.eclipse.papyrus.uml.diagram.paletteconfiguration.ToolConfiguration;
import org.eclipse.papyrus.uml.diagram.paletteconfiguration.util.PaletteconfigurationSwitch;
import org.eclipse.ui.IEditorPart;
import org.osgi.framework.Bundle;
/**
* Palette provider with enhanced elements types
*/
public class ExtendedPluginPaletteProvider extends AbstractProvider implements IPaletteProvider {
/** name of the type */
public static final String TYPE_NAME = "testSpecializationTypeName";
/** palette factory */
protected ExtendedPaletteFactory paletteFactory = new ExtendedPaletteFactory();
/** path to the palette configuration model in the bundle */
protected static final String PATH = "path";
/** name of the attribute: id of the palette */
private static final String ID = "ID";
/** id of the plugin declaring the extension */
protected String contributorID;
/** unique identifier for this palette contribution */
protected String paletteID;
/** contributions to the palette */
protected List<PaletteConfiguration> contributions;
/** map for toolID => extended element type */
protected Map<String, IElementTypesBasedTool> mapToolId2Entries = new HashMap<String, IElementTypesBasedTool>();
/** default icon for tools */
protected static final ImageDescriptor DEFAULT_TOOL_IMAGE_DESCRIPTOR = Activator.imageDescriptorFromPlugin(Activator.PLUGIN_ID, "/icons/tool.gif");
/** default icon for stacks */
protected static final ImageDescriptor DEFAULT_STACK_IMAGE_DESCRIPTOR = Activator.imageDescriptorFromPlugin(Activator.PLUGIN_ID, "/icons/stack.gif");
/** default icon for drawers */
protected static ImageDescriptor DEFAULT_DRAWER_IMAGE_DESCRIPTOR = Activator.imageDescriptorFromPlugin(Activator.PLUGIN_ID, "/icons/drawer.gif");
/**
* Constructor.
*/
public ExtendedPluginPaletteProvider() {
}
/**
* {@inheritDoc}
*/
public boolean provides(IOperation operation) {
return false;
}
/**
* {@inheritDoc}
*/
public void contributeToPalette(IEditorPart editor, Object content, PaletteRoot root, Map predefinedEntries) {
// for each element in the contribution list, create drawers/tools/stacks/etc.
if(contributions == null || contributions.size() == 0) {
return;
}
// work for each configuration
for(PaletteConfiguration configuration : contributions) {
List<DrawerConfiguration> drawerConfigurations = configuration.getDrawerConfigurations();
if(drawerConfigurations != null && drawerConfigurations.size() > 0) {
for(DrawerConfiguration drawerConfiguration : drawerConfigurations) {
generateDrawer(root, drawerConfiguration, predefinedEntries);
}
}
}
}
/**
* Generates the drawer and its content from its configuration
*
* @param root
* the root container of the palette
* @param drawerConfiguration
* the configuration that manages the drawer
* @param predefinedEntries
* predefined existing entries
*/
@SuppressWarnings("restriction")
protected PaletteDrawer generateDrawer(PaletteRoot root, DrawerConfiguration drawerConfiguration, Map predefinedEntries) {
String id = drawerConfiguration.getId();
// retrieve drawer or create one if necessary
PaletteDrawer drawer = retrieveExistingEntry(predefinedEntries, id, PaletteDrawer.class);
if(drawer == null) {
String label = drawerConfiguration.getLabel();
drawer = new ExtendedPaletteDrawer(label, id);
// complete entry: description and icon
completeEntry(drawerConfiguration, drawer);
predefinedEntries.put(id, drawer);
root.add(drawer);
}
generateContent(drawer, drawerConfiguration, predefinedEntries);
return drawer;
}
/**
* Completes the entry with information like description, icon, etc.
*
* @param configuration
* the configuration of the entry
* @param entry
* the entry to customize
*/
protected void completeEntry(Configuration configuration, PaletteEntry entry) {
// description
entry.setDescription(configuration.getDescription());
// icon. If it is not set, the tool should use the icon of the type created by the tool
ImageDescriptor imageDescriptor = null;
IconDescriptor iconDescriptor = configuration.getIcon();
if(iconDescriptor != null) {
String bundleID = iconDescriptor.getPluginID();
if(bundleID == null) {
// by default, try to load images in the plugin that declares the configuration
bundleID = contributorID;
}
String iconPath = iconDescriptor.getIconPath();
imageDescriptor = Activator.imageDescriptorFromPlugin(bundleID, iconPath);
}
if(imageDescriptor == null && configuration instanceof ToolConfiguration) {
ToolConfiguration toolConfiguration = ((ToolConfiguration)configuration);
// FIXME retrieve icon from the element type
}
// retrieve the default icon for drawers, stacks or tools.
if(imageDescriptor == null) {
imageDescriptor = new PaletteconfigurationSwitch<ImageDescriptor>() {
/**
* {@inheritDoc}
*/
@Override
public ImageDescriptor caseDrawerConfiguration(DrawerConfiguration object) {
return ExtendedPluginPaletteProvider.DEFAULT_DRAWER_IMAGE_DESCRIPTOR;
};
/**
* {@inheritDoc}
*/
@Override
public ImageDescriptor caseToolConfiguration(ToolConfiguration object) {
return ExtendedPluginPaletteProvider.DEFAULT_TOOL_IMAGE_DESCRIPTOR;
};
/**
* {@inheritDoc}
*/
@Override
public ImageDescriptor caseStackConfiguration(StackConfiguration object) {
return ExtendedPluginPaletteProvider.DEFAULT_STACK_IMAGE_DESCRIPTOR;
};
/**
* {@inheritDoc}
*/
@Override
public ImageDescriptor defaultCase(org.eclipse.emf.ecore.EObject object) {
return null;
};
}.doSwitch(configuration);
}
if(imageDescriptor != null) {
entry.setLargeIcon(imageDescriptor);
entry.setSmallIcon(imageDescriptor);
}
}
/**
* Generates the content for a palette drawer
*
* @param drawer
* the drawer to complete
* @param drawerConfiguration
* the configuration of the drawer
* @param predefinedEntries
* predefined existing entries
*/
protected void generateContent(PaletteDrawer drawer, DrawerConfiguration drawerConfiguration, Map predefinedEntries) {
for(ChildConfiguration configuration : drawerConfiguration.getOwnedConfigurations()) {
if(configuration.eClass().equals(PaletteconfigurationPackage.eINSTANCE.getStackConfiguration())) {
generateStack(drawer, (StackConfiguration)configuration, predefinedEntries);
} else if(configuration.eClass().equals(PaletteconfigurationPackage.eINSTANCE.getToolConfiguration())) {
generateTool(drawer, (ToolConfiguration)configuration, predefinedEntries);
} else if(configuration.eClass().equals(PaletteconfigurationPackage.eINSTANCE.getSeparatorConfiguration())) {
generateSeparator(drawer, (SeparatorConfiguration)configuration, predefinedEntries);
}
}
}
/**
* Generates the tool from its configuration and container
*
* @param container
* the container in which the tool should be added
* @param configuration
* the configuration of the tool entry
* @param predefinedEntries
* predefined existing entries
*/
protected CombinedTemplateCreationEntry generateTool(PaletteContainer container, ToolConfiguration configuration, Map predefinedEntries) {
switch(configuration.getKind()) {
case CONNECTION_TOOL:
return generateConnectionTool(container, configuration, predefinedEntries);
case CREATION_TOOL:
return generateCreationTool(container, configuration, predefinedEntries);
}
return null;
}
/**
* Generates the connection tool from its configuration and container
*
* @param container
* the container in which the tool should be added
* @param configuration
* the configuration of the tool entry
* @param predefinedEntries
* predefined existing entries
*/
protected CombinedTemplateCreationEntry generateConnectionTool(PaletteContainer container, ToolConfiguration configuration, Map predefinedEntries) {
String toolID = configuration.getId();
CombinedTemplateCreationEntry toolEntry = retrieveExistingEntry(predefinedEntries, toolID, CombinedTemplateCreationEntry.class);
if(toolEntry == null) {
// create a new one from the configuration
String label = configuration.getLabel();
// create icon descriptor
toolEntry = new ExtendedConnectionToolEntry(toolID, label, paletteFactory, configuration.getElementDescriptors());
completeEntry(configuration, toolEntry);
container.add(toolEntry);
// register the tool in the tool predefined entries
predefinedEntries.put(toolID, toolEntry);
mapToolId2Entries.put(toolID, (ExtendedConnectionToolEntry)toolEntry);
}
return toolEntry;
}
/**
* Generates the creation tool from its configuration and container
*
* @param container
* the container in which the tool should be added
* @param configuration
* the configuration of the tool entry
* @param predefinedEntries
* predefined existing entries
*/
protected CombinedTemplateCreationEntry generateCreationTool(PaletteContainer container, ToolConfiguration configuration, Map predefinedEntries) {
String toolID = configuration.getId();
CombinedTemplateCreationEntry toolEntry = retrieveExistingEntry(predefinedEntries, toolID, CombinedTemplateCreationEntry.class);
if(toolEntry == null) {
// create a new one from the configuration
String label = configuration.getLabel();
// create icon descriptor
toolEntry = new ExtendedCreationToolEntry(toolID, label, paletteFactory, configuration.getElementDescriptors());
completeEntry(configuration, toolEntry);
container.add(toolEntry);
// register the tool in the tool predefined entries
predefinedEntries.put(toolID, toolEntry);
mapToolId2Entries.put(toolID, (ExtendedCreationToolEntry)toolEntry);
}
return toolEntry;
}
/**
* Try to retrieve a tool entry in the list of predefined entries
*
* @param toolID
* id of the tool to look for
* @param predefinedEntries
* predefined existing entries
* @return the tool found or <code>null</code>
*/
protected PaletteToolEntry retrieveTool(String toolID, Map predefinedEntries) {
Object value = predefinedEntries.get(toolID);
if(value instanceof PaletteToolEntry) {
return ((PaletteToolEntry)value);
}
return null;
}
/**
* Generates the stack and its content from its configuration and container
*
* @param container
* the container in which the stack should be added
* @param configuration
* the configuration of the stack
* @param predefinedEntries
* predefined existing entries
*/
@SuppressWarnings("restriction")
protected PaletteStack generateStack(PaletteContainer container, StackConfiguration configuration, Map predefinedEntries) {
String stackID = configuration.getId();
PaletteStack stack = retrieveExistingEntry(predefinedEntries, stackID, PaletteStack.class);
if(stack == null) {
// create a new one from the configuration
String label = configuration.getLabel();
String description = configuration.getDescription();
// create icon descriptor
stack = new PaletteStack(stackID, label, description, DEFAULT_STACK_IMAGE_DESCRIPTOR);
completeEntry(configuration, stack); // seems to be not useful, as the constructor has all informations
predefinedEntries.put(stackID, stack);
container.add(stack);
}
// generate the nodes of the stack
for(LeafConfiguration leafConfiguration : configuration.getOwnedConfigurations()) {
if(leafConfiguration instanceof SeparatorConfiguration) {
generateSeparator(stack, (SeparatorConfiguration)leafConfiguration, predefinedEntries);
} else if(leafConfiguration instanceof ToolConfiguration) {
generateTool(stack, (ToolConfiguration)leafConfiguration, predefinedEntries);
}
}
return stack;
}
/**
* Generates a {@link PaletteSeparator}, adds it to a container and returns it
* @param container the container where to add the created separator
* @param leafConfiguration the configuration of the element to create
* @param predefinedEntries the predefined entries (unused here)
* @return the created separator
*/
protected PaletteSeparator generateSeparator(PaletteContainer container, SeparatorConfiguration leafConfiguration, Map predefinedEntries) {
PaletteSeparator separator = new PaletteSeparator(leafConfiguration.getId()) ;
container.add(separator);
return separator;
}
/**
* Retrieve an existing drawer from the current root node
*
* @param predefinedEntries
* the currently existing palette entries
* @param id
* the id of the drawer to retrieve
* @param entryClass
* the type of element to retrieve
* @return the drawer found or <code>null</code> if no drawer was retrieved
*/
@SuppressWarnings({ "unchecked", "rawtypes" })
protected <T extends PaletteEntry> T retrieveExistingEntry(Map predefinedEntries, String id, Class<T> elementClass) {
Object value = predefinedEntries.get(id);
if(value == null) {
return null;
}
if(elementClass.isAssignableFrom(value.getClass())) {
return (T)value;
}
return null;
}
/**
* {@inheritDoc}
*/
public void setContributions(IConfigurationElement configElement) {
// retrieve the model file
contributorID = configElement.getNamespaceIdentifier();
paletteID = configElement.getAttribute(ID);
String path = configElement.getAttribute(PATH);
if(paletteID == null) {
Activator.log.error("Impossible to find the palette identifier for contribution " + configElement.getValue(), null);
contributions = Collections.emptyList();
return;
}
if(path == null) {
Activator.log.error("Impossible to find the path for contribution " + configElement.getValue(), null);
contributions = Collections.emptyList();
return;
}
Bundle bundle = Platform.getBundle(contributorID);
if(bundle == null) {
Activator.log.error("Impossible to find the bundle with ID: " + contributorID, null);
contributions = Collections.emptyList();
return;
}
try {
contributions = loadConfigurationModel(bundle, path);
} catch (FileNotFoundException e) {
Activator.log.error(e);
contributions = Collections.emptyList();
} catch (IOException e) {
Activator.log.error(e);
contributions = Collections.emptyList();
}
}
/**
* Loads and returns the model, given the bundle and path of the model file.
*
* @param bundle
* the id of the bundle
* @param path
* the path to the file in the bundle
*/
protected List<PaletteConfiguration> loadConfigurationModel(Bundle bundle, String path) throws FileNotFoundException, IOException {
// stores the bundle in which the resource is located.
// warning: in case of fragments, the contributor id can the the plugin, but the file can be localized in the fragment
// In this case, the real bundle used to load the file is the fragment bundle...
// String bundleId = null;
// creates a resource set that will load the configuration
ResourceSet resourceSet = new ResourceSetImpl();
Resource resource = loadResourceFromPreferences(resourceSet);
if(resource == null) {
resource = loadResourceFromPlugin(bundle, path, resourceSet);
}
if(resource == null) {
throw new FileNotFoundException("Loading palette configuration... Impossible to find a resource for path; " + path + " for bundle: " + bundle);
}
resource.load(Collections.emptyMap());
if(resource.getContents().size() > 0) {
return new ArrayList<PaletteConfiguration>(EcoreUtil.<PaletteConfiguration> getObjectsByType(resource.getContents(), PaletteconfigurationPackage.eINSTANCE.getPaletteConfiguration()));
}
return Collections.emptyList();
}
/**
* Returns the id of the bundle that declares this provider, can be null if not yet initialized
*
* @return the id of the bundle, or <code>null</code>
*/
protected String getContributorID() {
return contributorID;
}
/**
* Returns the list of contribution for this provider
*
* @return the list of contribution for this provider
*/
public List<PaletteConfiguration> getContributions() {
return contributions;
}
/**
* Returns the resource used for the configuration.
* <P>
* It checks in the preferences area, then in the plugin area
* </P>
*/
protected Resource loadResourceFromPreferences(ResourceSet resourceSet) {
Resource resource = null;
// look in preference area
String path = PapyrusPalettePreferences.getPaletteRedefinition(paletteID);
if(path != null) {
// read in preferences area of diagram common! Thus, it can be accessed from the common plugin...
IPath resourcePath = org.eclipse.papyrus.uml.diagram.common.Activator.getDefault().getStateLocation().append(path);
URI uri = URI.createFileURI(resourcePath.toOSString());
if(uri != null && uri.isFile()) {
resource = resourceSet.createResource(uri);
if(resource != null) {
return resource;
}
}
}
return resource;
}
/**
* Loads the resource from the plugin area
*
* @return the resource to load.
* @throws FileNotFoundException
*/
protected Resource loadResourceFromPlugin(Bundle bundle, String path, ResourceSet resourceSet) throws FileNotFoundException {
Resource resource = null;
String bundleId = null;
// try to load the resource in the fragments of the bundle, then the bundle itself.
URL entry = null;
// try in fragments...
Bundle[] fragments = Platform.getFragments(bundle);
if(fragments != null) {
for(Bundle fragment : fragments) {
entry = fragment.getEntry(path);
if(entry != null) {
bundleId = fragment.getSymbolicName();
continue;
}
}
}
// look now in the bundle itself.
if(entry == null) {
entry = bundle.getEntry(path);
// no entry was found in the children fragments, look in the bundle itself
if(entry == null) {
throw new FileNotFoundException("Loading palette configuration... Impossible to find a resource for path; " + path + " for bundle: " + bundle);
} else {
bundleId = bundle.getSymbolicName();
}
}
resource = resourceSet.createResource(URI.createPlatformPluginURI("/" + bundleId + "/" + path, true));
return resource;
}
/**
* factory used to create new tools for the extended palette provider. It will find or create a new element type for each tool which has extended
* features.
*/
public class ExtendedPaletteFactory extends PaletteFactory.Adapter {
/**
* {@inheritDoc}
*/
@Override
public Tool createTool(String toolId) {
IElementTypesBasedTool toolEntry = mapToolId2Entries.get(toolId);
if(toolEntry instanceof ExtendedCreationToolEntry) {
return new AspectUnspecifiedTypeCreationTool(((ExtendedCreationToolEntry)toolEntry).getElementTypes());
} else if(toolEntry instanceof ExtendedConnectionToolEntry) {
return new AspectUnspecifiedTypeConnectionTool(((ExtendedConnectionToolEntry)toolEntry).getElementTypes());
}
Activator.log.warn("Impossible to create a tool for the given tool id: " + toolId + ". Tool Entry found was :" + toolEntry);
return null;
}
}
}