Bug 72042 - code generation ignores unidirectional association
Summary: code generation ignores unidirectional association
Status: RESOLVED FIXED
Alias: None
Product: umbrello
Classification: Applications
Component: general (show other bugs)
Version: unspecified
Platform: Gentoo Packages Linux
: NOR normal
Target Milestone: ---
Assignee: Umbrello Development Group
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2004-01-07 11:13 UTC by Diederik van der Boor
Modified: 2007-02-24 10:05 UTC (History)
0 users

See Also:
Latest Commit:
Version Fixed In:


Attachments
class diagram with unidirectional association (3.15 KB, image/png)
2007-02-24 01:08 UTC, Antoine Dopffer
Details
patch for #77042 (10.19 KB, patch)
2007-02-24 01:21 UTC, Antoine Dopffer
Details

Note You need to log in before you can comment on or make changes to this bug.
Description Diederik van der Boor 2004-01-07 11:13:53 UTC
Version:            (using KDE KDE 3.1.94)
Installed from:    Gentoo Packages
OS:          Linux

If class associations are made in umbrello (ie. private properties), they don't show up the in the code as private variables in the code. ..which I've seen in Rational Rose before.

The expected behaviour was seeing something like: 'private <classname> <rolename>' in the code. Thanks for your assistance.
Comment 1 Brian Thomas 2004-02-17 19:32:31 UTC
Can you describe this further? The current code generation *wont* show
any attributes for the association *IF* you didnt supply a role name.
Is this the case?
Comment 2 Diederik van der Boor 2004-05-11 12:41:20 UTC
I'm sorry for not replying sooner.

I've indeed misunderstood the association name; because I made an unidirectional-association, I assumed that the "association name" represented the role-name field.

Could it be possible to:
- edit the role name from the context menu?
- gray-out the second role name for a unidirectional-association?
Comment 3 Jonathan Riddell 2004-05-28 01:22:14 UTC
I just tried this and the C++ code generator does not show the attribute from an association whatever I do.
Comment 4 Romel Sandoval 2006-06-09 00:31:56 UTC
This bug is present on 1.5.2 version. 

I use unidirectional assotiation and supply a role B name. But the code generated dont show any atribute.

I think this is an important issue.
Comment 5 Antoine Dopffer 2007-02-18 13:37:52 UTC
If we use unidirectional assotiations , what shoud up in the code :
1) private variable 
2) getter method 
3) setter method 
4) private variable and getter and setter methods

Comment 6 Antoine Dopffer 2007-02-24 01:08:05 UTC
Created attachment 19795 [details]
class diagram with unidirectional association 

With the patch submited further and this class diagram, the C++ generated code
is:
---------------------------------------------------
......
#ifndef CLASS1_H
#define CLASS1_H

#include <string>
#include <vector>

class Class2;


/**
  * class Class1
  */

class Class1
{
public:

  // Constructors/Destructors
  //  


  /**
   * Empty Constructor
   */
  Class1 ( );

  /**
   * Empty Destructor
   */
  virtual ~Class1 ( );

  // Static public attributes
  //  

  // public attributes
  //  


  Class2 * m_rolea;

  // public attribute accessor methods
  //  


  // public attribute accessor methods
  //  

  /**
   * Set the value of m_rolea
   * @param new_var the new value of m_rolea
   */
  void setroleA ( Class2 * new_var );

  /**
   * Get the value of m_rolea
   * @return the value of m_rolea
   */
  Class2 * getroleA ( );
protected:
  // Static protected attributes
  //  
  // protected attributes
  //  
public:
  // protected attribute accessor methods
  //  
protected:
public:
  // protected attribute accessor methods
  //  
protected:
private:
  // Static private attributes
  //  
  // private attributes
  //  
public:
  // private attribute accessor methods
  //  
private:
public:
  // private attribute accessor methods
  //  
private:
};

#endif // CLASS1_H
--------------------------------------------------------------
the Java generated code is:
--------------------------------------------------------------
import java.util.*;


/**
 * Class Class1
 */
public class Class1 {

  //
  // Fields
  //


  public Class2 m_roleA;
  
  //
  // Constructors
  //
  public Class1 () { };
  
  //
  // Methods
  //


  //
  // Accessor methods
  //

  /**
   * Set the value of m_roleA
   * @param newVar the new value of m_roleA
   */
  public void setRoleA ( Class2 newVar ) {
    m_roleA = newVar;
  }

  /**
   * Get the value of m_roleA
   * @return the value of m_roleA
   */
  public Class2 getRoleA ( ) {
    return m_roleA;
  }

