View Javadoc
1   package fr.ifremer.quadrige2.magicdraw.action;
2   
3   /*-
4    * #%L
5    * Quadrige2 Core :: Quadrige2 Magicdraw plugin
6    * $Id:$
7    * $HeadURL:$
8    * %%
9    * Copyright (C) 2017 Ifremer
10   * %%
11   * This program is free software: you can redistribute it and/or modify
12   * it under the terms of the GNU Affero General Public License as published by
13   * the Free Software Foundation, either version 3 of the License, or
14   * (at your option) any later version.
15   * 
16   * This program is distributed in the hope that it will be useful,
17   * but WITHOUT ANY WARRANTY; without even the implied warranty of
18   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19   * GNU General Public License for more details.
20   * 
21   * You should have received a copy of the GNU Affero General Public License
22   * along with this program.  If not, see <http://www.gnu.org/licenses/>.
23   * #L%
24   */
25  import com.nomagic.ci.persistence.IAttachedProject;
26  import com.nomagic.ci.persistence.IProject;
27  import com.nomagic.magicdraw.actions.MDAction;
28  import com.nomagic.magicdraw.core.Application;
29  import com.nomagic.magicdraw.core.Project;
30  import com.nomagic.magicdraw.core.ProjectUtilities;
31  import com.nomagic.magicdraw.core.project.ProjectDescriptorsFactory;
32  import com.nomagic.magicdraw.openapi.uml.ModelElementsManager;
33  import com.nomagic.magicdraw.openapi.uml.ReadOnlyElementException;
34  import com.nomagic.magicdraw.openapi.uml.SessionManager;
35  import com.nomagic.magicdraw.ui.dialogs.MDDialogParentProvider;
36  import com.nomagic.uml2.ext.jmi.helpers.ModelHelper;
37  import com.nomagic.uml2.ext.jmi.helpers.StereotypesHelper;
38  import com.nomagic.uml2.ext.magicdraw.auxiliaryconstructs.mdmodels.Model;
39  import com.nomagic.uml2.ext.magicdraw.classes.mdkernel.*;
40  import com.nomagic.uml2.ext.magicdraw.classes.mdkernel.Class;
41  import com.nomagic.uml2.ext.magicdraw.classes.mdkernel.Enumeration;
42  import com.nomagic.uml2.ext.magicdraw.mdprofiles.Profile;
43  import com.nomagic.uml2.ext.magicdraw.mdprofiles.Stereotype;
44  import com.nomagic.uml2.impl.ElementsFactory;
45  import fr.ifremer.quadrige2.magicdraw.helper.PropertiesHelper;
46  import fr.ifremer.quadrige2.magicdraw.helper.TransformModelHelper;
47  
48  import javax.swing.*;
49  import java.awt.event.ActionEvent;
50  import java.io.File;
51  import java.net.URI;
52  import java.util.*;
53  
54  /**
55   * Apply some transformations on model
56   * 
57   * @author Benoit Lavenier <benoit.lavenier@e-is.pro>
58   */
59  public class TransformModelAction extends MDAction {
60  
61      private static final long serialVersionUID = 1L;
62  
63      private Map<String, String> oldTypesMapping = null;
64      private Map<String, DataType> types = null;
65      private Map<String, Class> classes = null;
66  
67      private Profile persistenceProfile = null;
68      private Stereotype entityStereotype = null;
69      private Map<String, EnumerationLiteral> hibernateGeneratorClassMap = null;
70      private Stereotype embeddedValueStereotype = null;
71      private Stereotype identifierStereotype = null;
72      private Stereotype persistentPropertyStereotype = null;
73      private Stereotype persistentAssociationEndStereotype = null;
74      private Stereotype persistentAttributeStereotype = null;
75      private Stereotype naturalIdStereotype = null;
76      private Stereotype localPersistentPropertyStereotype = null;
77  
78      private Properties properties = null;
79      private String propertyFile = null;
80      private TransformationType transformationType = null;
81      private String columnUpdateDateName;
82  
83      public TransformModelAction(String id, String name, TransformationType transformationType, String propertyFile) {
84          super(id, name, null, null);
85          this.propertyFile = propertyFile;
86          this.transformationType = transformationType;
87      }
88  
89      /**
90       * @see java.awt.event.ActionListener#actionPerformed(java.awt.event.ActionEvent)
91       */
92      @Override
93      public void actionPerformed(ActionEvent e) {
94          // get project model
95          Project project = Application.getInstance().getProject();
96          if (project == null) {
97              JOptionPane.showMessageDialog(MDDialogParentProvider.getProvider().getDialogParent(), "Please load a project first.");
98              return;
99          }
100 
101         boolean isInit = init(project);
102 
103         if (isInit) {
104             Model model = project.getModel();
105             if (model == null) {
106                 JOptionPane.showMessageDialog(MDDialogParentProvider.getProvider().getDialogParent(), "No model found !");
107                 release(project);
108                 return;
109             }
110 
111             processModel(project, model);
112         }
113 
114         release(project);
115     }
116 
117     private boolean init(Project project) {
118         SessionManager.getInstance().createSession(project, "Transform model: " + transformationType.name());
119 
120         // Load data types
121         types = new HashMap<String, DataType>();
122         IAttachedProject datatypeModule = getModule(project, "andromda-profile-datatype-3.4.xml.zip");
123         if (datatypeModule == null) {
124             JOptionPane.showMessageDialog(MDDialogParentProvider.getProvider().getDialogParent(),
125                     "Could not load module andromda-profile-datatype-3.4.xml.zip");
126             return false;
127         }
128         boolean dataTypeLoaded =
129                 loadDataType(datatypeModule, "Integer", types, true) && loadDataType(datatypeModule, "String", types, true)
130                         && loadDataType(datatypeModule, "Date", types, true) && loadDataType(datatypeModule, "Double", types, true)
131                         && loadDataType(datatypeModule, "Float", types, true) && loadDataType(datatypeModule, "Timestamp", types, true)
132                         && loadDataType(datatypeModule, "Short", types, true)
133                         && loadDataType(datatypeModule, "Long", types, true);
134         if (!dataTypeLoaded) {
135             return false;
136         }
137 
138         // Load classes (need to set some properties typ, like geometry types)
139         classes = new HashMap<String, Class>();
140         boolean classesLoaded =
141                 loadClass(project, "Geometry", classes, true) && loadClass(project, "Point", classes, true)
142                         && loadClass(project, "MultiPoint", classes, true) && loadClass(project, "LineString", classes, true)
143                         && loadClass(project, "MultiLineString", classes, true) && loadClass(project, "Polygon", classes, true)
144                         && loadClass(project, "MultiPolygon", classes, true) && loadClass(project, "BigInteger", classes, true);
145         if (!classesLoaded) {
146             return false;
147         }
148 
149         // Load old data types
150         oldTypesMapping = new HashMap<String, String>();
151         oldTypesMapping.put("java.lang.String", "String");
152         oldTypesMapping.put("java.util.Date", "Date");
153         oldTypesMapping.put("double", "Double");
154         oldTypesMapping.put("float", "Float");
155 
156         // Get Entity stereotype
157         persistenceProfile = StereotypesHelper.getProfile(project, "andromda-persistence");
158         if (persistenceProfile == null) {
159             JOptionPane.showMessageDialog(MDDialogParentProvider.getProvider().getDialogParent(),
160                     "Could not load profile 'andromda-persistence' !");
161             return false;
162         }
163 
164         // Get stereotype 'Entity'
165         entityStereotype = StereotypesHelper.getStereotype(project, "Entity", persistenceProfile);
166         if (entityStereotype == null) {
167             JOptionPane.showMessageDialog(MDDialogParentProvider.getProvider().getDialogParent(), "Could not load Stereotype 'Entity' !");
168             return false;
169         }
170 
171         // Get tagged property 'andromda_hibernate_generator_class'
172         hibernateGeneratorClassMap = new HashMap<String, EnumerationLiteral>();
173         for (Property entityProperty : entityStereotype.getOwnedAttribute()) {
174             if ("andromda_hibernate_generator_class".equals(entityProperty.getName()) && entityProperty.getType() instanceof Enumeration) {
175                 Enumeration generatorClassEnum = (Enumeration) entityProperty.getType();
176                 for (EnumerationLiteral generatorClassValue : ModelHelper.getAllLiterals(generatorClassEnum)) {
177                     hibernateGeneratorClassMap.put(generatorClassValue.getName(), generatorClassValue);
178                 }
179             }
180         }
181         if (hibernateGeneratorClassMap.size() == 0) {
182             JOptionPane.showMessageDialog(MDDialogParentProvider.getProvider().getDialogParent(),
183                     "Could not load 'andromda_hibernate_generator_class' in stereotype 'Entity' !");
184             return false;
185         }
186         if (hibernateGeneratorClassMap.get("assigned") == null) {
187             JOptionPane.showMessageDialog(MDDialogParentProvider.getProvider().getDialogParent(),
188                     "Could not value 'assigned' from enumeration 'andromda_hibernate_generator_class' in stereotype 'Entity' !");
189             return false;
190         }
191         if (hibernateGeneratorClassMap.get("default") == null) {
192             JOptionPane.showMessageDialog(MDDialogParentProvider.getProvider().getDialogParent(),
193                     "Could not value 'default' from enumeration 'andromda_hibernate_generator_class' in stereotype 'Entity' !");
194             return false;
195         }
196 
197         // Get stereotype 'EmbeddedValue'
198         embeddedValueStereotype = StereotypesHelper.getStereotype(project, "EmbeddedValue", persistenceProfile);
199         if (embeddedValueStereotype == null) {
200             JOptionPane.showMessageDialog(MDDialogParentProvider.getProvider().getDialogParent(),
201                     "Could not load Stereotype 'EmbeddedValue' !");
202             return false;
203         }
204 
205         // Get stereotype 'PersistenceAttribute'
206         persistentPropertyStereotype = StereotypesHelper.getStereotype(project, "PersistentProperty", persistenceProfile);
207         if (persistentPropertyStereotype == null) {
208             JOptionPane.showMessageDialog(MDDialogParentProvider.getProvider().getDialogParent(),
209                     "Could not load Stereotype 'PersistentProperty' !");
210             return false;
211         }
212 
213         persistentAttributeStereotype = StereotypesHelper.getStereotype(project, "PersistentAttribute", persistenceProfile);
214         if (persistentAttributeStereotype == null) {
215             JOptionPane.showMessageDialog(MDDialogParentProvider.getProvider().getDialogParent(),
216                     "Could not load Stereotype 'PersistenceAttribute' !");
217             return false;
218         }
219 
220         persistentAssociationEndStereotype = StereotypesHelper.getStereotype(project, "PersistentAssociationEnd", persistenceProfile);
221         if (persistentAssociationEndStereotype == null) {
222             JOptionPane.showMessageDialog(MDDialogParentProvider.getProvider().getDialogParent(),
223                     "Could not load Stereotype 'PersistentAssociationEnd' !");
224             return false;
225         }
226 
227         // Get <<Identifier>> stereotype
228         identifierStereotype = StereotypesHelper.getStereotype(project, "Identifier", persistenceProfile);
229         if (identifierStereotype == null) {
230             JOptionPane.showMessageDialog(MDDialogParentProvider.getProvider().getDialogParent(),
231                     "Could not load Stereotype 'Identifier' !");
232             return false;
233         }
234 
235         // Get new NaturalId stereotype
236         Profile andromdaIfremerProfile = StereotypesHelper.getProfile(project, "andromda-ifremer-profile");
237         naturalIdStereotype = StereotypesHelper.getStereotype(project, "NaturalId", andromdaIfremerProfile);
238         if (naturalIdStereotype == null) {
239             JOptionPane.showMessageDialog(MDDialogParentProvider.getProvider().getDialogParent(),
240                     "Could not load Stereotype 'NaturalId' from profile 'andromda-ifremer-profile' !");
241             return false;
242         }
243 
244         // Get new LcoaPersistentProperty stereotype
245         localPersistentPropertyStereotype = StereotypesHelper.getStereotype(project, "LocalPersistentProperty", andromdaIfremerProfile);
246         if (localPersistentPropertyStereotype == null) {
247             JOptionPane.showMessageDialog(MDDialogParentProvider.getProvider().getDialogParent(),
248                     "Could not load Stereotype 'LocalPersistentProperty' from profile 'andromda-ifremer-profile' !");
249             return false;
250         }
251 
252         // Load properties file
253         if (!reloadProperties()) {
254             return false;
255         }
256 
257         // Get the updateDt name (could be different in future, on another project)
258         this.columnUpdateDateName = properties.getProperty("entity.updateDateAttributeName", "updateDt");
259 
260         return true;
261     }
262 
263     private void release(Project project) {
264         SessionManager.getInstance().closeSession(project);
265 
266         entityStereotype = null;
267         persistenceProfile = null;
268         persistentPropertyStereotype = null;
269         persistentAttributeStereotype = null;
270         naturalIdStereotype = null;
271         properties = null;
272         hibernateGeneratorClassMap = null;
273     }
274 
275     private boolean reloadProperties() {
276         Properties newProperties = PropertiesHelper.loadProperties(propertyFile, getClass().getClassLoader());
277         
278         if (newProperties == null) {
279             return false;
280         }
281         
282         properties = newProperties;
283         return true;
284     }
285 
286     private void processModel(Project project, Model model) {
287         String text = "";
288 
289         // For each entity classes
290         for (Element entity : getEntities(model)) {
291 
292             // Process entity class
293             text += processEntity((Class) entity);
294         }
295 
296         // For each entity classes
297         for (Element embeddedValue : getEmbeddedValues(model)) {
298 
299             // Process entity class
300             text += processEmbeddedValue((Class) embeddedValue);
301         }
302 
303         if (text == null || text.trim().length() == 0) {
304             JOptionPane.showMessageDialog(MDDialogParentProvider.getProvider().getDialogParent(), "Nothing done");
305         }
306         
307         // Display the result
308         else {
309             // create a JTextArea
310             JTextArea textArea = new JTextArea(25, 70);
311             textArea.setText(text);
312             textArea.setEditable(false);
313     
314             // wrap a scrollpane around it
315             JScrollPane scrollPane = new JScrollPane(textArea);
316     
317             JOptionPane.showMessageDialog(MDDialogParentProvider.getProvider().getDialogParent(), scrollPane);
318         }
319     }
320 
321     private String processEntity(Class entity) {
322         String text = "";
323 
324         if (transformationType == TransformationType.PSF_SERVER) {
325             // Si entitée marquée comme assigned (referentiel), le supprimer cote server
326             text += checkAssignedEntity(entity, transformationType);
327 
328             // Remove remoteId
329             text += transformRemoveRemoteId(entity);
330 
331         }
332         else if (transformationType == TransformationType.PSF_CLIENT) {
333             // Make sure some Id are assigned
334             text += checkAssignedEntity(entity, transformationType);
335         }
336 
337         // For each class properties
338         for (Property p : entity.getOwnedAttribute()) {
339             String propertyText = "";
340 
341             if (transformationType == TransformationType.PIM) {
342 
343                 // remove unique=false (to avoid {nonunique} on UML diagrams
344                 propertyText += resetUnique(entity, p);
345 
346             } else {
347                 // Check length
348                 propertyText += checkAttributeLength(entity, p);
349 
350                 // Check multiplicity
351                 propertyText += checkMultiplicity(entity, p);
352 
353                 // Check if must be sync
354                 propertyText += checkSynchronize(entity, p);
355 
356                 // Check if integrity violation
357                 propertyText += checkIntegrityViolation(entity, p);
358 
359                 // Check if unique
360                 propertyText += checkUnique(entity, p);
361 
362                 // Specific action for server part:
363                 if (transformationType == TransformationType.PSF_SERVER) {
364                     // Change updateDate cardinality to [1]
365                     propertyText += checkUpdateDateNotNull(entity, p);
366 
367                     // Change some properties to not null, when integrity
368                     // violation is need for Client
369                     propertyText += transformIntegrityViolationToNotNull(entity, p);
370 
371                     // Make sure NaturalId is an unique constraint
372                     // propertyText +=
373                     // transformNaturalIdToUniqueConstraint(entity, p);
374 
375                 }
376 
377                 // Specific action for client part:
378                 if (transformationType == TransformationType.PSF_CLIENT) {
379                     // Change updateDate cardinality to [0..1]
380                     propertyText += checkUpdateDateNullable(entity, p);
381 
382                     // Change somes properties to nullable if integrity
383                     // violation is need
384                     propertyText += transformIntegrityViolationToNullbale(entity, p);
385 
386                     // Add remoteId if need
387                     propertyText += checkRemoteId(entity, p);
388 
389                     // Make sure NaturalId is an index
390                     propertyText += transformNaturalIdToIndex(entity, p);
391                 }
392             }
393 
394             if (propertyText.length() > 0) {
395                 String propertyName = p.getName();
396                 if (propertyName == null || propertyName.length() == 0) {
397                     propertyName = p.getType().getName();
398                 }
399                 text += "\n    " + propertyName + propertyText;
400             }
401         }
402 
403         text += checkDuplicateIdentifier(entity);
404 
405         if ("".equals(text)) {
406             return "";
407         }
408         return "\n\r  " + entity.getName() + text;
409     }
410 
411 
412     private List<Element> getEntities(Model model) {
413         List<Element> classes = StereotypesHelper.getExtendedElements(entityStereotype);
414         return classes;
415     }
416 
417     private List<Element> getEmbeddedValues(Model model) {
418         List<Element> embeddedValues = StereotypesHelper.getExtendedElements(embeddedValueStereotype);
419         return embeddedValues;
420     }
421 
422     private List<Property> getIdentifiers(Class entity) {
423         List<Property> identifiers = new ArrayList<Property>();
424         for (Property p : entity.getAttribute()) {
425             if (StereotypesHelper.hasStereotype(p, identifierStereotype)) {
426                 identifiers.add(p);
427             }
428         }
429         return identifiers;
430     }
431 
432     private String checkAssignedEntity(Class entity, TransformationType transformationType) {
433         boolean needAssignedId = false;
434         boolean isClientTarget = transformationType == TransformationType.PSF_CLIENT;
435 
436         List<Property> identifiers = getIdentifiers(entity);
437         if (identifiers != null && identifiers.size() > 1) {
438             needAssignedId = true;
439         }
440         else {
441             String assignedIdValue = null;
442 
443             // Try to get default value from property (e.g. column.regex)
444             if (identifiers.size() == 1){
445                 Property identifier = identifiers.iterator().next();
446                 assignedIdValue = TransformModelHelper.getDefaultValue(entity.getName(), identifier.getName(), "assignedId", properties);
447             }
448             // Try to get default value from package and/or entity name
449             if (assignedIdValue == null) {
450                 assignedIdValue = TransformModelHelper.getDefaultValue(entity.getPackage(), entity.getName(), "assignedId", properties);
451             }
452 
453             String remoteIdValue = TransformModelHelper.getDefaultValue(entity.getPackage(), entity.getName(), "remoteId", properties);
454 
455             if (assignedIdValue != null) {
456                 needAssignedId = "true".equals(assignedIdValue)
457                         || (isClientTarget && "ifClient".equals(assignedIdValue))
458                         || (!isClientTarget && "ifServer".equals(assignedIdValue));
459             }
460             else if (isClientTarget && "true".equalsIgnoreCase(remoteIdValue)) {
461                 needAssignedId = false;
462             }
463         }
464 
465         if (needAssignedId) {
466             return setEntityGeneratorClassToAssigned(entity);
467         }
468         else {
469             return setEntityGeneratorClassToDefault(entity);
470         }
471     }
472 
473     private String setEntityGeneratorClassToAssigned(Class entity) {
474         String text = "";
475         EnumerationLiteral expectedValue = hibernateGeneratorClassMap.get("assigned");
476         EnumerationLiteral defaultGeneratorClassValue = hibernateGeneratorClassMap.get("default");
477 
478         List<?> values = StereotypesHelper.getStereotypePropertyValue(entity, entityStereotype, "andromda_hibernate_generator_class");
479         EnumerationLiteral actualValue = (values != null && values.size() > 0) ? (EnumerationLiteral) values.get(0) : null;
480 
481         if (actualValue == null
482                 || (!expectedValue.equals(actualValue)
483                     /* ONLY change when previous value is 'default' (e.g. keep if 'foreign')*/
484                     && defaultGeneratorClassValue.equals(actualValue))) {
485             StereotypesHelper.setStereotypePropertyValue(entity, entityStereotype, "andromda_hibernate_generator_class", expectedValue);
486             text = "(assigned)";
487         }
488         return text;
489     }
490 
491     private String setEntityGeneratorClassToDefault(Class entity) {
492         String text = "";
493         EnumerationLiteral expectedValue = hibernateGeneratorClassMap.get("default");
494         EnumerationLiteral assignedGeneratorClassValue = hibernateGeneratorClassMap.get("assigned");
495 
496         List<?> values = StereotypesHelper.getStereotypePropertyValue(entity, entityStereotype, "andromda_hibernate_generator_class");
497         EnumerationLiteral actualValue = (values != null && values.size() > 0) ? (EnumerationLiteral) values.get(0) : null;
498 
499         if (actualValue == null
500                 || (!expectedValue.equals(actualValue)
501                     /* ONLY change when previous value is 'assigned' (e.g. keep if 'foreign')*/
502                     && assignedGeneratorClassValue.equals(actualValue))) {
503             StereotypesHelper.setStereotypePropertyValue(entity, entityStereotype, "andromda_hibernate_generator_class", expectedValue);
504             text = "(unassigned)";
505         }
506         return text;
507     }
508 
509     // there can only be one winner
510     private String checkDuplicateIdentifier(Class entity) {
511         String result = "";
512         List<Property> identifiers = getIdentifiers(entity);
513         List<Property> propId = new ArrayList<Property>();
514         List<Property> propCd = new ArrayList<Property>();
515         if (identifiers.size() > 1) {
516             for (Property p : identifiers) {
517                 if (p.getName().endsWith("Id")) {
518                     propId.add(p);
519                 } else if (p.getName().endsWith("Cd")) {
520                     propCd.add(p);
521                 }
522             }
523             if (!propId.isEmpty() && !propCd.isEmpty()) {
524                 for (Property p : propCd) {
525                     StereotypesHelper.removeStereotype(p, identifierStereotype);
526                     result += p.getName() + " remove <<Identifier>>";
527                 }
528             }
529             if (propId.size() > 1) {
530                 Property winner = null;
531                 Integer min = Integer.MAX_VALUE;
532                 for (Property p : propId) {
533                     if (p.getName().length() < min) {
534                         winner = p;
535                         min = p.getName().length();
536                     }
537                 }
538                 for (Property p : propId) {
539                     if (!p.getName().equals(winner.getName())) {
540                         StereotypesHelper.removeStereotype(p, identifierStereotype);
541                         result += p.getName() + " remove <<Identifier>>";
542                     }
543                 }
544             }
545         }
546         if (result == null || "".equals(result.trim())) {
547             return "";
548         }
549 
550         return result;
551     }
552 
553     private String processEmbeddedValue(Class embeddedValue) {
554         String text = "";
555         for (Property p : embeddedValue.getOwnedAttribute()) {
556             String propertyText = "";
557 
558             // Check if integrity violation
559             propertyText += checkIntegrityViolation(embeddedValue, p);
560 
561             if (propertyText.length() > 0) {
562                 String propertyName = p.getName();
563                 if (propertyName == null || propertyName.length() == 0) {
564                     propertyName = p.getType().getName();
565                 }
566                 text += "\n    " + propertyName + propertyText;
567             }
568         }
569 
570         if ("".equals(text)) {
571             return "";
572         }
573         return "\n\r  " + embeddedValue.getName() + text;
574     }
575 
576     private String checkAttributeLength(Class entity, Property attribute) {
577         String text = "";
578         String defaultLength = TransformModelHelper.getDefaultValue(entity, attribute, "length", properties);
579         if (defaultLength != null) {
580             if ("-1".equals(defaultLength) || "NULL".equals(defaultLength)) {
581                 text += setLength(attribute, null, true);
582 
583             } else {
584                 text += setLength(attribute, defaultLength, true);
585             }
586         }
587 
588         if (text == null || "".equals(text.trim())) {
589             return "";
590         }
591 
592         return text;
593     }
594 
595 
596     private String setLength(Property attribute, String length, boolean overrideIfExists) {
597         // Check if stereotype could be applied
598         if (StereotypesHelper.canApplyStereotype(attribute, persistentPropertyStereotype)) {
599             // return "";
600         }
601 
602         // Remove old stereotype 'PersistentAttribute'
603         if (StereotypesHelper.hasStereotype(attribute, persistentAttributeStereotype)) {
604             StereotypesHelper.removeStereotype(attribute, persistentAttributeStereotype);
605         }
606 
607         // Retrieve old tag value
608         String oldTagValue =
609                 TransformModelHelper.getStereotypePropertyValue(attribute, persistentPropertyStereotype, "andromda_persistence_column_length");
610 
611         // Exit if length has been set already
612         if (!overrideIfExists && !"".equals(oldTagValue)) {
613             return "";
614         }
615 
616         // Remove the length if set to 'null'
617         if (length == null) {
618             if (StereotypesHelper.hasStereotype(attribute, persistentPropertyStereotype)) {
619                 StereotypesHelper.clearStereotypeProperty(attribute, persistentPropertyStereotype, "andromda_persistence_column_length");
620                 StereotypesHelper.removeStereotype(attribute, persistentPropertyStereotype);
621                 return "(no length)";
622             }
623             return "";
624         }
625 
626         // Add the stereotype, if need
627         if (StereotypesHelper.hasStereotype(attribute, persistentPropertyStereotype) == false) {
628             StereotypesHelper.addStereotype(attribute, persistentPropertyStereotype);
629         }
630 
631         // Add the length tagged value
632         String newTagValue = String.valueOf(length);
633 
634         // Exit if length is not changed
635         if (newTagValue.equals(oldTagValue)) {
636             return "";
637         }
638         StereotypesHelper.setStereotypePropertyValue(attribute, persistentPropertyStereotype, "andromda_persistence_column_length",
639                 newTagValue);
640 
641         return " (" + newTagValue + ")";
642     }
643 
644     private String checkMultiplicity(Class entity, Property attribute) {
645         String text = "";
646 
647         /// Check if define as nullable in the properties file
648         String defaultIsNullable = TransformModelHelper.getDefaultValue(entity, attribute, "nullable", properties);
649         boolean isNullable = "true".equalsIgnoreCase(defaultIsNullable);
650         if (isNullable) {
651             // If an update is need
652             if (attribute.getLowerValue() == null 
653                     || ((LiteralInteger)attribute.getLowerValue()).getValue() != 0) {
654                 // set lower value to '0'                
655                 ElementsFactory ef = Application.getInstance().getProject().getElementsFactory();
656                 LiteralInteger lowerValue = ef.createLiteralIntegerInstance();
657                 lowerValue.setValue(0);
658                 attribute.setLowerValue(lowerValue);
659 
660                 if (attribute.getUpper() > 0) {
661                     text += " [" + attribute.getLower() + ".." + attribute.getUpper() + "]";
662                 }
663                 else {
664                     text += " [" + attribute.getLower() + "]";
665                 }
666             }
667         }
668         
669         String type = null;
670         if (attribute.getType() != null) {
671             type = attribute.getType().getName();
672         }
673         if ("String".equals(type) == false) {
674             if (attribute.getUpperValue() == null) {
675                 ElementsFactory ef = Application.getInstance().getProject().getElementsFactory();
676                 // set lower value
677                 LiteralInteger lowerValue = ef.createLiteralIntegerInstance();
678                 lowerValue.setValue(isNullable ? 0 : 1);
679                 attribute.setLowerValue(lowerValue);
680 
681                 // set upper value
682                 LiteralInteger upperValue = ef.createLiteralIntegerInstance();
683                 upperValue.setValue(1);
684                 attribute.setUpperValue(upperValue);
685 
686                 if (attribute.getUpper() > 0) {
687                     text += " [" + attribute.getLower() + ".." + attribute.getUpper() + "]";
688                 }
689                 else {
690                     text += " [" + attribute.getLower() + "]";
691                 }
692             }
693         }
694 
695         return text;
696     }
697 
698     private String checkSynchronize(Class entity, Property attribute) {
699         String text = "";
700 
701         String defaultIntegrityViolation = TransformModelHelper.getDefaultValue(entity, attribute, "synchronize", properties);
702 
703         if ("false".equals(defaultIntegrityViolation)) {
704             String oldValue =
705                     TransformModelHelper.getStereotypePropertyValue(attribute, localPersistentPropertyStereotype,
706                             "andromda_local_persistence_synchronize");
707             if (defaultIntegrityViolation.equals(oldValue) == false) {
708                 StereotypesHelper.setStereotypePropertyValue(attribute, localPersistentPropertyStereotype,
709                         "andromda_local_persistence_synchronize", defaultIntegrityViolation);
710                 text += " (no sync)";
711             }
712         }
713 
714         return text;
715     }
716 
717     private String checkIntegrityViolation(Class entity, Property attribute) {
718         String text = "";
719 
720         String defaultIntegrityViolation = TransformModelHelper.getDefaultValue(entity, attribute, "integrityViolation", properties);
721 
722         if ("true".equals(defaultIntegrityViolation)) {
723             String oldValue =
724                     TransformModelHelper.getStereotypePropertyValue(attribute, localPersistentPropertyStereotype,
725                             "andromda_local_persistence_integrity_violation");
726             if (defaultIntegrityViolation.equals(oldValue) == false) {
727                 StereotypesHelper.setStereotypePropertyValue(attribute, localPersistentPropertyStereotype,
728                         "andromda_local_persistence_integrity_violation", defaultIntegrityViolation);
729                 text += " (integrity violation)";
730             }
731         }
732 
733         return text;
734     }
735 
736     private String checkUnique(Class entity, Property attribute) {
737         String text = "";
738 
739         boolean isUnique = attribute.isUnique();
740 
741         String defaultUnique = TransformModelHelper.getDefaultValue(entity, attribute, "unique", properties);
742 
743         // If default value = true
744         if ("true".equals(defaultUnique)) {
745             if (!isUnique) {
746                 attribute.setUnique(true);
747                 text += " (unique)";
748             }
749         }
750 
751         // If not unique
752         else if (isUnique) {
753             attribute.setUnique(false);
754             text += " (no unique)";
755         }
756 
757         return text;
758     }
759 
760     private String resetUnique(Class entity, Property attribute) {
761         String text = "";
762 
763         boolean isUnique = attribute.isUnique();
764 
765         // If default value = true
766         if (!isUnique) {
767             attribute.setUnique(true);
768             text += " (unique)";
769         }
770 
771         return text;
772     }
773 
774     private String checkRemoteId(Class entity, Property attribute) {
775         String propertyName = attribute.getName();
776         if (propertyName == null || "".equals(propertyName.trim())
777                 || !propertyName.toLowerCase().endsWith("id")
778                 || !StereotypesHelper.hasStereotype(attribute, identifierStereotype)) {
779             return "";
780         }
781 
782         String remoteId = TransformModelHelper.getDefaultValue(entity.getPackage(), entity.getName(), "remoteId", properties);
783         String text = "";
784 
785         // If remoteId if need for this class, add it
786         if ("true".equals(remoteId)) {
787             text += transformAddRemoteId(entity);
788         }
789 
790         // If class not need a remote id, make sure id is assigned, and remove
791         // previous remote id
792         else {
793             text += transformRemoveRemoteId(entity);
794         }
795         return text;
796     }
797 
798     private String checkUpdateDateNotNull(Class entity, Property attribute) {
799         return checkUpdateDate(entity, attribute, false);
800     }
801 
802     private String checkUpdateDateNullable(Class entity, Property attribute) {
803         return checkUpdateDate(entity, attribute, true);
804     }
805 
806     private String checkUpdateDate(Class entity, Property attribute, boolean nullable) {
807         String propertyName = attribute.getName();
808         if (propertyName == null || "".equals(propertyName.trim()) || !columnUpdateDateName.equals(propertyName)) {
809             return "";
810         }
811 
812         int oldLowerValue = attribute.getLower();
813         ElementsFactory ef = Application.getInstance().getProject().getElementsFactory();
814 
815         if (nullable && oldLowerValue != 0) {
816             // Set lower value to '0'
817             LiteralInteger lowerValue = ef.createLiteralIntegerInstance();
818             lowerValue.setValue(0);
819             attribute.setLowerValue(lowerValue);
820             return "[0..1]";
821         }
822         if (!nullable && oldLowerValue != 1) {
823             // Set lower value to '1'
824             LiteralInteger lowerValue = ef.createLiteralIntegerInstance();
825             lowerValue.setValue(1);
826             attribute.setLowerValue(lowerValue);
827             return "[1]";
828         }
829 
830         return "";
831     }
832 
833 
834     private String checkAssignedIdForClient(Class entity, Property attribute) {
835         String propertyName = attribute.getName();
836         if (propertyName == null || "".equals(propertyName.trim()) || !"id".equals(propertyName)
837                 || !StereotypesHelper.hasStereotype(attribute, identifierStereotype)) {
838             return "";
839         }
840 
841         String assignedId = TransformModelHelper.getDefaultValue(entity.getPackage(), entity.getName(), "assignedId", properties);
842         if (assignedId == null || assignedId.trim().length() == 0) {
843             String remoteId = TransformModelHelper.getDefaultValue(entity.getPackage(), entity.getName(), "remoteId", properties);
844             // If remoteId if need for this class, assigned=false by default
845             // If class not need a remote id, make sure id is assigned (i.e.
846             // referential classes)
847             if ("true".equals(remoteId)) {
848                 assignedId = "false";
849             } else {
850                 assignedId = "true";
851             }
852         }
853         String text = "";
854 
855         if ("false".equals(assignedId)) {
856             text += transformIdentifierGeneratorToSequence(entity, attribute);
857         }
858 
859         else {
860             text += transformIdentifierGeneratorToAssigned(entity, attribute);
861         }
862         return text;
863     }
864 
865     private String transformAddRemoteId(Class entity) {
866         DataType integerType = types.get("Integer");
867         if (integerType == null) {
868             return "";
869         }
870 
871         ElementsFactory ef = Application.getInstance().getProject().getElementsFactory();
872 
873         Property remoteId = TransformModelHelper.getRemoteId(entity);
874         if (remoteId == null) {
875             remoteId = ef.createPropertyInstance();
876             remoteId.setName("remoteId");
877             try {
878                 // Add
879                 ModelElementsManager.getInstance().addElement(remoteId, entity);
880             } catch (ReadOnlyElementException e) {
881                 return "";
882             }
883         }
884 
885         // Skip exists and no changes need
886         else if (remoteId.getType() == integerType && remoteId.getVisibility() == VisibilityKindEnum.PUBLIC && remoteId.getLower() == 0
887                 && remoteId.getUpper() == 1) {
888             return "";
889         }
890 
891         // Set type
892         remoteId.setType(integerType);
893 
894         // Set visibility
895         remoteId.setVisibility(VisibilityKindEnum.PUBLIC);
896 
897         // Set lower value to '0'
898         LiteralInteger lowerValue = ef.createLiteralIntegerInstance();
899         lowerValue.setValue(0);
900         remoteId.setLowerValue(lowerValue);
901 
902         // Set lower value to '1'
903         LiteralInteger upperValue = ef.createLiteralIntegerInstance();
904         upperValue.setValue(1);
905         remoteId.setUpperValue(upperValue);
906 
907         return " (remoteId)";
908     }
909 
910     private String transformRemoveRemoteId(Class entity) {
911         Property remoteId = TransformModelHelper.getRemoteId(entity);
912         if (remoteId == null)
913             return "";
914 
915         entity.getOwnedAttribute().remove(remoteId);
916         remoteId.refDelete();
917 
918         return " (no remoteId)";
919     }
920 
921     private String transformIdentifierGeneratorToAssigned(Class entity, Property attribute) {
922         String propertyName = attribute.getName();
923         if (propertyName == null || "".equals(propertyName.trim())
924                 || !propertyName.toLowerCase().endsWith("id")
925                 || !StereotypesHelper.hasStereotype(attribute, identifierStereotype)) {
926             return "";
927         }
928 
929         String identifierGeneratorClass =
930                 TransformModelHelper.getStereotypePropertyValue(entity, entityStereotype, "andromda_hibernate_generator_class");
931         if ("assigned".equals(identifierGeneratorClass) == false) {
932             EnumerationLiteral value = hibernateGeneratorClassMap.get("assigned");
933             StereotypesHelper.setStereotypePropertyValue(entity, entityStereotype, "andromda_hibernate_generator_class", value);
934             return " (assigned)";
935         }
936         return "";
937     }
938 
939     private String transformIdentifierGeneratorToSequence(Class entity, Property attribute) {
940         String propertyName = attribute.getName();
941         if (propertyName == null || "".equals(propertyName.trim()) || !"id".equals(propertyName)
942                 || !StereotypesHelper.hasStereotype(attribute, identifierStereotype)) {
943             return "";
944         }
945 
946         String identifierGeneratorClass =
947                 TransformModelHelper.getStereotypePropertyValue(entity, entityStereotype, "andromda_hibernate_generator_class");
948         if (identifierGeneratorClass != null) {
949             // no value = sequence (because of andromda.xml config)
950             StereotypesHelper.clearStereotypeProperty(entity, entityStereotype, "andromda_hibernate_generator_class");
951             return " (default)";
952         }
953         return "";
954     }
955 
956     private String transformIntegrityViolationToNullbale(Class entity, Property attribute) {
957         String integrityViolationValue =
958                 TransformModelHelper.getStereotypePropertyValue(attribute, localPersistentPropertyStereotype,
959                         "andromda_local_persistence_integrity_violation");
960         if ("true".equals(integrityViolationValue)) {
961             ElementsFactory ef = Application.getInstance().getProject().getElementsFactory();
962 
963             if (attribute.getLowerValue() == null || attribute.getLower() != 0) {
964                 // Set lower value to '0'
965                 LiteralInteger lowerValue = ef.createLiteralIntegerInstance();
966                 lowerValue.setValue(0);
967                 attribute.setLowerValue(lowerValue);
968                 return " (nullable)";
969             }
970         }
971 
972         return "";
973     }
974 
975     private String transformIntegrityViolationToNotNull(Class entity, Property attribute) {
976         String integrityViolationValue =
977                 TransformModelHelper.getStereotypePropertyValue(attribute, localPersistentPropertyStereotype,
978                         "andromda_local_persistence_integrity_violation");
979         if ("true".equals(integrityViolationValue)) {
980             ElementsFactory ef = Application.getInstance().getProject().getElementsFactory();
981 
982             if (attribute.getLowerValue() == null || attribute.getLower() != 1) {
983                 // Set lower value to '1'
984                 LiteralInteger lowerValue = ef.createLiteralIntegerInstance();
985                 lowerValue.setValue(1);
986                 attribute.setLowerValue(lowerValue);
987 
988                 return " [1]";
989             }
990         }
991 
992         return "";
993     }
994 
995     private String transformNaturalIdToUniqueConstraint(Class entity, Property attribute) {
996         Property otherEnd = attribute.getOpposite();
997 
998         // Simple property
999         if (otherEnd == null && StereotypesHelper.hasStereotype(attribute, naturalIdStereotype) == true) {
1000             // If a natural id as uniqueGroup exists : remove it
1001             String uniqueGroup =
1002                     TransformModelHelper.getStereotypePropertyValue(attribute, persistentPropertyStereotype,
1003                             "andromda_persistence_column_index");
1004             if (uniqueGroup != null && uniqueGroup.endsWith(TransformModelConstants.NATURAL_ID_INDEX_SUFFIX)) {
1005                 StereotypesHelper.clearStereotypeProperty(attribute, persistentPropertyStereotype, "andromda_persistence_column_index");
1006             }
1007 
1008             String uniqueConstraintName = TransformModelHelper.getUniqueConstraintsName(entity, entityStereotype);
1009             String oldValue =
1010                     TransformModelHelper.getStereotypePropertyValue(attribute, persistentPropertyStereotype,
1011                             "andromda_persistence_column_uniqueGroup");
1012             if (uniqueConstraintName.equals(oldValue) == false) {
1013                 StereotypesHelper.setStereotypePropertyValue(attribute, persistentPropertyStereotype,
1014                         "andromda_persistence_column_uniqueGroup", uniqueConstraintName);
1015                 return " (NaturalId as uniqueGroup)";
1016             }
1017         }
1018 
1019         // AssociationEnd: use the other end
1020         if (otherEnd != null && StereotypesHelper.hasStereotype(otherEnd, naturalIdStereotype) == true) {
1021             // If a natural id as uniqueGroup exists : remove it
1022             String uniqueGroup =
1023                     TransformModelHelper.getStereotypePropertyValue(attribute, persistentPropertyStereotype,
1024                             "andromda_persistence_column_index");
1025             if (uniqueGroup != null && uniqueGroup.endsWith(TransformModelConstants.NATURAL_ID_INDEX_SUFFIX)) {
1026                 StereotypesHelper.clearStereotypeProperty(attribute, persistentPropertyStereotype, "andromda_persistence_column_index");
1027             }
1028 
1029             String uniqueConstraintName = TransformModelHelper.getUniqueConstraintsName(entity, entityStereotype);
1030             String oldValue =
1031                     TransformModelHelper.getStereotypePropertyValue(attribute, persistentAssociationEndStereotype,
1032                             "andromda_persistence_associationEnd_uniqueGroup");
1033             if (uniqueConstraintName.equals(oldValue) == false) {
1034                 StereotypesHelper.setStereotypePropertyValue(attribute, persistentAssociationEndStereotype,
1035                         "andromda_persistence_associationEnd_uniqueGroup", uniqueConstraintName);
1036                 return " (NaturalId as uniqueGroup)";
1037             }
1038         }
1039 
1040         return "";
1041     }
1042 
1043     private String transformNaturalIdToIndex(Class entity, Property attribute) {
1044         Property otherEnd = attribute.getOpposite();
1045 
1046         // Simple property
1047         if (otherEnd == null && StereotypesHelper.hasStereotype(attribute, naturalIdStereotype) == true) {
1048             // If a natural id as index exists : remove it
1049             String naturalIdIndex =
1050                     TransformModelHelper.getStereotypePropertyValue(attribute, persistentPropertyStereotype,
1051                             "andromda_persistence_column_uniqueGroup");
1052             if (naturalIdIndex != null && naturalIdIndex.endsWith(TransformModelConstants.NATURAL_ID_CONSTRAINT_SUFFIX)) {
1053                 StereotypesHelper.clearStereotypeProperty(attribute, persistentPropertyStereotype,
1054                         "andromda_persistence_column_uniqueGroup");
1055             }
1056 
1057             String uniqueIndexName = TransformModelHelper.getUniqueIndexName(entity, entityStereotype);
1058             String oldValue =
1059                     TransformModelHelper.getStereotypePropertyValue(attribute, persistentPropertyStereotype,
1060                             "andromda_persistence_column_index");
1061             if (uniqueIndexName.equals(oldValue) == false) {
1062                 StereotypesHelper.setStereotypePropertyValue(attribute, persistentPropertyStereotype, "andromda_persistence_column_index",
1063                         uniqueIndexName);
1064                 return " (NaturalId as index)";
1065             }
1066         }
1067 
1068         // AssociationEnd: use the other end
1069         if (otherEnd != null && StereotypesHelper.hasStereotype(otherEnd, naturalIdStereotype) == true) {
1070             String uniqueGroup =
1071                     TransformModelHelper.getStereotypePropertyValue(attribute, persistentAssociationEndStereotype,
1072                             "andromda_persistence_associationEnd_uniqueGroup");
1073             if (uniqueGroup != null && uniqueGroup.endsWith(TransformModelConstants.NATURAL_ID_CONSTRAINT_SUFFIX)) {
1074                 StereotypesHelper.clearStereotypeProperty(attribute, persistentAssociationEndStereotype,
1075                         "andromda_persistence_associationEnd_uniqueGroup");
1076             }
1077 
1078             String uniqueIndexName = TransformModelHelper.getUniqueIndexName(entity, entityStereotype);
1079             String oldValue =
1080                     TransformModelHelper.getStereotypePropertyValue(attribute, persistentPropertyStereotype,
1081                             "andromda_persistence_column_index");
1082             if (uniqueIndexName.equals(oldValue) == false) {
1083                 StereotypesHelper.setStereotypePropertyValue(attribute, persistentPropertyStereotype, "andromda_persistence_column_index",
1084                         uniqueIndexName);
1085                 return " (NaturalId as index)";
1086             }
1087         }
1088 
1089         return "";
1090     }
1091 
1092     private File getProjectFile(Project project) {
1093         com.nomagic.magicdraw.core.project.ProjectDescriptor projectDescriptor =
1094                 ProjectDescriptorsFactory.createLocalProjectDescriptor(project);
1095         File projectFile = new File(projectDescriptor.getURI());
1096         return projectFile;
1097     }
1098 
1099     private IAttachedProject getModule(Project project, String filename) {
1100         File projectFile = getProjectFile(project);
1101 
1102         URI moduleFileURI = new File(projectFile.getParent(), filename).toURI();
1103         com.nomagic.magicdraw.core.project.ProjectDescriptor projectDescriptor =
1104                 ProjectDescriptorsFactory.createProjectDescriptor(moduleFileURI);
1105 
1106         IAttachedProject ap = Application.getInstance().getProjectsManager().findAttachedProject(project, projectDescriptor);
1107         return ap;
1108 
1109     }
1110 
1111     private boolean loadDataType(IProject project, String name, Map<String, DataType> types, boolean displayError) {
1112 
1113         final Collection<com.nomagic.uml2.ext.magicdraw.classes.mdkernel.Package> sharedPackages =
1114                 ProjectUtilities.getSharedPackages(project);
1115         final com.nomagic.uml2.ext.magicdraw.classes.mdkernel.Package aPackage = sharedPackages.iterator().next();
1116 
1117         NamedElement foundElement = getElementByName(aPackage, name);
1118 
1119         if (foundElement != null && (foundElement instanceof DataType)) {
1120             types.put(name, (DataType) foundElement);
1121             return true;
1122         }
1123 
1124         if (displayError) {
1125             JOptionPane.showMessageDialog(MDDialogParentProvider.getProvider().getDialogParent(),
1126                     String.format("Could not load data type %s", name));
1127         }
1128         return false;
1129     }
1130 
1131     private boolean loadClass(Project project, String name, Map<String, Class> classes, boolean displayError) {
1132         return loadClass(project.getModel(), name, classes, displayError);
1133     }
1134 
1135     private boolean loadClass(Model model, String name, Map<String, Class> classes, boolean displayError) {
1136 
1137         final Collection<com.nomagic.uml2.ext.magicdraw.classes.mdkernel.Package> packages = model.getNestedPackage();
1138 
1139         NamedElement foundElement = null;
1140         for (com.nomagic.uml2.ext.magicdraw.classes.mdkernel.Package aPackage : packages) {
1141             foundElement = getElementByName(aPackage, name);
1142             if (foundElement != null) {
1143                 break;
1144             }
1145         }
1146 
1147         if (foundElement != null && (foundElement instanceof Class)) {
1148             classes.put(name, (Class) foundElement);
1149             return true;
1150         }
1151 
1152         if (displayError) {
1153             JOptionPane.showMessageDialog(MDDialogParentProvider.getProvider().getDialogParent(),
1154                     String.format("Could not load class %s", name));
1155         }
1156         return false;
1157     }
1158 
1159     /**
1160      * search a element by name, in a recursive way
1161      * 
1162      * @param aPackage
1163      * @param name
1164      * @return
1165      */
1166     protected NamedElement getElementByName(com.nomagic.uml2.ext.magicdraw.classes.mdkernel.Package aPackage, String name) {
1167         NamedElement foundElement = null;
1168         for (NamedElement ne : aPackage.getMember()) {
1169             if (name.equals(ne.getName())) {
1170                 foundElement = ne;
1171             } else if (ne instanceof com.nomagic.uml2.ext.magicdraw.classes.mdkernel.Package) {
1172                 foundElement = getElementByName((com.nomagic.uml2.ext.magicdraw.classes.mdkernel.Package) ne, name);
1173             }
1174             if (foundElement != null) {
1175                 return foundElement;
1176             }
1177         }
1178         return null;
1179     }
1180 
1181 }