| /** |
| ******************************************************************************** |
| * 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"); |
| } |
| |
| } |