  //
  // Other methods
  //

}
Comment 7 Antoine Dopffer 2007-02-24 01:21:06 UTC
Created attachment 19796 [details]
patch for #77042

This patch fixes (for C++ and Java) the problem that the code generation
ignored unidirectional association.
For the other languages, I am not very sure. I don't promise anything but I
will try to have a look.

The patch try to create nothing when there is already a attribute with the same
name that the role name (the role name to consider is the role A name).
But, in trunk, I wasn't able to add an attribute ? 
In the dialog window, I can only see the Help, OK and Cancel button ?

The patch also fixes another bug in the java code generator.
The name used for the declaration of the attribute (for any kind of
association) and the name used in the body of the getter and the setter were
not equal.

Thanks
Comment 8 Oliver Kellogg 2007-02-24 08:17:13 UTC
> The patch try to create nothing when there is already a attribute
> with the same name that the role name

That's a very good idea and I think we need that for the other
association types too (aggregations, compositions)

>  (the role name to consider is the role A name).

Here is a UniAssociation in Umbrello (I hope whitespace is preserved):

+--------+         roleB +--------+
| Class1 |-------------->| Class2 |
+--------+               +--------+

The role A is at Class1 and the role B is at Class2.
Now the UML standard says the member name in Class1 should be
the role B name. So it's the role name "farther away" from the
referencing class - in the case of UniAssociation, the name
at the arrow head.

> But, in trunk, I wasn't able to add an attribute ?

Trunk still needs a lot of work.
Comment 9 Oliver Kellogg 2007-02-24 08:22:11 UTC
SVN commit 636790 by okellogg:

Attachment 19796 [details] from Antoine Dopffer adds code generation for UniAssociation
 in C++and Java.  I modified the patch for role B as described in comment #8.
Many thanks Antoine for your work.
BUG:72042


 M  +1 -0      ChangeLog  
 M  +29 -0     umbrello/classifier.cpp  
 M  +5 -0      umbrello/classifier.h  
 M  +4 -1      umbrello/codegenerators/classifierinfo.cpp  
 M  +1 -0      umbrello/codegenerators/classifierinfo.h  
 M  +10 -0     umbrello/codegenerators/cppwriter.cpp  
 M  +7 -3      umbrello/codegenerators/javawriter.cpp  


--- branches/KDE/3.5/kdesdk/umbrello/ChangeLog #636789:636790
@@ -4,6 +4,7 @@
 * C# Code Generation and export (53368)
 * Java interface inheritance, abstract classes and generics in code generation
   (53376)
+* Code generation ignores unidirectional association (72042)
 * %date% and %time% not being parsed (96612)
 * Operations of the Interface are not implemented in the class automatically
   (111593)
--- branches/KDE/3.5/kdesdk/umbrello/umbrello/classifier.cpp #636789:636790
@@ -833,6 +833,33 @@
     return m_isRef;
 }
 
