Copying EGenericType instances with ECoreUtil.Copier - eclipse

I am working with EMF models and need to make copies of these. My models consist of interlinked instances of three meta-models, one of which is Ecore itself. So far, so good.
However, when it comes to copying these models (which I do using EcoreUtil.Copier.copyAll following the usual protocol), some parts of my model are not copied. Specifically, the Ecore instance contains a number of instances of EGenericType (because there are references and attributes and these are automatically set up with EGenericType instances to show their types). The result of copying contains everything, but these instances of EGenericType.
I have searched high and low and looked into the EMF source code, too, but couldn't figure out what the problem is. I have looked at the source of EcoreUtil.Copier and it checks for each structural feature whether it is changeable and not derived to decide whether to actually copy it. This condition is true for the reference to EGenericType, so this should be copied as a containment reference.
Interestingly, the result of the copy does contain copied instances of EGenericType in all the right places in the object graph. However, these are not mapped in the copier, so don't seem to have been created by a call to EcoreUtil.Copier.copy along the way, but implicitly.
Any ideas when these are created and how I might get them to show up in the copier map?
Many thanks,
Steffen

OK, so I have dug deeper into this with the debugger and think I now understand what's going on:
Essentially, ETypedElement (which is what contains types and generic types) is a bit loose with its contract: neither eType nor eGenericType are marked as derived, but depending on the situation, they one of them will be derived from the other.
Specifically, if you set the eType of an ETypedElement, this implicitly creates a new eGenericType. Similarly, if you set the eGenericType, this will implicitly set the eType to be the erasure of this generic type.
Unfortunately, this behaviour confuses EcoreUtil.Copier when an ETypedElement has had its eType set explicitly. In this situation, the following happens:
EcoreUtil.Copier.copy creates a new instance of the ETypedElement and then starts copying all of its features.
When it gets to the eType feature, it doesn't copy it at this point, because eType is not a containment reference (for obvious reasons).
Next it comes to eGenericType, which is a containment reference. However, the first thing it does is to check whether this is set in the original ETypedElement. For eGenericType and eType, this check has been customised to make sure only one of the two actually returns true. As a result, for our ETypedElement, isSetEType() returns true and isSetEGenericType() returns true. Therefore, copyContainment() decides there is nothing to copy and moves on.
By the time copy() or copyAll() returns, neither the eType nor the eGenericType have been set for the newly created object. We now call copyReferences().
This will eventually attempt to copy the eType reference (remember that this is marked as not a containment reference). isSetType() returns true, so copyReference() goes on and copies the type information across. The setter for eType in the copied object then creates the new instance of EGenericType, but EcoreUtil.Copier never gets to see it.
Thus, if I want to get the original EGenericType instance and its copy to show up in the copy map, I will need to sub-class EcoreUtil.Copier and override copyReference() or copyContainment() to handle this special case.

Related

Make the compiler tell me that two variables can’t have the same value

I have a list of objects that need to be loaded into my app. But the load order matters. I’ve created a var which is a part of each object that indicates the load order where the lowest number gets loaded in first. Then I’ve set the default to be 9999. I can manually set this value on each object and override the default. Currently this is where I’m at.
Should I add more objects which need to be loaded in a specific order in the future, I want to make sure there are no conflicts. It seems to me like I could do that by making each load order number on the objects unique, (i.e. no duplicate numbers in all the load order variables).
Is there a way I can make Xcode throw an error or warning if it detects that anything conforming to a protocol has the same value on a variable from that protocol as any of the other conforming objects?
Well, you could list all the objects in an array literal in the order you want them loaded, and forgo the load order property entirely:
let objectsToLoad = [
ObjectDescriptor("hello"),
ObjectDescriptor("world"),
]
But if you have objects defined in disparate places and don't want a single unified array literal that lists them all, then I don't think you can get help from the compiler (or the linker).
In a few months, when Swift will have macros, you'll probably be able to get help from the compiler or the linker. The strategy is to use a macro to wrap each load-order constant in a macro that also generates some type definition whose name includes the constant, e.g. enum _LoadOrder_1 {}, enum _LoadOrder_357 {}, etc. Then, if you have a duplicate, either the compiler or the linker will fail due to the multiple definitions for a single identifier.

Is it possible to get the type of a variable while computing completion suggestions?

I'm thinking of creating a VSCode extension to help people use a library I am creating. I've looked in 50 places and can only see documentation on getting the plain text of the document in which the completion suggestions are triggered.
My library exposes a function with two parameters, both objects with the same keys, but the first one will be defined already and the 2nd one will be passed in as an object literal. For example
const obj1 = {a:1, b:2};
doLibraryThing(obj1, {a:[], b:[]});
I want to provide code completion, or a snippet, or anything that can detect that doLibraryThing is being called, and know what properties were defined in obj1, even though usually it will be imported from another file; and then use those properties to generate the suggestion with the same properties, {a:[], b:[]}.
Is this possible?

