Skip to main content
aboutsummaryrefslogtreecommitdiffstats
blob: 7924ca0e6b6dc12483b0eca2fe583c36d75cb6e3 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
/*****************************************************************************
 * Copyright (c) 2017 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:
 *  Pauline DEVILLE (CEA LIST) pauline.deville@cea.fr - Initial API and implementation
 *
 *****************************************************************************/
package org.eclipse.papyrus.toolsmiths.profilemigration;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.eclipse.emf.common.notify.Notifier;
import org.eclipse.emf.common.util.TreeIterator;
import org.eclipse.emf.common.util.URI;
import org.eclipse.emf.compare.Comparison;
import org.eclipse.emf.compare.EMFCompare;
import org.eclipse.emf.compare.scope.DefaultComparisonScope;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.resource.Resource;
import org.eclipse.emf.ecore.resource.ResourceSet;
import org.eclipse.emf.ecore.resource.impl.ResourceSetImpl;
import org.eclipse.emf.ecore.xmi.XMIResource;
import org.eclipse.emf.edit.tree.TreeNode;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.osgi.util.NLS;
import org.eclipse.papyrus.toolsmiths.profilemigration.factory.IMigratorFactory;
import org.eclipse.papyrus.toolsmiths.profilemigration.factory.MigratorFactory;
import org.eclipse.papyrus.toolsmiths.profilemigration.internal.data.structure.StereotypeApplicationRegistry;
import org.eclipse.papyrus.toolsmiths.profilemigration.internal.extensionPoint.AtomicMigratorRegistry;
import org.eclipse.papyrus.toolsmiths.profilemigration.internal.utils.AtomicMigratorComparator;
import org.eclipse.papyrus.toolsmiths.profilemigration.internal.utils.DifferenceTreeBuilder;
import org.eclipse.papyrus.toolsmiths.profilemigration.migrators.ICompositeMigrator;
import org.eclipse.papyrus.toolsmiths.profilemigration.migrators.atomic.IAtomicMigrator;
import org.eclipse.papyrus.toolsmiths.profilemigration.ui.preferences.ProfileMigrationPreferencePage;
import org.eclipse.swt.SWT;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.FileDialog;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.uml2.uml.Package;
import org.eclipse.uml2.uml.Profile;

/**
 * The class is used to migrated the model to the new version of the profile
 */
public class MigratorProfileApplication {

	/**
	 * The profiled model which is migrated
	 */
	public static Package profiledModel;

	/**
	 * The applied profile which is currently use for the migration
	 */
	public static Profile appliedProfile;

	/**
	 * The comparison of the two profile (output of EMF compare)
	 */
	public static Comparison comparison;

	/**
	 * The list of profile applied during the migration
	 */
	public static Set<Profile> newAppliedProfile = new HashSet<>();

	/**
	 * The list of stereotype applied during the migration
	 */
	public static List<EObject> newStereotypeApplication = new ArrayList<>();

	/**
	 * the list of atomic migration detected between the two files
	 */
	protected List<IAtomicMigrator> atomicList;

	/**
	 * the list of composite migration detected between the two files
	 */
	protected List<ICompositeMigrator> compositeList;

	/**
	 * The list of file already open which is keep to avoid asking the user to select the file each time
	 */
	private static Map<String, String> cacheProfileToFile = new HashMap<>();

	/**
	 * Constructor.
	 *
	 */
	public MigratorProfileApplication() {
		atomicList = new ArrayList<>();
		compositeList = new ArrayList<>();
	}

	/**
	 * Reapply the profile on the package_
	 * 
	 * @param package_
	 *            package owning the stereotype application
	 * @param profile
	 *            the profile which must be reapply
	 * @return the list of newly applied stereotype
	 */
	public List<EObject> reapplyProfile(Package package_, Profile profile) {
		Resource profileAfterResource = profile.eResource();

		String path = getFileName(profile, profileAfterResource);
		if (path != null) {
			URI uri = URI.createFileURI(path);
			ResourceSet profileBeforeResourceSet = new ResourceSetImpl();
			Resource profileBeforeResource = profileBeforeResourceSet.getResource(uri, true);

			migrateNewAppliedProfile(package_, profile, profileBeforeResource, profileAfterResource);
		} else {
			MessageDialog message = new MessageDialog(Display.getDefault().getActiveShell(), "Incorect file", null, "The selected path is incorect so the profile will just be reapply.", MessageDialog.INFORMATION, new String[] { "OK" }, 0); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
			message.open();
			package_.applyProfile(profile);
		}
		return newStereotypeApplication;
	}

	/**
	 * Migrate package_ to the new version of profile
	 * 
	 * @param package_
	 *            the package to migrate
	 * @param profile
	 *            the new profile
	 * @param profileBeforeResource
	 *            the resource corresponding to the profile before modification
	 * @param profileAfterResource
	 *            the resource corresponding to the new profile
	 * @return the migrated package
	 */
	public Package migrateNewAppliedProfile(Package package_, Profile profile, Resource profileBeforeResource, Resource profileAfterResource) {
		profiledModel = package_;
		appliedProfile = profile;
		newAppliedProfile.clear();

		try {
			// 1] initialize treeNode and registry (save all necessary data)
			TreeNode rootTreeNode = getTreeNode(profileBeforeResource, profileAfterResource);
			if (rootTreeNode != null) {
				new StereotypeApplicationRegistry(rootTreeNode, profiledModel);

				// 2] migrate
				migrateNewAppliedProfile(profiledModel, rootTreeNode, profile, true);
			} else {
				// this is the case where there is no modification in the profile
				profiledModel.applyProfile(profile);
			}
		} catch (Exception e) {
			Activator.log.error(e);
		}
		return profiledModel;
	}

