Closed jbsarrodie closed 2 years ago
Just added Label Expression
to the list.
Model import feature
This is in branch specialization-modelimporter
and needs testing.
CSV export/import
Not sure what needs to be done for this.
Copy/Paste (inside same or different models)
Working in same model. But if between different models, Profiles and Profile refs are not pasted as it can get extremely complicated (would be like the model importer)
Format Painter
Working. But any images are not pasted if pasting to a different model (again, that gets very complicated adding images to target ArchiveManager, undo/redo, etc)
Potential impact on model templates
Not aware of any impact.
Potential impact on Canvas template
Not aware of any impact.
Label Expression
What do we want to do here?
CSV export/import
Not sure what needs to be done for this.
I think I would add a Specialization
column on elements.csv
and relations.csv
. This columns should be the last one (in case someone uses the columns order, but who would do such thing nowadays?) and should contain the name of the specialization of the element or relationship.
Label Expression
What do we want to do here?
I was thinking about ${specialization}
that would return the name of the specialization of the concept (if any).
In fact the goal is to be able to reproduce the default guillemets notation, so in fact we need a bit more than that because if we use a label such as...
${name}
<<${specialization}>>
...we'll end up we something like...
Some Concept
<<>>
...if the concept has no specialization.
So I see two options:
${specialization}
return the concept type if there's no specialization defined. Simple but not inline with the intended goal.${specialization:before:after}
which (only if specialization is defined) adds before
before the specialization name and after
, well... after the specialization name. So my example would become ${name}${specialization:\n<<:>>}
${concat:str1:str2}
which return the concatenation of str1
and str2
only if none of them is null. So my example would become ${name}${concat:${concat:\n<<:${specialization}}:>>}
Potential impact on model templates
Not aware of any impact.
Potential impact on Canvas template
Not aware of any impact.
I think none but I have to test.
Format Painter
Working. But any images are not pasted if pasting to a different model (again, that gets very complicated adding images to target ArchiveManager, undo/redo, etc)
I have to test and see if this is intuitive.
Copy/Paste (inside same or different models)
Working in same model. But if between different models, Profiles and Profile refs are not pasted as it can get extremely complicated (would be like the model importer)
I have to better document my idea which is to set the specialization if one exist in the target model with the same id or the same name (if no match on id).
Model import feature
This is in branch
specialization-modelimporter
and needs testing.
I have to test. I think there's an underlying issue: if I import a model containing the definition of one specialization which has the same name of a specialization already defined in the target model, then they should be considered the same.
In fact, from an ArchiMate point of view, the specialization is identified by its name (that's the only thing visible to the user). But in a tool, we need to have a technical id or else renaming a specialization would impact lots of concepts. So:
I think there's an underlying issue: if I import a model containing the definition of one specialization which has the same name of a specialization already defined in the target model, then they should be considered the same.
Name should be used as an equivalent of the id when importing/merging/pasting (I can elaborate on this and provide examples if needed)
I just don't think that's possible, or at least very difficult to implement and manage,
I just don't think that's possible, or at least very difficult to implement and manage,
My feeling is that I'm not explaining it well and that this should not be complicated, so that's why I have to check the code for model import and see myself if/how it could be done. Of course, maybe I'm totally wrong and there's a blocking point I just don't see for the moment.
that's why I have to check the code for model import and see myself if/how it could be done.
This seems possible I've pushed a first attempt (see this commit). It needs a bit more work but I'll continue tomorrow.
This seems possible I've pushed a first attempt (see this commit). It needs a bit more work but I'll continue tomorrow.
Ok, I've worked a bit more on this and I think it is possible (as shown in shared code).
While working on it, I faced a bug that I thought was due to my changes, but in fact I can reproduce it with your code (the one from specialization-modelimporter
branch):
My analysis:
ProfileImporter
takes care of updating the list of profiles for the model, but it doesn't update already existing concepts (in targetModel) that were referencing the now superseeded profile.It seems we should use ArchimateModelUtils#findProfilesUsage()
to get the list of concepts attached to the original profile and then add a command similar to ConceptImporter.UpdateProfilesCommand
.
While trying to uderstand the logic behind, this seems due to the fact that, for concepts, we use ModelImporter#updateObject()
which copies attributes of importedObject into targetObject, while for profiles, ProfileImporter#updateProfile()
relies on ModelImporter#cloneObject()
which creates a new eObject.
This made me wonder if we could not simplify the code a bit to have a mode generic implementation of updateObject()
that would be inspired by EcoreUtil.Copier#copy()
and would work on (almost) any object.
This made me wonder if we could not simplify the code a bit to have a mode generic implementation of updateObject() that would be inspired by
EcoreUtil.Copier#copy()
and would work on (almost) any object.
No, don't use EcoreUtil.Copier#copy()
directly. It will make a deep copy with references to other objects that we don't want. That's why I made updateObject()
shallow.
It might be better to use a modified version of updateObject()
that copies the Profiles other info.
No, don't use
EcoreUtil.Copier#copy()
directly. It will make a deep copy with references to other objects that we don't want. That's why I madeupdateObject()
shallow.
I was thinking about a simplified version of copy
that would just update all EStructuralFeature
:
public copyInto(EObject eObject, EObject copyEObject)
{
if (eObject == null || copyEObject == null)
{
return null;
}
else
{
EClass eClass = eObject.eClass();
for (int i = 0, size = eClass.getFeatureCount(); i < size; ++i)
{
EStructuralFeature eStructuralFeature = eClass.getEStructuralFeature(i);
if (eStructuralFeature.isChangeable() && !eStructuralFeature.isDerived())
{
if (eStructuralFeature instanceof EAttribute)
{
copyAttribute((EAttribute)eStructuralFeature, eObject, copyEObject);
}
/*
else
{
EReference eReference = (EReference)eStructuralFeature;
if (eReference.isContainment())
{
copyContainment(eReference, eObject, copyEObject);
}
}
*/
}
}
}
return copyEObject;
}
Of course this won't do 100% of the job but would help us not forget to update some EFeature...
...And this made me think about a potential case in Archi 4.8.1:
A
and add an an Application Component which accesses a Data Object. Set the relationship to read/write.B
and import A
into it.B
, change the access relationship to be juste "access" (i.e. no read nor write)A
again into B
and make sure you allowed updates.B
, access should be back to read/write but it isn't.This is because ModelImporter#updateObject()
only updates Name, Documentation, Properties and Features. So I guess this issue exists in the following cases:
So which approach do you prefer? extending ModelImporter#updateObject()
to support these exact cases, or make it a bit more generic by using (a variant of) my copyInto()
function?
Yes, those cases are missing. We do need a generic copy()
function that doesn't have to be maintained. Something like copyInto()
would do it, as long as it was Undo-able.
Yes, those cases are missing. We do need a generic
copy()
function that doesn't have to be maintained. Something likecopyInto()
would do it, as long as it was Undo-able.
Ok, I'm on it. I'll first provide a generic fix based on master
(ie. the code without specialization/profiles) that we'll backport here when tested and agreed.
So, I'm creating a dedicated issue for this topic.
I'll first provide a generic fix based on master
Is there any point? We won't be releasing Archi 4.8.2 so any changes will be part of 4.9.
Also, there is some refactoring in specialization-modelimporter
branch that is useful
Is there any point? We won't be releasing Archi 4.8.2 so any changes will be part of 4.9.
Well, I prefer working on something "simple" first and then upgrade to support profiles. It just seems incremental and easier for me this way. (this doesn't mean it will take a long time)
Edit: I was not aware of some refactoring done in specialization-modelimporter
. I'll work on this branch.
was thinking about
${specialization}
that would return the name of the specialization of the concept (if any).
I've implemented (with unit tests) the base implementation of this in the working
branch.
Playing devil's advocate for a moment...
Do we need anything more complex than that? Does it matter if a user sees <<>>
if no Specialization is set? Perhaps they didn't want to set one and so the label expression is not needed? Will users really use something like ${concat:${concat:\n<<:${specialization}}:>>}
? ;-)
Does it matter if a user sees
<<>>
if no Specialization is set?
Not very user friendly
Perhaps they didn't want to set one and so the label expression is not needed?
If someone doesn't want to use specialization images but instead want to use the guillemet notation, he/she will certainly set the label expression on all elements of the view as this can be done quickly (select all and edit the label). At least, that's ahow I manage such cases.
Will users really use something like
${concat:${concat:\n<<:${specialization}}:>>}
? ;-)
I have seen worse than that ;-)
But people can also create a model property named Specialization Label
, set its value to ${concat:${concat:\n<<:${specialization}}:>>}
, and then, on each visual object they can simply use $model{property:Specialization Label}
...
OK. I just wanted to hear the use case. :-)
Gonna need help with concat
:cat: 🐈
Gonna need help with
concat
No pb.
BTW, might be best to name it condcat
for conditional concatenate ;-)
BTW, concat
(or condcat
) makes it possible to create conditionnal labels as that's a basic if
then
🤯
BTW,
concat
(orcondcat
) makes it possible to create conditionnal labels as that's a basicif
then
Thinking about it... maybe we should really go this way and define ${if:condition:text}
where text is returned if condition evaluate to a non empty string
So ${name}${concat:${concat:\n<<:${specialization}}:>>}
could be simplified to ${name}${if:${specialization}:\n<<${specialization}}>>}
which is easier to understand.
🤯 🤯 🤯 🤯 🤯 🤯 🤯 🤯 🤯
While working on it, I faced a bug that I thought was due to my changes, but in fact I can reproduce it with your code (the one from
specialization-modelimporter
branch):* Create a model A which only contains the definition for a specialization (default one for Business Actor is perfect) * Create a model B and import A into it. * In B, add a new Business Actor with the specialization imported from A. * Try to import A again into B while allowing update, you'll get an error similar to this one: ![image](https://user-images.githubusercontent.com/5757396/123939073-0958c100-d998-11eb-8fe7-c27fa9df6e32.png)
My analysis:
* When importing the second time, the profile object will be re-created because we allowed updates. * `ProfileImporter` takes care of updating the list of profiles for the model, but it doesn't update already existing concepts (in targetModel) that were referencing the now superseeded profile.
It seems we should use
ArchimateModelUtils#findProfilesUsage()
to get the list of concepts attached to the original profile and then add a command similar toConceptImporter.UpdateProfilesCommand
.While trying to uderstand the logic behind, this seems due to the fact that, for concepts, we use
ModelImporter#updateObject()
which copies attributes of importedObject into targetObject, while for profiles,ProfileImporter#updateProfile()
relies onModelImporter#cloneObject()
which creates a new eObject.This made me wonder if we could not simplify the code a bit to have a mode generic implementation of
updateObject()
that would be inspired byEcoreUtil.Copier#copy()
and would work on (almost) any object.
Should be fixed. See https://github.com/archimatetool/archi/issues/746
Any reason for ${type}
not to return the specialization (if there is one), this is how I think of specialization - a new specialized type. You could extend the expression, e.g. ${type:base}
if you explicitly want the base type. Both of these will always return something.
Perhaps I wasn't clear.
Yes I understand ${type}
is currently returning the base type of the
object but if it is specialized why wouldn't ${type}
return the
specialized type name?
E.g. a specialization of the parent concept "Business Actor" to the new
concept "Threat Agent" (from the standard "Specialization is a simple and
powerful way to define new elements or relationships based on the existing
ones.").
I was suggesting the ${type:base}
or ${type:parent}
would always return
the core type. Whereas ${type}
would return the specialized type if one
is defined or the base type. (Perhaps parent
more aligns to the standard
which describes "Parent Concept" and "Specialized Concept").
On Wed, Jul 28, 2021 at 12:57 AM Phil Beauvoir @.***> wrote:
Any reason for ${type} not to return the specialization (if there is one), this is how I think of specialization - a new specialized type. You could extend the expression, e.g. ${type:base} if you explicitly want the base type. Both of these will always return something.
We already use ${type}:
See https://github.com/archimatetool/archi/wiki/Label-Expressions
— You are receiving this because you commented. Reply to this email directly, view it on GitHub https://github.com/archimatetool/archi/issues/740#issuecomment-888032726, or unsubscribe https://github.com/notifications/unsubscribe-auth/ABBZ2TOYRMWYMO2K7JQEEJTTZ6L4VANCNFSM47IGIODQ .
One other thought is perhaps use shell (e.g. bash) like parameter substitutions as an alternative to if
or concat
, e.g.:
From bash:
${parameter+alt_value} - If parameter set, use alt_value, else use null string.
So if you optionally want to print <<${specialization}>>
after ${name}
${name}${specialization+\n<<${specialization}>>}
Note: there is also ${param-default}
which provides a default value if param
isn't set, e.g. if you want to print <<${specialization}>> if it exists or <<${type}>> if it doesn't:
<<${specialization-${type}}>>
Note: The specialization
and specialization-modelimporter
branches have been rebased onto the latest master branch.
Any reason for
${type}
not to return the specialization (if there is one), this is how I think of specialization - a new specialized type.
I personnaly prefer to have two separated expression because in some cases one could want to get access to the real (i.e. ArchiMate) type instead of the specialization. This also matches jArchi API.
You could extend the expression, e.g.
${type:base}
if you explicitly want the base type. Both of these will always return something.
That could be an option, but we'll then end up with:
${type}
for returning type or specialiation if defined${type:base}
for returning the ArchiMate type${type:specialization}
for returning the specializationAnd this seems (at least to me) less user friendly and explicit than:
${type}
for returning the ArchiMate type${specialization}
for returning the specializationOf course, this makes it a bit more complexe to get either type or specialization:
${if:${specialization}:${specialization}:${name}}
(assuming an new ${if:condition:string:string}
generix expression)
One other thought is perhaps use shell (e.g. bash) like parameter substitutions as an alternative to
if
orconcat
I like the idea (I used to do a lot of shell scripting). I have to think about it a bit more to see if it doesn't make it more complexe for a typical user (usually more the kind of people that is ok with Excel formulas not not so much with scripting).
I like the idea (I used to do a lot of shell scripting). I have to think about it a bit more to see if it doesn't make it more complexe for a typical user (usually more the kind of people that is ok with Excel formulas not not so much with scripting).
After some more thought, I don't think it would be useful because this shell notation assumes the first part is the name of a variable while with labels this is not the case: it looks similar for simple case (type, documentation, specialization) but not for expressions taking arguments. This also means that we would have to implement it for each expression, which is a lot more work than a unique ${if...}
.
So I think I'll go for some conditionnal expressions such as:
${if:condition:stringIfConditionNotNull:stringIfConditionNull}
: generic if/then/else structure${if:condition:stringIfConditionNotNull}
: simplified if/then structure ${ifnull:condition:stringIfConditionNull}
: simplified if-not/then structure${nvl:stringToCheck:stringIfNull}
: returns stringToCheck
if it is not null or stringIfNull
otherwise@Phillipus : I'll implement them myself
@jbsarrodie Keep in mind if more complex expressions might slow things down. Every action performed in Archi will refresh the label expression for every visible object (and Models Tree).
@jbsarrodie Keep in mind if more complex expressions might slow things down. Every action performed in Archi will refresh the label expression for every visible object (and Models Tree).
Of course, but I don't think it will be worse than some expressions I already tested (element's label referencing expressions snippets stored as model properties). Testing only if condition
is empty (and no other math based condition) should make it easier for most of them because in fact I don't mind the exact content of condition
most of the time and will just have to test if the keyword (if, ifnull, nvl) is followed by two :
or not.
So I think I'll go for some conditionnal expressions such as:
${if:condition:stringIfConditionNotNull:stringIfConditionNull}
: generic if/then/else structure${if:condition:stringIfConditionNotNull}
: simplified if/then structure${nvl:stringToCheck:stringIfNull}
: returnsstringToCheck
if it is not null orstringIfNull
otherwise
These 3 have been implemented in commit 847beeca02a024b1470ad1bf275f7b941995a453
${ifnull:condition:stringIfConditionNull}
: simplified if-not/then structure
This one won't be implemented (I don't see much value in it)
I've added a first check based on String.contains()
to see if keywords (if, nvl) are there, so that we don't use Matcher
if not needed.
To keep track of recent offline discussions with Phil about CSV
In commit 765260b4802dee0f65c05127d05a38d9486f152b, @Phillipus wrote
We should perhaps have a new file for Specialization containing ID, Name in case we add further info such as Documentation and Properties
This will be needed in the future when we'll have more informations attached to a profile/specialization. But for the moment (MVP) we only need a way to know which specialization is attached to a concept.
This could be done either by adding the name of the specialization to elements.csv
and relationships.csv
, or by adding the id of the specialization and add a specialization.csv
. Because a specialization can always be identified by its name and the concept type it refers to, we can safely use this name and not a technical id. This has two advantages: easier to read and use for people, and no need for a new file yet.
Thanks for the conditional expressions!
We need some unit tests for them. I can do the unit tests if you can provide some examples of usage.
Do you think the expressions will have much impact on refresh times?
We need some unit tests for them. I can do the unit tests if you can provide some examples of usage.
Here's my test model for this: Test for specialization, if and nvl.zip
It contains a single view with 3 label expression to test, each with two objects (one with specialization, one without) :
Do you think the expressions will have much impact on refresh times?
I don't think so because I've made sure we actually use RegExp only if we are sure the expressions are used. Maybe we could even use this trick elsewhere.
But at some point it would be good to have a real, testable way to mesure impact of new expressions.
Maybe we could even use this trick elsewhere.
private String renderWithExpression(IArchimateModelObject object, String formatExpression, String defaultText) {
if(!StringUtils.isSet(formatExpression)) {
return defaultText;
}
But, yeah, on each renderer we could do a match to test it.
Edit: there is an overhead in String#contains
which may be about the same as calling Matcher#find
in the simpler renderers.
But at some point it would be good to have a real, testable way to mesure impact of new expressions.
I did this when we first created the label renderers. I had a huge diagram with hundreds of label expressions. I added a timer to check the results. Time overhead was not too bad. As long as people don't have too many Views open it should be OK. Anyway, Apple have a new M1 chip now, maybe we can persuade them to add Archi label renderer support... ;-)
I've pushed a new commit with unit tests for the renderer. It makes it easier to test all kinds of cases should the need arise.
If you want to add any more tests you can add them to IfRendererTests
(that's not a hint to add more, just if you are curious)
See https://github.com/archimatetool/archi/wiki/JUnit-Tests for how to run them in Eclipse.
reverted...fixing a bug...before pushing again...
reverted...fixing a bug...before pushing again...
...done
@jbsarrodie It's getting harder to manage these branches and I think there's no going back now. I suggest:
specialization
into masterhtml-report2
into masterhtml-report
(old branch)specialization
to the same commit as master and continue development work in thatspecialization-model-importer
onto masterAgree?
Agree?
Yes
@jbsarrodie It's getting harder to manage these branches and I think there's no going back now. I suggest:
* merge `specialization` into master * merge `html-report2` into master * delete `html-report` (old branch) * set `specialization` to the same commit as master and continue development work in that * rebase `specialization-model-importer` onto master
Agree?
Done. Some other branches have been rebased/cherry picked too,
CSV import/export is done now in branch specialization-csv
. Just needs to be tested.
@jbsarrodie There are now three branches that need testing. I've re-based them onto latest master
which uses a target of Eclipse 4.21 so you'll have to do the reload target thing...
specialization-csv
- CSV import and export
specialization-model-importer
- Model importer
copy-paste
- Format painter copies the image to an object if it is in a different model, and Copy/Paste copies images and specializations if the target is a different model (if a target specialization exists use that, else create a new one)
...just found a bug in copy-paste
...that will need fixing first.
and Copy/Paste copies images and specializations if the target is a different model (if a target specialization exists use that, else create a new one)
Temporarily removed this. It's not as simple as I first thought.
Temporarily removed this. It's not as simple as I first thought.
And...it's back again. That was tricky - undo/redo can have some hidden gotchas... 🤓
copy-paste
- Format painter copies the image to an object if it is in a different model, and Copy/Paste copies images and specializations if the target is a different model (if a target specialization exists use that, else create a new one)
I've just tested it and for me it works as expected. Now I only need to test new jArchi features...
Hey chaps, (fabulous work on Specialisations thus far!) will there be any support for Specialisations in the "Search Filters" functionality, it would be great if we could filter on our specialisations here
Maybe as a "third tier" list under the "parent" concept, e.g. under "Artifact > Database (icon), File (icon)"?
@smileham Where do you think they should appear? Alongside the concepts, or in a separate menu list?
See #705 for the core support for specialization and images
This issue is used to keep track of the work needed to make specialization find its way into other Archi's features (excluding coArchi and jArchi).
Here's a list of things to do or check (will be updated when needed):
For a better description of each item, see this wiki page