archimatetool / archi-portico-plugin

Archi plugin to import/merge a reference model into another model
6 stars 2 forks source link

Merge Folders and Concepts #1

Open Phillipus opened 4 years ago

Phillipus commented 4 years ago

The first thing I'm tackling for the import/merge operation is Folders and their contents.

"source" = model that is imported and merged "target" = model that receives the imported model

What happens in the following cases?

How should we handle merging folders and their child objects:

jbsarrodie commented 4 years ago

First we have to define what exists means. My definition would be that an object exists in the model with the same type and the same Id. This means that if an object exists with the same Id but different type, operation should be aborted.

We also have to restate some generic principles when merging model:

What happens in the following cases?

  • A folder exists in source but not in target

We simply create it in target.

  • A folder exist in both but has been moved

This is in fact one case of: a folder exist in both but has been changed (name, documentation, properties or location changed).

If Keep version from target then do nothing.

If Keep version from source then it should be restored to its original state (ie. we should restore its name, documentation and properties, and it should be moved back to its original location). This might involve doing it in two passes (1. import folders, 2. move them in their original location).

  • Other cases?

Can't see any for the moment

Phillipus commented 4 years ago

First we have to define what exists means. My definition would be that an object exists in the model with the same type and the same Id. This means that if an object exists with the same Id but different type, operation should be aborted.

That's already taken care of.

don't try to merge properties...

That's not hard to do if we want to do that...

jbsarrodie commented 4 years ago

That's not hard to do if we want to do that...

I prefer an all or nothing approach at the moment. My purpose here is to say that we'll not create a dialog box similar to the one we have in coArchi, to manage conflicts.

Phillipus commented 4 years ago

We could do a simple merge of Properties and Features. If key exists in target then set the value, if not create a new one.

jbsarrodie commented 4 years ago

We could do a simple merge of Properties and Features. If key exists in target then set the value, if not create a new one.

But keys are not unique. So better not to bother.

Phillipus commented 4 years ago

We now have a first working version that merges/updates Folders and concepts.

Please test this before we proceed onto merging Views!

jbsarrodie commented 4 years ago

Please test this before we proceed onto merging Views!

Ok, I'll do later today

jbsarrodie commented 4 years ago

Testing now !

Here is model A: image

Here is model B: image

1st Test A is opened, B is closed. Merging B into A with "update" option set.

2nd Test Same as 1st Test + Rename a Folder and one of the Capabilities, delete from other Capabilities then import B into A again, "update" option not set.

**3rd Test"" Same as 2nd Test but with "update" option set.

4st Test Same as 2nd Test, but in addition create a view based on a changed concept and add a relationship between changed concept and an element from model A.

5st Test Same as 4st Test but with "update" option set.

6st Test Same as 2nd Test, but create a view containing two capabilities from B and their composition relationship, then change on end of the relationship so that the composition now targets the renamed concept (which is also a capability)

7st Test Same as 4st Test but with "update" option set.

Perfect!

Remark: When managing views, Test #6 and #7 should be looked at, because we might have some edge cases:

Another edge case:

Phillipus commented 4 years ago

Dirty flag is not set on A, so when closing without prior saving, it is not saved and changes are lost.

This is a side-effect of resetting the Command Stack. It can only be "dirty" if there's something on the Command Stack. This is temporary until we have it all working and I can look at sorting out undo/redo.

I'll open a new issue for Views...

jbsarrodie commented 4 years ago

This is a side-effect of resetting the Command Stack.

That was my assumption. Just wanted to confirm.

jbsarrodie commented 4 years ago

Slight bug: when "update mode" is on, the model element itself is also updated which override its documentation and properties (but shouldn't).

Phillipus commented 4 years ago

Slight bug: when "update mode" is on, the model element itself is also updated which override its documentation and properties (but shouldn't).

But surely that means replace them?

        // Upate root model information
        if(replaceWithSource) {
            targetModel.setPurpose(importedModel.getPurpose());
            updateProperties(importedModel, targetModel);
            updateFeatures(importedModel, targetModel);
        }
jbsarrodie commented 4 years ago

But surely that means replace them?

Seems so, but we really don't want to do this for the model element itself :-)

Phillipus commented 4 years ago

Seems so, but we really don't want to do this for the model element itself :-)

OK, easy enough to take that out.

Phillipus commented 4 years ago

With reference to bug in #2:

This happens when the relationship involves another relationship. Maybe you first import elements and then only relationship, assuming that then relationships can be imported without issues, but two passes (or some other trick) should be used.

I could use a simple test case if you have one?

Import Concept:

  1. If concept does not exist, clone a new one
  2. If concept does exist, update it
  3. If concept is a new one OR we have selected "update with source" AND it's a relationship Update Relationship Ends

Update Relationship Ends:

  1. Find sourceEnd concept in target model. If not found, Import Concept. Set the end.
  2. Find targetEnd concept in target model. If not found, Import Concept. Set the end.

So if a relationship end is another relationship end that has not been imported yet a new relationship is created. When the relationship is imported, it is just updated. So it should work, but clearly it isn't. Hmmm.....

BTW - I've set it to run the Model Integrity Checker after the import, so you don't have to save it.

Phillipus commented 4 years ago

Hmmm... I get a bad feeling about the logic in ConceptImporter...

Phillipus commented 4 years ago

I've created a new branch "test" with some changes to ConceptImporter. I'm not sure if this will solve the bug of missing ends of a relationship. The bug might lie elsewhere, but if you could test it... :-)

jbsarrodie commented 4 years ago

I've created a new branch "test" with some changes to ConceptImporter. I'm not sure if this will solve the bug of missing ends of a relationship. The bug might lie elsewhere, but if you could test it... :-)

