001/*
002 * Copyright c 2018 Rusi Popov, MDA Tools.net All rights reserved.
003 *
004 * This program and the accompanying materials are made available under the terms of the
005 * Eclipse Public License v2.0 which accompanies this distribution, and is available at
006 * http://www.eclipse.org/legal/epl-v20.html
007 */
008package net.mdatools.modelant.uml13.reverse;
009
010import java.util.ArrayList;
011import java.util.Collection;
012import java.util.Iterator;
013
014import javax.jmi.reflect.RefObject;
015import javax.jmi.reflect.RefPackage;
016
017import org.omg.uml13.foundation.core.AssociationEnd;
018import org.omg.uml13.foundation.core.Attribute;
019import org.omg.uml13.foundation.core.Classifier;
020import org.omg.uml13.foundation.core.DataType;
021import org.omg.uml13.foundation.core.Dependency;
022import org.omg.uml13.foundation.core.Generalization;
023import org.omg.uml13.foundation.core.Interface;
024import org.omg.uml13.foundation.core.ModelElement;
025import org.omg.uml13.foundation.core.Namespace;
026import org.omg.uml13.foundation.core.Operation;
027import org.omg.uml13.foundation.core.Parameter;
028import org.omg.uml13.foundation.core.UmlAssociation;
029import org.omg.uml13.foundation.core.UmlClass;
030import org.omg.uml13.foundation.datatypes.AggregationKindEnum;
031import org.omg.uml13.foundation.datatypes.Expression;
032import org.omg.uml13.foundation.datatypes.Multiplicity;
033import org.omg.uml13.foundation.datatypes.MultiplicityRange;
034import org.omg.uml13.foundation.datatypes.ScopeKindEnum;
035import org.omg.uml13.foundation.datatypes.VisibilityKindEnum;
036import org.omg.uml13.foundation.extensionmechanisms.Stereotype;
037import org.omg.uml13.foundation.extensionmechanisms.TaggedValue;
038import org.omg.uml13.modelmanagement.Model;
039import org.omg.uml13.modelmanagement.ModelClass;
040import org.omg.uml13.modelmanagement.UmlPackage;
041
042import net.mdatools.modelant.core.api.name.ClassName;
043import net.mdatools.modelant.core.api.name.Name;
044import net.mdatools.modelant.core.api.name.PackageName;
045import net.mdatools.modelant.core.name.ClassNameImpl;
046import net.mdatools.modelant.core.name.NameImpl;
047import net.mdatools.modelant.core.name.PackageNameImpl;
048
049public class Uml13ModelFactory {
050
051  /**
052   * The stereotype that indicates the ELEMENT declarations in the XSD
053   */
054  public static final String STEREOTYPE_ELEMENT = "element";
055
056  private static final PackageName foundation;
057  private static final PackageName core;
058  private static final PackageName modelManagement;
059  private static final PackageName dataTypes;
060  private static final PackageName extensionMechanisms;
061  private static final PackageName bebehavioralElements;
062  private static final PackageName commonBehavior;
063  private static final ClassName taggedValue;
064  private static final ClassName attribute;
065  private static final ClassName parameter;
066  private static final ClassName className;
067  private static final ClassName interfaceName;
068  private static final ClassName dependencyName;
069  private static final ClassName exceptionName;
070  private static final ClassName expression;
071  private static final ClassName umlPackage;
072  private static final ClassName stereotype;
073  private static final ClassName modelName;
074  private static final ClassName generalization;
075  private static final ClassName operationName;
076  private static final ClassName association;
077  private static final ClassName associationEnd;
078  private static final ClassName multiplicity;
079  private static final ClassName multiplicityRange;
080  private static final ClassName dataType;
081
082  static {
083    foundation = new PackageNameImpl("Foundation");
084    core = new PackageNameImpl(foundation, "Core");
085    dataTypes = new PackageNameImpl(foundation, "Data_Types");
086    extensionMechanisms = new PackageNameImpl(foundation, "Extension_Mechanisms");
087
088    modelManagement = new PackageNameImpl("Model_Management");
089
090    bebehavioralElements = new PackageNameImpl("Behavioral_Elements");
091    commonBehavior = new PackageNameImpl(bebehavioralElements,"Common_Behavior");
092
093    taggedValue = new ClassNameImpl(extensionMechanisms, "TaggedValue");
094    attribute = new ClassNameImpl(core, "Attribute");
095    parameter = new ClassNameImpl(core, "Parameter");
096
097    className = new ClassNameImpl(core, "Class");
098    interfaceName = new ClassNameImpl(core, "Interface");
099    exceptionName = new ClassNameImpl(commonBehavior, "Exception");
100    operationName = new ClassNameImpl(core, "Operation");
101    dependencyName= new ClassNameImpl(core, "Dependency");
102
103    association = new ClassNameImpl(core, "Association");
104    associationEnd = new ClassNameImpl(core, "AssociationEnd");
105    dataType = new ClassNameImpl(core, "DataType");
106    expression = new ClassNameImpl(dataTypes, "Expression");
107    umlPackage = new ClassNameImpl(modelManagement, "Package");
108    modelName = new ClassNameImpl(modelManagement, "Model");
109    stereotype = new ClassNameImpl(extensionMechanisms, "Stereotype");
110    generalization = new ClassNameImpl(core, "Generalization");
111
112    multiplicity = new ClassNameImpl(dataTypes, "Multiplicity");
113    multiplicityRange = new ClassNameImpl(dataTypes, "MultiplicityRange");
114  }
115
116  private final RefPackage extent;
117  private final Model model;
118
119  public Uml13ModelFactory(RefPackage extent) {
120    this.extent = extent;
121    this.model = constructModel(extent);
122  }
123
124  private Model constructModel(RefPackage extent) {
125    Model result;
126    ModelClass metaClass;
127    Collection<Model> allModels;
128
129    metaClass = (ModelClass) modelName.getMetaClass( extent );
130    allModels = metaClass.refAllOfClass();
131    if ( allModels.isEmpty() ) {
132      result = (Model) metaClass.refCreateInstance( null );
133    } else {
134      result = allModels.iterator().next();
135    }
136    return result;
137  }
138
139  public void setModelName(String modelName) {
140    model.setName( modelName );
141  }
142
143  public UmlAssociation constructAssociation(Classifier thisClass,
144                                             String thisRole,
145                                             int thisEndUpper,
146                                             boolean isComposition,
147                                             boolean isThisNavigable,
148                                             Classifier otherClass,
149                                             String otherRole,
150                                             int otherEndUpper,
151                                             Namespace namespace,
152                                             String documentation) {
153    UmlAssociation result;
154    AssociationEnd thisEnd;
155
156    result = (UmlAssociation) association.getMetaClass( extent ).refCreateInstance( null );
157    result.setNamespace( namespace );
158    result.setName( "" );
159    result.setVisibility( VisibilityKindEnum.VK_PUBLIC );
160
161    // this end (the class with the associative attribute)
162    thisEnd = constructAssociationEnd( thisClass, thisRole, thisEndUpper, result );
163
164    if ( isComposition ) {
165      thisEnd.setAggregation( AggregationKindEnum.AK_COMPOSITE );
166    }
167
168    constructAssociationEnd( otherClass, otherRole, otherEndUpper, result );
169
170    return result;
171  }
172
173  private AssociationEnd constructAssociationEnd(Classifier thisClass,
174                                                 String thisRole,
175                                                 int thisEndUpper,
176                                                 UmlAssociation result) {
177    AssociationEnd thisEnd;
178
179    thisEnd = (AssociationEnd) associationEnd.getMetaClass( extent ).refCreateInstance( null );
180    thisEnd.setName( thisRole );
181    thisEnd.setType( thisClass );
182    thisEnd.setVisibility( VisibilityKindEnum.VK_PUBLIC );
183    thisEnd.setAssociation( result );
184    thisEnd.setMultiplicity( constructMultiplicity( thisEndUpper ) );
185
186    return thisEnd;
187  }
188
189  public Multiplicity constructMultiplicity(int otherEndUpper) {
190    Multiplicity result;
191    MultiplicityRange range;
192
193    result = (Multiplicity) multiplicity.getMetaClass( extent ).refCreateInstance( null );
194
195    range = (MultiplicityRange) multiplicityRange.getMetaClass( extent ).refCreateInstance( null );
196    range.setUpper( otherEndUpper );
197    result.getRange().add(range);
198
199    return result;
200  }
201
202  public UmlClass constructClass(String simpleTypeName) {
203    return constructClass( model, simpleTypeName );
204  }
205
206  public UmlClass constructClass(Namespace namespace, String simpleTypeName) {
207    UmlClass result;
208    Object lookedUp;
209
210    lookedUp = locateLocalModelElement( namespace, simpleTypeName );
211    if ( lookedUp == null ) { // none found - build it
212      result = (UmlClass) className.getMetaClass( extent ).refCreateInstance( null );
213
214      result.setName( simpleTypeName );
215      result.setVisibility( VisibilityKindEnum.VK_PUBLIC );
216      result.setNamespace( namespace );
217
218    } else if ( lookedUp instanceof UmlClass ) {
219      result = (UmlClass) lookedUp;
220
221    } else {
222      throw new IllegalArgumentException("Expected to lookup a UmlClass instance for the name: "+namespace.getName()+"."+simpleTypeName+" isntead of "+lookedUp);
223    }
224    return result;
225  }
226
227  /**
228   * Construct the data type with the name
229   * @param dataTypeName is the non-null type name
230   * @return the data type identified or class identified
231   */
232  public DataType constructDataType(String dataTypeName) {
233    DataType result;
234
235    try {
236      result = (DataType) locateModelElement(dataTypeName);
237
238    } catch (IllegalArgumentException ex) { // data type not found
239      result = (DataType) dataType.getMetaClass( extent ).refCreateInstance( null );
240      result.setName( dataTypeName );
241      result.setNamespace( model );
242      result.setVisibility( VisibilityKindEnum.VK_PUBLIC );
243
244    }
245    return result;
246  }
247
248  public Attribute constructAttribute(String name) {
249    Attribute result;
250
251    result = (Attribute) attribute.getMetaClass( extent ).refCreateInstance( null );
252
253    result.setName( name );
254    result.setVisibility( VisibilityKindEnum.VK_PUBLIC );
255    result.setOwnerScope( ScopeKindEnum.SK_INSTANCE );
256
257    return result;
258  }
259
260  public Parameter constructParameter(String name) {
261    Parameter result;
262
263    result = (Parameter) parameter.getMetaClass( extent ).refCreateInstance( null );
264
265    result.setName( name );
266    result.setVisibility( VisibilityKindEnum.VK_PUBLIC );
267
268    return result;
269  }
270
271  public Expression constructExpression(String body) {
272    Expression result;
273
274    result = (Expression) expression.getMetaClass( extent ).refCreateInstance( null );
275    result.setBody( body );
276
277    return result;
278  }
279
280  /**
281   * This method creates the public interface in the package provided
282   *
283   * @param umlPackage the containing package
284   * @param name is the name of the interface to create
285   * @return the interface built
286   */
287  public Interface constructInterface(Namespace umlPackage, String name) {
288    Interface result = (Interface) locateLocalModelElement( umlPackage, name );
289
290    if ( result == null ) { // none found - build it
291      result = (Interface) interfaceName.getMetaClass( extent ).refCreateInstance(null);
292      result.setName( name );
293      result.setVisibility( VisibilityKindEnum.VK_PUBLIC );
294      result.setNamespace( umlPackage );
295    }
296    return result;
297  }
298
299  /**
300   * Instantiate a public exception with the qualified name provided, but the Exceptions might be messed with the regular classes so they
301   * are registered as DataTypes at the model level instead of UnlExceprion-s when they are also
302   * reverse engineered.
303   * @param qualifiedName of the exception to be created
304   * @return the newly created public exception
305   */
306  public Classifier constructException(String qualifiedName) {
307    Classifier result = (Classifier) locateLocalModelElement( model, qualifiedName );
308
309    if ( result == null ) { // still not created, thus it is not included
310      result = (Classifier) exceptionName.getMetaClass( extent ).refCreateInstance( null );
311      result.setName( qualifiedName );
312      result.setNamespace( model );
313      result.setVisibility( VisibilityKindEnum.VK_PUBLIC );
314    }
315    return result;
316  }
317
318  public Operation constructOperation(String name) {
319    Operation result;
320
321    result = (Operation) operationName.getMetaClass( extent ).refCreateInstance( null );
322    result.setName( name );
323    result.setVisibility( VisibilityKindEnum.VK_PUBLIC );
324    result.setOwnerScope( ScopeKindEnum.SK_INSTANCE );
325
326    return result;
327  }
328
329  public void constructGeneralization(Classifier subClass, Classifier superClass) {
330    Generalization result;
331
332    result = (Generalization) generalization.getMetaClass( extent ).refCreateInstance( null );
333
334    result.setNamespace( superClass.getNamespace() );
335    result.setParent( superClass );
336    result.setChild( subClass );
337
338  }
339
340  public Dependency constructDependency(ModelElement client, ModelElement supplier, String name) {
341    Dependency result;
342
343    result = (Dependency) dependencyName.getMetaClass( extent ).refCreateInstance( null );
344    result.getSupplier().add( supplier );
345    result.getClient().add( client );
346    result.setNamespace( model );
347    result.setSpecification( false );
348    result.setName( name );
349    result.setVisibility( VisibilityKindEnum.VK_PUBLIC );
350
351    return result;
352  }
353
354  public UmlPackage constructPackage(String name) {
355    return constructPackage( NameImpl.parseQualifiedName(name) );
356  }
357
358  /**
359   * @param name not null name of the UML package to lookup/construct
360   * @return not null UML package with the name provided from the current model
361   */
362  public UmlPackage constructPackage(Name name) {
363    UmlPackage result;
364    UmlPackage namespace;
365
366    if ( name.getOwner() == null ) {
367      namespace = model;
368    } else {
369      namespace = constructPackage( name.getOwner() );
370    }
371
372    result = (UmlPackage) locateLocalModelElement( namespace, name.getName() );
373    if ( result == null ) { // still not created, thus it is not included
374      result = (UmlPackage) umlPackage.getMetaClass( extent ).refCreateInstance( null );
375      result.setName( name.getName() );
376      result.setNamespace( namespace );
377    }
378    return result;
379  }
380
381  public void constructStereotypeElement(ModelElement extendedElement) {
382    constructStereotype( extendedElement, STEREOTYPE_ELEMENT );
383  }
384
385  public void constructStereotype(ModelElement extendedElement, String name) {
386    Stereotype result;
387
388    result = (Stereotype) locateLocalModelElement( model, name );
389    if ( result == null ) { // still not created, thus it is not included
390      result = (Stereotype) stereotype.getMetaClass( extent ).refCreateInstance( null );
391
392      result.setName( name );
393      result.setNamespace( model );
394      result.setVisibility( VisibilityKindEnum.VK_PUBLIC );
395    }
396    result.getExtendedElement().add( extendedElement );
397  }
398
399  /**
400   * @param tagName a non-null tag name
401   * @return the tag with the name bound to this model element
402   */
403  public TaggedValue getTaggedValue(ModelElement modelElement, String tagName) {
404    TaggedValue result = null;
405    TaggedValue tag;
406    Iterator<TaggedValue> sourceIterator;
407
408    sourceIterator = allTaggedValues(modelElement).iterator();
409    while ( result == null && sourceIterator.hasNext() ) {
410      tag = sourceIterator.next();
411
412      if ( tagName.equals( tag.getTag() ) ) {
413        result = tag;
414      }
415    }
416    return result;
417  }
418
419  public void constructTagDocumentation(ModelElement otherClass, String contents) {
420    TaggedValue documentation;
421
422    documentation = getTaggedValue( otherClass, net.mdatools.modelant.uml13.metamodel.Convention.TAG_VALUE_DOCUMENTATION);
423    if ( documentation == null ) {
424      constructTag( otherClass, net.mdatools.modelant.uml13.metamodel.Convention.TAG_VALUE_DOCUMENTATION, contents );
425
426    } else if ( documentation.getValue() == null ) {
427      documentation.setValue(contents);
428
429    } else if ( documentation.getValue().indexOf( contents ) < 0 ) {
430      documentation.setValue(documentation.getValue()+"\n\r"+contents);
431    }
432  }
433
434  /**
435   * @param modelElement is a non-null model element.
436   * @return a non-null collection of tagged values bound to that model element
437   */
438  private Collection<TaggedValue> allTaggedValues(ModelElement modelElement) {
439    Collection<TaggedValue> result;
440    TaggedValue tag;
441    Iterator sourceIterator;
442
443    result = new ArrayList<TaggedValue>();
444
445    sourceIterator = taggedValue.getMetaClass( extent ).refAllOfClass().iterator();
446    while ( sourceIterator.hasNext() ) {
447      tag = (TaggedValue) sourceIterator.next();
448
449      if ( tag.getModelElement() == modelElement ) {
450        result.add( tag );
451      }
452    }
453    return result;
454  }
455
456  public void constructTag(ModelElement modelElement, String name, String value) {
457    TaggedValue result;
458
459    result = (TaggedValue) taggedValue.getMetaClass( extent ).refCreateInstance( null );
460
461    result.setTag( name );
462    result.setValue( value );
463    result.setModelElement( modelElement );
464  }
465
466  public void constructTagFieldPrecision(ModelElement intoClass, int precision) {
467    constructTag(intoClass,
468                 net.mdatools.modelant.uml13.metamodel.Convention.TAG_VALUE_DATA_TYPE_PRECISION,
469                 "" + precision );
470  }
471
472  public void constructTagSize(ModelElement intoClass, int parseInt) {
473    constructTag(intoClass,
474                 net.mdatools.modelant.uml13.metamodel.Convention.TAG_VALUE_DATA_LENGTH,
475                 "" + parseInt );
476  }
477
478  public void constructTagPersistent(UmlClass intoClass) {
479    constructTag(intoClass,
480                 net.mdatools.modelant.uml13.metamodel.Convention.TAG_VALUE_PERSISTENCE,
481                 net.mdatools.modelant.uml13.metamodel.Convention.TAG_VALUE_PERSISTENCE_VALUE );
482  }
483
484  public void constructTagPrimaryKey(Attribute attribute, int order) {
485    constructTag(attribute,
486                 net.mdatools.modelant.uml13.metamodel.Convention.TAG_VALUE_PRIMARY_KEY,
487                 Integer.toString( order ));
488  }
489
490  /**
491   * Locate the model element using its qualified name in this model object. The empty
492   * (string) name is recognized as the name of the default package - the model itself.
493   *
494   * @param qualifiedName is a non-empty string with the qualified name of a model element
495   * @return the non-null model element with the name specified.
496   */
497  public RefObject locateModelElement(String qualifiedName) throws IllegalArgumentException {
498    return locateModelElement( model, NameImpl.parseQualifiedName(qualifiedName));
499  }
500
501  /**
502   * Locate the model element using its qualified Name in this model object.
503   * @param name is a non-empty qualified name of a model element
504   *
505   * @return the model element with the name specified.
506   */
507  public RefObject locateModelElement(Name<?> name) throws IllegalArgumentException {
508    return locateModelElement(model, name);
509  }
510
511
512  /**
513   * Locate the model element using its Name in this model object.
514   * @param namespace not null namespace to look for the name
515   * @param name not null element name
516   * @return the non-null model element with the name specified.
517   * @throws IllegalArgumentException when not found
518   */
519  public RefObject locateModelElement(Namespace namespace, Name<?> name) throws IllegalArgumentException {
520    RefObject result;
521    RefObject resultNamespace;
522
523    if ( name.getOwner() != null ) {
524      resultNamespace = locateModelElement(namespace, name.getOwner());
525    } else {
526      resultNamespace = namespace;
527    }
528
529    // TODO: Reconsider the locateRelativeModelElement to throw exception when not applicable or found nothing (N/A again)
530
531    if ( !(resultNamespace instanceof Namespace) ) {
532      throw new IllegalArgumentException("Looking up "+name.getOwner()+" reached a non-namespace element to lookup "+name.getName());
533    }
534    result = locateLocalModelElement((Namespace) resultNamespace, name.getName());
535
536    if (result == null ) {
537      throw new IllegalArgumentException("Not found "+name+" in "+name.getOwner()+" namespace");
538    }
539
540    return result;
541  }
542
543  /**
544   * This method locates the model element with name provided in <b>elementName </b> within the UML
545   * namespace <b>outerPackage </b>.
546   *
547   * @param elementName is a non-null name
548   * @return null if no package found, otherwise the package with the name specified
549   */
550  public ModelElement locateLocalModelElement(Namespace namespace, String elementName) {
551    ModelElement result = null;
552    ModelElement ownedElement;
553    Iterator ownedElementsIterator;
554
555    ownedElementsIterator = namespace.getOwnedElement().iterator();
556    while ( result == null && ownedElementsIterator.hasNext() ) {
557      ownedElement = (ModelElement) ownedElementsIterator.next();
558
559      if ( elementName.equals( ownedElement.getName() ) ) {
560        result = ownedElement;
561      }
562    }
563    return result;
564  }
565}