blob: a477596409bae7fa8f5d48b21a0cb48ae3493b44 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2018, 2020 Obeo.
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Obeo - initial API and implementation
*******************************************************************************/
package org.eclipse.sirius.server.backend.internal.services.project;
import static org.eclipse.sirius.server.api.SiriusServerResponse.STATUS_INTERNAL_SERVER_ERROR;
import static org.eclipse.sirius.server.api.SiriusServerResponse.STATUS_NOT_FOUND;
import static org.eclipse.sirius.server.api.SiriusServerResponse.STATUS_OK;
import com.google.gson.Gson;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.Reader;
import java.nio.file.Files;
import java.text.DecimalFormat;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;
import javax.servlet.http.HttpServletRequest;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IProjectDescription;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.Status;
import org.eclipse.emf.common.util.BasicEList;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.sirius.business.api.dialect.DialectManager;
import org.eclipse.sirius.business.api.modelingproject.ModelingProject;
import org.eclipse.sirius.business.api.session.Session;
import org.eclipse.sirius.business.api.session.danalysis.DAnalysisSession;
import org.eclipse.sirius.common.interpreter.api.IEvaluationResult;
import org.eclipse.sirius.diagram.description.DiagramDescription;
import org.eclipse.sirius.server.api.ISiriusServerService;
import org.eclipse.sirius.server.api.SiriusServerPath;
import org.eclipse.sirius.server.api.SiriusServerResponse;
import org.eclipse.sirius.server.backend.internal.SiriusServerBackendPlugin;
import org.eclipse.sirius.server.backend.internal.expressions.SiriusBackendInterpreter;
import org.eclipse.sirius.server.backend.internal.services.workflow.WorkflowHelper;
import org.eclipse.sirius.server.backend.internal.utils.SiriusServerUtils;
import org.eclipse.sirius.table.metamodel.table.description.TableDescription;
import org.eclipse.sirius.tree.description.TreeDescription;
import org.eclipse.sirius.viewpoint.DAnalysis;
import org.eclipse.sirius.viewpoint.DRepresentationDescriptor;
import org.eclipse.sirius.viewpoint.description.RepresentationDescription;
import org.eclipse.sirius.viewpoint.description.Viewpoint;
import org.eclipse.sirius.workflow.ActivityDescription;
import org.eclipse.sirius.workflow.PageDescription;
import org.eclipse.sirius.workflow.SectionDescription;
/**
* Service used to manipulate a specific project.
*
* @author sbegaudeau
*/
@SiriusServerPath("/projects/{projectName}")
public class SiriusServerProjectService implements ISiriusServerService {
/**
* The name of the self variable.
*/
private static final String SELF = "self"; //$NON-NLS-1$
/**
* The name of the variable used to capture the name of the project.
*/
private static final Object PROJECT_NAME = "projectName"; //$NON-NLS-1$
@Override
public SiriusServerResponse doGet(HttpServletRequest request, Map<String, String> variables, String remainingPart) {
Optional<String> optionalProjectName = Optional.ofNullable(variables.get(PROJECT_NAME));
Optional<ModelingProject> optionalModelingProject = optionalProjectName.flatMap(this::findModelingProjectByName);
Optional<SiriusServerProjectDto> optionalProject = optionalModelingProject.map(this::getProjectFromModelingProject);
return optionalProject.map(project -> new SiriusServerResponse(STATUS_OK, project)).orElseGet(() -> new SiriusServerResponse(STATUS_NOT_FOUND));
}
/**
* Finds the modeling project with the given name.
*
* @param projectName
* The name of the project
* @return An optional with the modeling project or an empty optional if it
* could not be found
*/
private Optional<ModelingProject> findModelingProjectByName(String projectName) {
Optional<IProject> optionalProject = Optional.ofNullable(ResourcesPlugin.getWorkspace().getRoot().getProject(projectName));
// @formatter:off
return optionalProject.filter(ModelingProject::hasModelingProjectNature)
.filter(IProject::isOpen)
.map(iProject -> ModelingProject.asModelingProject(iProject).get()); // FIXME Sirius Optional removal!
// @formatter:on
}
/**
* Converts the given modeling project into a project to be returned by the
* service.
*
* @param modelingProject
* The modeling project
* @return The project to be returned by the service
*/
private SiriusServerProjectDto getProjectFromModelingProject(ModelingProject modelingProject) {
Session session = SiriusServerUtils.getSession(modelingProject);
String projectName = modelingProject.getProject().getName();
String description = SiriusServerUtils.getProjectDescription(modelingProject.getProject());
List<AbstractSiriusServerRepresentationDto> representations = this.getRepresentations(session);
List<SiriusServerSemanticResourceDto> semanticResources = this.getSemanticResources(modelingProject.getProject(), session);
List<SiriusServerPageDto> pages = this.getPages(modelingProject, session);
List<SiriusServerSectionDto> currentPageSections = this.getFirstPageSections(session);
return new SiriusServerProjectDto(projectName, description, representations, semanticResources, pages, currentPageSections);
}
/**
* Returns the list of workflow page from the given session.
*
* @param modelingProject
* The modeling project
* @param session
* The session
* @return The list of workflow page from the given session
*/
private List<SiriusServerPageDto> getPages(ModelingProject modelingProject, Session session) {
return WorkflowHelper.on(session).getPageDescriptions().map(page -> {
DAnalysis self = ((DAnalysisSession) session).allAnalyses().stream().findFirst().orElse(null);
Map<String, Object> variables = new HashMap<>();
variables.put(SELF, self);
IEvaluationResult result = new SiriusBackendInterpreter(session).evaluateExpression(variables, page.getTitleExpression());
String identifier = page.getName();
String name = result.asString();
return new SiriusServerPageDto(identifier, name);
}).collect(Collectors.toList());
}
/**
* Returns the list of the sections of the current page.
*
* @param session
* The session
* @return The list of the sections of the current page
*/
private List<SiriusServerSectionDto> getFirstPageSections(Session session) {
Optional<PageDescription> optionalPageDescription = WorkflowHelper.on(session).getPageDescriptions().findFirst();
List<SectionDescription> sectionDescriptions = optionalPageDescription.map(PageDescription::getSections).orElseGet(BasicEList::new);
// @formatter:off
return sectionDescriptions.stream()
.map(sectionDescription -> this.convertSection(session, sectionDescription))
.collect(Collectors.toList());
// @formatter:on
}
/**
* Converts the given {@link SectionDescription}.
*
* @param session
* The Sirius session
* @param sectionDescription
* The section description
* @return The section DTO
*/
private SiriusServerSectionDto convertSection(Session session, SectionDescription sectionDescription) {
String sectionIdentifier = sectionDescription.getName();
DAnalysis self = ((DAnalysisSession) session).allAnalyses().stream().findFirst().orElse(null);
Map<String, Object> variables = new HashMap<>();
variables.put(SELF, self);
IEvaluationResult result = new SiriusBackendInterpreter(session).evaluateExpression(variables, sectionDescription.getTitleExpression());
String sectionName = result.asString();
// @formatter:off
List<SiriusServerActivityDto> activities = sectionDescription.getActivities().stream()
.map(activityDescription -> this.convertActivity(session, activityDescription))
.collect(Collectors.toList());
// @formatter:on
return new SiriusServerSectionDto(sectionIdentifier, sectionName, activities);
}
/**
* Converts the given {@link ActivityDescription}.
*
* @param session
* The Sirius session
* @param activityDescription
* The activity description
* @return The activity DTO
*/
private SiriusServerActivityDto convertActivity(Session session, ActivityDescription activityDescription) {
String activityIdentifier = activityDescription.getName();
DAnalysis self = ((DAnalysisSession) session).allAnalyses().stream().findFirst().orElse(null);
Map<String, Object> variables = new HashMap<>();
variables.put(SELF, self);
IEvaluationResult result = new SiriusBackendInterpreter(session).evaluateExpression(variables, activityDescription.getLabelExpression());
String activityName = result.asString();
return new SiriusServerActivityDto(activityIdentifier, activityName);
}
/**
* Returns the list of representations from the given session.
*
* @param session
* The session
* @return The list of representations from the given session
*/
private List<AbstractSiriusServerRepresentationDto> getRepresentations(Session session) {
Collection<DRepresentationDescriptor> representationDescriptors = DialectManager.INSTANCE.getAllRepresentationDescriptors(session);
// @formatter:off
return representationDescriptors.stream()
.filter(descriptor -> !descriptor.getDescription().eIsProxy())
.map(this::convertToRepresentation)
.collect(Collectors.toList());
// @formatter:on
}
/**
* Converts the given {@link DRepresentationDescriptor} into an
* {@link AbstractSiriusServerRepresentationDto}.
*
* @param descriptor
* The descriptor
* @return The {@link AbstractSiriusServerRepresentationDto}
*/
private AbstractSiriusServerRepresentationDto convertToRepresentation(DRepresentationDescriptor descriptor) {
String name = descriptor.getName();
RepresentationDescription description = descriptor.getDescription();
String descriptionName = description.getName();
// @formatter:off
String viewpointName = Optional.of(description.eContainer())
.filter(Viewpoint.class::isInstance)
.map(Viewpoint.class::cast)
.map(Viewpoint::getName)
.orElse(""); //$NON-NLS-1$
// @formatter:on
AbstractSiriusServerRepresentationDto representation = null;
if (description instanceof DiagramDescription) {
representation = new SiriusServerDiagramDto(viewpointName, descriptionName, name);
} else if (description instanceof TableDescription) {
representation = new SiriusServerTableDto(viewpointName, descriptionName, name);
} else if (description instanceof TreeDescription) {
representation = new SiriusServerTreeDto(viewpointName, descriptionName, name);
}
return representation;
}
/**
* Returns the list of semantic resources from the given session.
*
* @param session
* The Sirius session
* @return The list of semantic resources from the given session
*/
private List<SiriusServerSemanticResourceDto> getSemanticResources(IProject project, Session session) {
Collection<Resource> semanticResources = session.getSemanticResources();
// @formatter:off
return semanticResources.stream()
.map(Resource::getURI)
.filter(URI::isPlatformResource)
.map(uri -> {
String platformString = uri.toPlatformString(true);
return ResourcesPlugin.getWorkspace().getRoot().getFile(new Path(platformString));
})
.filter(iFile -> iFile.getProject().equals(project))
.map(this::convertToSemanticResource)
.collect(Collectors.toList());
// @formatter:on
}
/**
* Converts the given IFile into a {@link SiriusServerSemanticResourceDto}.
*
* @param iFile
* The semantic resource
* @return A {@link SiriusServerSemanticResourceDto}.
*/
private SiriusServerSemanticResourceDto convertToSemanticResource(IFile iFile) {
String name = iFile.getName();
String path = iFile.getProjectRelativePath().toString();
long size = 0;
File file = iFile.getLocation().toFile();
try {
size = Files.size(file.toPath());
} catch (IOException exception) {
IStatus status = new Status(IStatus.ERROR, SiriusServerBackendPlugin.PLUGIN_ID, exception.getMessage(), exception);
SiriusServerBackendPlugin.getPlugin().log(status);
}
String sizeLabel = this.getSizeLabel(size);
return new SiriusServerSemanticResourceDto(path, name, sizeLabel);
}
/**
* Returns a label displaying the given size in KB or MB.
*
* @param size
* The size
* @return The label
*/
private String getSizeLabel(long size) {
DecimalFormat decimalFormat = new DecimalFormat("0.00"); //$NON-NLS-1$
double kb = 1024d;
double mb = kb * 1024d;
if (size > mb) {
return decimalFormat.format(size / mb) + "MB"; //$NON-NLS-1$
}
return decimalFormat.format(size / kb) + "KB"; //$NON-NLS-1$
}
@Override
public SiriusServerResponse doPut(HttpServletRequest request, Map<String, String> variables, String remainingPart) {
SiriusServerResponse response = null;
try {
Reader reader = new InputStreamReader(request.getInputStream(), SiriusServerUtils.UTF_8);
SiriusServerUpdateProjectDescriptionDto updateProjectDescription = new Gson().fromJson(reader, SiriusServerUpdateProjectDescriptionDto.class);
String projectName = variables.get(PROJECT_NAME);
IProject project = ResourcesPlugin.getWorkspace().getRoot().getProject(projectName);
if (project.exists()) {
IProjectDescription description = project.getDescription();
description.setComment(updateProjectDescription.getDescription());
project.setDescription(description, new NullProgressMonitor());
response = new SiriusServerResponse(STATUS_OK, new SiriusServerProjectDescriptionUpdatedDto(description.getComment()));
} else {
response = new SiriusServerResponse(STATUS_NOT_FOUND);
}
} catch (@SuppressWarnings("unused") IOException | CoreException exception) {
// We don't want to send back the message of the exception
response = new SiriusServerResponse(STATUS_INTERNAL_SERVER_ERROR);
}
return response;
}
}