	/**
	 * This method is used to get the treeNode from to files (before and after)
	 *
	 * @param before
	 *            the resource for the model before modification
	 * @param after
	 *            the resource for the model after modification
	 * @return the treeNode base on modifications
	 */
	protected TreeNode getTreeNode(Notifier before, Notifier after) {
		// get comparison model
		DefaultComparisonScope scope = new DefaultComparisonScope(after, before, null);
		EMFCompare comparator = EMFCompare.builder().build();
		comparison = comparator.compare(scope);

		// get TreeNode
		DifferenceTreeBuilder builder = new DifferenceTreeBuilder(comparison);
		TreeNode differenceTree = builder.buildMatchTree();

		return differenceTree;
	}

	/**
	 * This method migrate to model to the new version of the profile
	 * 
	 * @param model
	 *            the profiled model to migrate
	 * @param treeNode
	 *            the treeNode corresponding to differences between the profile before and after modifications
	 * @param profile
	 *            the concerned profile
	 * @param shouldReapply
	 *            true if we have to reapply the profile, false otherwise
	 */
	protected void migrateNewAppliedProfile(Package model, TreeNode treeNode, Profile profile, boolean shouldReapply) {
		// 1] clear lists
		atomicList.clear();
		compositeList.clear();

		// 2] initialize lists
		initAtomicList(treeNode, MigratorFactory.INSTANCE);
		initCompositeList();
		postProcessing();

		// 3] reapply profile if it is necessary
		if (shouldReapply) {
			model.applyProfile(appliedProfile);
		}

		// 4] migrate the initialize atomicList
		for (IAtomicMigrator atomic : atomicList) {
			atomic.migrationAction();
		}

		// 5] migrate for newly applied profiles (when move into new profile)
		for (Profile newProfile : newAppliedProfile) {
			newAppliedProfile.remove(newProfile);
			migrateNewAppliedProfile(model, treeNode, newProfile, false);
		}
	}

	/**
	 * Initialize the list of atomic migration
	 *
	 * @param treeNode
	 */
	protected void initAtomicList(TreeNode treeNode, IMigratorFactory migratorFactory) {
		atomicList.addAll(migratorFactory.instantiateMigrator(treeNode));
		for (TreeNode childNode : treeNode.getChildren()) {
			initAtomicList(childNode, migratorFactory);
		}
		atomicList.sort(new AtomicMigratorComparator());
	}

	/**
	 * Remove every erased migrator (erased by extension point)
	 */
	private void postProcessing() {
		List<IAtomicMigrator> toRemove = new ArrayList<>();
		for (AtomicMigratorRegistry.Descriptor descriptor : AtomicMigratorRegistry.INSTANCE.getRegistry()) {
			for (String replacement : descriptor.getErasedMigrators()) {
				for (IAtomicMigrator migrator : atomicList) {
					if (!(toRemove.contains(migrator)) && migrator.getClass().getName().equals(replacement)) {
						toRemove.add(migrator);
					}
				}
			}
		}
		atomicList.removeAll(toRemove);
	}


	/**
	 * Initialize the list of atomic migration from the list of atomic migration
	 */
	protected void initCompositeList() {
		// TODO [Composite migration] initialize this list of composite migration, a composite migration is composed of atomic migration, all atomic migration which compose it shall be remove from the atomic list
	}

	private static Shell getActiveShell() {
		Display display = Display.getDefault();
		Shell result = display.getActiveShell();

		if (result == null) {
			Shell[] shells = display.getShells();
			for (Shell shell : shells) {
				if (shell.getShells().length == 0) {
					result = shell;
				}
			}
		}

		return result;
	}

	private String getFileName(Profile profile, Resource profileAfterResource) {
		if (cacheProfileToFile.containsKey(((XMIResource) profileAfterResource).getID(profile))) {
			String path = cacheProfileToFile.get(((XMIResource) profileAfterResource).getID(profile));
			if (ProfileMigrationPreferencePage.getCachedFiles().contains(path)) {
				return path;
			} else {
				cacheProfileToFile.remove(((XMIResource) profileAfterResource).getID(profile));
			}
		}

		FileDialog dialog = new FileDialog(getActiveShell(), SWT.OPEN);
		dialog.setText(NLS.bind(Messages.MigratorProfileApplicationDelegate_SelectFileDialogTitle, profile.getName()));
		dialog.setFilterExtensions(new String[] { "*.profile.uml", "*" }); //$NON-NLS-1$ //$NON-NLS-2$
		dialog.setFilterNames(new String[] { "Profiles", "All" }); //$NON-NLS-1$ //$NON-NLS-2$
		String path = dialog.open();

		if (path != null) {
			// add all profile to the cache
			URI uri = URI.createFileURI(path);
			ResourceSet profileBeforeResourceSet = new ResourceSetImpl();
			Resource profileBeforeResource = profileBeforeResourceSet.getResource(uri, true);
			TreeIterator<EObject> iter = profileBeforeResource.getAllContents();
			while (iter.hasNext()) {
				EObject object = iter.next();
				if (object instanceof Package) {
					if (object instanceof Profile) {
						cacheProfileToFile.put(((XMIResource) profileBeforeResource).getID(object), path);
						if (!ProfileMigrationPreferencePage.getCachedFiles().contains(path)) {
							ProfileMigrationPreferencePage.addFile(path);
						}
					}
				} else {
					iter.prune();
				}
			}
		}
		return path;
	}
}

Back to the top