/****************************************************************************** * Copyright (c) 2002, 2007 IBM Corporation 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: * IBM Corporation - initial API and implementation ****************************************************************************/ package org.eclipse.gmf.runtime.diagram.ui.providers; import java.net.MalformedURLException; import java.net.URL; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.Map; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.FileLocator; import org.eclipse.core.runtime.IConfigurationElement; import org.eclipse.core.runtime.Path; import org.eclipse.core.runtime.Platform; import org.eclipse.gef.Tool; import org.eclipse.gef.palette.PaletteContainer; import org.eclipse.gef.palette.PaletteEntry; import org.eclipse.gef.palette.PaletteRoot; import org.eclipse.gmf.runtime.common.core.service.AbstractProvider; import org.eclipse.gmf.runtime.common.core.service.IOperation; import org.eclipse.gmf.runtime.common.core.service.AbstractProviderConfiguration.ObjectDescriptor; import org.eclipse.gmf.runtime.common.core.util.Log; import org.eclipse.gmf.runtime.common.core.util.Trace; import org.eclipse.gmf.runtime.common.ui.util.ActivityUtil; import org.eclipse.gmf.runtime.diagram.ui.internal.DiagramUIDebugOptions; import org.eclipse.gmf.runtime.diagram.ui.internal.DiagramUIPlugin; import org.eclipse.gmf.runtime.diagram.ui.internal.DiagramUIStatusCodes; import org.eclipse.gmf.runtime.diagram.ui.internal.services.palette.PaletteTemplateEntry; import org.eclipse.gmf.runtime.diagram.ui.internal.services.palette.PaletteToolEntry; import org.eclipse.gmf.runtime.diagram.ui.l10n.DiagramUIMessages; import org.eclipse.gmf.runtime.diagram.ui.providers.internal.DiagramProvidersPlugin; import org.eclipse.gmf.runtime.diagram.ui.providers.internal.DiagramProvidersStatusCodes; 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.PaletteDrawer; import org.eclipse.gmf.runtime.gef.ui.internal.palette.PaletteSeparator; import org.eclipse.gmf.runtime.gef.ui.internal.palette.PaletteStack; import org.eclipse.jface.resource.ImageDescriptor; import org.eclipse.ui.IEditorPart; import org.osgi.framework.Bundle; import com.ibm.icu.util.StringTokenizer; /** * The defaul palette provider. It reads XML palette contributions from the * provider's extension point and contributes them to an editor's palette * based on different contribution criteria * * The provider class should not be subclassed since it does its contribution totally from XML * However, if programatic contribution is required, then the <code>IPaletteProvider</code> * interface should be implemented directly instead * * @author melaasar */ public class DefaultPaletteProvider extends AbstractProvider implements IPaletteProvider { /** constants corresponding to different symbols in the extention schema */ private static final String CONTRIBUTION = "contribution"; //$NON-NLS-1$ private static final String FACTORY_CLASS = "factoryClass"; //$NON-NLS-1$ private static final String ENTRY = "entry"; //$NON-NLS-1$ private static final String KIND = "kind"; //$NON-NLS-1$ private static final String ID = "id"; //$NON-NLS-1$ private static final String PATH = "path"; //$NON-NLS-1$ private static final String LABEL = "label"; //$NON-NLS-1$ private static final String DESCRIPTION = "description"; //$NON-NLS-1$ private static final String SMALL_ICON = "small_icon"; //$NON-NLS-1$ private static final String LARGE_ICON = "large_icon"; //$NON-NLS-1$ // private static final String PERMISSION = "permission"; //$NON-NLS-1$ private static final String EXPAND = "expand"; //$NON-NLS-1$ private static final String FORCE = "force"; //$NON-NLS-1$ private static final String CONTENT = "content"; //$NON-NLS-1$ private static final String DEFINE_ONLY = "defineOnly"; //$NON-NLS-1$ private static final String PREDEFINED_ENTRY = "predefinedEntry"; //$NON-NLS-1$ private static final String REMOVE = "remove"; //$NON-NLS-1$ /** palette entry kind enumeration */ private static final String DRAWER = "drawer"; //$NON-NLS-1$ private static final String STACK = "stack"; //$NON-NLS-1$ private static final String SEPARATOR = "separator"; //$NON-NLS-1$ private static final String TEMPLATE = "template"; //$NON-NLS-1$ private static final String TOOL = "tool"; //$NON-NLS-1$ private static final int ENUM_DRAWER = 0; private static final int ENUM_STACK = 1; private static final int ENUM_SEPARATOR = 2; private static final int ENUM_TEMPLATE = 3; private static final int ENUM_TOOL = 4; /** palette entry permission enumeration */ private static final String NONE = "None"; //$NON-NLS-1$ private static final String HIDEONLY = "HideOnly"; //$NON-NLS-1$ private static final String LIMITED = "limited"; //$NON-NLS-1$ private static final String FULL = "full"; //$NON-NLS-1$ /** * A descriptor for XML-based palette contribution */ private static class ContributionDescriptor { private PaletteFactoryProxy paletteFactory; private List entries = new ArrayList(); /** * Reads XML entries for a contribution * @param configElement */ public ContributionDescriptor(IConfigurationElement configElement) { paletteFactory = new PaletteFactoryProxy(configElement); // read the palette entries IConfigurationElement configChildren[] = configElement.getChildren(ENTRY); for (int i = 0; i < configChildren.length; i++) { entries.add(new EntryDescriptor(configChildren[i])); } configChildren = configElement.getChildren(PREDEFINED_ENTRY); for (int i = 0; i < configChildren.length; i++) { entries.add(new PredefinedEntryDescriptor(configChildren[i])); } } /** * Contributes to the given palette root based on the given editor's * content * * @param content * @param root * @param predefinedEntries * map of predefined palette entries where the key is the * palette entry id and the value is the palette entry */ public void contribute(Object content, PaletteRoot root, Map predefinedEntries, String pluginID) { Iterator iter = entries.iterator(); while (iter.hasNext()) { IEntryDescriptor descriptor = (IEntryDescriptor) iter.next(); if (ActivityUtil.isEnabled(descriptor.getID(), pluginID)) { descriptor.contribute(content, root, paletteFactory, predefinedEntries); } } } } /** * An interface describing the types of palette entries in the schema. * * @author cmahoney */ private static interface IEntryDescriptor { /** * Contributes the palette entry based on the given content, starting * from the given root and into the given path * * @param content * @param root * @param paletteFactory * @param predefinedEntries * map of predefined palette entries where the key is the * palette entry id and the value is the palette entry */ public void contribute(Object content, PaletteRoot root, PaletteFactoryProxy paletteFactory, Map predefinedEntries); /** * Gets the ID of this entry descriptor. * * @return the id */ public String getID(); } /** * A descriptor for an XML-based palette entry */ private static class EntryDescriptor implements IEntryDescriptor { private Integer kind; private String id; private String path; private String label; private String description; private Integer permission; private ImageDescriptor small_icon; private ImageDescriptor large_icon; private boolean noIcon = false; private DrawerExpandHelper expandHelper; private boolean defineOnly; /** * Reads an XML palette entry and its attributes * @param configElement */ public EntryDescriptor(IConfigurationElement configElement) { String kindStr = configElement.getAttribute(KIND); if (DRAWER.equals(kindStr)) kind = new Integer(ENUM_DRAWER); else if (STACK.equals(kindStr)) kind = new Integer(ENUM_STACK); else if (SEPARATOR.equals(kindStr)) kind = new Integer(ENUM_SEPARATOR); else if (TEMPLATE.equals(kindStr)) kind = new Integer(ENUM_TEMPLATE); else if (TOOL.equals(kindStr)) kind = new Integer(ENUM_TOOL); else Log.info(DiagramProvidersPlugin.getInstance(), DiagramProvidersStatusCodes.SERVICE_FAILURE, "No factory class name is provided"); //$NON-NLS-1$ id = configElement.getAttribute(ID); if (id == null) Log.info(DiagramProvidersPlugin.getInstance(), DiagramProvidersStatusCodes.SERVICE_FAILURE, "No factory class name is provided"); //$NON-NLS-1$ defineOnly = Boolean.valueOf( configElement.getAttribute(DEFINE_ONLY)).booleanValue(); path = configElement.getAttribute(PATH); if (path == null && !defineOnly) Log.info(DiagramProvidersPlugin.getInstance(), DiagramProvidersStatusCodes.SERVICE_FAILURE, "Path must be provided when contributing a palette entry"); //$NON-NLS-1$ label = configElement.getAttribute(LABEL); if (label == null) label = DiagramUIMessages.PaletteEntry_DefaultLabel; description = configElement.getAttribute(DESCRIPTION); if (NONE.equals(kindStr)) permission = new Integer(PaletteEntry.PERMISSION_NO_MODIFICATION); if (HIDEONLY.equals(kindStr)) permission = new Integer(PaletteEntry.PERMISSION_HIDE_ONLY); if (LIMITED.equals(kindStr)) permission = new Integer(PaletteEntry.PERMISSION_LIMITED_MODIFICATION); if (FULL.equals(kindStr)) permission = new Integer(PaletteEntry.PERMISSION_FULL_MODIFICATION); String smallIconPath = configElement.getAttribute(SMALL_ICON); if (NONE.equals(smallIconPath)) { noIcon = true; } else { small_icon = findIconImageDescriptor(configElement, smallIconPath); } String largeIconPath = configElement.getAttribute(LARGE_ICON); large_icon = findIconImageDescriptor(configElement, largeIconPath); if (kind.intValue() == ENUM_DRAWER) { IConfigurationElement[] configChildren = configElement.getChildren(EXPAND); if (configChildren.length > 0) expandHelper = new DrawerExpandHelper(configChildren[0]); else expandHelper = new DrawerExpandHelper(Boolean.FALSE); } } /** * Finds the image descriptor that is associated with the icon path. * @param configElement * @param smallIconPath * @return */ private ImageDescriptor findIconImageDescriptor(IConfigurationElement configElement, String iconPath) { String pluginId = configElement.getDeclaringExtension().getNamespaceIdentifier(); Bundle bundle = Platform.getBundle(pluginId); try { if (iconPath != null) { URL fullPathString = FileLocator.find(bundle, new Path(iconPath), null); fullPathString = fullPathString != null ? fullPathString : new URL(iconPath); if (fullPathString != null) { return ImageDescriptor.createFromURL(fullPathString); } } } catch (MalformedURLException e) { Trace.catching(DiagramUIPlugin.getInstance(), DiagramUIDebugOptions.EXCEPTIONS_CATCHING, DefaultPaletteProvider.class, e.getLocalizedMessage(), e); Log.error(DiagramUIPlugin.getInstance(), DiagramUIStatusCodes.RESOURCE_FAILURE, e.getMessage(), e); } return null; } public void contribute( Object content, PaletteRoot root, PaletteFactoryProxy paletteFactory, Map predefinedEntries) { if (kind == null || id == null || label == null) return; PaletteEntry paletteEntry = null; switch (kind.intValue()) { case ENUM_DRAWER : PaletteDrawer drawer = new PaletteDrawer(id, label); if (expandHelper.expand(content)) { drawer.setInitialState( PaletteDrawer.INITIAL_STATE_OPEN); } if (noIcon) { drawer.setShowDefaultIcon(false); } paletteEntry = drawer; break; case ENUM_STACK: paletteEntry = new PaletteStack(id, label, description, small_icon); break; case ENUM_SEPARATOR : paletteEntry = new PaletteSeparator(id); break; case ENUM_TEMPLATE : paletteEntry = new PaletteTemplateEntry(id, label, paletteFactory); break; case ENUM_TOOL : paletteEntry = new PaletteToolEntry(id, label, paletteFactory); break; } if (paletteEntry != null) { paletteEntry.setDescription(description); paletteEntry.setSmallIcon(small_icon); paletteEntry.setLargeIcon(large_icon); if (permission != null) paletteEntry.setUserModificationPermission( permission.intValue()); if (defineOnly) { predefinedEntries.put(id, paletteEntry); } else { appendPaletteEntry(root, predefinedEntries, path, paletteEntry); } } } public String getID() { return id; } } /** * A descriptor for an XML-based predefined palette entry. */ private static class PredefinedEntryDescriptor implements IEntryDescriptor { private String id; private String path; private DrawerExpandHelper expandHelper; private boolean remove; /** * Reads an XML palette entry and its attributes * @param configElement */ public PredefinedEntryDescriptor(IConfigurationElement configElement) { id = configElement.getAttribute(ID); if (id == null) { Log.info(DiagramProvidersPlugin.getInstance(), DiagramProvidersStatusCodes.SERVICE_FAILURE, "No ID provided"); //$NON-NLS-1$ } path = configElement.getAttribute(PATH); IConfigurationElement[] configChildren = configElement .getChildren(EXPAND); if (configChildren.length > 0) expandHelper = new DrawerExpandHelper(configChildren[0]); else expandHelper = new DrawerExpandHelper(Boolean.FALSE); remove = Boolean.valueOf(configElement.getAttribute(REMOVE)) .booleanValue(); } public void contribute( Object content, PaletteRoot root, PaletteFactoryProxy paletteFactory, Map predefinedEntries) { if (id == null) return; // first try to find it in the palette root PaletteEntry paletteEntry = findPaletteEntry(root, id); if (paletteEntry != null) { if (remove) { paletteEntry.getParent().remove(paletteEntry); return; } // Set expand state on drawers. if (paletteEntry instanceof PaletteDrawer && expandHelper.expand(content)) { ((PaletteDrawer) paletteEntry) .setInitialState(PaletteDrawer.INITIAL_STATE_OPEN); } } // now check to see if it has been predefined only if (paletteEntry == null) { paletteEntry = findPredefinedEntry(predefinedEntries, id); } if (paletteEntry != null) { if (path != null) { appendPaletteEntry(root, predefinedEntries, path, paletteEntry); // Set expand state on drawers. if (paletteEntry instanceof PaletteDrawer && expandHelper.expand(content)) { ((PaletteDrawer) paletteEntry) .setInitialState(PaletteDrawer.INITIAL_STATE_OPEN); } } } } public String getID() { return id; } } /** * Searches the predefined entries for a palette entry given the full path * as it was predefined. * * @param predefinedEntries * map of predefined palette entries where the key is the palette * entry id and the value is the palette entry * @param path * the path to the palette entry starting as it was predefined * @return the palette entry if one exists; null otherwise. */ private static PaletteEntry findPredefinedEntry(Map predefinedEntries, String path) { StringTokenizer tokens = new StringTokenizer(path, "/"); //$NON-NLS-1$ PaletteEntry root = (PaletteEntry) predefinedEntries.get(tokens .nextToken()); while (tokens.hasMoreElements()) { if (root instanceof PaletteContainer) root = findChildPaletteEntry((PaletteContainer) root, tokens .nextToken()); else return null; } return root; } /** * Finds a palette container starting from the given root and using the * given path * * @param root * @param aPath * @return the container or <code>null</code> if not found */ private static PaletteEntry findPaletteEntry(PaletteEntry root, String aPath) { StringTokenizer tokens = new StringTokenizer(aPath, "/"); //$NON-NLS-1$ while (tokens.hasMoreElements()) { if (root instanceof PaletteContainer) root = findChildPaletteEntry( (PaletteContainer) root, tokens.nextToken()); else return null; } return root; } /** * Finds a palette entry starting from the given container * and using the given path * @param root * @param path * @return the entry or <code>null</code> if not found */ private static PaletteEntry findChildPaletteEntry( PaletteContainer container, String childId) { Iterator entries = container.getChildren().iterator(); while (entries.hasNext()) { PaletteEntry entry = (PaletteEntry) entries.next(); if (entry.getId().equals(childId)) return entry; } return null; } /** * Appends the given palette entry to the appropriate location in either a * predefined palette entry or the palette root. * * @param root * @param predefinedEntries * map of predefined palette entries where the key is the palette * entry id and the value is the palette entry * @param path * @param paletteEntry */ private static void appendPaletteEntry(PaletteRoot root, Map predefinedEntries, String path, PaletteEntry paletteEntry) { PaletteEntry fEntry = findPaletteEntry(root, path); if (fEntry == null) { fEntry = findPredefinedEntry(predefinedEntries, path); } if (fEntry == null) Log.info(DiagramProvidersPlugin.getInstance(), DiagramProvidersStatusCodes.SERVICE_FAILURE, "Invalid palette entry path"); //$NON-NLS-1$ else if (fEntry instanceof PaletteContainer) ((PaletteContainer) fEntry).add(paletteEntry); else if (fEntry instanceof PaletteSeparator) appendTo((PaletteSeparator) fEntry, paletteEntry); else fEntry.getParent().add( fEntry.getParent().getChildren().indexOf(fEntry) + 1, paletteEntry); } /** * Appends the given entry to the end of the group of the given separator. * * @param separator * @param entry */ private static void appendTo(PaletteSeparator separator, PaletteEntry entry) { List children = separator.getParent().getChildren(); int index = children.indexOf(separator); for (index++; index < children.size(); index++) { if (children.get(index) instanceof PaletteSeparator) break; } separator.getParent().add(index, entry); } /** * A proxy for a palette factory that instantiates the real factory * on demand (when a palette entry is selcted) */ private static class PaletteFactoryProxy extends PaletteFactory.Adapter { private IConfigurationElement configElement; private PaletteFactory factory; public PaletteFactoryProxy(IConfigurationElement configElement) { this.configElement = configElement; } /** * @see org.eclipse.gmf.runtime.diagram.ui.services.palette.PaletteFactory#getTemplate(java.lang.String) */ public Object getTemplate(String templateId) { if (factory == null) { try { Object ext = configElement.createExecutableExtension(FACTORY_CLASS); factory = (PaletteFactory) ext; } catch (CoreException e) { Log.info(DiagramProvidersPlugin.getInstance(), DiagramProvidersStatusCodes.SERVICE_FAILURE, "No factory class name is provided"); //$NON-NLS-1$ } } if (factory != null) { Object template = factory.getTemplate(templateId); if (template == null) Log.info(DiagramProvidersPlugin.getInstance(), DiagramProvidersStatusCodes.SERVICE_FAILURE, "No factory class name is provided"); //$NON-NLS-1$ return template; } return null; } /** * @see org.eclipse.gmf.runtime.diagram.ui.services.palette.PaletteFactory#createTool(java.lang.String) */ public Tool createTool(String toolId) { if (factory == null) { try { Object ext = configElement.createExecutableExtension(FACTORY_CLASS); factory = (PaletteFactory) ext; } catch (CoreException e) { Log.info(DiagramProvidersPlugin.getInstance(), DiagramProvidersStatusCodes.SERVICE_FAILURE, "No factory class name is provided"); //$NON-NLS-1$ } } if (factory != null) { Tool tool = factory.createTool(toolId); if (tool == null) Log.info(DiagramProvidersPlugin.getInstance(), DiagramProvidersStatusCodes.SERVICE_FAILURE, "No factory class name is provided"); //$NON-NLS-1$ return tool; } return null; } } /** * A helper class in expanding palette drawer. It reads * the relavent XML entries for that. */ private static class DrawerExpandHelper { private Boolean force; private ObjectDescriptor content; /** * Initialize the helper with a foced value * @param force */ public DrawerExpandHelper(Boolean force) { this.force = force; } /** * Initialize the helper from the config element in the XML * @param configElement */ public DrawerExpandHelper(IConfigurationElement configElement) { String forceStr = configElement.getAttribute(FORCE); force = forceStr == null ? Boolean.FALSE : Boolean.valueOf(forceStr); IConfigurationElement[] configChildren = configElement.getChildren(CONTENT); if (configChildren.length > 0) content = new ObjectDescriptor(configChildren[0]); } /** * Determines whether to initially expand the palette drawer * @param targetContent * @return */ public boolean expand(Object targetContent) { if (Boolean.TRUE.equals(force)) return true; if (content != null && content.sameAs(targetContent)) return true; return false; } } /** * The list of palette provider XML contributions */ private List contributions = new ArrayList(); /** * The pluginID of the XML contributions */ private String pluginID; /** * * Adds the configuration elements to the * list of palette provider XML contributions * * @param configElement */ public void setContributions(IConfigurationElement configElement) { pluginID = configElement.getContributor().getName(); IConfigurationElement configChildren[] = configElement.getChildren(CONTRIBUTION); for (int i = 0; i < configChildren.length; i++) { contributions.add(new ContributionDescriptor(configChildren[i])); } } /** * @see org.eclipse.gmf.runtime.diagram.ui.services.palette.IPaletteProvider#contributeToPalette(org.eclipse.ui.IEditorPart, java.lang.Object) */ public void contributeToPalette( IEditorPart editor, Object content, PaletteRoot root, Map predefinedEntries) { Iterator iter = contributions.iterator(); while (iter.hasNext()) { ((ContributionDescriptor) iter.next()).contribute(content, root, predefinedEntries, pluginID); } } /* (non-Javadoc) * @see org.eclipse.gmf.runtime.common.core.service.IProvider#provides(org.eclipse.gmf.runtime.common.core.service.IOperation) */ public boolean provides(IOperation operation) { return false; // all logic is done in the service } }