Skip to main content
aboutsummaryrefslogtreecommitdiffstats
blob: 7b7b5880b8bcfcf71bb2c577463a16b0cb33819a (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
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
/*****************************************************************************
 * Copyright (c) 2015 CEA LIST.
 *
 *
 * 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:
 *  Mauricio Alferez (CEA LIST) mauricio.alferez@cea.fr - Initial API and implementation
 *  Patrick Tessier (CEA LIST) patrick.tesseir@cea.fr- modification
 *
 *****************************************************************************/
package org.eclipse.papyrus.requirements.reqif.util;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;

import org.eclipse.emf.common.command.Command;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.transaction.TransactionalEditingDomain;
import org.eclipse.gmf.runtime.common.core.command.ICommand;
import org.eclipse.gmf.runtime.emf.type.core.requests.DestroyElementRequest;
import org.eclipse.papyrus.infra.emf.gmf.command.GMFtoEMFCommandWrapper;
import org.eclipse.papyrus.infra.services.edit.service.ElementEditServiceUtils;
import org.eclipse.papyrus.infra.services.edit.service.IElementEditService;
import org.eclipse.uml2.uml.Class;
import org.eclipse.uml2.uml.Classifier;
import org.eclipse.uml2.uml.Comment;
import org.eclipse.uml2.uml.Element;
import org.eclipse.uml2.uml.NamedElement;
import org.eclipse.uml2.uml.Package;
import org.eclipse.uml2.uml.PackageableElement;
import org.eclipse.uml2.uml.Property;
import org.eclipse.uml2.uml.Stereotype;

/**
 * This is a basic merger in order to add requirement from right to left
 * could be improved by using hashmap
 * This algorithm is n2
 *
 */
public class BasicRequirementMerger implements IRequirementMerger {

	protected Package leftPackage = null;

	protected Package rightPackage = null;

	protected String matchProperty = null;

	protected String changeableProperty = null;

	protected EqualityHelperWithoutContainment equalityHelper = new EqualityHelperWithoutContainment();

	protected CopierWithoutContainment copier = new CopierWithoutContainment();

	protected TransactionalEditingDomain domain;

	protected ArrayList<Element> elementToDelete = new ArrayList<Element>();

	protected ArrayList<Element> addedElements = new ArrayList<Element>();

	protected boolean deleteElements = true;

	protected HashSet<Element> modifiedElement = new HashSet<Element>();

	@Override
	public HashSet<Element> getModifiedElement() {
		return modifiedElement;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.eclipse.papyrus.requirements.reqif.transformation.IRequirementMerger#getElementToDelete()
	 */
	@Override
	public ArrayList<Element> getElementToDelete() {
		return elementToDelete;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.eclipse.papyrus.requirements.reqif.transformation.IRequirementMerger#getAddedElements()
	 */
	@Override
	public ArrayList<Element> getAddedElements() {
		return addedElements;
	}

	/**
	 * Merge information from version2 into version1
	 * 
	 * @param leftPackage
	 *            is the package where we will do the modifications
	 * @param rightPackage
	 *            is the package that we will analyze
	 * @param matchProperty
	 *            is the stereotype's property name used to determine if one
	 *            element in basePk is the same than other element in extPk. For
	 *            example, "id" is a good matchProperty when comparing SysML
	 *            Requirements
	 * @param changeableProperty
	 *            is the property that we will change if the value of
	 *            copyAllPropertyValues is false. For example "text".
	 **/
	public BasicRequirementMerger(Package leftPackage, Package rightPackage, String matchProperty, boolean deleteElements, TransactionalEditingDomain domain) {
		this.leftPackage = leftPackage;
		this.rightPackage = rightPackage;
		this.matchProperty = matchProperty;
		this.domain = domain;
		this.deleteElements = deleteElements;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see org.eclipse.papyrus.requirements.reqif.transformation.IRequirementMerger#merge()
	 */
	@Override
	public boolean merge() {
		boolean merged = false;
		equalityHelper = new EqualityHelperWithoutContainment();
		merged = updateAndAddFromRight(leftPackage, rightPackage, matchProperty);
		equalityHelper = new EqualityHelperWithoutContainment();
		if (merged) {
			deleteInsideLeft(matchProperty, leftPackage, rightPackage);
		}
		return true;
	}

	/**
	 * 
	 * @param matchProperty
	 *            the property if stereotype used to delete element
	 * @param checkPackageName
	 * @param leftPackage
	 *            the package where element will be removed
	 * @param rightPackage
	 *            the package that will be compared
	 */
	protected void deleteInsideLeft(String matchProperty, Package leftPackage, Package rightPackage) {
		elementToDelete = getElementsToBeDeleted(leftPackage, rightPackage, matchProperty, elementToDelete);
		filterReq(matchProperty);
		if (deleteElements) {
			deleteElements(elementToDelete, leftPackage);
		}
	}

	/**
	 * filter elements that has an ID property
	 * 
	 * @param matchProperty
	 */
	protected void filterReq(String matchProperty) {
		ArrayList<Element> result = new ArrayList<Element>();
		for (Element elementToDelete : elementToDelete) {
			if (elementToDelete.getAppliedStereotypes().size() > 0) {
				Stereotype appliedStereotype = elementToDelete.getAppliedStereotypes().get(0);
				if ((elementToDelete.getStereotypeApplication(appliedStereotype)).eClass().getEStructuralFeature(matchProperty) != null) {
					String idValue = (String) elementToDelete.getValue(appliedStereotype, matchProperty);
					if (idValue != null) {
						result.add(elementToDelete);
					}
				}
			}
		}
		elementToDelete = result;
	}

	/**
	 * Prepare a list of Packageable Elements to be deleted from basePk
	 * 
	 * @param version1
	 *            is the package where we will try to find elements to be
	 *            deleted because they are not anymore in the extPk (which is a
	 *            version of basePk that evolved separately).
	 * @param version2
	 *            is the package where we will try to find elements that are not
	 *            in the basePk.
	 * @param matchProperty
	 *            is the stereotype's property name used to determine if one
	 *            element in basePk is the same than other element in extPk. For
	 *            example, "id" is a good matchProperty when comparing SysML
	 *            Requirements
	 * @param delList
	 *            empty list that will store the packageableElements to be
	 *            deleted.
	 * @return list of packageableElements to be deleted.
	 */
	protected ArrayList<Element> getElementsToBeDeleted(Element containerLeft, Element containerRight, String matchProperty, ArrayList<Element> delList) {
		for (Element elementLeft : containerLeft.getOwnedElements()) {
			Element foundRightElement = null;
			foundRightElement = lookforEquivalent(containerRight, elementLeft, matchProperty);
			if (foundRightElement == null) {
				delList.add(elementLeft);
			}
			if (elementLeft.getOwnedElements().size() > 0 && foundRightElement != null) {
				getElementsToBeDeleted(elementLeft, foundRightElement, matchProperty, delList);
			}
		}
		return delList;
	}

	/**
	 * Delete a list of packageable elements without any confirmation from the user
	 * 
	 * @param delList
	 *            list of elements to be deleted
	 * @param basePk
	 *            the container of all element it can be indirect.
	 * @param matchProperty
	 * @return true if no problems
	 */
	public boolean deleteElements(List<Element> delList, Package basePk) {
		IElementEditService provider = ElementEditServiceUtils.getCommandProvider(basePk);
		if (provider == null) {
			return false;
		}
		for (Element elementToDelete : delList) {
			if (elementToDelete != null) {
				ICommand createGMFCommand = provider.getEditCommand(new DestroyElementRequest(domain, elementToDelete, false));
				if (createGMFCommand != null) {
					Command emfCommand = new GMFtoEMFCommandWrapper(createGMFCommand);
					domain.getCommandStack().execute(emfCommand);
				}
			}
		}
		return true;
	}

	/**
	 * Merge information from right into left
	 * 
	 * @param leftContainer
	 *            is the package where we will do the modifications
	 * @param rightContainer
	 *            is the package that we will analyze
	 * @param matchProperty
	 *            is the stereotype's property name used to determine if one
	 *            element in basePk is the same than other element in extPk. For
	 *            example, "id" is a good matchProperty when comparing SysML
	 *            Requirements
	 * @param changeableProperty
	 *            is the property that we will change if the value of
	 *            copyAllPropertyValues is false. For example "text".
	 * @param copyOption
	 *            is an option. true means that the user wants to copy the name
	 *            and all the stereotype values. This option disables the
	 *            changeableProperty parameter.
	 * @return true merge finishes successfully
	 */
	protected boolean updateAndAddFromRight(org.eclipse.uml2.uml.Element leftContainer, org.eclipse.uml2.uml.Element rightContainer, String matchProperty) {
		for (Element subElementFromRight : rightContainer.getOwnedElements()) {
			Element foundLeftElement = null;
			foundLeftElement = lookforEquivalent(leftContainer, subElementFromRight, matchProperty);
			if (foundLeftElement != null) {
				copyConfig(subElementFromRight, foundLeftElement);
			} else {
				foundLeftElement = addElementInsideLeft(leftContainer, subElementFromRight);
			}
			if (subElementFromRight.getOwnedElements().size() > 0) {
				updateAndAddFromRight(foundLeftElement, subElementFromRight, matchProperty);
			}
		}
		return true;
	}

	/**
	 * Add an element from right to left
	 * 
	 * @param leftContainer
	 *            is the package where we will add new elements from rightPackage.
	 * @param elementFromRight
	 *            element in Right package.
	 * @param appliedSterotypeRight
	 *            stereotype of peInExtPk.
	 * @return createdElement
	 */
	protected Element addElementInsideLeft(Element leftContainer, Element elementFromRight) {
		Element result = null;
		ArrayList<EObject> subsetToCopy = new ArrayList<EObject>();
		subsetToCopy.add(elementFromRight);
		for (EObject stereoAppli : elementFromRight.getStereotypeApplications()) {
			subsetToCopy.add(stereoAppli);
		}
		Collection<EObject> copy = copier.copyAll(subsetToCopy);
		copier.copyReferences();
		for (EObject eObject : copy) {
			if (eObject instanceof Element) {
				if (eObject instanceof Comment) {
					addedElements.add((Element) eObject);
					(leftContainer).getOwnedComments().add((Comment) eObject);
				} else if (leftContainer instanceof Package && eObject instanceof PackageableElement) {
					addedElements.add((Element) eObject);
					((Package) leftContainer).getPackagedElements().add((PackageableElement) eObject);
				} else if (leftContainer instanceof Classifier && eObject instanceof Classifier) {
					addedElements.add((Element) eObject);
					((Class) leftContainer).getNestedClassifiers().add((Classifier) eObject);
				} else {
					System.err.println("Impossible to add " + eObject + " inside" + leftContainer);
				}
				result = (Element) eObject;
			} else {
				leftContainer.eResource().getContents().add(eObject);
			}
		}
		return result;
	}

	/**
	 * Copy all not derived stereotype property values without changing the base
	 * class
	 * 
	 * @param peInExtPk
	 *            Packageable element in external package.
	 * @param stOfExtPe
	 *            stereotype of peInExtPk.
	 * @param peInBasePk
	 *            Packageable element in base package that will receive the new
	 *            values from peInExtPk.
	 */
	protected void copyAllStereotypePropertyValues(Element peInExtPk, Stereotype stOfExtPe, Element peInBasePk) {
		for (Property stProperty : stOfExtPe.getAllAttributes()) {
			if (!stProperty.isReadOnly() && !stProperty.isDerived() && !stProperty.getName().startsWith("base_")) {
				if (peInBasePk.getValue(stOfExtPe, stProperty.getName()) != null) {
					if (!(peInBasePk.getValue(stOfExtPe, stProperty.getName()).equals(peInExtPk.getValue(stOfExtPe, stProperty.getName())))) {
						modifiedElement.add(peInBasePk);
						peInBasePk.setValue(stOfExtPe, stProperty.getName(), peInExtPk.getValue(stOfExtPe, stProperty.getName()));
					}
				} else if (peInExtPk.getValue(stOfExtPe, stProperty.getName()) != null) {
					if (!(peInExtPk.getValue(stOfExtPe, stProperty.getName()).equals(peInBasePk.getValue(stOfExtPe, stProperty.getName())))) {
						modifiedElement.add(peInBasePk);
						peInBasePk.setValue(stOfExtPe, stProperty.getName(), peInExtPk.getValue(stOfExtPe, stProperty.getName()));
					}
				}
			}
		}
	}

	/**
	 * Matches two stereotyped packageable elements based on a matchProperty at the current level
	 * 
	 * @param researchSpace
	 *            is the container where we look for elementRight
	 * @param wantedElement
	 *            is the stereotyped PackagedElement to be matched. For example,
	 *            the packaged element of type "org.eclipse.uml2.uml.Class"
	 *            stereotyped with "ReqType5".
	 * @param matchProperty
	 *            is the stereotype's property name used to determine if one
	 *            element in basePk matches other element in extPk. For example,
	 *            the stereotype "Requirement" in the profile SysML Requirements
	 *            defines the property "id" that is used frequently as a
	 *            matchProperty.
	 * 
	 * @return the element that matches
	 */
	protected Element lookforEquivalent(Element researchSpace, Element wantedElement, String matchProperty) {
		for (Element potentialElement : researchSpace.getOwnedElements()) {
			if (equalityHelper.equals(potentialElement, wantedElement)) {
				if (wantedElement.getAppliedStereotypes().size() == 0) {
					return potentialElement;
				}
				// they have a stereotype
				Stereotype wantedAppliedStereotype = wantedElement.getAppliedStereotypes().get(0);
				// have they the same applied stereotype?
				if (!potentialElement.isStereotypeApplied(wantedAppliedStereotype)) {
					return null;
				}
				String wantedPropertyValue = null;
				String potentialPropertyValueLeft = null;
				if ((wantedElement.getStereotypeApplication(wantedAppliedStereotype)).eClass().getEStructuralFeature(matchProperty) != null) {
					wantedPropertyValue = (String) wantedElement.getValue(wantedAppliedStereotype, matchProperty);
				}
				if ((potentialElement.getStereotypeApplication(wantedAppliedStereotype)).eClass().getEStructuralFeature(matchProperty) != null) {
					potentialPropertyValueLeft = (String) potentialElement.getValue(wantedAppliedStereotype, matchProperty);
				}
				if (potentialPropertyValueLeft == null && wantedPropertyValue == null) {
					return potentialElement;
				}
				if (potentialPropertyValueLeft != null && wantedPropertyValue != null) {
					if (wantedPropertyValue.trim().equals(potentialPropertyValueLeft.trim())) {
						return potentialElement;
					}
				}
			}
		}
		return null;
	}

	/**
	 * Copy the name of an element and either one or all stereotype property
	 * values depending on the parameter copyOption
	 * 
	 * @param rightElement
	 *            is the packageable element in external package
	 * @param leftElement
	 *            is the stereotyped packageable element whose value(s) will be
	 *            modified.
	 */
	protected void copyConfig(Element rightElement, Element leftElement) {
		if (leftElement instanceof NamedElement) {
			((NamedElement) leftElement).setName(((NamedElement) rightElement).getName());
		}
		for (Stereotype st : leftElement.getAppliedStereotypes()) {
			copyAllStereotypePropertyValues(rightElement, st, leftElement);
		}
	}
}

Back to the top