Anylogic: Declare parameter of type ArrayList

I'm building a class (sorry - Agent) that will work with a set of Tank objects (Fluid Library) - doing things like monitoring individual levels or total level of all tanks, reporting on levels and initiating actions based on levels - things of that nature. For argument's sake let's call it a "TankMonitor" agent.
Ideally I'd like to be able to define a Parameter in my "TankMonitor" agent that allows me to define the tanks of interest when I place a TankMonitor in main. I have tried to define the type of the parameter as Other - ArrayList<Tank> however I don't know how to set up the next step to allow me to populate the ArrayList of Tanks when I put an instance of this agent in main. My preference would be to have a list type control to populate the ArrayList - much like the way the AnyLogic Seize block allows you to specify multiple resource pools to choose from.
Has anyone attempted this before and been successful?
This is possible as follows:
Change the type to "Other" and then 'Tank[]' , i.e. an Array of Tanks
Change the control type to "one-dimensional array"
Example below. Now you have the same UI to pre-define tanks at design time for your agent instance.
In addition to Benjamin's perfect answer, I'd just add the manual workaround, which is not needed here but can be useful when the parameter in question has a more complicated structure than covered by the pre-made controls, say a list of lists, a map, or similar.
In such a case, the Control Type is still Text, and populating it in an instance happens by pointing it to a new object of the parameter's type. E.g. for an ArrayList<Tank> parameter, you might instantiate a new ArrayList object, which you fill with a list of objects like so:
new ArrayList<Tank>(Arrays.asList(tankA, tankB))
In the Java code, whatever is written into that text box will end up on the right side of a parameter assignment statement in the embedded Agent instance's auto-generated parameter setup function. Therefore, multi-statement code won't work in this spot. Instead, if the process of building the parameter value doesn't fit neatly into a single expression, you can hide the code in a function which returns the desired object, and call that from the parameter's text box.

How to get an OrderedSet of OccurrenceSpecifications from a Lifeline in QVTo?

