/*****************************************************************************
* 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:
* Camille Letavernier (CEA LIST) camille.letavernier@cea.fr - Initial API and implementation
*****************************************************************************/
package org.eclipse.papyrus.views.properties.creation;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EPackage;
import org.eclipse.emf.ecore.EReference;
import org.eclipse.jface.viewers.ILabelProvider;
import org.eclipse.jface.window.Window;
import org.eclipse.papyrus.infra.emf.utils.EClassNameComparator;
import org.eclipse.papyrus.infra.emf.utils.EMFHelper;
import org.eclipse.papyrus.infra.widgets.providers.IStaticContentProvider;
import org.eclipse.papyrus.views.properties.Activator;
import org.eclipse.papyrus.views.properties.messages.Messages;
import org.eclipse.papyrus.views.properties.providers.CreateInFeatureContentProvider;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.SelectionListener;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.swt.widgets.MenuItem;
/**
* A ReferenceFactory used to instantiate and edit EObjects.
* The specified nsUri and ClassName are used to instantiate the EObject.
* The matching {@link org.eclipse.papyrus.views.properties.contexts.View}s from all
* applied {@link org.eclipse.papyrus.views.properties.contexts.Context}s are used to
* display the right form to edit the EObject.
*
* If no EClass is specified, a list of all concrete subclasses of {@link #type} will be displayed before the instantiation.
*
* @author Camille Letavernier
*/
public class EcorePropertyEditorFactory extends PropertyEditorFactory {
/**
* The (abstract) EClass to instantiate
*/
protected EClass type;
/**
* The (concrete) EClass to instantiate
* Should be a subclass of {@link #type}
*/
protected EClass eClass;
/**
* The Namespace URI of the (concrete) EClass to instantiate
*/
protected String nsUri;
/**
* The name of the (concrete) EClass to instantiate
*/
protected String className;
/**
* The reference in which the object will be set.
*/
protected EReference referenceIn;
/**
* The ContentProvider for browsing potential container EObjects
*/
protected IStaticContentProvider containerContentProvider;
/**
* The ContentProvider for browsing potential containment EReferences.
* The input of this content provider is the object selected through the
* containerContentProvider
*/
protected CreateInFeatureContentProvider referenceContentProvider;
/**
* The LabelProvider for displaying potential container EObjects
*/
protected ILabelProvider containerLabelProvider;
/**
* The LabelProvider for displaying potential containment EReferences
*/
protected ILabelProvider referenceLabelProvider;
/**
* Store information about where each object should be added on validation
*/
protected Map<EObject, CreateIn> createIn = new HashMap<EObject, CreateIn>();
/**
*
* Constructor.
*
* The factory will be able to instantiate the given EClass
*
* @param type
* The type of EClass to instantiate when creating new EObjects.
*/
public EcorePropertyEditorFactory(EReference referenceIn) {
if(referenceIn == null) {
throw new IllegalArgumentException("The referenceIn parameter must be set"); //$NON-NLS-1$
}
this.referenceIn = referenceIn;
this.type = referenceIn.getEReferenceType();
}
/**
* @return the nsUri of the EClass used by this factory to instantiate new EObjects
* @see #getClassName
*/
public String getNsUri() {
return nsUri;
}
/**
* @return the className of the EClass used by this factory to instantiate new EObjects
* @see #getNsUri()
*/
public String getClassName() {
return className;
}
/**
* Sets the nsUri of the EClass used by this factory to instantiate new EObjects
*
* @param nsUri
* @see #getClassName
*/
public void setNsUri(String nsUri) {
this.nsUri = nsUri;
checkEClass();
}
/**
* Sets the className of the EClass used by this factory to instantiate new EObjects
*
* @param className
* @see #getNsUri()
*/
public void setClassName(String className) {
this.className = className;
checkEClass();
}
private void checkEClass() {
if(nsUri != null && className != null) {
EPackage ePackage = EPackage.Registry.INSTANCE.getEPackage(nsUri);
if(ePackage == null) {
Activator.log.warn("Cannot find the EPackage corresponding to URI " + nsUri); //$NON-NLS-1$
}
eClass = (EClass)ePackage.getEClassifier(className);
if(eClass == null) {
Activator.log.warn("Cannot find the EClass " + className + " in the package " + nsUri); //$NON-NLS-1$ //$NON-NLS-2$
}
}
}
/**
* {@inheritDoc}
*/
@Override
public boolean canCreateObject() {
return true;
}
/**
* {@inheritDoc}
*/
@Override
public Object createObject(Control widget) {
Object instance;
if(referenceIn.isContainment()) {
instance = simpleCreateObject(widget);
} else {
instance = createObjectInDifferentContainer(widget);
}
return super.createObject(widget, instance);
}
protected EObject simpleCreateObject(Control widget) {
EClass eClass = chooseEClass(widget);
if(eClass == null) {
return null;
}
EObject instance = eClass.getEPackage().getEFactoryInstance().create(eClass);
return instance;
}
protected EObject createObjectInDifferentContainer(Control widget) {
EObject instance = simpleCreateObject(widget);
if(instance == null) {
return null;
}
containerContentProvider.inputChanged(null, null, instance);
referenceContentProvider.setType(instance.eClass());
CreateInDialog dialog = new CreateInDialog(widget.getShell(), instance);
dialog.setProviders(containerContentProvider, referenceContentProvider, containerLabelProvider, referenceLabelProvider);
dialog.setTitle(getCreationDialogTitle());
int result = dialog.open();
if(result != Window.OK) {
return null;
}
CreateIn createIn = new CreateIn();
createIn.createInObject = dialog.getContainer();
createIn.createInReference = dialog.getContainmentReference();
this.createIn.put(instance, createIn);
return instance;
}
/**
* Gets the EClass to instantiate
* If the {@link #eClass} has been specified, then it is returned.
* Otherwise, displays a list of all valid concrete EClasses that
* are subtypes of {@link #type}, from which the user can choose
* the one to instantiate.
*
* @param widget
* The control used to open a selection list (if more than one EClass
* can be instantiated)
* @return
* The EClass to instantiate
*/
protected EClass chooseEClass(Control widget) {
if(eClass != null) {
return eClass;
}
List<EClass> availableClasses = getAvailableEClasses();
if(availableClasses.isEmpty()) {
return null;
}
if(availableClasses.size() == 1) {
this.className = availableClasses.get(0).getName();
return availableClasses.get(0);
}
final Menu menu = new Menu(widget);
for(EClass eClass : availableClasses) {
final MenuItem item = new MenuItem(menu, SWT.NONE);
item.setText(eClass.getName());
item.setData("eClass", eClass); //$NON-NLS-1$
item.addSelectionListener(new SelectionListener() {
public void widgetSelected(SelectionEvent e) {
EcorePropertyEditorFactory.this.eClass = (EClass)item.getData("eClass"); //$NON-NLS-1$
}
public void widgetDefaultSelected(SelectionEvent e) {
// Nothing
}
});
}
menu.setVisible(true);
//The menu is blocking the thread
Display display = widget.getDisplay();
while(menu.isVisible()) {
try {
if(!display.readAndDispatch()) {
display.sleep();
}
} catch (Throwable ex) {
Activator.log.error(ex);
}
}
if(!display.isDisposed()) {
display.update();
}
EClass eClass = this.eClass;
if(eClass != null) {
className = eClass.getName();
}
this.eClass = null;
return eClass;
}
/**
* @return
* The list of {@link EClass} that can be instantiated.
* This is the list of all concrete subclasses of {@link #type}
*/
protected List<EClass> getAvailableEClasses() {
List<EClass> availableEClasses = EMFHelper.getSubclassesOf(type, true);
Collections.sort(availableEClasses, new EClassNameComparator());
return availableEClasses;
}
/**
* {@inheritDoc}
*/
@Override
public Collection<Object> validateObjects(Collection<Object> objectsToValidate) {
if(!referenceIn.isContainment()) {
for(Object objectToValidate : objectsToValidate) {
//We add the object to the containment reference
//They will be automatically added to the edited reference
//(referenceIn) after this method returns
CreateIn creationInformation = this.createIn.get(objectToValidate);
if(creationInformation != null) {
creationInformation.createInObject.eSet(creationInformation.createInReference, objectToValidate);
} else {
Activator.log.warn("Unknown object : " + objectToValidate);
}
}
}
return objectsToValidate;
}
/**
* {@inheritDoc}
*/
@Override
public String getCreationDialogTitle() {
return Messages.EcorePropertyEditorFactory_CreateANew + className;
}
@Override
public String getEditionDialogTitle(Object objectToEdit) {
if(objectToEdit instanceof EObject) {
return "Edit " + ((EObject)objectToEdit).eClass().getName();
}
return super.getEditionDialogTitle(objectToEdit);
}
/**
* @return
* The EClass that will be instantiated, or null if this hasn't been forced
*/
public EClass getEClass() {
return eClass;
}
protected class CreateIn {
/**
* The (containment) reference in which the object will be created
* May be the same or different from {@link #referenceIn}
*/
public EReference createInReference;
/**
* The (container) EObject in which the object will be created
*/
public EObject createInObject;
}
/**
* Sets the same label provider for both #referenceLabelProvider
* and #containerLabelProvider
*
* @param labelProvider
*/
public void setLabelProvider(ILabelProvider labelProvider) {
setContainerLabelProvider(labelProvider);
setReferenceLabelProvider(labelProvider);
}
public void setReferenceLabelProvider(ILabelProvider labelProvider) {
this.referenceLabelProvider = labelProvider;
}
public void setContainerLabelProvider(ILabelProvider labelProvider) {
this.containerLabelProvider = labelProvider;
}
public void setContainerContentProvider(IStaticContentProvider contentProvider) {
this.containerContentProvider = contentProvider;
}
public void setReferenceContentProvider(CreateInFeatureContentProvider contentProvider) {
this.referenceContentProvider = contentProvider;
}
}