View Javadoc

1   package org.devaki.nextobjects.util;
2   /*
3   nextobjects Copyright (C) 2001-2005 Emmanuel Florent
4   
5   This program is free software; you can redistribute it and/or modify
6   it under the terms of the GNU General Public License as published by the
7   Free Software Foundation; either version 2 of the License, or (at your
8   option) any later version.
9   
10  This program is distributed in the hope that it will
11  be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
12  of MERCHANTABILITY or FITNESS FOR A PARTICULAR
13  PURPOSE. See the GNU General Public License for more details.
14  
15  You should have received a copy of the GNU General Public License along
16  with this program; if not, write to the Free Software Foundation, Inc., 59
17  Temple Place - Suite 330, Boston, MA 02111-1307, USA.
18  
19  */
20  import java.io.File;
21  import java.io.FileInputStream;
22  import java.io.FileOutputStream;
23  import java.io.ObjectInputStream;
24  import java.io.ObjectOutputStream;
25  import java.util.Vector;
26  import org.apache.commons.logging.Log;
27  import org.apache.commons.logging.LogFactory;
28  import org.devaki.nextobjects.workspace.models.ConceptualModel;
29  import org.devaki.nextobjects.workspace.models.PhysicalModel;
30  import org.devaki.nextobjects.workspace.models.columns.Column;
31  import org.devaki.nextobjects.workspace.models.objects.Association;
32  import org.devaki.nextobjects.workspace.models.objects.AssociationLink;
33  import org.devaki.nextobjects.workspace.models.objects.Constraint;
34  import org.devaki.nextobjects.workspace.models.objects.InheritanceLink;
35  import org.devaki.nextobjects.workspace.models.objects.Entity;
36  import org.devaki.nextobjects.workspace.models.objects.Table;
37  import org.devaki.nextobjects.workspace.models.graphics.TableView;
38  /***
39   *  The class responsible of all the CDM2PDM work.
40   *
41   * @see http://www.devaki.org/transformation.html
42   * @author <a href="mailto:eflorent@devaki.org">Emmanuel Florent</a>
43   * @author gregorybr@users.sourceforge.net
44   * @TODO check/fix N.I. abstract
45   *
46   */
47  public final class MeriseTransform
48  {
49      /***
50       * The logger from the log4j project
51       */
52      private static Log logger =
53          LogFactory.getLog(MeriseTransform.class.getName());
54      /***
55       *  Dummy constructor
56       */
57      private MeriseTransform()
58      {
59      }
60      /***
61       * Generate a PDM from the current CDM  *
62       * @param pConceptualModel the context model
63       * @return the physical model
64       */
65      public static PhysicalModel cdm2pdm(final ConceptualModel pConceptualModel)
66      {
67          /***
68           *  Temporary CDM where we are going to pick all the objects.
69           */
70          ConceptualModel anotherMerise = null;
71          /***
72           * The returned physical model
73           */
74          PhysicalModel db = new PhysicalModel("--///--");
75          /***
76           *  The temp file.
77           */
78          File tmpFile = null;
79          logger.warn("Using EXPERIMENTAL merise transform");
80          // I need a copy of the conceptual model to make on it the moves
81          // neccessary for transformation .
82          try
83          {
84              tmpFile = File.createTempFile("erd", "cdm");
85          }
86          catch (Exception ioex)
87          {
88              logger.error(
89                  "Unable to write temp-file - check space left on device:");
90              logger.error(ioex.getMessage());
91          }
92          if (CDMVerifier.verify(pConceptualModel))
93          {
94              try
95              {
96                  ObjectOutputStream flux =
97                      new ObjectOutputStream(new FileOutputStream(tmpFile));
98                  flux.writeObject(pConceptualModel);
99                  flux.flush();
100                 flux.close();
101             }
102             catch (Exception ioex)
103             {
104                 logger.error("Unable to save temporary file " + tmpFile);
105             }
106             // then reload.
107             try
108             {
109                 ObjectInputStream flux =
110                     new ObjectInputStream(new FileInputStream(tmpFile));
111                 anotherMerise = (ConceptualModel) flux.readObject();
112                 flux.close();
113                 tmpFile.delete();
114             }
115             catch (Exception ioex)
116             {
117                 logger.error("I can't understand file : " + ioex);
118             }
119             //Pass to store not serializable stuff.
120             anotherMerise.setDevelopers(pConceptualModel.getDevelopers());
121             anotherMerise.setBuild(pConceptualModel.getBuild());
122             anotherMerise.setContributors(pConceptualModel.getContributors());
123             anotherMerise.setDependencies(pConceptualModel.getDependencies());
124             anotherMerise.setLicenses(pConceptualModel.getLicenses());
125             anotherMerise.setMailingLists(pConceptualModel.getMailingLists());
126             anotherMerise.setProperties(pConceptualModel.getProperties());
127             if (anotherMerise != null)
128             {
129                 ModelMan.newPhysicalDatamodel(db);
130                 ModelMan.setCurrentModel(db);
131                 MeriseTransform.initDatabase(db, anotherMerise);
132                 MeriseTransform.applyRule00(db, anotherMerise);
133                 ModelMan.resizeModelObjects(db);
134                 db.getModelView().repaint();
135             }
136             else
137             {
138                 logger.error("Giving up :(");
139             }
140         }
141         else
142         {
143             // the model is not verifiable.
144             logger.error("I can't continue because the model is wrong.");
145         }
146         db.getRedoLog().clear();
147         return db;
148     }
149     /***
150      *  Utility function to take all needed attribute of a pdm into a cdm.
151      *
152      * @param pDatabase context model
153      * @param pMerise context model
154      * @return physical model
155      * @task implements generate a pdm filename/path
156      */
157     private static PhysicalModel initDatabase(
158         final PhysicalModel pDatabase,
159         final ConceptualModel pMerise)
160     {
161         pDatabase.setName(pMerise.getName());
162         pDatabase.setId(pMerise.getId());
163         pDatabase.setDefaultIdMethod(pMerise.getDefaultIdMethod());
164         pDatabase.setDefaultJavaType(pMerise.getDefaultJavaType());
165         pDatabase.setPackageName(pMerise.getPackageName());
166         pDatabase.setBaseClass(pMerise.getBaseClass());
167         pDatabase.setBasePeer(pMerise.getBasePeer());
168         pDatabase.setDefaultJavaNamingMethod(
169             pMerise.getDefaultJavaNamingMethod());
170         pDatabase.setHeavyIndexing(pMerise.getHeavyIndexing());
171         pDatabase.setDescription(pMerise.getDescription());
172         pDatabase.setNotes(pMerise.getNotes());
173         pDatabase.setCompany(pMerise.getCompany());
174         pDatabase.setProjectURL(pMerise.getProjectURL());
175         pDatabase.setCreateDatabaseUrl(pMerise.getCreateDatabaseUrl());
176         pDatabase.setBuildDatabaseUrl(pMerise.getBuildDatabaseUrl());
177         pDatabase.setSchema(pMerise.getSchema());
178         pDatabase.setDatabaseHost(pMerise.getDatabaseHost());
179         pDatabase.setDatabasePassword(pMerise.getDatabasePassword());
180         pDatabase.setDatabaseUser(pMerise.getDatabaseUser());
181         pDatabase.setDbType(pMerise.getDbType());
182         pDatabase.setProjectURL(pMerise.getProjectURL());
183         pDatabase.setShortDescription(pMerise.getShortDescription());
184         pDatabase.setLogo(pMerise.getLogo());
185         pDatabase.setInceptionYear(pMerise.getInceptionYear());
186         pDatabase.setOrganizationName(pMerise.getOrganizationName());
187         pDatabase.setOrganizationLogo(pMerise.getOrganizationLogo());
188         pDatabase.setOrganizationUrl(pMerise.getOrganizationUrl());
189         pDatabase.setSiteAddress(pMerise.getSiteAdress());
190         pDatabase.setDistributionDirectory(pMerise.getDistributionDirectory());
191         pDatabase.setSiteDirectory(pMerise.getSiteDirectory());
192         pDatabase.setIssueTrackingUrl(pMerise.getIssueTrackingUrl());
193         pDatabase.setIssueTrackingUrl(pMerise.getIssueTrackingUrl());
194         pDatabase.setAlternateProjectId(pMerise.getAlternateProjectId());
195         pDatabase.setParentProject(pMerise.getParentProject());
196         pDatabase.setDevelopers(pMerise.getDevelopers());
197         pDatabase.setBuild(pMerise.getBuild());
198         pDatabase.setContributors(pMerise.getContributors());
199         pDatabase.setDependencies(pMerise.getDependencies());
200         pDatabase.setLicenses(pMerise.getLicenses());
201         pDatabase.setMailingLists(pMerise.getMailingLists());
202         pDatabase.setProperties(pMerise.getProperties());
203         return pDatabase;
204     }
205     /***
206      * Construct a new 'Table' object
207      * @param pDatabase the database
208      * @param pObject the entity give'n as argument.
209      * @return the table
210      */
211     private static Table initTable(
212         final PhysicalModel pDatabase,
213         final Entity pObject)
214     {
215         Table t = new Table(pDatabase);
216         t.setName(pObject.getName());
217         t.setJavaName(pObject.getJavaName());
218         t.setCode(pObject.getCode());
219         t.setIdMethod(pObject.getIdMethod());
220         t.setSkipSql(pObject.getSkipSql());
221         t.setBaseClass(pObject.getBaseClass());
222         t.setBasePeer(pObject.getBasePeer());
223         t.setAlias(pObject.getAlias());
224         t.setJavaNamingMethod(pObject.getJavaNamingMethod());
225         t.setHeavyIndexing(pObject.getHeavyIndexing());
226         t.setDescription(pObject.getDescription());
227         t.setNotes(pObject.getNotes());
228         return t;
229     }
230     /***
231      * Rule 0: each entity become a table.
232      * Also act as Rules iteration starting points
233      *
234      * @param pDatabase the physical model
235      * @param pMerise the conceptual model
236      * @return the model
237      */
238     private static PhysicalModel applyRule00(
239         final PhysicalModel pDatabase,
240         final ConceptualModel pMerise)
241     {
242         Entity swapEntity;
243         Table swapTable;
244         for (int i = 0; i < pMerise.getEntities().size(); i++)
245         {
246             swapEntity = (Entity) pMerise.getEntities().elementAt(i);
247             swapTable = initTable(pDatabase, swapEntity);
248             swapEntity.setSubsequentTable(swapTable);
249             if (swapEntity.getData() != null)
250             {
251                 swapTable.setData(new Vector(swapEntity.getData()));
252             }
253             swapTable.getObjectView().setLocation(
254                 swapEntity.getObjectView().getLocation());
255             ((TableView) swapTable.getObjectView()).setOldLocation(
256                 swapEntity.getObjectView().getLocation());
257             swapTable.getObjectView().setSize(
258                 swapEntity.getObjectView().getSize());
259             ModelMan.addTable(pDatabase, swapTable);
260         }
261         for (int i = 0; i < pMerise.getAssociations().size(); i++)
262         {
263             switch (getCardType(pMerise.getAssociationAt(i)))
264             {
265                 case ConceptualModel.ASSO_11 :
266                     applyRule11(pDatabase, pMerise.getAssociationAt(i));
267                     break;
268                 case ConceptualModel.ASSO_1N :
269                     applyRule1N(pDatabase, pMerise.getAssociationAt(i));
270                     break;
271                 case ConceptualModel.ASSO_NM :
272                     applyRuleNN(pDatabase, pMerise.getAssociationAt(i));
273                     break;
274                 default :
275                     logger.error("Impossible rule");
276                     break;
277             }
278         }
279         // work on Inheritance Link
280         for (int i = 0; i < pMerise.getInheritanceLinks().size(); i++)
281         {
282             InheritanceLink iLnk =
283                 new InheritanceLink(
284                     pMerise,
285                     ((Entity) pMerise.getInheritanceLinkAt(i).getChildClass())
286                         .getSubsequentTable(),
287                     ((Entity) pMerise.getInheritanceLinkAt(i).getParentClass())
288                         .getSubsequentTable());
289             ModelMan.addInheritanceLink(pDatabase, iLnk);
290         }
291         return pDatabase;
292     }
293     /***
294      *  Rule 2 : In case of 1:1 relation, table must share the same key.
295      *
296      * @param theDatabase context model
297      * @param pAsso context association
298      */
299     private static void applyRule11(
300         final PhysicalModel theDatabase,
301         final Association pAsso)
302     {
303         int cardIs =
304             ((AssociationLink) pAsso.getAssociationLinks().elementAt(0))
305                 .getCard();
306         int isCard =
307             ((AssociationLink) pAsso.getAssociationLinks().elementAt(1))
308                 .getCard();
309         // if we have 1,1 - 1,1 the table can be the same (fusion)
310         // if we have 1,1 - 0,1 we can apply rule 2.
311         if (cardIs == ConceptualModel.ASSO_11
312             && isCard == ConceptualModel.ASSO_11)
313         {
314             applyFusion(theDatabase, pAsso);
315         }
316         else
317         {
318             applyRule11a(theDatabase, pAsso);
319         }
320     }
321     /***
322      *  All the table of this association are going to be fusioned.
323      *  this is most often due all _11_ relation in this association.
324      *
325      * @param pDatabase context model
326      * @param pAsso context association
327      */
328     private static void applyFusion(
329         final PhysicalModel pDatabase,
330         final Association pAsso)
331     {
332         //For now work, on association fields, if any property in the relation.
333         for (int i = 0; i < pAsso.getData().size(); i++)
334         {
335             Column tmpColumn =
336                 new Column((Column) pAsso.getData().elementAt(i));
337             pAsso.getEntityAt(0).getSubsequentTable().getData().addElement(
338                 tmpColumn);
339         }
340         //now work on association fields, if any property in the relation.
341         for (int j = 1; j < pAsso.countMyAssociationLinks(); j++)
342         {
343             // j start at 1 in order to omit he first table.
344             for (int i = 0; i < pAsso.getEntityAt(j).getData().size(); i++)
345             {
346                 Column tmpColumn =
347                     new Column(
348                         (Column) pAsso.getEntityAt(j).getData().elementAt(i));
349                 pAsso.getEntityAt(0).getSubsequentTable().getData().addElement(
350                     tmpColumn);
351             }
352             //remove the old table.
353             ModelMan.removeTable(
354                 pDatabase,
355                 pAsso.getEntityAt(j).getSubsequentTable());
356             // fix new name,code.
357             String newCode =
358                 pAsso.getEntityAt(0).getSubsequentTable().getCode()
359                     + "_"
360                     + pAsso.getEntityAt(j).getSubsequentTable().getCode();
361             pAsso.getEntityAt(0).getSubsequentTable().setCode(newCode);
362             pAsso.getEntityAt(0).getSubsequentTable().setName(newCode);
363         }
364     }
365     /***
366      * Rule 2 : In case of 1:1 relation, table must share the same key.
367      * this could have be donne differently, by reversing right/left but also
368      * by keeping 2 different primary key. IOn that case this can cause
369      * performance loss due to index multiplication.
370      *
371      * @param pDatabase context model
372     *  @param pAsso context association
373      *
374      */
375     private static void applyRule11a(
376         final PhysicalModel pDatabase,
377         final Association pAsso)
378     {
379         Column oneId;
380         int cardIs =
381             ((AssociationLink) pAsso.getAssociationLinks().elementAt(0))
382                 .getCard();
383         int isCard =
384             ((AssociationLink) pAsso.getAssociationLinks().elementAt(1))
385                 .getCard();
386         int zeroSide = 0;
387         int oneSide = 1;
388         if (cardIs == ConceptualModel.ASSO_11)
389         {
390             oneSide = 0;
391             zeroSide = 1;
392         }
393         else if (isCard == ConceptualModel.ASSO_11)
394         {
395             oneSide = 1;
396             zeroSide = 0;
397         }
398         //
399         oneId = new Column((Column) pAsso.getEntityAt(oneSide).getIdentifier());
400         Column zeroId =
401             new Column((Column) pAsso.getEntityAt(zeroSide).getIdentifier());
402         Column sameId = new Column(oneId);
403         sameId.setPrimaryKey(false);
404         // Tables must share the same PK.
405         pAsso
406             .getEntityAt(zeroSide)
407             .getSubsequentTable()
408             .getData()
409             .removeElement(
410             zeroId);
411         pAsso.getEntityAt(zeroSide).getSubsequentTable().getData().addElement(
412             sameId);
413         Vector v1 = new Vector(1);
414         v1.add(oneId);
415         Vector v0 = new Vector(1);
416         v0.addElement(sameId);
417         // now work in adding 2 constraints
418         /*
419         Constraint newCst =
420             new Constraint(
421                 pDatabase,
422                 pAsso.getEntityAt(oneSide).getSubsequentTable(),
423                 v1,
424                 pAsso.getEntityAt(zeroSide).getSubsequentTable(),
425                 v0);
426         ModelMan.addConstraint(pDatabase, newCst);
427        */
428         Constraint newCst2 =
429             new Constraint(
430                 pDatabase,
431                 pAsso.getEntityAt(zeroSide).getSubsequentTable(),
432                 v0,
433                 pAsso.getEntityAt(oneSide).getSubsequentTable(),
434                 v1);
435         ModelMan.addConstraint(pDatabase, newCst2);
436        
437         //now work on association fields, if any property in the relation.
438         for (int i = 0; i < pAsso.getData().size(); i++)
439         {
440             oneId = new Column((Column) pAsso.getData().elementAt(i));
441             if (isCard == ConceptualModel.ASSO_01
442                 || cardIs == ConceptualModel.ASSO_01)
443             {
444                 oneId.setRequired(false);
445             }
446             pAsso
447                 .getEntityAt(oneSide)
448                 .getSubsequentTable()
449                 .getData()
450                 .addElement(
451                 oneId);
452         }
453         if (pAsso.getCardAt(oneSide) == ConceptualModel.ASSO_01
454             || pAsso.getCardAt(zeroSide) == ConceptualModel.ASSO_01)
455         {
456             oneId.setRequired(false);
457         }
458         else
459         {
460             oneId.setRequired(true);
461         }
462     }
463     /***
464      * This transformation rule deal with  1 --> * relation.
465      *
466      * Rule 3 : : In the case of entities connected by associations of the 1:n
467      * type, each table has its own key, but the key of the entity side 0,n (or
468      * 1,n) migrates towards the table side 0,1 (or 1,1) and becomes a foreign
469      * key (secondary index).
470      *
471      * @param pDatabase Database context
472      * @param pAsso Association to be treated
473      *
474      */
475     private static void applyRule1N(
476         final PhysicalModel pDatabase,
477         final Association pAsso)
478     {
479         /*
480          *  a temp column to be created under one of the two tables.
481          */
482         int oneSide, nSide;
483         Column newField;
484         /*
485          *  The constraint to be created
486          */
487         Constraint newCst = null;
488         // here we try to reach the left or the right part of the Association
489         // I mean not the cards or anything but find the side we want:
490         // side-N vs side-1
491         // Mark the sides
492         if (pAsso.getCardAt(1) == ConceptualModel.ASSO_0N
493             || pAsso.getCardAt(1) == ConceptualModel.ASSO_1N)
494         {
495             oneSide = 0;
496             nSide = 1;
497         }
498         else
499         {
500             oneSide = 1;
501             nSide = 0;
502         }
503         // move all identifier of the N table  to the 1 table.
504         Vector pIdent = pAsso.getEntityAt(nSide).getAllIdentifier();
505         for (int j = 0; j < pIdent.size(); j++)
506         {
507             newField = new Column((Column) pIdent.get(j));
508             pAsso
509                 .getEntityAt(oneSide)
510                 .getSubsequentTable()
511                 .getData()
512                 .addElement(
513                 newField);
514             if (j == 0)
515             {
516                 Vector vfk = new Vector();
517                 vfk.addElement(pAsso.getEntityAt(nSide).getIdentifier());
518                 Vector vlc = new Vector();
519                 vlc.addElement(newField);
520                 newCst =
521                     new Constraint(
522                         pDatabase,
523                         pAsso.getEntityAt(oneSide).getSubsequentTable(),
524                         vlc,
525                         pAsso.getEntityAt(nSide).getSubsequentTable(),
526                         vfk);
527                 ModelMan.addConstraint(pDatabase, newCst);
528             }
529             newField.setPrimaryKey(false);
530             if (pAsso.getCardAt(oneSide) == ConceptualModel.ASSO_01)
531             {
532                 newField.setRequired(false);
533             }
534             else
535             {
536                 newField.setRequired(true);
537             }
538             // as a FK it never autoincrement
539             newField.setAutoIncrement(false);
540         }
541         // If the type of relation is 1:n, it is advisable to make slip the
542         // attributes towards entity provided with cardinalities the 1:1.
543         for (int i = 0; i < pAsso.getData().size(); i++)
544         {
545             pAsso
546                 .getEntityAt(oneSide)
547                 .getSubsequentTable()
548                 .getData()
549                 .addElement(
550                 new Column((Column) pAsso.getData().elementAt(i)));
551         }
552     }
553     /***
554      *  Rule 4 : In the case of entities connected by associations of the n:m
555      *  type, an intermediate Table known as "Link Table", must be created, and
556      *  must have as primary key a conjunction of the primary keys of the two
557      *  tables for which it is connected.
558      *
559      * @param pDatabase The context database
560      * @param pAsso The association to be treated.
561      *
562      */
563     private static void applyRuleNN(
564         final PhysicalModel pDatabase,
565         final Association pAsso)
566     {
567         /***
568          *  The table to be created.
569          */
570         Table newTable = new Table(pDatabase);
571         newTable.setName(pAsso.getName());
572         newTable.setCode(pAsso.getCode());
573         newTable.setName(pAsso.getName());
574         newTable.setJavaName(pAsso.getJavaName());
575         newTable.setCode(pAsso.getCode());
576         newTable.setIdMethod("none");
577         newTable.setSkipSql(pAsso.getSkipSql());
578         //newTable.setAbstractClass(pAsso.getAbstractClass()); //ni
579         newTable.setBaseClass(pAsso.getBaseClass());
580         newTable.setBasePeer(pAsso.getBasePeer());
581         newTable.setAlias(pAsso.getAlias());
582         newTable.setJavaNamingMethod(pAsso.getJavaNamingMethod());
583         newTable.setHeavyIndexing(pAsso.getHeavyIndexing());
584         newTable.setDescription(pAsso.getDescription());
585         newTable.setDescription(pAsso.getDescription());
586         newTable.getObjectView().setLocation(
587             pAsso.getObjectView().getLocation());
588         // if the type of relation is n:m, then the attributes of association
589         //  becomes attributes of the link table.
590         for (int i = 0; i < pAsso.getData().size(); i++)
591         {
592             newTable.getData().addElement(
593                 new Column((Column) pAsso.getData().elementAt(i)));
594         }
595         /***
596          * Column we create
597          */
598         Column newField;
599         /***
600          *  Constraint to be created(s)
601          */
602         Constraint newCst = null;
603         for (int i = 0; i < pAsso.getAssociationLinks().size(); i++)
604         {
605             Vector pIdent = pAsso.getAllIdentifierAt(i);
606             for (int j = 0; j < pIdent.size(); j++)
607             {
608                 newField = new Column((Column) pIdent.get(j));
609                 newField.setPrimaryKey(true);
610                 //set required if we don't have a zero (01,0N)
611                 newField.setRequired(
612                     (pAsso.getCardAt(i) == ConceptualModel.ASSO_11
613                         || pAsso.getCardAt(i) == ConceptualModel.ASSO_1N));
614                 newField.setAutoIncrement(false); // always in that case!
615                 newTable.getData().addElement(newField);
616                 if (j == 0)
617                 { // only one constraint ( = relation 1-->* ) between two tables
618                     Vector vlk = new Vector();
619                     Vector vfk = new Vector();
620                     vlk.addElement(newField);
621                     vfk.addElement(pIdent.get(j));
622                     newCst =
623                         new Constraint(
624                             pDatabase,
625                             newTable,
626                             vlk,
627                             pAsso.getEntityAt(i).getSubsequentTable(),
628                             vfk);
629                     ModelMan.addConstraint(pDatabase, newCst);
630                 }
631             }
632         }
633         ModelMan.addTable(pDatabase, newTable);
634     }
635     /***
636      * return the type of the relation (11,1N,1N)
637      * so we know wich Merise rule to apply for
638      * a given association (cardinalities)
639      *
640     *    card_is  01      11      0N      1N (right)
641     * is_card --------------------------------
642     * 01  | 11      11      1N      1N
643     * 11  | 11      11      1N      1N
644     * 0N  | 1N     1N      NM     NM
645     * 1N   | 1N     1N      NM     NM
646     *  (left)
647      * @param theAssociation the association to indentify
648      * @return the card type
649      */
650     public static int getCardType(final Association theAssociation)
651     {
652         int cardIs =
653             ((AssociationLink) theAssociation
654                 .getAssociationLinks()
655                 .elementAt(0))
656                 .getCard();
657         int isCard =
658             ((AssociationLink) theAssociation
659                 .getAssociationLinks()
660                 .elementAt(1))
661                 .getCard();
662         int[][] results =
663             {
664                 {
665                     ConceptualModel.ASSO_11,
666                     ConceptualModel.ASSO_11,
667                     ConceptualModel.ASSO_1N,
668                     ConceptualModel.ASSO_1N },
669                 {
670                 ConceptualModel.ASSO_11,
671                     ConceptualModel.ASSO_11,
672                     ConceptualModel.ASSO_1N,
673                     ConceptualModel.ASSO_1N },
674                     {
675                 ConceptualModel.ASSO_1N,
676                     ConceptualModel.ASSO_1N,
677                     ConceptualModel.ASSO_NM,
678                     ConceptualModel.ASSO_NM },
679                     {
680                 ConceptualModel.ASSO_1N,
681                     ConceptualModel.ASSO_1N,
682                     ConceptualModel.ASSO_NM,
683                     ConceptualModel.ASSO_NM }
684         };
685         int cardType = results[cardIs][isCard];
686         // In any case overwrite previous switch
687         if (theAssociation.getAssociationLinks().size() > 2)
688         {
689             cardType = ConceptualModel.ASSO_NM;
690         }
691         return cardType;
692     }
693 }