| /***************************************************************************** |
| * Copyright (c) 2016, 2021 CEA LIST. |
| * |
| * |
| * All rights reserved. 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: |
| * Ansgar Radermacher ansgar.radermacher@cea.fr |
| * |
| *****************************************************************************/ |
| |
| package org.eclipse.papyrus.designer.transformation.library.transformations; |
| |
| import java.util.StringTokenizer; |
| |
| import org.eclipse.core.resources.IProject; |
| import org.eclipse.core.resources.IResource; |
| import org.eclipse.core.resources.IWorkspaceRoot; |
| import org.eclipse.core.resources.ResourcesPlugin; |
| import org.eclipse.core.runtime.CoreException; |
| import org.eclipse.core.runtime.NullProgressMonitor; |
| import org.eclipse.emf.common.util.EList; |
| import org.eclipse.papyrus.designer.deployment.profile.Deployment.DeploymentPlan; |
| import org.eclipse.papyrus.designer.deployment.profile.Deployment.OperatingSystem; |
| import org.eclipse.papyrus.designer.deployment.profile.Deployment.Target; |
| import org.eclipse.papyrus.designer.deployment.tools.DepUtils; |
| import org.eclipse.papyrus.designer.languages.common.base.HintUtils; |
| import org.eclipse.papyrus.designer.languages.common.extensionpoints.AbstractSettings; |
| import org.eclipse.papyrus.designer.languages.common.extensionpoints.ILangProjectSupport; |
| import org.eclipse.papyrus.designer.languages.common.extensionpoints.LanguageProjectSupport; |
| import org.eclipse.papyrus.designer.transformation.base.UIContext; |
| import org.eclipse.papyrus.designer.transformation.base.utils.CommandSupport; |
| import org.eclipse.papyrus.designer.transformation.base.utils.ProjectManagement; |
| import org.eclipse.papyrus.designer.transformation.base.utils.TransformationException; |
| import org.eclipse.papyrus.designer.transformation.core.m2minterfaces.IM2MTrafoCDP; |
| import org.eclipse.papyrus.designer.transformation.core.m2minterfaces.IM2MTrafoElem; |
| import org.eclipse.papyrus.designer.transformation.core.transformations.ApplyRecursive; |
| import org.eclipse.papyrus.designer.transformation.core.transformations.ExecuteTransformationChain; |
| import org.eclipse.papyrus.designer.transformation.core.transformations.TransformationContext; |
| import org.eclipse.papyrus.designer.transformation.library.Messages; |
| import org.eclipse.papyrus.designer.transformation.profile.Transformation.M2MTrafo; |
| import org.eclipse.papyrus.uml.tools.utils.PackageUtil; |
| import org.eclipse.swt.widgets.Display; |
| import org.eclipse.uml2.uml.Classifier; |
| import org.eclipse.uml2.uml.Element; |
| import org.eclipse.uml2.uml.InstanceSpecification; |
| import org.eclipse.uml2.uml.Package; |
| import org.eclipse.uml2.uml.util.UMLUtil; |
| |
| public class CreateAndConfigureProject implements IM2MTrafoCDP { |
| |
| protected AbstractSettings settings; |
| |
| protected ILangProjectSupport projectSupport; |
| |
| /** |
| * Gather configuration data for a code generation project In particular, a |
| * class might be stereotyped to provide additional information about |
| * required compilation options (in case of C++ include paths, libraries, |
| * ...) |
| */ |
| public class GatherConfigData implements IM2MTrafoElem { |
| |
| @Override |
| public void transformElement(M2MTrafo trafo, Element element) throws TransformationException { |
| if (element instanceof Classifier) { |
| projectSupport.gatherConfigData((Classifier) element, settings); |
| } |
| } |
| } |
| |
| protected String getTargetOS(InstanceSpecification node) { |
| if (node != null) { |
| Target target = UMLUtil.getStereotypeApplication(node, Target.class); |
| if (target == null) { |
| // get information from node referenced by the instance |
| target = UMLUtil.getStereotypeApplication(DepUtils.getClassifier(node), Target.class); |
| } |
| if (target != null) { |
| OperatingSystem os = target.getUsedOS(); |
| if (os != null) { |
| return os.getBase_Class().getName(); |
| } |
| } |
| } |
| // default = return OS from host (development environment) |
| return System.getProperty("os.name"); //$NON-NLS-1$ |
| } |
| |
| /** |
| * Get an existing or create a new project for a given language |
| * |
| * @param projectSupport |
| * project support instance (for a given programming language) |
| * @param projectName |
| * the name of the project to create (or get, if it already |
| * exists) |
| * @param targetLanguage |
| * @param modelRoot |
| * @return the project or null, if no project creation support is available |
| * for the target language |
| * @throws TransformationException |
| */ |
| protected IProject getOrCreateProject(ILangProjectSupport projectSupport, String projectName, String targetLanguage, |
| Package modelRoot) throws TransformationException { |
| IProject genProject = ProjectManagement.getNamedProject(projectName); |
| if ((genProject == null) || !genProject.exists()) { |
| IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot(); |
| IProject[] projectsBeforeCreation = root.getProjects(); |
| genProject = projectSupport.createProject(projectName, modelRoot); |
| try { |
| root.refreshLocal(IResource.DEPTH_INFINITE, new NullProgressMonitor()); |
| IProject[] projectsAfterCreation = root.getProjects(); |
| boolean found = false; |
| for (int i = 0; i < projectsBeforeCreation.length; i++) { |
| if (!projectsAfterCreation[i].getName().equals(projectsBeforeCreation[i].getName())) { |
| genProject = projectsAfterCreation[i]; |
| found = true; |
| break; |
| } |
| } |
| // The generated project is the last in the projects list in the |
| // workspace |
| if ((found == false) && (projectsBeforeCreation.length < projectsAfterCreation.length)) { |
| genProject = projectsAfterCreation[projectsAfterCreation.length - 1]; |
| } |
| |
| } catch (CoreException e) { |
| e.printStackTrace(); |
| } |
| if (genProject == null) { |
| throw new TransformationException(ExecuteTransformationChain.USER_CANCEL); |
| } |
| if (!genProject.exists()) { |
| throw new RuntimeException(String.format( |
| Messages.CreateAndConfigureProject_CANNOT_CREATE_PROJECT, |
| projectName, targetLanguage)); |
| } |
| |
| // project is new, force re-write of settings |
| UIContext.configureProject = true; |
| } |
| return genProject; |
| } |
| |
| /** |
| * Update the project name in the mapping (part of deployment plan). The object is to take a user decision into account |
| * to change the project name during project generation. |
| * @param genProject the generated project |
| * @param model the root model (used to calculate default project name) |
| * @param node the deployment node |
| */ |
| void storeProjectName(final IProject genProject, final String defaultProjectName) { |
| if (genProject != null && TransformationContext.initialDeploymentPlan != null) { |
| final String pName = genProject.getName(); |
| final Runnable storeProject = new Runnable() { |
| |
| @Override |
| public void run() { |
| Package deploymentPlanElement = TransformationContext.initialDeploymentPlan; |
| DeploymentPlan deploymentPlan = UMLUtil.getStereotypeApplication(deploymentPlanElement, |
| DeploymentPlan.class); |
| EList<String> projectMappings = deploymentPlan.getProjectMappings(); |
| String newMapping = defaultProjectName + "=" + pName; //$NON-NLS-1$ |
| for (int j = 0; j < projectMappings.size(); j++) { |
| StringTokenizer str = new StringTokenizer(projectMappings.get(j), "="); //$NON-NLS-1$ |
| String genName = str.nextToken(); |
| if (genName.equals(defaultProjectName)) { |
| projectMappings.remove(j); |
| break; |
| } |
| } |
| projectMappings.add(newMapping); |
| } |
| }; |
| // command must be run in UI thread, otherwise subsequent updates of Papyrus widgets will raise an SWT exception |
| Runnable storeProjectCmd = new Runnable() { |
| |
| @Override |
| public void run() { |
| CommandSupport.exec(TransformationContext.initialDeploymentPlan, Messages.CreateAndConfigureProject_CHANGE_PROJECT_MAPPING, storeProject); |
| } |
| }; |
| Display.getDefault().asyncExec(storeProjectCmd); |
| } |
| } |
| |
| /** |
| * Return the name of a project that is associated with a model that is |
| * deployed on a node (in the context of a deployment plan). The function |
| * takes a user defined project name into account |
| * |
| * @param model |
| * The model that is deployed |
| * @param node |
| * The node onto which the software is deployed |
| * @return The resulting project name |
| */ |
| public String getProjectName(Package model, InstanceSpecification node) { |
| String projectName = node != null ? |
| model.getName() + "_" + node.getName() : //$NON-NLS-1$ |
| model.getName(); |
| |
| projectName += "_" + TransformationContext.current.deploymentPlan.getName(); //$NON-NLS-1$ |
| DeploymentPlan depPlan = UMLUtil.getStereotypeApplication(TransformationContext.current.deploymentPlan, |
| DeploymentPlan.class); |
| if (depPlan != null) { |
| for (String mapping : depPlan.getProjectMappings()) { |
| if (mapping.startsWith(projectName)) { |
| int index = mapping.indexOf("="); //$NON-NLS-1$ |
| if (index != -1) { |
| return mapping.substring(index + 1); |
| } |
| } |
| } |
| } |
| return projectName; |
| } |
| |
| /** |
| * Return the default name of a project deployment on a node (in the context of a deployment plan) |
| * @param model |
| * The model that is deployed |
| * @param node |
| * The node onto which the software is deployed |
| * @return The resulting project name |
| */ |
| public String getDefaultProjectName(Package model, InstanceSpecification node) { |
| String projectName = model.getName() + "_" + node.getName(); //$NON-NLS-1$ |
| projectName += "_" + TransformationContext.current.deploymentPlan.getName(); //$NON-NLS-1$ |
| return projectName; |
| } |
| |
| @Override |
| public void applyTrafo(M2MTrafo trafo, Package deploymentPlan) throws TransformationException { |
| TransformationContext tc = TransformationContext.current; |
| EList<InstanceSpecification> topLevelInstances = DepUtils |
| .getTopLevelInstances(TransformationContext.current.deploymentPlan); |
| // get first language (restricted to single target language, |
| // acceptable?) |
| String targetLanguage = topLevelInstances.size() > 0 ? |
| DepUtils.getTargetLanguage(topLevelInstances.iterator().next()) : |
| HintUtils.getLanguageFromElement(deploymentPlan); |
| String projectName = getProjectName(tc.modelRoot, tc.node); |
| projectSupport = LanguageProjectSupport.getProjectSupport(targetLanguage); |
| |
| IProject genProject = getOrCreateProject(projectSupport, projectName, targetLanguage, tc.deploymentPlan); |
| if (genProject == null) { |
| throw new TransformationException( |
| String.format(Messages.DeployToNodes_CouldNotCreateProject, targetLanguage)); |
| } |
| |
| if (!genProject.getName().equals(projectName)) { |
| // if name has been changed during project creation, the new name should be stored in the project mappings |
| storeProjectName(genProject, getDefaultProjectName(tc.modelRoot, tc.node)); |
| } |
| |
| tc.projectSupport = projectSupport; |
| tc.project = genProject; |
| |
| if (UIContext.configureProject) { |
| GatherConfigData gatherData = new GatherConfigData(); |
| settings = projectSupport.initialConfigurationData(); |
| if (settings != null) { |
| settings.targetOS = getTargetOS(TransformationContext.current.node); |
| } |
| ApplyRecursive ar = new ApplyRecursive(trafo, gatherData); |
| ar.applyRecursive(PackageUtil.getRootPackage(deploymentPlan)); |
| projectSupport.setSettings(TransformationContext.current.project, settings); |
| } |
| } |
| } |