+UMLAssociationList  UMLClassifier::getUniAssociationToBeImplemented() {
+    UMLAssociationList associations = getSpecificAssocs(Uml::at_UniAssociation);
+    UMLAssociationList uniAssocListToBeImplemented;
+
+    for(UMLAssociation *a = associations.first(); a; a = associations.next()) {
+        if (a->getObjectId(Uml::B) == getID())
+            continue;  // we need to be at the A side
+
+        QString roleNameB = a->getRoleName(Uml::B);
+        if (!roleNameB.isEmpty()) {
+            UMLAttributeList atl = getAttributeList();
+            bool found = false;
+            //make sure that an attribute with the same name doesn't already exist 
+            for (UMLAttribute *at = atl.first(); at ; at = atl.next()) {
+                if (at->getName() == roleNameB) {
+                    found = true;
+                    break;
+                }
+            }
+            if (!found) {
+                uniAssocListToBeImplemented.append(a);
+            }
+        }
+    }
+    return uniAssocListToBeImplemented;
+}
+
 void UMLClassifier::saveToXMI(QDomDocument & qDoc, QDomElement & qElement) {
     QString tag;
     switch (m_BaseType) {
@@ -986,4 +1013,6 @@
     return totalSuccess;
 }
 
+
+
 #include "classifier.moc"
--- branches/KDE/3.5/kdesdk/umbrello/umbrello/classifier.h #636789:636790
@@ -436,6 +436,11 @@
      */
     UMLClassifierListItem* makeChildObject(const QString& xmiTag);
 
+    /**
+     * Return the list of unidirectional association that should show up in the code
+     */
+    virtual UMLAssociationList  getUniAssociationToBeImplemented();
+
 signals:
     /** Signals that a new UMLOperation has been added to the classifer.
      */
--- branches/KDE/3.5/kdesdk/umbrello/umbrello/codegenerators/classifierinfo.cpp #636789:636790
@@ -85,6 +85,9 @@
     plainAssociations = c->getSpecificAssocs(Uml::at_Association); // BAD! only way to get "general" associations.
     plainAssociations.setAutoDelete(false);
 
+    uniAssociations = c->getUniAssociationToBeImplemented();
+    uniAssociations.setAutoDelete(false);
+
     aggregations = c->getAggregations();
     aggregations.setAutoDelete(false);
 
@@ -92,7 +95,7 @@
     compositions.setAutoDelete(false);
 
     // set some summary information about the classifier now
-    hasAssociations = plainAssociations.count() > 0 || aggregations.count() > 0 || compositions.count() > 0;
+    hasAssociations = plainAssociations.count() > 0 || aggregations.count() > 0 || compositions.count() > 0 || uniAssociations.count() > 0;
     hasAttributes = atpub.count() > 0 || atprot.count() > 0 || atpriv.count() > 0
                     || static_atpub.count() > 0
                     || static_atprot.count() > 0
--- branches/KDE/3.5/kdesdk/umbrello/umbrello/codegenerators/classifierinfo.h #636789:636790
@@ -65,6 +65,7 @@
      * Lists of types of associations this classifier has
      */
     UMLAssociationList plainAssociations;
+    UMLAssociationList uniAssociations;
     UMLAssociationList aggregations;
     UMLAssociationList compositions;
 
--- branches/KDE/3.5/kdesdk/umbrello/umbrello/codegenerators/cppwriter.cpp #636789:636790
@@ -183,6 +183,8 @@
     // associations
     writeAssociationMethods(m_classifierInfo->plainAssociations, permitScope,
                             true, INLINE_ASSOCIATION_METHODS, true, c->getID(), stream);
+    writeAssociationMethods(m_classifierInfo->uniAssociations, permitScope,
+                            true, INLINE_ASSOCIATION_METHODS, true, c->getID(), stream);
     writeAssociationMethods(m_classifierInfo->aggregations, permitScope,
                             true,  INLINE_ASSOCIATION_METHODS, true, c->getID(), stream);
     writeAssociationMethods(m_classifierInfo->compositions, permitScope,
@@ -200,6 +202,7 @@
 
     // associations
     writeAssociationDecls(m_classifierInfo->plainAssociations, permitScope, c->getID(), stream);
+    writeAssociationDecls(m_classifierInfo->uniAssociations, permitScope, c->getID(), stream);
     writeAssociationDecls(m_classifierInfo->aggregations, permitScope, c->getID(), stream);
     writeAssociationDecls(m_classifierInfo->compositions, permitScope, c->getID(), stream);
 
@@ -279,6 +282,8 @@
     // public
     writeAssociationMethods(m_classifierInfo->plainAssociations, Uml::Visibility::Public, false,
                             !INLINE_ASSOCIATION_METHODS, true, c->getID(), cpp);
+    writeAssociationMethods(m_classifierInfo->uniAssociations, Uml::Visibility::Public, false,
+                            !INLINE_ASSOCIATION_METHODS, true, c->getID(), cpp);
     writeAssociationMethods(m_classifierInfo->aggregations, Uml::Visibility::Public, false,
                             !INLINE_ASSOCIATION_METHODS, true, c->getID(), cpp);
     writeAssociationMethods(m_classifierInfo->compositions, Uml::Visibility::Public, false,
@@ -287,6 +292,8 @@
     // protected
     writeAssociationMethods(m_classifierInfo->plainAssociations, Uml::Visibility::Protected, false,
                             !INLINE_ASSOCIATION_METHODS, true, c->getID(), cpp);
+    writeAssociationMethods(m_classifierInfo->uniAssociations, Uml::Visibility::Protected, false,
+                            !INLINE_ASSOCIATION_METHODS, true, c->getID(), cpp);
     writeAssociationMethods(m_classifierInfo->aggregations, Uml::Visibility::Protected, false,
                             !INLINE_ASSOCIATION_METHODS, true, c->getID(), cpp);
     writeAssociationMethods(m_classifierInfo->compositions, Uml::Visibility::Protected, false,
@@ -296,6 +303,8 @@
     // private
     writeAssociationMethods(m_classifierInfo->plainAssociations, Uml::Visibility::Private, false,
                             !INLINE_ASSOCIATION_METHODS, true, c->getID(), cpp);
+    writeAssociationMethods(m_classifierInfo->uniAssociations, Uml::Visibility::Private, false,
+                            !INLINE_ASSOCIATION_METHODS, true, c->getID(), cpp);
     writeAssociationMethods(m_classifierInfo->aggregations, Uml::Visibility::Private, false,
                             !INLINE_ASSOCIATION_METHODS, true, c->getID(), cpp);
     writeAssociationMethods(m_classifierInfo->compositions, Uml::Visibility::Private, false,
@@ -349,6 +358,7 @@
     {
         // write all includes we need to include other classes, that arent us.
         printAssociationIncludeDecl (m_classifierInfo->plainAssociations, c->getID(), cpp);
+        printAssociationIncludeDecl (m_classifierInfo->uniAssociations, c->getID(), cpp);
         printAssociationIncludeDecl (m_classifierInfo->aggregations, c->getID(), cpp);
         printAssociationIncludeDecl (m_classifierInfo->compositions, c->getID(), cpp);
 
--- branches/KDE/3.5/kdesdk/umbrello/umbrello/codegenerators/javawriter.cpp #636789:636790
@@ -115,10 +115,12 @@
 
     // another preparation, determine what we have
     UMLAssociationList associations = c->getSpecificAssocs(Uml::at_Association); // BAD! only way to get "general" associations.
+    UMLAssociationList uniAssociations = c->getUniAssociationToBeImplemented();
+    
     UMLAssociationList aggregations = c->getAggregations();
     UMLAssociationList compositions = c->getCompositions();
 
-    bool hasAssociations = aggregations.count() > 0 || associations.count() > 0 || compositions.count() > 0;
+    bool hasAssociations = aggregations.count() > 0 || associations.count() > 0 || compositions.count() > 0 || uniAssociations.count() > 0;
     bool hasAttributes = (atl.count() > 0);
     bool hasAccessorMethods = hasAttributes || hasAssociations;
     bool hasOperationMethods = (c->getOpList().count() > 0);
@@ -188,6 +190,7 @@
     writeAttributeDecls(atpub, atprot, atpriv, java);
 
     writeAssociationDecls(associations, c->getID(), java);
+    writeAssociationDecls(uniAssociations, c->getID(), java);
     writeAssociationDecls(aggregations, c->getID(), java);
     writeAssociationDecls(compositions, c->getID(), java);
 
@@ -232,6 +235,7 @@
 
     // first: determine the name of the other class
     writeAssociationMethods(associations, c, java);
+    writeAssociationMethods(uniAssociations, c, java);
     writeAssociationMethods(aggregations, c, java);
     writeAssociationMethods(compositions, c, java);
 
@@ -527,7 +531,7 @@
     // multiplicity object that we don't have to figure out what it means via regex.
     if(multi.isEmpty() || multi.contains(QRegExp("^[01]$")))
     {
-        QString fieldVarName = roleName.replace(0, 1, roleName.left(1).lower());
+        QString fieldVarName = "m_" + roleName.replace(0, 1, roleName.left(1).lower());
         java<<startline<<scope<<" "<<fieldClassName<<" "<<fieldVarName<<";";
     }
     else
@@ -586,7 +590,7 @@
 {
     if(multi.isEmpty() || multi.contains(QRegExp("^[01]$")))
     {
-        QString fieldVarName = "m_" + roleName.lower();
+        QString fieldVarName = "m_" + roleName.replace(0, 1, roleName.left(1).lower());
         writeSingleAttributeAccessorMethods(fieldClassName, fieldVarName, roleName,
                                             description, visib, change, false, java);
     }
Comment 10 Oliver Kellogg 2007-02-24 08:32:07 UTC
In comment #8, I wrote:
 
> That's a very good idea and I think we need that for the other 
> association types too (aggregations, compositions) 

but I was referring to:

> the role name to consider is the role B name

I.e. it would be great to have the role B filtering methods
like UMLClassifier::get{Aggregation,Composition}ToBeImplemented()

BTW, the check against attributes is not needed because UMLAssocation
is never tied to UMLAttribute. The class that ties to UMLAttribute
is AssociationWidget, and that class is not used for generating code.
Comment 11 Antoine Dopffer 2007-02-24 10:05:39 UTC
Ok, 
sorry for I took the wrong way of the arrow.
I think I am kind of UML dyslexiac.

The check against attributes was created because I was affraid that attributes could be generated twice in this situation:

 +---------------+         roleB +--------+ 
 | Class1        |-------------->| Class2 | 
 |---------------|               |        |
 | -roleB: Class2|               |        |
 +---------------+               +--------+ 

roleB (declaration, getter, setter) could be generated because roleB is the role B name of the unidirectional association

roleB (declaration, getter, setter) could be also generated once again because roleB is an attribute of Class1

To get this kind of diagram with Umbrello, you have to delete the agregation that is automatically generated when you add attribute roleB in C1