Skip to content
This repository was archived by the owner on Dec 18, 2025. It is now read-only.

Commit 3c6cbb7

Browse files
Fix synchronisation of model roots (fixes #73)
The adapter now handles ADD, ADD_MANY, REMOVE, and REMOVE_MANY at the resource level. There are tests to check for this as well. The adapter propagation test had to be rewritten, as we need to have a Jena graph set up in place before we can make any changes to the model (otherwise, we have nothing to synchronise!). --------- Co-authored-by: Owen Reynolds <owen.reynolds@york.ac.uk>
1 parent a91ff94 commit 3c6cbb7

File tree

20 files changed

+377
-28
lines changed

20 files changed

+377
-28
lines changed

bundles/org.eclipse.epsilon.rdf.emf/src/org/eclipse/epsilon/rdf/emf/RDFGraphResourceImpl.java

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
import org.eclipse.core.runtime.FileLocator;
4040
import org.eclipse.emf.common.util.URI;
4141
import org.eclipse.emf.ecore.EObject;
42+
import org.eclipse.emf.ecore.EPackage;
4243
import org.eclipse.emf.ecore.resource.impl.ResourceImpl;
4344
import org.eclipse.epsilon.rdf.emf.config.RDFResourceConfiguration;
4445
import org.eclipse.epsilon.rdf.validation.RDFValidation.ValidationMode;
@@ -124,7 +125,15 @@ protected void doLoad(InputStream inputStream, Map<?, ?> options) throws IOExcep
124125

125126
multiValueAttributeMode = MultiValueAttributeMode.fromValue(this.config.getMultiValueAttributeMode());
126127

127-
deserializer = new RDFDeserializer(() -> this.getResourceSet().getPackageRegistry());
128+
deserializer = new RDFDeserializer(() -> {
129+
if (this.getResourceSet() != null) {
130+
// Prefer the resource set's package registry
131+
return this.getResourceSet().getPackageRegistry();
132+
} else {
133+
// Fall back to the global package registry
134+
return EPackage.Registry.INSTANCE;
135+
}
136+
});
128137
deserializer.deserialize(rdfOntModel);
129138
for (EObject eob : deserializer.getEObjectToResourceMap().keySet()) {
130139
if (eob.eContainer() == null) {

bundles/org.eclipse.epsilon.rdf.emf/src/org/eclipse/epsilon/rdf/emf/RDFGraphResourceNotificationAdapterChangeRDF.java

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,15 +29,82 @@ public RDFGraphResourceNotificationAdapterChangeRDF(RDFGraphResourceImpl rdfGrap
2929
this.initialRDFGraphResource = rdfGraphResource;
3030
}
3131

32+
private RDFGraphResourceImpl getGraphResourceForEObject(EObject eObject) {
33+
RDFGraphResourceImpl graphResource = (RDFGraphResourceImpl) eObject.eResource();
34+
if (null == graphResource) {
35+
System.err.println("The Graph resource has been removed, using the initial graph resource instead");
36+
graphResource = initialRDFGraphResource;
37+
}
38+
return graphResource;
39+
}
40+
41+
private List<Resource> getNamedModelsToUpdate(EObject eObject, RDFGraphResourceImpl graphResource) {
42+
List<Resource> namedModelResources = graphResource.getResourcesForNamedModelsContaining(eObject);
43+
if (!namedModelResources.isEmpty()) {
44+
return namedModelResources;
45+
} else {
46+
// No named RDF models contain the object yet - fall back on the first one
47+
namedModelResources = graphResource.getResourcesForAllNamedModels();
48+
if (!namedModelResources.isEmpty()) {
49+
Resource first = namedModelResources.get(0);
50+
namedModelResources.clear();
51+
namedModelResources.add(first);
52+
System.out.println("using first named models: " + first);
53+
return namedModelResources;
54+
} else {
55+
// This is a problem...
56+
System.out.println("no model resource?");
57+
return null;
58+
}
59+
}
60+
}
61+
3262
@Override
3363
public void notifyChanged(Notification notification) {
3464
Object feature = notification.getFeature();
3565
if (null != feature) {
3666
featureNotification(feature, notification);
67+
} else {
68+
// Check for adding and removing of an EObject to the resource (model root)
69+
handleNonFeatureNotification(notification.getNewValue(), notification);
70+
handleNonFeatureNotification(notification.getOldValue(), notification);
3771
}
3872
super.notifyChanged(notification);
3973
}
4074

75+
private void handleNonFeatureNotification(Object value, Notification notification) {
76+
if (value instanceof EObject eObjectValue) {
77+
handleEObjectAttachOrDetach(notification, eObjectValue);
78+
}
79+
if (value instanceof List eObjectList) {
80+
for (Object o : eObjectList) {
81+
if (o instanceof EObject eob) {
82+
handleEObjectAttachOrDetach(notification, eob);
83+
}
84+
}
85+
}
86+
}
87+
88+
protected void handleEObjectAttachOrDetach(Notification notification, EObject eObjectValue) {
89+
RDFGraphResourceImpl graphResource = getGraphResourceForEObject(eObjectValue);
90+
RDFGraphResourceUpdate rdfUpdater = graphResource.getRDFGraphUpdater();
91+
92+
switch (notification.getEventType()) {
93+
case Notification.ADD:
94+
case Notification.ADD_MANY:
95+
for (Resource namedModelResource : getNamedModelsToUpdate(eObjectValue, graphResource)) {
96+
rdfUpdater.addToResource(eObjectValue, graphResource.getNamedModel(namedModelResource));
97+
}
98+
break;
99+
case Notification.REMOVE:
100+
case Notification.REMOVE_MANY:
101+
for (Resource namedModelResource : getNamedModelsToUpdate(eObjectValue, graphResource)) {
102+
rdfUpdater.removeFromResource(eObjectValue, graphResource.getNamedModel(namedModelResource));
103+
}
104+
break;
105+
}
106+
}
107+
41108
private void featureNotification (Object feature, Notification notification){
42109
if (feature instanceof EStructuralFeature) {
43110
eStructuralFeatureNotification((EStructuralFeature) feature, notification);

bundles/org.eclipse.epsilon.rdf.emf/src/org/eclipse/epsilon/rdf/emf/RDFGraphResourceNotificationAdapterTrace.java

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,44 @@ public void notifyChanged(Notification notification) {
4848
} else {
4949
// Notification is not for a feature
5050
processTrace.append(String.format("\n - Not a Feature notification \n - %s ", notification));
51+
processTrace.append(String.format("Event: %s", eventTypeToString(notification.getEventType())));
52+
53+
if (null != notification.getNewValue()) {
54+
processTrace.append(String.format("NewValue: %s", notification.getNewValue()));
55+
if (notification.getNewValue() instanceof EObject && notification.getEventType() == Notification.ADD) {
56+
processTrace.append("\nEObject ADDED to Model");
57+
processTrace.append(
58+
String.format("EObject: %s#%s", ((EObject) notification.getNewValue()).eClass().getName(),
59+
((EObject) notification.getNewValue()).hashCode()));
60+
}
61+
if (notification.getNewValue() instanceof List el && notification.getEventType() == Notification.ADD_MANY) {
62+
processTrace.append("\nEObjects ADDED to Model:");
63+
for (Object o : el) {
64+
processTrace.append(
65+
String.format("\n - EObject: %s#%s", ((EObject) o).eClass().getName(),
66+
((EObject) o).hashCode()));
67+
}
68+
}
69+
}
70+
71+
if (null != notification.getOldValue()) {
72+
processTrace.append(String.format("OldValue: %s", notification.getOldValue()));
73+
if (notification.getOldValue() instanceof EObject
74+
&& notification.getEventType() == Notification.REMOVE) {
75+
processTrace.append(String.format("\nEObject REMOVED from Model "));
76+
processTrace.append(
77+
String.format("EObject: %s#%s", ((EObject) notification.getOldValue()).eClass().getName(),
78+
((EObject) notification.getOldValue()).hashCode()));
79+
}
80+
if (notification.getNewValue() instanceof List el && notification.getEventType() == Notification.REMOVE_MANY) {
81+
processTrace.append("\nEObjects REMOVED from Model:");
82+
for (Object o : el) {
83+
processTrace.append(
84+
String.format("\n - EObject: %s#%s", ((EObject) o).eClass().getName(),
85+
((EObject) o).hashCode()));
86+
}
87+
}
88+
}
5189
}
5290

5391
System.out.println(processTrace + "\n\n");

bundles/org.eclipse.epsilon.rdf.emf/src/org/eclipse/epsilon/rdf/emf/RDFGraphResourceUpdate.java

Lines changed: 29 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,8 @@
3434
import org.apache.jena.rdf.model.Seq;
3535
import org.apache.jena.rdf.model.Statement;
3636
import org.apache.jena.vocabulary.RDF;
37-
import org.eclipse.emf.common.util.EList;
3837
import org.eclipse.emf.common.notify.Notification;
38+
import org.eclipse.emf.common.util.EList;
3939
import org.eclipse.emf.ecore.EObject;
4040
import org.eclipse.emf.ecore.EReference;
4141
import org.eclipse.emf.ecore.EStructuralFeature;
@@ -220,6 +220,10 @@ private Statement createStatement(Model model, Resource eObjectResource, EStruct
220220
return ResourceFactory.createStatement(rdfNode, property, object);
221221
}
222222

223+
private String getEMFResourceURI(EObject eObject) {
224+
return eObject.eResource().getURI().toString();
225+
}
226+
223227
private String createEObjectIRI(Model model, EObject eObject) {
224228
// If you configure a bad URI for the defaultModelNamespace then jena will revert to filename <File://path/file.ext>
225229

@@ -237,8 +241,8 @@ private String createEObjectIRI(Model model, EObject eObject) {
237241
eObjectNamespace = prefix;
238242
} else {
239243
// Fallback to file nameswith a fragment for the eObject <file>#<eObjectname>
240-
eObjectNamespace = "#";
241-
System.err.println("Warning, fallback for eObject namespace used! eObject IRI: " + eObjectNamespace + eObjectName );
244+
eObjectNamespace = getEMFResourceURI(eObject) + "#";
245+
System.err.println("Warning, fallback for eObject namespace used! (EMF Resource URI used)\n - eObject IRI: " + eObjectNamespace + eObjectName );
242246
}
243247
}
244248

@@ -632,7 +636,7 @@ private void removeAllEStructuralFeatureStatements(EObject eObject, Model model)
632636
EList<EStructuralFeature> eStructuralFeatureList = eObject.eClass().getEAllStructuralFeatures();
633637

634638
if (CONSOLE_OUTPUT_ACTIVE) {
635-
System.out.println("\nRemove all statements for: " + eObject.eClass().getName() + " #" + eObject.hashCode());
639+
System.out.println("\nRemove all statements for: " + getEObjectInstanceLabel(eObject));
636640
}
637641

638642
for (EStructuralFeature eStructuralFeature : eStructuralFeatureList) {
@@ -706,7 +710,26 @@ private void removeAllEObjectStatements(Model model, EObject eObject) {
706710

707711
deserializer.deregisterEObject(eObject);
708712
}
709-
713+
714+
//
715+
// Model resource operations (model root changes)
716+
717+
protected void addToResource(EObject eObject, Model model) {
718+
System.out.println("ADD to Resource EObject: " + eObject.eClass().getName() + " #"
719+
+ eObject.hashCode() + "\n Container: " + eObject.eContainer());
720+
if (null == eObject.eContainer()) {
721+
addAllEObjectStatements(model, eObject);
722+
}
723+
}
724+
725+
protected void removeFromResource(EObject eObject, Model model) {
726+
System.out.println("Remove from Resource EObject: " + eObject.eClass().getName() + " #"
727+
+ eObject.hashCode() + "\n Container: " + eObject.eContainer());
728+
if (null == eObject.eContainer()) {
729+
removeAllEObjectStatements(model, eObject);
730+
}
731+
}
732+
710733
//
711734
// Single-value Features operations
712735

@@ -977,7 +1000,7 @@ private static void reportRDFList (String label, RDFList container) {
9771000
}
9781001

9791002
private String getEObjectInstanceLabel (EObject eObject) {
980-
return String.format( "%s-#%s", eObject.eClass().getName(), eObject.hashCode());
1003+
return String.format( "%s#%s", eObject.eClass().getName(), eObject.hashCode());
9811004
}
9821005

9831006
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
if(consoleOutput) {
2+
"\n-Start-\n".println();
3+
}
4+
5+
var p = new Project;
6+
p.title = 'My First Project';
7+
8+
var t = new Task;
9+
t.title = 'My First Task';
10+
p.tasks.add(t);
11+
12+
if(consoleOutput) {
13+
Model.contents.println();
14+
"\n-End-\n".println();
15+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
if(consoleOutput) {
2+
"\n-Start-\n".println();
3+
}
4+
5+
var p1 = new Project;
6+
p1.title = 'My First Project';
7+
8+
var p2 = new Project;
9+
p2.title = 'My Second Project';
10+
11+
if(consoleOutput) {
12+
Model.contents.println();
13+
"\n-End-\n".println();
14+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
if(consoleOutput) {
2+
"\n-Start-\n".println();
3+
}
4+
5+
var p1 = new Project;
6+
p1.title = 'My First Project';
7+
8+
var p2 = new Project;
9+
p2.title = 'My Second Project';
10+
11+
// EOL automatically adds new objects to the
12+
// resource. Remove them from the resource and
13+
// re-add them with a single addAll operation
14+
// to test this scenario.
15+
delete p1;
16+
delete p2;
17+
18+
theModel.resource.contents.addAll(Sequence {p1, p2});
19+
20+
if(consoleOutput) {
21+
Model.contents.println();
22+
"\n-End-\n".println();
23+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
@namespace(uri="http://eclipse.org/epsilon/rdf/changeEquivalence", prefix="")
2+
package psl;
3+
4+
class Project {
5+
attr String title;
6+
attr String description;
7+
val Task[*] tasks;
8+
@diagram(direction="right")
9+
val Person[*] people;
10+
}
11+
12+
class Task {
13+
attr String title;
14+
attr int start;
15+
attr int duration;
16+
@diagram(direction="right")
17+
val Effort[*] effort;
18+
}
19+
20+
class Person {
21+
attr String name;
22+
}
23+
24+
class Effort {
25+
@diagram(direction="up")
26+
ref Person person;
27+
attr int percentage = 100;
28+
}
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
<?xml version="1.0" ?>
2+
<xmi:XMI xmi:version="2.1" xmlns:xmi="http://schema.omg.org/spec/XMI/2.1" />
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
2+
# empty! :-)

0 commit comments

Comments
 (0)