From the diagram on page 570 of the UML spec I concluded that a Lifeline should have the events property, holding an OrderedSet(OcurrenceSpecification). Unfortunately it is not there, at least in the QVTo implementation that I use.
All I have is the coveredBy property, serving me with an (unordered) Set(InteractionFragment). Since my transformation relies on the correct order of MessageOcurrenceSpecification I somehow need to implement myself what I expected to be implemented by the missing events property.
This is what I have so far:
helper Lifeline::getEvents (): OrderedSet(OccurrenceSpecification) {
return self.coveredBy->selectByKind(OccurrenceSpecification)->sortedBy(true);
}
Obviously sortedBy(true) does not get me far, but I don't know any further. Who can help?
All I could find so far were other people struggling with the same issue years ago, but no solution:
https://www.eclipse.org/forums/index.php/m/1049555/
https://www.eclipse.org/forums/index.php/m/1050025/
https://www.eclipse.org/forums/index.php/m/1095764/
Based on the answer by Vincent combined with input from a colleague of mine I came up with the following solution for QVTo:
-- -----------------------------------------------------------------------------
-- Polyfill for the missing Lifeline::events property
query Lifeline::getEvents (): OrderedSet(OccurrenceSpecification) {
return self.interaction.fragment
->selectByKind(OccurrenceSpecification)
->select(os: OccurrenceSpecification | os.covered->includes(self))
->asOrderedSet();
}
I don't know if it is possible, using directly coveredBy to get an ordered collection. As coveredBy is unordered, if you access it directly by the feature, you will have an unpredictible order and if you try to access it using the eGet(...) stuff, the same result will occur.
However, if I understood correctly, there is a "trick" that could work.
It relies on the assumption that each OccurrenceSpecification instance you need is held by the same Interaction that contains the Lifeline and uses the way EMF stores contained elements. Actually, each contained element is always kind of 'ordered' relatively to its parent (and for each collection so EMF can find elements back when XMI references are expressed using the element position in collections). So, the idea would be to access all the elements contained by the Interaction that owns the lifeline and filter the ones that are contained in coveredBy.
Expression with Acceleo
This is easy to write in MTL/Acceleo. In know you don't use it, but it illustrates what the expression does:
# In Acceleo:
# 'self' is the lifeline instance
self.interaction.eAllContents(OccurrenceSpecification)->select(e | self.coveredBy->includes(e))->asOrderedSet()
with self.interaction we retrieve the Interaction, then we get all the contained elements with eAllContents(...) and we filter the ones that are in the self.coveredBy collection.
But it is way less intuitive in QVT as eAllContents(...) does not exist. Instead you have to gain access to eContents() which is defined on EObject and returns an EList which is transtyped to a Sequence (in QVT,eAllContents() returns a ETreeIterator which is not transtyped by the QVT engine).
So, how to gain access to eContents() in the helper? There is two solutions:
Solution 1: Using the emf.tools library
The emf.tools library give you the ability to use asEObject() which casts your object in a pure EObject and give you more methods to access to (as eClass() for example...etc).
import emf.tools; -- we import the EMF tools library
modeltype UML ...; -- all your metamodel imports and stuffs
...
helper Lifeline::getEvents (): OrderedSet(OccurrenceSpecification) {
return self.interaction.asEObject().eContents()[OccurrenceSpecification]->select(e | self.coveredBy->includes(e))->asOrderedSet();
}
Solution 2: Using oclAstype(...)
If for some reason emf.tools cannot be accessed, you can still cast to an EObject using oclAsType(...).
modeltype UML ...; -- all your metamodel imports and stuffs
modeltype ECORE "strict" uses ecore('http://www.eclipse.org/emf/2002/Ecore'); -- you also register the Ecore metamodel
...
helper Lifeline::getEvents (): OrderedSet(OccurrenceSpecification) {
return self.interaction.oclAsType(EObject).eContents()[OccurrenceSpecification]->select(e | self.coveredBy->includes(e))->asOrderedSet();
}
Limitation
Ok, let's be honest here, this solution seems to work on the quick tests I performed, but I'm not a 100% sure that you will have all the elements you want as this code relies on the strong assumption that every OccurrenceSpecification you need are in the same Interaction as the Liteline instance. If you are sure that all the coveredBy elements you need are in the Interaction (I think they should be), then, that's not the sexiest solution, but it should do the work.
EDIT>
The solution proposed by hielsnoppe is more eleguant than the one I presented here and should be prefered.
You are correct. The Lifeline::events property is clearly shown on the diagram and appears in derived models such as UML.merged.uml.
Unfortunately Eclipse QVTo uses the Ecore projection of the UML metamodel to UML.ecore where unnavigable opposites are pruned. (A recent UML2Ecore enhancement allows the name to be persisted as an EAnnotation.) However once the true property name "events" has been pruned the implicit property name "OccurrenceSpecification" should work.
All associations are navigable in both directions in OCL, so this loss is a bug. (The new Pivot-based Eclipse OCL goes back to the primary UML model in order to avoid UML2Ecore losses. Once Eclipse QVTo migrates to the Pivot OCL you should see the behavior you expected.)

How a value typed variable is copied when it is passed to a function, what hold this copy?

Swift's string type is a value type. If you create a new String value, that String value is copied when it is passed to a function or method, or when it is assigned to a constant or variable.
It is copied when assigned to a constant or variable, this makes sense for me. But when a value typed variable passed to a function it also get copied, this confuses me.
Question
How it gets copied when passing a value typed variable to a function? what kind of "space" holds this copy? Is it some sort of temporary variables invisibly created behind the scene and after the process of the function it gets destroyed?
Thanks
When passing value type to a function, think of it like assigning it to a local variable whose scope is that function, so the copy behavior is analogous to just assigning a new local variable.
Regarding where it is copied, we should recognize that the copy behavior is actually more complicated than it sounds. As they point out in Building Better Apps with Value Types in Swift (WWDC 2015, Session 414), "Copies are Cheap":
Copying a low-level, fundamental type is constant time
Int, Double, etc.
Copying a struct, enum, or tuple of value types is constant time
CGPoint, etc.
Extensible data structures use copy-on-write
Copying involves a fixed number of reference-counting operations
String, Array, Set, Dictionary, etc.
Regarding that last point, behind the scenes Swift does some sleight of hand that avoids copying extensible value types every time they're referenced, but rather just points to the original reference but keeps track of how many references there are and actually only makes copies (a) upon write; where (b) there's more than one reference. This behavior is discussed in more detail in that video.
Value types in Swift get copied, however in most of the cases only some minor details get duplicated, most of the data remains the same. Take the example of a String value. When passing the value to a function some information get's copied, like the actual pointer to the chars buffer, or its length, but the actual data just gets passed along. This means that passing by value in Swift is a fast operation.
Swift clones the actual data when it detects that it needs to write on it. For example if an array is passed from a let to a var no major copies are made. If however you update the new array by appending a new element, then at that point a clone of the array contents is created, and the new element is added there.