blob: 9b0a2636cf030d46d3897a0d437f0fbaaa38f0a6 [file] [log] [blame]
/********************************************************************************
* Copyright (c) 2011 Eike Stepper (Berlin, Germany) and others.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Eike Stepper - initial API and implementation
********************************************************************************/
package org.eclipse.emf.ecp.spi.ui;
import java.io.IOException;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import org.eclipse.core.runtime.Platform;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EClassifier;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EPackage;
import org.eclipse.emf.ecore.EReference;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.util.EcoreUtil;
import org.eclipse.emf.ecore.xmi.XMLResource;
import org.eclipse.emf.ecp.common.spi.ChildrenDescriptorCollector;
import org.eclipse.emf.ecp.core.ECPProject;
import org.eclipse.emf.ecp.core.ECPRepository;
import org.eclipse.emf.ecp.core.util.ECPCheckoutSource;
import org.eclipse.emf.ecp.core.util.ECPContainer;
import org.eclipse.emf.ecp.core.util.ECPProperties;
import org.eclipse.emf.ecp.core.util.ECPUtil;
import org.eclipse.emf.ecp.internal.core.util.Disposable;
import org.eclipse.emf.ecp.internal.core.util.Element;
import org.eclipse.emf.ecp.internal.ui.Activator;
import org.eclipse.emf.ecp.internal.ui.Messages;
import org.eclipse.emf.ecp.internal.ui.composites.PropertiesComposite;
import org.eclipse.emf.ecp.spi.core.InternalProvider;
import org.eclipse.emf.ecp.spi.ui.util.ECPHandlerHelper;
import org.eclipse.emf.edit.command.AddCommand;
import org.eclipse.emf.edit.command.CommandParameter;
import org.eclipse.emf.edit.domain.EditingDomain;
import org.eclipse.emf.edit.provider.ItemProviderAdapter;
import org.eclipse.emf.edit.ui.action.CreateChildAction;
import org.eclipse.emf.edit.ui.provider.ExtendedImageRegistry;
import org.eclipse.jface.action.Action;
import org.eclipse.jface.action.IContributionItem;
import org.eclipse.jface.action.IMenuListener;
import org.eclipse.jface.action.IMenuManager;
import org.eclipse.jface.action.MenuManager;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Text;
/**
* @author Eike Stepper
* @author Eugen Neufeld
* @since 1.1
*/
public class DefaultUIProvider extends Element implements UIProvider {
private static final String PROJECT_OPEN_ICON = "icons/project_open.gif"; //$NON-NLS-1$
private static final String PROJECT_CLOSED_ICON = "icons/project_closed.gif"; //$NON-NLS-1$
private static final String REPOSITORY_ICON = "icons/repository.gif"; //$NON-NLS-1$
private static final String UNKNOWN_PACKAGE_ICON = "icons/EPackageUnknown.gif"; //$NON-NLS-1$
private static final String EPACKAGE_ICON = "icons/EPackage.gif"; //$NON-NLS-1$
private final Disposable disposable = new Disposable(this) {
@Override
protected void doDispose() {
DefaultUIProvider.this.doDispose();
}
};
private String label;
private String description;
/**
* Constructor of a {@link DefaultUIProvider}.
*
* @param name the name for this {@link UIProvider}
*/
public DefaultUIProvider(String name) {
super(name);
label = name;
description = ""; //$NON-NLS-1$
}
/** {@inheritDoc} **/
@Override
public String getType() {
return TYPE;
}
/** {@inheritDoc} **/
@Override
public InternalProvider getProvider() {
return (InternalProvider) ECPUtil.getECPProviderRegistry().getProvider(getName());
}
/** {@inheritDoc} **/
@Override
public final String getLabel() {
return label;
}
/** {@inheritDoc} **/
@Override
public final void setLabel(String label) {
this.label = label;
}
/** {@inheritDoc} **/
@Override
public final String getDescription() {
return description;
}
/** {@inheritDoc} **/
@Override
public final void setDescription(String description) {
this.description = description;
}
/** {@inheritDoc} **/
@Override
public <T> T getAdapter(Object adaptable, Class<T> adapterType) {
return null;
}
/**
* Returns an object which is an instance of the given class associated with this object. Returns <code>null</code>
* if
* no such object can be found.
* <p>
* This implementation of the method declared by <code>IAdaptable</code> passes the request along to the platform's
* adapter manager; roughly <code>Platform.getAdapterManager().getAdapter(this, adapter)</code>. Subclasses may
* override this method (however, if they do so, they should invoke the method on their superclass to ensure that
* the Platform's adapter manager is consulted).
* </p>
*
* @param adapterType
* the class to adapt to
* @return the adapted object or <code>null</code>
* @see org.eclipse.core.runtime.IAdaptable#getAdapter(Class)
*/
@Override
public Object getAdapter(@SuppressWarnings("rawtypes") Class adapterType) {
return Platform.getAdapterManager().getAdapter(this, adapterType);
}
/** {@inheritDoc} **/
@Override
public final boolean isDisposed() {
return disposable.isDisposed();
}
/** {@inheritDoc} **/
@Override
public final void dispose() {
disposable.dispose();
}
/** {@inheritDoc} **/
@Override
public final void addDisposeListener(DisposeListener listener) {
disposable.addDisposeListener(listener);
}
/** {@inheritDoc} **/
@Override
public final void removeDisposeListener(DisposeListener listener) {
disposable.removeDisposeListener(listener);
}
/**
* Subclasses can override.
*/
protected void doDispose() {
// Subclasses can override.
}
/** {@inheritDoc} **/
@Override
public String getText(Object element) {
if (element instanceof Resource) {
final Resource resource = (Resource) element;
return resource.getURI().lastSegment();
}
return UIProvider.EMF_LABEL_PROVIDER.getText(element);
}
/** {@inheritDoc} **/
@Override
public Image getImage(Object element) {
if (element instanceof ECPProject) {
final ECPProject project = (ECPProject) element;
return project.isOpen() ? Activator.getImage(PROJECT_OPEN_ICON) : Activator.getImage(PROJECT_CLOSED_ICON);
}
if (element instanceof ECPRepository) {
return Activator.getImage(REPOSITORY_ICON);
}
return UIProvider.EMF_LABEL_PROVIDER.getImage(element);
}
/** {@inheritDoc} **/
// TODO is this the right place for this implementation?
@Override
public void fillContextMenu(IMenuManager manager, ECPContainer context, Object[] elements) {
if (elements.length == 1) {
final Object element = elements[0];
if (context instanceof ECPProject) {
fillContextMenuForProject(manager, (ECPProject) context, element);
}
}
}
private void fillContextMenuForProject(IMenuManager manager, final ECPProject project, Object element) {
if (element instanceof Resource) {
// TODO: does it make sense to show "all" registered EPackages in context menu of a resource?
// ZH: Do nothing for now. Instead, we show an "Add new Model Element..." in context menu,
// when element is a resource
// final Resource resource = (Resource) element;
// populateNewRoot(resource, manager);
} else if (element instanceof EObject) {
final EditingDomain domain = project.getEditingDomain();
final ChildrenDescriptorCollector childrenDescriptorCollector = new ChildrenDescriptorCollector();
final Collection<?> descriptors = childrenDescriptorCollector.getDescriptors((EObject) element);
if (descriptors != null) {
fillContextMenuWithDescriptors(manager, descriptors, domain, element, project);
}
} else if (element instanceof ItemProviderAdapter
&& ((ItemProviderAdapter) element).getTarget() instanceof EObject) {
final EditingDomain domain = project.getEditingDomain();
final ItemProviderAdapter adapter = (ItemProviderAdapter) element;
element = adapter.getTarget();
final Collection<?> descriptors = adapter.getNewChildDescriptors(element, domain, null);
fillContextMenuWithDescriptors(manager, descriptors, domain, element, project);
}
}
/**
* @param descriptors
*/
private void fillContextMenuWithDescriptors(IMenuManager manager, Collection<?> descriptors,
final EditingDomain domain, Object object, final ECPProject project) {
if (!EObject.class.isInstance(object)) {
return;
}
final EObject eObject = (EObject) object;
for (final Object descriptor : descriptors) {
if (!CommandParameter.class.isInstance(descriptor)) {
continue;
}
final CommandParameter cp = (CommandParameter) descriptor;
if (cp.getEReference() == null) {
continue;
}
if (!cp.getEReference().isMany() && eObject.eIsSet(cp.getEStructuralFeature())) {
continue;
} else if (cp.getEReference().isMany() && cp.getEReference().getUpperBound() != -1
&& cp.getEReference().getUpperBound() <= ((List<?>) eObject.eGet(cp.getEReference())).size()) {
continue;
}
// TODO: Temporal hack to remove all other elements of the view model for 1.1.M1
// final EObject objectToCreate = cp.getEValue();
// if (objectToCreate.eClass().getEPackage().getNsURI().equals("http://org/eclipse/emf/ecp/view/model")) {
// if (!objectToCreate.eClass().getName().equals("Control")) {
// continue;
// }
// }
// if
// (objectToCreate.eClass().getEPackage().getNsURI().equals("http://org/eclipse/emf/ecp/view/rule/model")) {
// continue;
// }
// TODO needed?
// if (!cp.getEReference().isMany() || !cp.getEReference().isContainment()) {
// continue;
// }
manager.add(new CreateChildAction(domain, new StructuredSelection(eObject), descriptor) {
@Override
public void run() {
super.run();
final EReference reference = ((CommandParameter) descriptor).getEReference();
if (!reference.isContainment()) {
domain.getCommandStack().execute(
AddCommand.create(domain, eObject.eContainer(), null, cp.getEValue()));
}
// try {
// TODO what is correct
domain.getCommandStack().execute(AddCommand.create(domain, eObject, reference, cp.getEValue()));
// object.eResource().save(null);
ECPHandlerHelper.openModelElement(cp.getEValue(), project);
// } catch (IOException ex) {
// Activator.log(ex);
// }
}
});
}
}
/** {@inheritDoc} **/
@Override
public Control createAddRepositoryUI(Composite parent, ECPProperties repositoryProperties, Text repositoryNameText,
Text repositoryLabelText, Text repositoryDescriptionText) {
return new PropertiesComposite(parent, true, repositoryProperties);
}
/** {@inheritDoc} **/
@Override
public Control createCheckoutUI(Composite parent, ECPCheckoutSource checkoutSource,
ECPProperties projectProperties) {
return new PropertiesComposite(parent, true, projectProperties);
}
/** {@inheritDoc} **/
@Override
public Control createNewProjectUI(Composite parent, CompositeStateObserver observer,
ECPProperties projectProperties) {
return null;
}
protected boolean populateNewRoot(Resource resource, IMenuManager manager) {
boolean populated = false;
final EPackage.Registry packageRegistry = EPackage.Registry.INSTANCE;
for (final Map.Entry<String, Object> entry : getSortedRegistryEntries(packageRegistry)) {
final IContributionItem item = populateSubMenu(resource, entry.getKey(), entry.getValue(), packageRegistry);
if (item != null) {
manager.add(item);
populated = true;
}
}
return populated;
}
private static IContributionItem populateSubMenu(final Resource resource, String nsURI, Object value,
final EPackage.Registry packageRegistry) {
if (value instanceof EPackage) {
final EPackage ePackage = (EPackage) value;
final ImageDescriptor imageDescriptor = Activator.getImageDescriptor(EPACKAGE_ICON);
final MenuManager submenuManager = new MenuManager(nsURI, imageDescriptor, nsURI);
populateSubMenu(resource, ePackage, submenuManager);
return submenuManager;
}
final ImageDescriptor imageDescriptor = Activator.getImageDescriptor(UNKNOWN_PACKAGE_ICON);
final MenuManager submenuManager = new MenuManager(nsURI, imageDescriptor, nsURI);
submenuManager.setRemoveAllWhenShown(true);
submenuManager.add(new Action(Messages.DefaultUIProvider_Calculating) {
});
submenuManager.addMenuListener(new IMenuListener() {
@Override
public void menuAboutToShow(IMenuManager manager) {
final String nsURI = submenuManager.getMenuText();
final EPackage ePackage = packageRegistry.getEPackage(nsURI);
if (ePackage != null) {
populateSubMenu(resource, ePackage, submenuManager);
} else {
Activator.log(MessageFormat.format(Messages.DefaultUIProvider_CantFindInPackageRegistry, nsURI));
}
}
});
return submenuManager;
}
private static void populateSubMenu(final Resource resource, EPackage ePackage, final MenuManager submenuManager) {
final List<EObject> objects = new ArrayList<EObject>();
for (final EClassifier eClassifier : ePackage.getEClassifiers()) {
if (eClassifier instanceof EClass) {
final EClass eClass = (EClass) eClassifier;
if (!eClass.isAbstract() && !eClass.isInterface()) {
objects.add(EcoreUtil.create(eClass));
}
}
}
if (!objects.isEmpty()) {
Collections.sort(objects, new Comparator<EObject>() {
@Override
public int compare(EObject o1, EObject o2) {
return o1.eClass().getName().compareTo(o2.eClass().getName());
}
});
for (final EObject object : objects) {
final String text = object.eClass().getName();
final Image image = UIProvider.EMF_LABEL_PROVIDER.getImage(object);
final ImageDescriptor imageDescriptor = ExtendedImageRegistry.getInstance().getImageDescriptor(image);
final Action action = new Action(text, imageDescriptor) {
@Override
public void run() {
resource.getContents().add(object);
try {
resource.save(Collections.singletonMap(XMLResource.OPTION_ENCODING, "UTF-8")); //$NON-NLS-1$
} catch (final IOException ex) {
Activator.log(ex);
}
}
};
submenuManager.add(action);
}
}
}
private static Map.Entry<String, Object>[] getSortedRegistryEntries(EPackage.Registry packageRegistry) {
final Set<Map.Entry<String, Object>> entries = packageRegistry.entrySet();
@SuppressWarnings("unchecked")
final Map.Entry<String, Object>[] array = entries.toArray(new Entry[entries.size()]);
Arrays.sort(array, new Comparator<Map.Entry<String, Object>>() {
@Override
public int compare(Map.Entry<String, Object> o1, Map.Entry<String, Object> o2) {
return o1.getKey().compareTo(o2.getKey());
}
});
return array;
}
}