blob: 9008b88b232110bfc7982e65703ee377a5787e5f [file] [log] [blame]
/**
********************************************************************************
* Copyright (c) 2021 Robert Bosch GmbH and others.
*
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Robert Bosch GmbH - initial API and implementation
********************************************************************************
*/
package org.eclipse.app4mc.amalthea.model.editor.container;
import java.io.IOException;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import org.eclipse.app4mc.amalthea.model.editor.util.AmaltheaEditorUtil;
import org.eclipse.app4mc.amalthea.model.emf.AmaltheaResource;
import org.eclipse.app4mc.amalthea.model.emf.AmaltheaResourceSetImpl;
import org.eclipse.app4mc.amalthea.model.impl.SchedulingParameterImpl;
import org.eclipse.app4mc.amalthea.model.presentation.ExtendedAmaltheaEditor;
import org.eclipse.app4mc.amalthea.model.provider.ExtendedAmaltheaItemProviderAdapterFactory;
import org.eclipse.core.resources.IContainer;
import org.eclipse.core.resources.IFile;
import org.eclipse.emf.common.command.AbstractCommand;
import org.eclipse.emf.common.command.BasicCommandStack;
import org.eclipse.emf.common.command.Command;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.EObject;
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.util.EcoreUtil;
import org.eclipse.emf.ecore.xmi.XMLResource;
import org.eclipse.emf.edit.command.AddCommand;
import org.eclipse.emf.edit.command.CommandParameter;
import org.eclipse.emf.edit.domain.AdapterFactoryEditingDomain;
import org.eclipse.emf.edit.provider.ComposedAdapterFactory;
import org.eclipse.emf.edit.provider.ReflectiveItemProviderAdapterFactory;
import org.eclipse.emf.edit.provider.resource.ResourceItemProviderAdapterFactory;
import org.eclipse.emf.edit.ui.provider.DiagnosticDecorator;
import org.eclipse.ui.IDecoratorManager;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.part.EditorPart;
public class AmaltheaModelContainer {
private final IContainer iContainer;
private final Set<EditorPart> openEditors = new HashSet<>();
private final Set<IFile> initialModelFiles = new HashSet<>();
private ComposedAdapterFactory adapterFactory;
private AdapterFactoryEditingDomain editingDomain;
public AmaltheaModelContainer(IContainer container) {
iContainer = container;
}
public boolean isInUse() {
return ! openEditors.isEmpty();
}
/**
* Adds an editor to the set of active editors
*
* @param editor
* @return true if the editor set did not already contain the editor
*/
public boolean addEditor(EditorPart editor) {
boolean wasEmpty = openEditors.isEmpty();
boolean added = openEditors.add(editor);
if (wasEmpty && added) {
// get all valid model files (of current container)
initialModelFiles.addAll(AmaltheaEditorUtil.getValidModelFiles(iContainer));
// initialize editing domain and resource set
initializeEditingDomain();
// load files
for (IFile iFile : initialModelFiles) {
// Be sure to encode the path when creating a URI for it,
// i.e., URI.createPlatformResourceURI(iFile.getFullPath().toString(), true).
// Use workspaceRoot.getFile(new Path(uri.toPlatformResourceString(true))) to convert it back to an IFile.
//
// see https://wiki.eclipse.org/EMF/FAQ#How_do_I_map_between_an_EMF_Resource_and_an_Eclipse_IFile.3F
//
final URI uri = URI.createPlatformResourceURI(iFile.getFullPath().toString(), true);
final Resource res = editingDomain.getResourceSet().createResource(uri);
// Compute now if the resource is zipped.
// This avoids unnecessary (and problematic) conversions from URI to file
// because iFile is already available in this context.
if (res instanceof XMLResource) {
((XMLResource) res).setUseZip(AmaltheaEditorUtil.isZipFile(iFile));
}
try {
res.load(null);
} catch (IOException e) {
// ignore
}
}
// resolve proxies
EcoreUtil.resolveAll(editingDomain.getResourceSet());
// disable caches for intrinsic IDs
for (Resource resource : editingDomain.getResourceSet().getResources()) {
if (resource instanceof AmaltheaResource) {
((AmaltheaResource) resource).setIntrinsicIDToEObjectMap(null);
}
}
updateDecorators();
}
return added;
}
/**
* Removes an editor from the set of active editors
*
* @param editor
* @return true if the editor set contained the editor
*/
public boolean removeEditor(EditorPart editor) {
boolean removed = openEditors.remove(editor);
if (removed && openEditors.isEmpty()) {
// reset collection of model files. unload required ?
initialModelFiles.clear();
// reset adapter factory, editing domain and resource set
adapterFactory = null;
editingDomain = null;
updateDecorators();
}
return removed;
}
public Set<IFile> getInitialModelFiles() {
return Collections.unmodifiableSet(initialModelFiles);
}
public Set<EditorPart> getOpenEditors() {
return Collections.unmodifiableSet(openEditors);
}
public void updateDirtyStateOfEditors() {
openEditors.stream()
.filter(ExtendedAmaltheaEditor.class::isInstance)
.map(ExtendedAmaltheaEditor.class::cast)
.forEach(ExtendedAmaltheaEditor::editorDirtyStateChanged);
}
public ComposedAdapterFactory getAdapterFactory() {
return adapterFactory;
}
public AdapterFactoryEditingDomain getEditingDomain() {
return editingDomain;
}
public ResourceSet getResourceSet() {
return (editingDomain == null) ? null : editingDomain.getResourceSet();
}
private void initializeEditingDomain() {
// Create an adapter factory that yields item providers.
//
adapterFactory = new ComposedAdapterFactory(ComposedAdapterFactory.Descriptor.Registry.INSTANCE);
adapterFactory.addAdapterFactory(new ResourceItemProviderAdapterFactory());
adapterFactory.addAdapterFactory(new ExtendedAmaltheaItemProviderAdapterFactory());
adapterFactory.addAdapterFactory(new ReflectiveItemProviderAdapterFactory());
// Create the command stack that will notify this editor as commands are executed.
//
BasicCommandStack commandStack = new BasicCommandStack() {
@Override
public void execute(Command command) {
// Cancel live validation before executing a command that will trigger a new round of validation.
//
if (!(command instanceof AbstractCommand.NonDirtying)) {
DiagnosticDecorator.cancel(editingDomain);
}
super.execute(command);
}
};
// Create the editing domain with a special command stack.
//
AmaltheaResourceSetImpl resourceSet = new AmaltheaResourceSetImpl();
editingDomain = new AdapterFactoryEditingDomain(adapterFactory, commandStack, resourceSet) {
@Override
public Command createCommand(Class<? extends Command> commandClass, CommandParameter commandParameter) {
// fix drag and drop problem (local workaround)
//
// Complete bugfix is expected as result of:
// https://bugs.eclipse.org/bugs/show_bug.cgi?id=577479
if (commandClass == AddCommand.class && commandParameter != null) {
EObject owner = commandParameter.getEOwner();
EStructuralFeature feature = commandParameter.getEStructuralFeature();
Collection<?> collection = commandParameter.getCollection();
int index = commandParameter.getIndex();
if (owner != null && feature == null && collection != null && !collection.isEmpty()) {
// try to set feature
Object content = collection.iterator().next();
if (content instanceof SchedulingParameterImpl) {
// if owner has feature schedulingParameters
feature = owner.eClass().getEAllStructuralFeatures().stream()
.filter(e -> "schedulingParameters".equals(e.getName()))
.findAny().orElse(null);
if (feature != null) {
return super.createCommand(commandClass, new CommandParameter(owner, feature, collection, index));
}
}
}
}
// default
return super.createCommand(commandClass, commandParameter);
}
};
// Add EditingDomainProvider adapter which EMF.Edit needs for retrieving EditingDomain from ResourceSet
resourceSet.eAdapters().add(new AdapterFactoryEditingDomain.EditingDomainProvider(editingDomain));
}
private void updateDecorators() {
IDecoratorManager decoratorMgr = PlatformUI.getWorkbench().getDecoratorManager();
decoratorMgr.update("org.eclipse.app4mc.amalthea.container.decorator");
decoratorMgr.update("org.eclipse.app4mc.amalthea.file.loaded.decorator");
}
}