blob: 73187fa8806c9cdc0d19af210f595cb52de1a3d1 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2011, 2018 THALES GLOBAL SERVICES 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:
* Obeo - initial API and implementation
*******************************************************************************/
package org.eclipse.sirius.ui.tools.internal.views.common.modelingproject.manager;
import java.text.MessageFormat;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import org.eclipse.core.resources.IContainer;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IMarker;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IProjectDescription;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IWorkspace;
import org.eclipse.core.resources.IWorkspaceRunnable;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.resources.WorkspaceJob;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.SubProgressMonitor;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.emf.common.command.Command;
import org.eclipse.emf.common.command.CompoundCommand;
import org.eclipse.emf.common.util.URI;
import org.eclipse.sirius.business.api.modelingproject.ModelingProject;
import org.eclipse.sirius.business.api.resource.strategy.ResourceStrategyRegistry;
import org.eclipse.sirius.business.api.session.DefaultLocalSessionCreationOperation;
import org.eclipse.sirius.business.api.session.Session;
import org.eclipse.sirius.business.api.session.SessionListener;
import org.eclipse.sirius.business.api.session.SessionManager;
import org.eclipse.sirius.business.api.session.SessionManagerListener;
import org.eclipse.sirius.business.internal.modelingproject.marker.ModelingMarker;
import org.eclipse.sirius.business.internal.query.ModelingProjectQuery;
import org.eclipse.sirius.ext.base.Option;
import org.eclipse.sirius.tools.api.command.semantic.AddSemanticResourceCommand;
import org.eclipse.sirius.ui.tools.api.project.ModelingProjectManager;
import org.eclipse.sirius.ui.tools.internal.views.common.modelingproject.OpenRepresentationsFileJob;
import org.eclipse.sirius.ui.tools.internal.views.modelexplorer.resourcelistener.ISessionFileLoadingListener;
import org.eclipse.sirius.viewpoint.SiriusPlugin;
import org.eclipse.sirius.viewpoint.provider.Messages;
import org.eclipse.sirius.viewpoint.provider.SiriusEditPlugin;
import com.google.common.base.Predicate;
import com.google.common.base.Predicates;
import com.google.common.collect.Iterators;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
/**
* A manager for modeling projects.
*
* @author <a href="mailto:laurent.redor@obeo.fr">Laurent Redor</a>
*/
public class ModelingProjectManagerImpl implements ModelingProjectManager {
/** The old Viewpoint nature id. */
private static final String VIEWPOINT_MODELING_PROJECT_NATURE_ID = "fr.obeo.dsl.viewpoint.nature.modelingproject"; //$NON-NLS-1$
private final SessionManagerListener sessionManagerListener = new SessionManagerListener.Stub() {
@Override
public void notify(Session updated, int notification) {
if (notification == SessionListener.OPENING) {
// No need to at it again to the sessionFileLoading list because
// we add it during the starting of the load
// ModelingProjectManager.loadAndOpenSession().
} else if (notification == SessionListener.OPENED) {
sessionFileLoading.remove(updated.getSessionResource().getURI());
} else if (notification == SessionListener.CLOSED) {
// make sure that the session is re-openable if CLOSED
// That is necessary because the session may not have opened
// correctly and the SessionListener.OPENED may not have been
// sent.
sessionFileLoading.remove(updated.getSessionResource().getURI());
}
}
};
private Predicate<URI> isAlreadyLoadedPredicate = new Predicate<URI>() {
@Override
public boolean apply(URI representationsFileURI) {
return isAlreadyLoaded(representationsFileURI);
}
};
/**
* Set of representations files that are currently loading. There can be only one representations file in loading at
* same time. However there may be many waiting to be loaded.
*/
private Set<URI> sessionFileLoading = Sets.newHashSet();
/**
* Avoid instantiation.
*/
protected ModelingProjectManagerImpl() {
}
/**
* Default initialization of a {@link ModelingProjectManagerImpl}.
*
* @return a new instance of {@link ModelingProjectManagerImpl}.
*/
public static ModelingProjectManagerImpl init() {
return new ModelingProjectManagerImpl();
}
@Override
public void loadAndOpenRepresentationsFile(final URI representationsFileURI) {
loadAndOpenRepresentationsFiles(Lists.newArrayList(representationsFileURI), false);
}
@Override
public void loadAndOpenRepresentationsFile(final URI representationsFileURI, boolean user) {
loadAndOpenRepresentationsFiles(Lists.newArrayList(representationsFileURI), user);
}
@Override
public void loadAndOpenRepresentationsFiles(final List<URI> representationsFilesURIs) {
loadAndOpenRepresentationsFiles(representationsFilesURIs, false);
}
/**
* Load and open representations files by scheduling a new job.
*
* @param representationsFilesURIs
* The URIs of the representations files to open.
* @param user
* <code>true</code> if this job is a user-initiated job, and <code>false</code> otherwise.
* @throws CoreException
* Only useful in case of <code>alreadyInUserWorkspaceModifyOperation</code> is true.
*/
private void loadAndOpenRepresentationsFiles(final List<URI> representationsFilesURIs, boolean user) {
try {
loadAndOpenRepresentationsFiles(representationsFilesURIs, false, false);
} catch (CoreException e) {
// Nothing do to, it can not happen as
// <code>alreadyInUserWorkspaceModifyOperation</code> is false.
}
}
/**
* Load and open representations files by scheduling a new job or by launching directly the job (when
* <code>alreadyInUserWorkspaceModifyOperation</code> is true).
*
* @param representationsFilesURIs
* The URIs of the representations files to open.
* @param user
* <code>true</code> if this job is a user-initiated job, and <code>false</code> otherwise.
* @param alreadyInUserWorkspaceModifyOperation
* true if the loading and opening of representations files already occurs in a WorkspaceModifyOperation
* launches by user.
* @throws CoreException
* Only useful in case of <code>alreadyInUserWorkspaceModifyOperation</code> is true.
*/
private void loadAndOpenRepresentationsFiles(final List<URI> representationsFilesURIs, boolean user, boolean alreadyInUserWorkspaceModifyOperation) throws CoreException {
// Add the specific sessions listener (if not already added).
SessionManager.INSTANCE.addSessionsListener(sessionManagerListener);
// List only the representations files that are not already loaded or
// that are not currently in loading.
Iterator<URI> representationsFilesURIsToLoadIterator = Iterators.filter(representationsFilesURIs.iterator(),
Predicates.not(Predicates.or(Predicates.in(sessionFileLoading), isAlreadyLoadedPredicate)));
if (!representationsFilesURIsToLoadIterator.hasNext()) {
return;
}
// We add the representationsFilesURIs to the list of representations
// files that are currently loading because the event
// SessionListener.OPENING comes too late (after loading of the
// representations file that can be very long).
List<URI> tempRepresentationsFilesURIs = Lists.newArrayList(representationsFilesURIsToLoadIterator);
sessionFileLoading.addAll(tempRepresentationsFilesURIs);
// Launch the silently job to open the representations files
for (URI representationsFilesURI : tempRepresentationsFilesURIs) {
if (alreadyInUserWorkspaceModifyOperation) {
WorkspaceJob job = new OpenRepresentationsFileJob(representationsFilesURI);
job.setUser(user);
job.setPriority(Job.SHORT);
job.runInWorkspace(new NullProgressMonitor());
} else {
OpenRepresentationsFileJob.scheduleNewWhenPossible(representationsFilesURI, user);
}
}
}
/**
* Check if the representations file is already loaded (known by SessionManager).
*
* @param representationsFileURI
* The URI of the representations file.
* @return true if already loaded, false otherwise
*/
private boolean isAlreadyLoaded(URI representationsFileURI) {
for (Session session : Collections.unmodifiableCollection(SessionManager.INSTANCE.getSessions())) {
if (representationsFileURI.equals(session.getSessionResource().getURI())) {
return true;
}
}
return false;
}
@Override
public void clearCache(URI representationsFileURI) {
sessionFileLoading.remove(representationsFileURI);
}
@Override
public IProject createNewModelingProject(String projectName, boolean createAndOpenBlankRepresentationsFile, IProgressMonitor monitor) throws CoreException {
return createNewModelingProject(projectName, null, createAndOpenBlankRepresentationsFile, monitor);
}
@Override
public IProject createNewModelingProject(final String projectName, final IPath projectLocationPath, final boolean createAndOpenBlankRepresentationsFile, IProgressMonitor monitor)
throws CoreException {
final IWorkspaceRunnable create = new IWorkspaceRunnable() {
@Override
public void run(final IProgressMonitor monitor) throws CoreException {
try {
monitor.beginTask(MessageFormat.format(Messages.ModelingProjectManagerImpl_createModelingProjectTask, projectName), 3);
final IProject project = ResourcesPlugin.getWorkspace().getRoot().getProject(projectName);
if (!project.exists()) {
final IProjectDescription desc = project.getWorkspace().newProjectDescription(projectName);
IPath projectLocationPathTemp = projectLocationPath;
if (projectLocationPath != null && ResourcesPlugin.getWorkspace().getRoot().getLocation().equals(projectLocationPath)) {
projectLocationPathTemp = null;
}
desc.setLocation(projectLocationPathTemp);
String[] natures = { ModelingProject.NATURE_ID };
desc.setNatureIds(natures);
monitor.subTask(Messages.ModelingProjectManagerImpl_createProjectTask);
project.create(desc, new SubProgressMonitor(monitor, 1));
monitor.subTask(Messages.ModelingProjectManagerImpl_openProjectTask);
project.open(new SubProgressMonitor(monitor, 1));
if (createAndOpenBlankRepresentationsFile) {
monitor.subTask(Messages.ModelingProjectManagerImpl_createRepresentationFileTask);
Session newSession = createLocalRepresentationsFile(project, new SubProgressMonitor(monitor, 1));
Set<ISessionFileLoadingListener> sessionFileLoadingListeners = SiriusEditPlugin.getPlugin().getSessionFileLoadingListeners();
for (ISessionFileLoadingListener sessionFileLoadingListener : sessionFileLoadingListeners) {
sessionFileLoadingListener.notifySessionLoadedFromModelingProject(newSession);
}
}
}
if (!project.isOpen()) {
project.open(new SubProgressMonitor(monitor, 1));
}
} finally {
monitor.done();
}
}
};
ResourcesPlugin.getWorkspace().run(create, monitor);
return ResourcesPlugin.getWorkspace().getRoot().getProject(projectName);
}
@Override
public void convertToModelingProject(final IProject project, IProgressMonitor monitor) throws CoreException {
final IWorkspaceRunnable create = new IWorkspaceRunnable() {
@Override
public void run(final IProgressMonitor monitor) throws CoreException {
try {
monitor.beginTask(Messages.ModelingProjectManagerImpl_convertToModelingProjectTask, 1);
doAddModelingNature(project, new SubProgressMonitor(monitor, 1));
} finally {
monitor.done();
}
}
};
if (alreadyIsInWorkspaceModificationOperation()) {
doAddModelingNature(project, monitor);
} else {
IWorkspace workspace = ResourcesPlugin.getWorkspace();
workspace.run(create, ResourcesPlugin.getWorkspace().getRoot(), IWorkspace.AVOID_UPDATE, monitor);
}
}
private boolean alreadyIsInWorkspaceModificationOperation() {
final Job currentJob = Job.getJobManager().currentJob();
return currentJob != null && currentJob.getRule() != null;
}
@Override
public void removeModelingNature(final IProject project, IProgressMonitor monitor) throws CoreException {
final IWorkspaceRunnable create = new IWorkspaceRunnable() {
@Override
public void run(final IProgressMonitor monitor) throws CoreException {
doRemoveModelingNature(project, monitor);
}
};
ResourcesPlugin.getWorkspace().run(create, monitor);
}
@Override
public Session createLocalRepresentationsFile(IProject project, IProgressMonitor monitor) throws CoreException {
URI representationsURI = URI.createPlatformResourceURI(project.getFullPath().append(ModelingProject.DEFAULT_REPRESENTATIONS_FILE_NAME).toString(), true);
/* Create a Session from the session model URI */
org.eclipse.sirius.business.api.session.SessionCreationOperation sessionCreationOperation = new DefaultLocalSessionCreationOperation(representationsURI, monitor);
sessionCreationOperation.execute();
return sessionCreationOperation.getCreatedSession();
}
/**
* Add the modeling nature. Open or create the main aird file. Look for semantic resources to add.<br>
* This method must be called from a WorkspaceModifyOperation with WorkspaceRoot scheduling rule as it modifies the
* nature of the project.
*
* @param project
* the project to convert.
* @param monitor
* a {@link IProgressMonitor} to show progression of Modeling Project nature addition
* @throws CoreException
* if something fails.
*/
protected void doAddModelingNature(IProject project, IProgressMonitor monitor) throws CoreException {
try {
monitor.beginTask(Messages.ModelingProjectManagerImpl_addingModelingNatureTask, 3);
IProjectDescription description = project.getDescription();
String[] natures = description.getNatureIds();
if (description.hasNature(ModelingProjectManagerImpl.VIEWPOINT_MODELING_PROJECT_NATURE_ID)) {
// Replace the old Viewpoint nature
for (int i = 0; i < natures.length; i++) {
if (ModelingProjectManagerImpl.VIEWPOINT_MODELING_PROJECT_NATURE_ID.equals(natures[i])) {
natures[i] = ModelingProject.NATURE_ID;
}
}
description.setNatureIds(natures);
} else {
// Add the nature
String[] newNatures = new String[natures.length + 1];
System.arraycopy(natures, 0, newNatures, 1, natures.length);
newNatures[0] = ModelingProject.NATURE_ID;
description.setNatureIds(newNatures);
}
project.setDescription(description, new SubProgressMonitor(monitor, 1));
// check project
Option<ModelingProject> optionalModelingProject = ModelingProject.asModelingProject(project);
if (optionalModelingProject.some()) {
ModelingProject modelingProject = optionalModelingProject.get();
try {
// See 525466. we set the validity to true in case of DefaultModelingProjectResourceListener that
// has set it to false or the representation file will not be created if it does not exist yet.
Option<URI> mainRepresentationsFileURI;
synchronized (modelingProject) {
modelingProject.setValid(true);
mainRepresentationsFileURI = modelingProject.getMainRepresentationsFileURI(new SubProgressMonitor(monitor, 1), false, true);
}
if (mainRepresentationsFileURI != null && mainRepresentationsFileURI.some()) {
// Open the session.
loadAndOpenRepresentationsFiles(Lists.newArrayList(mainRepresentationsFileURI.get()), true, true);
}
} catch (IllegalArgumentException e) {
// Clean existing marker if exists
try {
project.deleteMarkers(ModelingMarker.MARKER_TYPE, false, IResource.DEPTH_ZERO);
} catch (final CoreException ce) {
SiriusPlugin.getDefault().getLog().log(ce.getStatus());
}
if (e.getCause() != null && ModelingProjectQuery.ZERO_REPRESENTATIONS_FILE_FOUND_IN.equals(e.getCause().getMessage())) {
// 0 files has been found : create a representation
ModelingProjectManager.INSTANCE.createLocalRepresentationsFile(project, new SubProgressMonitor(monitor, 1));
// Project has been marked as invalid but now it has a
// main representation file, force the computation of
// its mainRepresentationFileURI.
modelingProject.getMainRepresentationsFileURI(new SubProgressMonitor(monitor, 1), true, true);
} else {
// Add a marker on this project
try {
final IMarker marker = project.createMarker(ModelingMarker.MARKER_TYPE);
marker.setAttribute(IMarker.SEVERITY, IMarker.SEVERITY_ERROR);
marker.setAttribute(IMarker.MESSAGE, e.getMessage());
} catch (final CoreException ce) {
SiriusPlugin.getDefault().getLog().log(ce.getStatus());
}
if (e.getCause() != null && ModelingProjectQuery.A_MODELING_PROJECT_MUST_CONTAIN_ONLY_ONE.equals(e.getCause().getMessage())) {
// several files have been found : rollback
removeModelingNature(project, new SubProgressMonitor(monitor, 1));
throw new CoreException(new Status(IStatus.ERROR, SiriusEditPlugin.ID, e.getMessage()));
}
}
}
if (modelingProject.getSession() != null) {
// add semantic resources if already existing in the project
addSemanticResources(project, modelingProject.getSession(), new SubProgressMonitor(monitor, 1));
}
}
} finally {
monitor.done();
}
}
/**
* Remove the modeling nature.
*
* @param project
* the project to convert.
* @param monitor
* a {@link IProgressMonitor} to show Modeling Project nature removal progression
* @throws CoreException
* if something fails.
*/
protected void doRemoveModelingNature(IProject project, IProgressMonitor monitor) throws CoreException {
try {
monitor.beginTask(Messages.ModelingProjectManagerImpl_removingModelingNatureTask, IProgressMonitor.UNKNOWN);
IProjectDescription description = project.getDescription();
String[] natures = description.getNatureIds();
for (int i = 0; i < natures.length; ++i) {
if (ModelingProject.NATURE_ID.equals(natures[i])) {
// Remove the nature
String[] newNatures = new String[natures.length - 1];
System.arraycopy(natures, 0, newNatures, 0, i);
System.arraycopy(natures, i + 1, newNatures, i, natures.length - i - 1);
description.setNatureIds(newNatures);
project.setDescription(description, new SubProgressMonitor(monitor, IProgressMonitor.UNKNOWN));
// remove markers corresponding to modeling projects
// Clean existing marker if exists
project.deleteMarkers(ModelingMarker.MARKER_TYPE, false, IResource.DEPTH_ZERO);
break;
}
}
} finally {
monitor.done();
}
}
private void addSemanticResources(IContainer container, Session session, IProgressMonitor monitor) throws CoreException {
try {
monitor.beginTask(Messages.ModelingProjectManagerImpl_semanticResourcesAdditionTask, 1);
Command semanticResourcesAdditionCommand = getSemanticResourcesAdditionCommand(container, session, monitor);
session.getTransactionalEditingDomain().getCommandStack().execute(semanticResourcesAdditionCommand);
} finally {
monitor.done();
}
}
private Command getSemanticResourcesAdditionCommand(IContainer container, Session session, IProgressMonitor monitor) throws CoreException {
CompoundCommand cc = new CompoundCommand();
if (container != null) {
for (IResource resource : container.members()) {
if (resource instanceof IFile) {
URI uri = URI.createPlatformResourceURI(resource.getFullPath().toOSString(), true);
if (ResourceStrategyRegistry.getInstance().isPotentialSemanticResource(uri) && ResourceStrategyRegistry.getInstance().isLoadableModel(uri, session)) {
AddSemanticResourceCommand cmd = new AddSemanticResourceCommand(session, uri, new SubProgressMonitor(monitor, 1));
cc.append(cmd);
}
} else if (resource instanceof IContainer) {
Command subCc = getSemanticResourcesAdditionCommand((IContainer) resource, session, monitor);
if (subCc.canExecute()) {
cc.append(subCc);
}
}
}
}
return cc;
}
}