aboutsummaryrefslogtreecommitdiffstats
path: root/Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/util/EMFHelper.java
diff options
context:
space:
mode:
Diffstat (limited to 'Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/util/EMFHelper.java')
-rw-r--r--Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/util/EMFHelper.java424
1 files changed, 424 insertions, 0 deletions
diff --git a/Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/util/EMFHelper.java b/Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/util/EMFHelper.java
new file mode 100644
index 00000000..14b3acfb
--- /dev/null
+++ b/Solvers/VIATRA-Solver/org.eclipse.viatra.dse/src/org/eclipse/viatra/dse/util/EMFHelper.java
@@ -0,0 +1,424 @@
1/*******************************************************************************
2 * Copyright (c) 2010-2014, Miklos Foldenyi, Andras Szabolcs Nagy, Abel Hegedus, Akos Horvath, Zoltan Ujhelyi and Daniel Varro
3 * This program and the accompanying materials are made available under the
4 * terms of the Eclipse Public License v. 2.0 which is available at
5 * http://www.eclipse.org/legal/epl-v20.html.
6 *
7 * SPDX-License-Identifier: EPL-2.0
8 *******************************************************************************/
9package org.eclipse.viatra.dse.util;
10
11import java.io.IOException;
12import java.util.Collections;
13import java.util.Comparator;
14import java.util.HashMap;
15import java.util.HashSet;
16import java.util.List;
17import java.util.Map;
18import java.util.Objects;
19import java.util.Set;
20import java.util.TreeSet;
21
22import org.apache.log4j.Logger;
23import org.eclipse.emf.common.command.BasicCommandStack;
24import org.eclipse.emf.common.notify.Notifier;
25import org.eclipse.emf.common.util.EList;
26import org.eclipse.emf.common.util.URI;
27import org.eclipse.emf.ecore.EAttribute;
28import org.eclipse.emf.ecore.EClass;
29import org.eclipse.emf.ecore.EClassifier;
30import org.eclipse.emf.ecore.ENamedElement;
31import org.eclipse.emf.ecore.EObject;
32import org.eclipse.emf.ecore.EPackage;
33import org.eclipse.emf.ecore.EReference;
34import org.eclipse.emf.ecore.resource.Resource;
35import org.eclipse.emf.ecore.resource.ResourceSet;
36import org.eclipse.emf.ecore.resource.impl.ResourceSetImpl;
37import org.eclipse.emf.ecore.util.EcoreUtil.Copier;
38import org.eclipse.emf.ecore.xmi.impl.XMIResourceFactoryImpl;
39import org.eclipse.emf.edit.command.AddCommand;
40import org.eclipse.emf.edit.domain.AdapterFactoryEditingDomain;
41import org.eclipse.emf.edit.domain.EditingDomain;
42import org.eclipse.viatra.query.runtime.matchers.util.Preconditions;
43
44/**
45 * This class contains static helper methods.
46 * @author Andras Szabolcs Nagy
47 */
48public final class EMFHelper {
49
50 private static final Logger logger = Logger.getLogger(EMFHelper.class);
51
52 private EMFHelper() {
53 }
54
55 public static class EmfHelperException extends RuntimeException {
56 private static final long serialVersionUID = 7635796550669616626L;
57
58 public EmfHelperException(String string) {
59 super(string);
60 }
61 public EmfHelperException(String string, Throwable e) {
62 super(string, e);
63 }
64 }
65
66 /**
67 * Gets the {@link EditingDomain} of either an {@link EObject}, {@link Resource} or {@link ResourceSet}.
68 * @param notifier The {@link Notifier}.
69 * @return The EditingDomain.
70 */
71 public static EditingDomain getEditingDomain(Notifier notifier) {
72 Objects.requireNonNull(notifier);
73 if (notifier instanceof EObject) {
74 EObject eObject = (EObject) notifier;
75 return AdapterFactoryEditingDomain.getEditingDomainFor(eObject);
76 } else if (notifier instanceof Resource) {
77 Resource resource = (Resource) notifier;
78 EList<EObject> contents = resource.getContents();
79 if (contents.isEmpty()) {
80 return null;
81 }
82 return AdapterFactoryEditingDomain.getEditingDomainFor(contents.get(0));
83 } else if (notifier instanceof ResourceSet) {
84 ResourceSet resourceSet = (ResourceSet) notifier;
85 if (resourceSet.getResources().isEmpty()) {
86 return null;
87 }
88 return getEditingDomain(resourceSet.getResources().get(0));
89 }
90
91 return null;
92 }
93
94 /**
95 * Creates (or gets if already exists) an {@link EditingDomain} over the given {@link Notifier},
96 * either an {@link EObject}, {@link Resource} or {@link ResourceSet}.
97 * @param notifier The {@link Notifier}.
98 * @return The EditingDomain.
99 */
100 public static EditingDomain createEditingDomain(Notifier notifier) {
101
102 EditingDomain domain = getEditingDomain(notifier);
103 if (domain != null) {
104 return domain;
105 }
106
107 registerExtensionForXmiSerializer("dummyext");
108
109 if (notifier instanceof EObject) {
110 EObject eObject = (EObject) notifier;
111
112 domain = new AdapterFactoryEditingDomain(null, new BasicCommandStack());
113 Resource resource = domain.getResourceSet().createResource(URI.createFileURI("dummy.dummyext"));
114 domain.getCommandStack().execute(new AddCommand(domain, resource.getContents(), eObject));
115
116 return domain;
117
118 } else if (notifier instanceof Resource) {
119 Resource resource = (Resource) notifier;
120
121 ResourceSet resourceSet = resource.getResourceSet();
122 if (resourceSet != null) {
123 return new AdapterFactoryEditingDomain(null, new BasicCommandStack(), resourceSet);
124 } else {
125 domain = new AdapterFactoryEditingDomain(null, new BasicCommandStack(), (ResourceSet) null);
126 resourceSet = domain.getResourceSet();
127 domain.getCommandStack().execute(new AddCommand(domain, resourceSet.getResources(), resource));
128 return domain;
129 }
130
131 } else if (notifier instanceof ResourceSet) {
132 return new AdapterFactoryEditingDomain(null, new BasicCommandStack(), (ResourceSet) notifier);
133 } else {
134 throw new EmfHelperException("Not supported argument type.");
135 }
136 }
137
138 /**
139 * Saves the EMF model (EObject or Resource) into the given file. An {@link XMIResourceFactoryImpl} will be
140 * registered if not already.
141 *
142 * Doesn't throw exception but logs an error if the save was unsuccessful.
143 *
144 * @param model Can be an {@link EObject} or a {@link Resource}.
145 * @param fileName
146 */
147 public static void saveModel(Notifier model, String fileName) {
148
149 Objects.requireNonNull(model);
150 Preconditions.checkArgument(fileName != null && !fileName.isEmpty(), "File name is null or empty.");
151
152 int extensionIndex = fileName.lastIndexOf('.');
153
154 Preconditions.checkState(extensionIndex > -1 && extensionIndex != fileName.length() - 1, "Bad file extension.");
155
156 String ext = fileName.substring(extensionIndex + 1);
157
158 registerExtensionForXmiSerializer(ext);
159
160 URI uri = URI.createFileURI(fileName);
161 Resource resource;
162
163 if (model instanceof ResourceSet) {
164 throw new EmfHelperException("Unsupported type: ResourceSet");
165 } else if (model instanceof Resource) {
166 resource = (Resource) model;
167 } else if (model instanceof EObject) {
168 EObject root = (EObject) model;
169 ResourceSet resSet = new ResourceSetImpl();
170 resource = resSet.createResource(uri);
171 resource.getContents().add(root);
172 } else {
173 throw new EmfHelperException("Unkown type: " + model.getClass());
174 }
175
176 resource.setURI(uri);
177 saveResource(resource);
178 }
179
180 private static void saveResource(Resource resource) {
181 try {
182 resource.save(Collections.emptyMap());
183 } catch (IOException e) {
184 logger.error(e);
185 }
186 }
187
188 /**
189 * Loads a model as a {@link Resource}. In headless mode, don't forget to call XYZPackage.eINSTANCE.
190 */
191 public static Resource loadModel(String fileName) throws IOException {
192 Preconditions.checkArgument(fileName != null && !fileName.isEmpty(), "File name is null or empty.");
193 int extensionIndex = fileName.lastIndexOf('.');
194 Preconditions.checkState(extensionIndex > -1 && extensionIndex != fileName.length() - 1, "Bad file extension.");
195
196 String ext = fileName.substring(extensionIndex + 1);
197 registerExtensionForXmiSerializer(ext);
198
199 ResourceSetImpl rSet = new ResourceSetImpl();
200 URI fileUri = URI.createFileURI(fileName);
201 Resource resource = rSet.createResource(fileUri);
202
203 resource.load(null);
204 return resource;
205 }
206
207 /**
208 * Retrieves the root EObject from a Resource or ResourceSet.
209 * <ul>
210 * <li>Returns null if there is no content.</li>
211 * <li>Returns the notifier itself if it is an EObject.</li>
212 * <li>Logs a warn if there are multiple roots.</li>
213 * </ul>
214 *
215 * @param notifier
216 * @return The root EObject or null.
217 */
218 public static EObject getRootEObject(Notifier notifier) {
219 if (notifier instanceof EObject) {
220 return (EObject) notifier;
221 } else if (notifier instanceof Resource) {
222 Resource resource = (Resource) notifier;
223 List<EObject> contents = resource.getContents();
224 if (contents.size() > 1) {
225 logger.warn("Resource has more than one root.");
226 }
227 if (contents.isEmpty()) {
228 return null;
229 } else {
230 return contents.get(0);
231 }
232 } else if (notifier instanceof ResourceSet) {
233 ResourceSet resourceSet = (ResourceSet) notifier;
234 List<Resource> resources = resourceSet.getResources();
235 if (resources.size() > 1) {
236 logger.warn("ResourceSet has more than one resources.");
237 }
238 if (resources.isEmpty()) {
239 return null;
240 } else {
241 return getRootEObject(resources.get(0));
242 }
243 } else {
244 throw new EmfHelperException("Unkown type: " + notifier.getClass());
245 }
246 }
247
248 /**
249 * Registers an {@link XMIResourceFactoryImpl} for the given extension.
250 * @param ext The extension as a String.
251 */
252 public static void registerExtensionForXmiSerializer(String ext) {
253 Resource.Factory.Registry reg = Resource.Factory.Registry.INSTANCE;
254 Map<String, Object> m = reg.getExtensionToFactoryMap();
255 m.computeIfAbsent(ext, e -> new XMIResourceFactoryImpl());
256 }
257
258 /**
259 * Clones the given model. Either an {@link EObject}, {@link Resource} or {@link ResourceSet}.
260 * @param notifier The root container of the model.
261 * @return The cloned model.
262 */
263 public static Notifier clone(Notifier notifier) {
264 Copier copier = new Copier();
265 Notifier clonedModel = clone(notifier, copier, null);
266 copier.copyReferences();
267 return clonedModel;
268 }
269
270 private static Notifier clone(Notifier notifier, Copier copier, ResourceSet resourceSetToCloneTo) {
271 Objects.requireNonNull(copier);
272
273 if (notifier instanceof EObject) {
274 EObject eObject = (EObject) notifier;
275 return copier.copy(eObject);
276 } else if (notifier instanceof Resource) {
277 Resource resource = (Resource) notifier;
278 ResourceSet rSetTemp = resourceSetToCloneTo;
279 if (resourceSetToCloneTo == null) {
280 rSetTemp = new ResourceSetImpl();
281 }
282 Resource clonedResource = rSetTemp.createResource(URI.createFileURI("dummy.dummyext"));
283
284 for (EObject eObject : resource.getContents()) {
285 EObject clonedEObject = copier.copy(eObject);
286 clonedResource.getContents().add(clonedEObject);
287 }
288
289 return clonedResource;
290 } else if (notifier instanceof ResourceSet) {
291 ResourceSet resourceSet = (ResourceSet) notifier;
292 ResourceSetImpl clonedResourceSet = new ResourceSetImpl();
293
294 for (Resource resource : resourceSet.getResources()) {
295 clone(resource, copier, clonedResourceSet);
296 }
297
298 return clonedResourceSet;
299 } else {
300 throw new EmfHelperException("Not supported argument type.");
301 }
302 }
303
304 public static class ENamedElementComparator implements Comparator<ENamedElement> {
305 @Override
306 public int compare(ENamedElement eClass1, ENamedElement eClass2) {
307 return eClass1.getName().compareTo(eClass2.getName());
308 }
309 }
310
311 /**
312 * This class is used to store
313 * <ul>
314 * <li>{@link EClass}es,</li>
315 * <li>{@link EAttribute}s,</li>
316 * <li>{@link EReference}s,</li>
317 * <li>EAttributes by EClasses,</li>
318 * <li>EReferences by EClasses</li>
319 * </ul>
320 * for a given set of {@link EPackage}s.
321 *
322 */
323 public static class MetaModelElements {
324 public Set<EPackage> metaModelPackages;
325 public Set<EClass> classes;
326 public Set<EAttribute> attributes;
327 public Set<EReference> references;
328 public Map<EClass, Set<EAttribute>> attributesOfClass;
329 public Map<EClass, Set<EReference>> referencesOfClass;
330 }
331
332 /**
333 * Traverses the full metamodel on the given {@link EPackage}s and returns all the classes, attributes and
334 * references it contains.
335 *
336 * @param metaModelPackages
337 * The set of {@link EPackage}s.
338 * @return A {@link MetaModelElements} instance containing the metamodel elements.
339 */
340 public static MetaModelElements getAllMetaModelElements(Set<EPackage> metaModelPackages) {
341 return getMetaModelElements(metaModelPackages, true, true, true);
342 }
343
344 /**
345 * Return a {@link MetaModelElements} instance populated with its {@link MetaModelElements#classes}.
346 *
347 * @param metaModelPackages
348 * The set of {@link EPackage}s.
349 * @return AA {@link MetaModelElements} instance.
350 */
351 public static MetaModelElements getClasses(Set<EPackage> metaModelPackages) {
352 return getMetaModelElements(metaModelPackages, true, false, false);
353 }
354
355 /**
356 * Return a {@link MetaModelElements} instance populated with its {@link MetaModelElements#references} and
357 * {@link MetaModelElements#referencesOfClass}.
358 *
359 * @param metaModelPackages
360 * The set of {@link EPackage}s.
361 * @return AA {@link MetaModelElements} instance.
362 */
363 public static MetaModelElements getReferences(Set<EPackage> metaModelPackages) {
364 return getMetaModelElements(metaModelPackages, false, true, false);
365 }
366
367 /**
368 * Return a {@link MetaModelElements} instance populated with its {@link MetaModelElements#attributes} and
369 * {@link MetaModelElements#attributesOfClass}.
370 *
371 * @param metaModelPackages
372 * The set of {@link EPackage}s.
373 * @return AA {@link MetaModelElements} instance.
374 */
375 public static MetaModelElements getAttrbiutes(Set<EPackage> metaModelPackages) {
376 return getMetaModelElements(metaModelPackages, false, false, true);
377 }
378
379 private static MetaModelElements getMetaModelElements(Set<EPackage> metaModelPackages, boolean getClasses,
380 boolean getReferences, boolean getAttrbiutes) {
381
382 Comparator<ENamedElement> comparator = new ENamedElementComparator();
383
384 MetaModelElements result = new MetaModelElements();
385 result.metaModelPackages = metaModelPackages;
386 if (getClasses) {
387 result.classes = new TreeSet<EClass>(comparator);
388 }
389 if (getReferences) {
390 result.references = new HashSet<EReference>();
391 result.referencesOfClass = new HashMap<EClass, Set<EReference>>();
392 }
393 if (getAttrbiutes) {
394 result.attributes = new HashSet<EAttribute>();
395 result.attributesOfClass = new HashMap<EClass, Set<EAttribute>>();
396 }
397 for (EPackage ePackage : metaModelPackages) {
398 for (EClassifier eClassifier : ePackage.getEClassifiers()) {
399 if (eClassifier instanceof EClass) {
400 EClass eClass = ((EClass) eClassifier);
401 if (getClasses) {
402 result.classes.add(eClass);
403 }
404 if (getReferences) {
405 result.referencesOfClass.put(eClass, new TreeSet<EReference>(comparator));
406 for (EReference eReference : eClass.getEAllReferences()) {
407 result.references.add(eReference);
408 result.referencesOfClass.get(eClass).add(eReference);
409 }
410 }
411 if (getAttrbiutes) {
412 result.attributesOfClass.put(eClass, new TreeSet<EAttribute>(comparator));
413 for (EAttribute eAttribute : eClass.getEAllAttributes()) {
414 result.attributes.add(eAttribute);
415 result.attributesOfClass.get(eClass).add(eAttribute);
416 }
417 }
418 }
419 }
420 }
421 return result;
422 }
423
424}