You can remove this new branch: I've found the issue!

I was looking at your changes and noticed that the original logic was good. So as I didn't understand why it was failing I checked a bit more, and....

And I saw this:

    private void setRelationshipEnds(IArchimateRelationship importedRelationship, IArchimateRelationship targetRelationship) throws PorticoException {
        IArchimateConcept source = findObjectInTargetModel(importedRelationship.getSource());
        if(source == null) {
            source = importConcept(importedRelationship.getSource());
        }
        targetRelationship.setSource(source);

        IArchimateConcept target = findObjectInTargetModel(importedRelationship.getTarget());
        if(target == null) {
            source = importConcept(importedRelationship.getTarget());
        }
        targetRelationship.setTarget(target);
    }

You don't see? I'm helping you a bit:

        IArchimateConcept target = findObjectInTargetModel(importedRelationship.getTarget());
        if(target == null) {
            source = importConcept(importedRelationship.getTarget());
        }

We check target but we set source.

Replace with this and it works as expected:

        IArchimateConcept target = findObjectInTargetModel(importedRelationship.getTarget());
        if(target == null) {
            target = importConcept(importedRelationship.getTarget());
        }

So IMHO, no need to change the logic.

Phillipus commented 4 years ago

D'oh!

Thanks for that.

I spent all morning looking at this code thinking "it should work..." and I didn't see that. I'm glad you found it as I was quite pleased with this recursive method. ;-)

jbsarrodie commented 4 years ago

I spent all morning looking at this code thinking "it should work..."

I thought the same! So I created a really small model to recreate the issue and then noticed that the error was always about "target". I switched to debug mode and found it :-)

jbsarrodie commented 4 years ago

Now I can go back to selecting the right looper pedal for Christmas ;-)

Phillipus commented 4 years ago

Now I can go back to selecting the right looper pedal for Christmas ;-)

I have a Boss RC-3 https://www.boss.info/us/products/rc-3/

jbsarrodie commented 4 years ago

I have a Boss RC-3 https://www.boss.info/us/products/rc-3/

I find it too expensive. I've seen multi-effects pedals that include looper for same price or less:

Phillipus commented 4 years ago

I have a Boss multi-fx pedal with looper...but..it doesn't remember the loop(s) when powered off.

boolean saved = saveLoop(ILoop loop);
if(!saved) {
    throw new PedalOutTheWindowException();
}
Phillipus commented 4 years ago

Be careful - there's a bug where relations are duplicated when connections are created. The duplicates are phantom relations and don't show in the UI.

Under investigation...

Phillipus commented 4 years ago

Be careful - there's a bug where relations are duplicated when connections are created. The duplicates are phantom relations and don't show in the UI.

Fixed in master branch.

Now I have to fix it in the dev branch of Undo/Redo...

jbsarrodie commented 4 years ago

Be careful - there's a bug where relations are duplicated when connections are created. The duplicates are phantom relations and don't show in the UI.

Whoops, I used it in production. Any tips on how to discover those phantom relations? Do they show up after a model close/open or are they contained in a folder which is not "Relationships" ?

jbsarrodie commented 4 years ago

Whoops, I used it in production. Any tips on how to discover those phantom relations? Do they show up after a model close/open or are they contained in a folder which is not "Relationships" ?

I guess it's easier for me to simply rollback the commit (I use coArchi) and redo the model merge.

Phillipus commented 4 years ago

I used it in production.

:-0

Double relationships are (were) created. This will only show up if you then drag and drop a relationship or concepts onto a View and connections are created. But then you won't be able to save it because the Model Checker will see these as orphans. So if you didn't do that then you should be OK since the duplicate relation does not have a parent folder...

