blob: 872a5e84ff395f91672057ed8374facc92dd1ca4 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2010, 2017 BestSolution.at 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:
* Tom Schindl <tom.schindl@bestsolution.at> - initial API and implementation
* Olivier Prouvost <olivier.prouvost@opcoach.com> - added some cache for e4xmi resource management
******************************************************************************/
package org.eclipse.e4.tools.emf.ui.common;
import java.util.ArrayList;
import java.util.List;
import java.util.Map.Entry;
import java.util.SortedSet;
import java.util.TreeSet;
import org.eclipse.core.databinding.Binding;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IResourceChangeEvent;
import org.eclipse.core.resources.IResourceChangeListener;
import org.eclipse.core.resources.IResourceDelta;
import org.eclipse.core.resources.IWorkspaceRoot;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.IConfigurationElement;
import org.eclipse.core.runtime.IExtension;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.e4.tools.emf.ui.common.IEditorFeature.FeatureClass;
import org.eclipse.e4.ui.model.application.MApplicationElement;
import org.eclipse.e4.ui.model.application.impl.ApplicationPackageImpl;
import org.eclipse.e4.ui.model.application.ui.MElementContainer;
import org.eclipse.e4.ui.model.application.ui.MUIElement;
import org.eclipse.e4.ui.model.application.ui.impl.UiPackageImpl;
import org.eclipse.e4.ui.model.fragment.impl.FragmentPackageImpl;
import org.eclipse.emf.common.command.Command;
import org.eclipse.emf.common.util.TreeIterator;
import org.eclipse.emf.common.util.URI;
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.EStructuralFeature;
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.edit.command.MoveCommand;
import org.eclipse.emf.edit.domain.EditingDomain;
import org.eclipse.jface.fieldassist.ControlDecoration;
import org.eclipse.jface.fieldassist.FieldDecoration;
import org.eclipse.jface.fieldassist.FieldDecorationRegistry;
import org.eclipse.pde.internal.core.PDEExtensionRegistry;
import org.eclipse.swt.SWT;
import org.eclipse.swt.widgets.Control;
public class Util {
private static final String APP_E4XMI_DEFAULT = "Application.e4xmi"; //$NON-NLS-1$
public static final boolean isNullOrEmpty(String element) {
return element == null || element.trim().length() == 0;
}
public static final boolean isImport(EObject object) {
return object.eContainingFeature() == FragmentPackageImpl.Literals.MODEL_FRAGMENTS__IMPORTS;
}
public static final void addClasses(EPackage ePackage, List<FeatureClass> list) {
for (final EClassifier c : ePackage.getEClassifiers()) {
if (c instanceof EClass) {
final EClass eclass = (EClass) c;
if (eclass != ApplicationPackageImpl.Literals.APPLICATION && !eclass.isAbstract()
&& !eclass.isInterface()
&& eclass.getEAllSuperTypes().contains(ApplicationPackageImpl.Literals.APPLICATION_ELEMENT)) {
list.add(new FeatureClass(eclass.getName(), eclass));
}
}
}
for (final EPackage eSubPackage : ePackage.getESubpackages()) {
addClasses(eSubPackage, list);
}
}
// TODO In future support different name formats something like
// ${project}.${classname}.${counter}
public static final String getDefaultElementId(Resource resource, MApplicationElement element, IProject project) {
try {
final EObject o = (EObject) element;
final String className = o.eClass().getName();
final String projectName = project.getName();
final String prefix = (projectName + "." + className).toLowerCase(); //$NON-NLS-1$
final TreeIterator<EObject> it = resource.getAllContents();
final SortedSet<Integer> numbers = new TreeSet<>();
while (it.hasNext()) {
final EObject tmp = it.next();
if (tmp instanceof MApplicationElement) {
final String elementId = ((MApplicationElement) tmp).getElementId();
if (elementId != null && elementId.length() > prefix.length() && elementId.startsWith(prefix)) {
final String suffix = elementId.substring(prefix.length());
if (suffix.startsWith(".") && suffix.length() > 1) { //$NON-NLS-1$
try {
numbers.add(Integer.parseInt(suffix.substring(1)));
} catch (final Exception e) {
// TODO: handle exception
}
}
}
}
}
int lastNumber = -1;
for (final Integer number : numbers) {
if (lastNumber + 1 != number) {
break;
}
lastNumber = number;
}
return (prefix + "." + ++lastNumber).toLowerCase(); //$NON-NLS-1$
} catch (final Exception e) {
// TODO: handle exception
e.printStackTrace();
}
return null;
}
public static List<InternalPackage> loadPackages() {
final List<InternalPackage> packs = new ArrayList<>();
for (final Entry<String, Object> regEntry : EPackage.Registry.INSTANCE.entrySet()) {
if (regEntry.getValue() instanceof EPackage) {
final EPackage ePackage = (EPackage) regEntry.getValue();
final InternalPackage iePackage = new InternalPackage(ePackage);
boolean found = false;
for (final EClassifier cl : ePackage.getEClassifiers()) {
if (cl instanceof EClass) {
final EClass eClass = (EClass) cl;
if (eClass.getEAllSuperTypes().contains(ApplicationPackageImpl.Literals.APPLICATION_ELEMENT)) {
if (!eClass.isInterface() && !eClass.isAbstract()) {
found = true;
final InternalClass ieClass = new InternalClass(iePackage, eClass);
iePackage.classes.add(ieClass);
for (final EReference f : eClass.getEAllReferences()) {
ieClass.features.add(new InternalFeature(ieClass, f));
}
}
}
}
}
if (found) {
packs.add(iePackage);
}
}
}
return packs;
}
public static boolean moveElementByIndex(EditingDomain editingDomain, MUIElement element, boolean liveModel,
int index, EStructuralFeature feature) {
if (liveModel) {
final EObject container = ((EObject) element).eContainer();
@SuppressWarnings("unchecked")
final List<Object> l = (List<Object>) container.eGet(feature);
l.remove(element);
if (index >= 0) {
l.add(index, element);
} else {
l.add(element);
}
return true;
}
final EObject container = ((EObject) element).eContainer();
final Command cmd = MoveCommand.create(editingDomain, container, feature, element, index);
if (cmd.canExecute()) {
editingDomain.getCommandStack().execute(cmd);
return true;
}
return false;
}
public static boolean moveElementByIndex(EditingDomain editingDomain, MUIElement element, boolean liveModel,
int index) {
if (liveModel) {
final MElementContainer<MUIElement> container = element.getParent();
container.getChildren().remove(element);
if (index >= 0) {
container.getChildren().add(index, element);
} else {
container.getChildren().add(element);
}
container.setSelectedElement(element);
return true;
}
final MElementContainer<MUIElement> container = element.getParent();
final Command cmd = MoveCommand.create(editingDomain, container,
UiPackageImpl.Literals.ELEMENT_CONTAINER__CHILDREN, element, index);
if (cmd.canExecute()) {
editingDomain.getCommandStack().execute(cmd);
return true;
}
return false;
}
/**
* The set of resources containing model element. Updated when one changes
* in the workspace
*/
private static ResourceSet modelResourceSet;
// The lis
private static boolean e4ModelResourceListenerRegistered = false;
/**
* This method searches for fragments or application model elements
* resources. It is updated when the workspace changes.. else it returns the
* cached values.
*/
public static ResourceSet getModelElementResources() {
// Return previous computed result while workspace did not change...
if (modelResourceSet != null) {
return modelResourceSet;
}
registerE4XmiListener(); // Done only once.
modelResourceSet = new ResourceSetImpl();
final PDEExtensionRegistry reg = new PDEExtensionRegistry();
IExtension[] extensions = reg.findExtensions("org.eclipse.e4.workbench.model", true); //$NON-NLS-1$
final IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot();
for (final IExtension ext : extensions) {
for (final IConfigurationElement el : ext.getConfigurationElements()) {
if (el.getName().equals("fragment")) { //$NON-NLS-1$
URI uri;
// System.err.println("Model-Ext: Checking: " +
// ext.getContributor().getName());
final IProject p = root.getProject(ext.getContributor().getName());
if (p.exists() && p.isOpen()) {
uri = URI.createPlatformResourceURI(
ext.getContributor().getName() + "/" + el.getAttribute("uri"), true); //$NON-NLS-1$ //$NON-NLS-2$
} else {
uri = URI.createURI("platform:/plugin/" + ext.getContributor().getName() + "/" //$NON-NLS-1$ //$NON-NLS-2$
+ el.getAttribute("uri")); //$NON-NLS-1$
}
// System.err.println(uri);
try {
modelResourceSet.getResource(uri, true);
} catch (final Exception e) {
e.printStackTrace();
// System.err.println("=============> Failing");
}
}
}
}
extensions = reg.findExtensions("org.eclipse.core.runtime.products", true); //$NON-NLS-1$
for (final IExtension ext : extensions) {
for (final IConfigurationElement el : ext.getConfigurationElements()) {
if (el.getName().equals("product")) { //$NON-NLS-1$
boolean xmiPropertyPresent = false;
for (final IConfigurationElement prop : el.getChildren("property")) { //$NON-NLS-1$
if (prop.getAttribute("name").equals("applicationXMI")) { //$NON-NLS-1$//$NON-NLS-2$
final String v = prop.getAttribute("value"); //$NON-NLS-1$
setUpResourceSet(modelResourceSet, root, v);
xmiPropertyPresent = true;
break;
}
}
if (!xmiPropertyPresent) {
setUpResourceSet(modelResourceSet, root,
ext.getNamespaceIdentifier() + "/" + APP_E4XMI_DEFAULT); //$NON-NLS-1$
break;
}
}
}
}
return modelResourceSet;
}
/**
* A listener to reset the cache of e4Xmi resource for the index research
*
*/
private static void registerE4XmiListener() {
// Register once on the workspace.
// the listener could be optimized to remember of changed resource since
// the last call to getModelElementResources...
if (!e4ModelResourceListenerRegistered) {
ResourcesPlugin.getWorkspace().addResourceChangeListener(new IResourceChangeListener() {
@Override
public void resourceChanged(IResourceChangeEvent event) {
// Nothing to do if resource set not yet used !
if (modelResourceSet == null) {
return;
}
IResourceDelta delta = event.getDelta();
checkDeltaContainsE4xmi(delta);
}
private void checkDeltaContainsE4xmi(IResourceDelta delta) {
if (modelResourceSet == null) {
return;
}
for (IResourceDelta rd : delta.getAffectedChildren()) {
IResource r = rd.getResource();
if (r instanceof IFile)
{
if ("e4xmi".equals(((IFile) r).getFileExtension())) {
modelResourceSet = null;
break;
}
} else {
checkDeltaContainsE4xmi(rd);
}
}
}
});
e4ModelResourceListenerRegistered = true;
}
}
private static void setUpResourceSet(ResourceSet resourceSet, IWorkspaceRoot root, String v) {
final String[] s = v.split("/"); //$NON-NLS-1$
URI uri;
// System.err.println("Product-Ext: Checking: " + v + " => P:" + s[0] +
// "");
final IProject p = root.getProject(s[0]);
if (p.exists() && p.isOpen()) {
uri = URI.createPlatformResourceURI(v, true);
} else {
uri = URI.createURI("platform:/plugin/" + v); //$NON-NLS-1$
}
try {
// prevent some unnecessary calls by checking the uri
if (resourceSet.getURIConverter().exists(uri, null)) {
resourceSet.getResource(uri, true);
}
} catch (final Exception e) {
e.printStackTrace();
}
}
/**
* This method checks if an EClass can be extended using a fragment. ie : it
* must have containment EReference to a model object.
*
* @param c
* @return true if at least one reference type is not a StringStringToMap or
* other no editable type
*/
public static boolean canBeExtendedInAFragment(EClass c) {
boolean result = false;
for (EReference r : c.getEAllReferences()) {
if (referenceIsModelFragmentCompliant(r)) {
result = true;
break;
}
}
return result;
}
/**
* This method checks if an EReference can be considered in a model fragment
* ie : it must be containment EReference to a model object.
*
* @param c
* @return true if the reference is containment and type is not a
* StringStringToMap or other no editable type
*/
public static boolean referenceIsModelFragmentCompliant(EReference r) {
String t = r.getEReferenceType().getName();
return (r.isContainment() && !t.equals("StringToStringMap") && !t.equals("StringToObjectMap"));
}
public static final void addDecoration(Control control, Binding binding) {
final ControlDecoration dec = new ControlDecoration(control, SWT.BOTTOM);
binding.getValidationStatus().addValueChangeListener(event -> {
final IStatus s = (IStatus) event.getObservableValue().getValue();
if (s.isOK()) {
dec.setDescriptionText(null);
dec.setImage(null);
} else {
dec.setDescriptionText(s.getMessage());
String fieldDecorationID = null;
switch (s.getSeverity()) {
case IStatus.INFO:
fieldDecorationID = FieldDecorationRegistry.DEC_INFORMATION;
break;
case IStatus.WARNING:
fieldDecorationID = FieldDecorationRegistry.DEC_WARNING;
break;
case IStatus.ERROR:
case IStatus.CANCEL:
fieldDecorationID = FieldDecorationRegistry.DEC_ERROR;
break;
}
final FieldDecoration fieldDecoration = FieldDecorationRegistry.getDefault()
.getFieldDecoration(fieldDecorationID);
dec.setImage(fieldDecoration == null ? null : fieldDecoration.getImage());
}
});
}
public static class InternalPackage {
public final EPackage ePackage;
public List<InternalClass> classes = new ArrayList<>();
public InternalPackage(EPackage ePackage) {
this.ePackage = ePackage;
}
@Override
public String toString() {
return ePackage.toString();
}
public List<EClass> getAllClasses() {
final ArrayList<EClass> rv = new ArrayList<>(classes.size());
for (final InternalClass c : classes) {
rv.add(c.eClass);
}
return rv;
}
}
public static class InternalClass {
public final InternalPackage pack;
public final EClass eClass;
public List<InternalFeature> features = new ArrayList<>();
public InternalClass(InternalPackage pack, EClass eClass) {
this.eClass = eClass;
this.pack = pack;
}
}
public static class InternalFeature {
public final InternalClass clazz;
public final EStructuralFeature feature;
public InternalFeature(InternalClass clazz, EStructuralFeature feature) {
this.clazz = clazz;
this.feature = feature;
}
}
}