Phillipus commented 4 years ago

Something to consider now...

The structure of sub-folders and their objects are updated when importing even if you don't select "Update and replace from selected model".

So, if you import a model then re-arrange those imported objects and/or subfolders in the tree they will return to their original positions if you import again.

I think I did it this way to ensure that nothing was orphaned or went weird if the source folder structure changes.

But probably not the behaviour we want, right?

Phillipus commented 4 years ago

...when "update mode" is on, the model element itself is also updated which override its documentation and properties (but shouldn't).

What about top level folders (Strategy, Business, Application, etc.)? Do we want to replace the Documentation and Properties of these?

jbsarrodie commented 4 years ago

...when "update mode" is on, the model element itself is also updated which override its documentation and properties (but shouldn't).

What about top level folders (Strategy, Business, Application, etc.)? Do we want to replace the Documentation and Properties of these?

That's a good question. IMHO (based on my original use-cases) I think we shouldn't update top level folders. That's for the MVP. After that, we could imagine another option (usable only when update mode is checked) to allow update of model element and top level folder too. Something like:

[x] Allow update
[ ] Update model object and top level folders too
Phillipus commented 4 years ago

we could imagine another option (usable only when update mode is checked) to allow update of model element and top level folder too. Something like:

Already thought of that. Easy enough to implement. ;-)

jbsarrodie commented 4 years ago

So, if you import a model then re-arrange those imported objects and/or subfolders in the tree they will return to their original positions if you import again.

I think I did it this way to ensure that nothing was orphaned or went weird if the source folder structure changes.

But probably not the behaviour we want, right?

Yes, not the wanted behavior. We should:

For me, moving concept in their "original positions" is already an "update" action...

Phillipus commented 4 years ago

For me, moving concept in their "original positions" is already an "update" action...

OK, so if update is selected then move concepts and sub-folders back to their original positions? Maybe this should be an additional option in the wizard?

jbsarrodie commented 4 years ago

For me, moving concept in their "original positions" is already an "update" action...

OK, so if update is selected then move concepts and sub-folders back to their original positions?

Yes.

Maybe this should be an additional option in the wizard?

No because the goal of "update" mode is exactly this: make sure that everyting that has been imported before but changed since, return to the right place with the right content. So this includes moving things to their original folders.

Model and top level folders are apart because they are forced to be in any model, so we know in advance that there will be "conflicts", but not because users did a first import and changed things.

Phillipus commented 4 years ago

That's a good question. IMHO (based on my original use-cases) I think we shouldn't update top level folders. That's for the MVP. After that, we could imagine another option (usable only when update mode is checked) to allow update of model element and top level folder too. Something like:

[x] Allow update
[ ] Update model object and top level folders too

Done.

Phillipus commented 4 years ago

Yes, not the wanted behavior. We should:

* create folders if they don't exist
* create elements that don't exist, and in this case create them under the right folder (like in source)
* if an element already exists, then do nothing (ie. don't move it)

For me, moving concept in their "original positions" is already an "update" action...

Done.

jbsarrodie commented 4 years ago

I've just tested and everything seems ok, so closing.

jbsarrodie commented 4 years ago

Reopening (I forgot that edge cases were described in this issue)

Edge case 1

  • Model B as already been imported into A
  • Some relationship's ends has been changed
  • Let's assume these relationships were used in a view "V" coming from B, but remove since
  • We merge again B into A with "update" option not set
  • "V" should be restored (because this is a merge and "V" no more exist), but "V" contains a relationhip whose ends have changed, so this relationiship can no more exist in "V"
  • So in fact we can't restore "V"

What do we do? Either we abort the whole merge operation because we can't merge/restore "V" exactly as it was in B, or we raise a warning?

From my tests, as of now, running this tests end up with the view being imported and the relationship ends moved back to their original elements (ie. relationship has been updated while update mode is off). I can confirm that relationship is not updated if no views from B contain it (expected behavior).

My thoughts:

Edge case 2

  • Model B as already been imported into A
  • Some relationship's ends has been changed
  • Let's assume these relationships were used in a view "V" which does not come from B (ie. created after the first merge)
  • We merge again B into A with "update" option set
  • Because "update" option is set, the relationship is restored to its original ends.
  • "V" is impacted because now the relationship is no more valid in the context of the view

What do we do? Warning?

Tested, and of course, "V" is impacted as expected.

My thoughts:

In addition:

Phillipus commented 4 years ago

This is in line with https://github.com/archimatetool/archi-portico-plugin/issues/2#issuecomment-565859209

jbsarrodie commented 4 years ago

This is in line with #2 (comment)

Yes, so I suggest to open a new issue for this (doing it atm).