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.io.File;
011import java.io.FileNotFoundException;
012import java.io.IOException;
013import java.util.Collection;
014
015import javax.jmi.reflect.RefPackage;
016
017import org.omg.uml13.foundation.core.Attribute;
018import org.omg.uml13.foundation.core.Classifier;
019import org.omg.uml13.foundation.core.Dependency;
020import org.omg.uml13.foundation.core.Operation;
021import org.omg.uml13.foundation.core.Parameter;
022import org.omg.uml13.foundation.datatypes.CallConcurrencyKindEnum;
023import org.omg.uml13.foundation.datatypes.ChangeableKindEnum;
024import org.omg.uml13.foundation.datatypes.Expression;
025import org.omg.uml13.foundation.datatypes.ParameterDirectionKindEnum;
026import org.omg.uml13.foundation.datatypes.ScopeKindEnum;
027import org.omg.uml13.foundation.datatypes.VisibilityKindEnum;
028import org.omg.uml13.modelmanagement.UmlPackage;
029
030import com.sun.javadoc.ClassDoc;
031import com.sun.javadoc.ConstructorDoc;
032import com.sun.javadoc.DocErrorReporter;
033import com.sun.javadoc.Doclet;
034import com.sun.javadoc.FieldDoc;
035import com.sun.javadoc.LanguageVersion;
036import com.sun.javadoc.MethodDoc;
037import com.sun.javadoc.PackageDoc;
038import com.sun.javadoc.ParamTag;
039import com.sun.javadoc.ParameterizedType;
040import com.sun.javadoc.RootDoc;
041import com.sun.javadoc.Tag;
042import com.sun.javadoc.Type;
043
044import net.mdatools.modelant.core.api.name.Name;
045import net.mdatools.modelant.core.name.ClassNameImpl;
046import net.mdatools.modelant.repository.api.ModelFactory;
047import net.mdatools.modelant.repository.api.ModelRepository;
048import net.mdatools.modelant.repository.api.ModelRepositoryFactory;
049import net.mdatools.modelant.uml13.metamodel.Convention;
050
051/**
052 * Javadoc - to - UML reverse engineering. It implements a standard Doclet
053 * functionality, uses the static resources of the outer class (as parameters) and generates UML
054 * classes for the java classes (and interfaces) reviewed.
055 * <p>
056 * The model created presents correctly the real packages, classes, interfaces, methods and attributes:<ul>
057 * <li> Nested classes and interfaces are presented as well, though ones that are marked as static in Java are not marked as
058 * static in the model.
059 * <li>The methods created are not bound to the exceptions they throw (due to some compatibility issues
060 * with Rose XML Plugin), though the exceptions and the stereotypes are registered in the model.
061 * <li>The classes that are not included for documenting but are still referenced by ones being documented are included
062 * in the model as data types with the qualified class name. They are placed in the top-most package - the model itself.
063 * <li> Only one-way X-to-many associations are identified and created. The two-way associations (navigable in both directions)
064 * are not identified.
065 * </ul>
066 * Expected to find system properties:<ul>
067 * <li>model - the name of the model to generate
068 * <li>targetDir - the absolute path to the directory where to create the result file
069 * <li>target - file name where to store the model. Assumed to be created in targetDir
070 * <li>workDir - (optional) the absolute path to the directory where to create the temporary files
071 * </ul>
072 * @author Rusi Popov (popovr@mdatools.net)
073 */
074public class ReverseEngineerJavaDoclet extends Doclet {
075
076  /**
077   * Doclet command-line parameter to specify the absolute file name of the model to produce
078   */
079  public static final String PARAMETER_TARGET_FILE = "target";
080
081  /**
082   * Doclet command-line parameter to specify the directory of model file to produce
083   */
084  public static final String PARAMETER_WORK_DIR = "workDir";
085
086  /**
087   * Doclet command-line parameter with the name of the model to construct
088   */
089  public static final String PARAMETER_MODEL = "model";
090
091  /**
092   * The name of the model parameter that describes a method's result
093   */
094  private static final String RETURN_PARAMETER_NAME = "return";
095
096  /**
097   * The name of the javadoc tag that describes a method's result
098   */
099  private static final String JAVADOC_RETURN_TAG_NAME = "@return";
100
101  /**
102   * Model file name to generate
103   */
104  private final File outputModelFile;
105
106  private final RefPackage extent;
107  private final ModelRepository repository;
108  private final ModelFactory modelFactory;
109  private final Uml13ModelFactory factory;
110
111  /**
112   * This constructor initializes the doclet and instantiates the MDR repository, loads the UML 1.3
113   * meta model and instantiates an extent where to create the uml classes idnetified.
114   *
115   * @throws Exception when creation failed
116   */
117  private ReverseEngineerJavaDoclet() {
118    File workDir;
119    String modelFile;
120    String modelName;
121
122    workDir = new File(System.getProperty(PARAMETER_WORK_DIR, System.getProperty( "java.io.tmpdir")));
123    modelName = System.getProperty(PARAMETER_MODEL, "model");
124    modelFile = System.getProperty(PARAMETER_TARGET_FILE, "model.xml");
125
126    outputModelFile = new File(modelFile);
127
128    repository = ModelRepositoryFactory.construct(workDir);
129    modelFactory = repository.loadMetamodel("UML13");
130    extent = modelFactory.instantiate();
131
132    factory = new Uml13ModelFactory( extent );
133    factory.setModelName(modelName);
134  }
135
136
137  /**
138   * The doclet entry point as of Doclet interface specified.
139   *
140   * @param root
141   * @return true if model extraction completed successfully
142   * @see com.sun.javadoc.Doclet#start(com.sun.javadoc.RootDoc)
143   */
144  public static boolean start(RootDoc root) {
145    ReverseEngineerJavaDoclet doclet;
146
147    try {
148      doclet = new ReverseEngineerJavaDoclet();
149      try {
150        doclet.generate( root );
151        doclet.export();
152      } finally {
153        doclet.shutDown();
154      }
155    } catch (Exception ex) {
156      throw new RuntimeException(ex);
157    }
158    return true;
159  }
160
161
162  /**
163   * This method is required by the doclet convention. It checks the option provided and returns the
164   * number of parameters it requires + 1 (the option itself).
165   *
166   * @param option is the non-null, not empty option name to read parameters of
167   * @return 0 as the number of options it requires including the option itself. 0 means that the
168   *         option is not used, -1 - an error occurred
169   * @see com.sun.javadoc.Doclet#optionLength(java.lang.String)
170   */
171  public static int optionLength(String option) {
172    return 0;
173  }
174
175
176  /**
177   * This method parses the option parameters and sets the proper parameters of this doclet
178   *
179   * @param options
180   * @param reporter
181   * @return true when correct options provided
182   * @see com.sun.javadoc.Doclet#validOptions(java.lang.String[][],
183   *      com.sun.javadoc.DocErrorReporter)
184   */
185  public static boolean validOptions(String[][] options, DocErrorReporter reporter) {
186    return true;
187  }
188
189  /**
190   * Indicates support of generics and annotations
191   */
192  public static LanguageVersion languageVersion() {
193    return LanguageVersion.JAVA_1_5;
194  }
195
196  /**
197   * This method exports the UML model created to the outputFile specified
198   * @throws IOException
199   * @throws FileNotFoundException
200   */
201  private void export() throws FileNotFoundException, IOException {
202    System.out.println("Writing "+outputModelFile+"...");
203
204    modelFactory.writeExtent(extent, outputModelFile);
205  }
206
207  /**
208   * This method releases all resources the doclet had allocated, so this instance cannot be reused
209   * anymore.
210   */
211  private void shutDown() {
212    repository.shutdown();
213  }
214
215  /**
216   * The entry point of the model generation from the JavaDoc results
217   *
218   * @param root
219   */
220  private void generate(RootDoc root) {
221    // generate classes & packages
222    ClassDoc[] classes;
223    ClassDoc classDoc;
224
225    classes = root.classes();
226
227    // create initially all classes/interfaces as empty ones
228    for (int i = 0; i < classes.length; ++i) {
229      generateType( classes[ i ] );
230    }
231
232    // add fields & methods to the created classes
233    for (int i = 0; i < classes.length; ++i) {
234      classDoc = classes[ i ];
235      fillInType(classDoc);
236    }
237  }
238
239
240  /**
241   * Add attributes, methods, constructors to the class' model
242   * @param classDoc
243   */
244  private void fillInType(ClassDoc classDoc) {
245    FieldDoc[] fields;
246    MethodDoc[] methods;
247    ConstructorDoc[] constructors;
248    Classifier umlClassifier;
249    Attribute attribute;
250    Operation constructor;
251    Operation method;
252
253
254    umlClassifier = (Classifier) factory.locateModelElement( classDoc.qualifiedName() ); // classDoc must be already existing
255
256    fields = classDoc.fields( false ); // find all attributes regardless of the scope specified
257
258    // bind the attributes
259    for (int j = 0; j < fields.length; j++) {
260      attribute = createAttribute( fields[ j ] );
261      attribute.setOwner( umlClassifier );
262
263      // mark the attributes in the interfaces as constants (static final)
264      if ( classDoc.isInterface() ) {
265        attribute.setVisibility( VisibilityKindEnum.VK_PUBLIC );    // public
266        attribute.setOwnerScope( ScopeKindEnum.SK_CLASSIFIER );     // static
267        attribute.setChangeability( ChangeableKindEnum.CK_FROZEN ); // final
268      }
269    }
270
271    // bind the constructors
272    constructors = classDoc.constructors( false ); // all declared constructors
273    for (int j = 0; j < constructors.length; j++) {
274      constructor = createConstructor( constructors[ j ], umlClassifier );
275      constructor.setOwner( umlClassifier );
276    }
277
278    // bind the methods
279    methods = classDoc.methods( false ); // all declared methods
280    for (int j = 0; j < methods.length; j++) {
281      method = createMethod( methods[ j ] );
282      method.setOwner( umlClassifier );
283
284      // mark the methods in the interfaces as abstract
285      if ( classDoc.isInterface() ) {
286        method.setVisibility( VisibilityKindEnum.VK_PUBLIC );    // public
287        method.setAbstract( true );
288      }
289    }
290  }
291
292  /**
293   * This method instantiates the proper interface, class or data type depending on
294   * the rules identified<ol>
295   * <li> If the class/interface is not included in the documentation scope, it is
296   * instantiated as a data type
297   * <li> If the cocumentation is for interface that has no attributes, then interface is
298   *  instantiated
299   * <li> Otherwise (this is a class or an interface with attributes) then a class is instantiated.
300   * If the class represents an interface, it is steretyped with &lt;&lt;Interface&gt;&gt;
301   * </ol>
302   * If the classifier has been already registered, then no change is done.
303   * @param classDoc
304   * @return the classifier instantiated. It might be Interface, UmlClass or DataType instance.
305   */
306  private Classifier generateType(ClassDoc classDoc) {
307    Classifier result;
308
309    try {
310      result = (Classifier) factory.locateModelElement( classDoc.qualifiedName() );
311
312    } catch (IllegalArgumentException ex)  { // still not created
313      if ( classDoc.isIncluded() ) {
314        if ( classDoc.isClass() ) {
315          result = gerenateClass( classDoc );
316
317        } else { // clasDoc describes an interface
318          if ( classDoc.fields().length == 0 ) { // no attributes exist in this interface
319            result = gerenateInterface( classDoc );
320
321          } else { // there are attributes - instantiate a class instead
322            result = gerenateClass( classDoc );
323
324            // stereotype the class as an interface
325            factory.constructStereotype( result, Convention.STEREOTYPE_INTERFACE);
326          }
327        }
328      } else { // this class is not included for documenting
329        // create a data type with the qualified class name
330
331        result = factory.constructDataType( classDoc.qualifiedName() );
332      }
333    }
334    return result;
335  }
336
337
338  /**
339   * @param classDoc not null class representation in JavaDoc
340   * @return non-null name representing the class
341   */
342  private Name parseQualifiedName(ClassDoc classDoc) {
343    Name result;
344
345//    if ( classDoc.containingClass() != null ) {
346      result = new ClassNameImpl(classDoc.qualifiedName());
347//    } else {
348// TODO
349//    }
350
351    return result;
352  }
353
354  /**
355   * This method instantiates the interface in the proper packages and contaning classifiers,
356   * together with the extended interfaces assigned. Once the UML does not allow attributes in
357   * interfaces, then if such exist, instead of Interface UmlClass is being instantiated with
358   * "Interface" stereotype.
359   * @param classDoc
360   * @return the classifier created
361   */
362  private Classifier gerenateInterface(ClassDoc classDoc) {
363    Classifier result;
364    UmlPackage umlPackage;
365
366    umlPackage = createPackage( classDoc.containingPackage() );
367    Classifier containingClassifier = null;
368
369    ClassDoc containingClassifierDoc;
370
371    String interfaceName = classDoc.name();
372
373    // define parent class
374    containingClassifierDoc = classDoc.containingClass();
375    if ( containingClassifierDoc != null ) {
376
377      // Instantiate properly the parent - it migt be an interface as well
378      containingClassifier = generateType( containingClassifierDoc );
379
380      // remove the name of the parent class (including '.' from the name of this class
381      interfaceName = interfaceName.substring( containingClassifierDoc.name().length() + 1 );
382    }
383    result = factory.constructInterface( umlPackage, interfaceName );
384
385    // set parent class
386    if ( containingClassifier != null ) {
387      result.setNamespace( containingClassifier );
388    }
389
390    // visiblity
391    if ( classDoc.isPublic() ) {
392      result.setVisibility( VisibilityKindEnum.VK_PUBLIC );
393    } else if ( classDoc.isPrivate() ) {
394      result.setVisibility( VisibilityKindEnum.VK_PRIVATE );
395
396    } else if ( classDoc.isProtected() ) {
397      result.setVisibility( VisibilityKindEnum.VK_PROTECTED );
398    }
399
400    // final / abstract
401    result.setLeaf( classDoc.isFinal() );
402    result.setAbstract( classDoc.isAbstract() );
403
404    factory.constructTagDocumentation( result, classDoc.commentText() );
405
406    // set up interface generalization
407    createSuperInetrfaces( classDoc, result );
408    return result;
409  }
410
411
412  /**
413   * This method instantiates the class in the proper packages and contaning classifiers,
414   * together with the extended class and implemented interfaces assigned.
415   * @param classDoc
416   * @return the newly established classifier as a class
417   */
418  private Classifier gerenateClass(ClassDoc classDoc) {
419    Classifier result;
420    Classifier superClass;
421    Classifier containingClassifier = null;
422    UmlPackage umlPackage;
423
424    ClassDoc superclassDoc;
425    ClassDoc containingClassifierDoc;
426
427    String className = classDoc.name();
428
429    umlPackage = createPackage( classDoc.containingPackage() );
430
431    // define parent class/interface
432    containingClassifierDoc = classDoc.containingClass();
433    if ( containingClassifierDoc != null ) {
434      // instantiate the containig class / interface as a class / interface /datatype
435      containingClassifier = generateType( containingClassifierDoc );
436
437      // remove the name of the container class (including '.' from the name of this class
438      className = className.substring( containingClassifierDoc.name().length() + 1 );
439    }
440
441    // instantiate this class
442    result = factory.constructClass( umlPackage, className );
443
444    // set containg class
445    if ( containingClassifier != null ) {
446      result.setNamespace( containingClassifier );
447    }
448
449    // visiblity
450    if ( classDoc.isPublic() ) {
451      result.setVisibility( VisibilityKindEnum.VK_PUBLIC );
452    } else if ( classDoc.isPrivate() ) {
453      result.setVisibility( VisibilityKindEnum.VK_PRIVATE );
454
455    } else if ( classDoc.isProtected() ) {
456      result.setVisibility( VisibilityKindEnum.VK_PROTECTED );
457    }
458
459    // final / abstract
460    result.setLeaf( classDoc.isFinal() );
461    result.setAbstract( classDoc.isAbstract() );
462
463    factory.constructTagDocumentation( result, classDoc.commentText() );
464
465    // set up class generalization
466    superclassDoc = classDoc.superclass();
467    if ( superclassDoc != null ) { // there is a superclass provided
468      superClass = generateType( superclassDoc );
469
470      factory.constructGeneralization( result, superClass );
471    }
472
473    // set up interface generalization
474    createSuperInetrfaces( classDoc, result );
475    return result;
476  }
477
478
479  /**
480   * This method creates and binds the umlClass provided with the interfaces it implements.
481   *
482   * @param classDoc is the non-null description of the umlClass
483   * @param umlClass is the non-null model class created
484   */
485  private void createSuperInetrfaces(ClassDoc classDoc, Classifier umlClass) {
486    ClassDoc[] interfacesDoc;
487    Classifier superClass;
488
489    interfacesDoc = classDoc.interfaces();
490    for (int i = 0; i < interfacesDoc.length; i++) {
491      superClass = generateType( interfacesDoc[ i ] );
492      factory.constructGeneralization( umlClass, superClass );
493    }
494  }
495
496
497  /**
498   * This method constructs an UML package with the documentation provided. Returns the UML package
499   * built.
500   * @param packageDoc
501   * @return the package built
502   */
503  private UmlPackage createPackage(PackageDoc packageDoc) {
504    UmlPackage result;
505
506    // parse the java package name and create the packages as nested ones
507    result = factory.constructPackage( packageDoc.name());
508    factory.constructTagDocumentation( result, packageDoc.commentText() );
509
510    return result;
511  }
512
513
514  /**
515   * This method creates a constructor method with the documentation provided
516   *
517   * @param doc the constructor (method) documentation
518   * @param thisClass is the class where the constructor is built
519   * @return the created method (operation). It is not null
520   */
521  private Operation createConstructor(ConstructorDoc doc, Classifier thisClass) {
522    Operation result;
523    com.sun.javadoc.Parameter[] parameters;
524    ParamTag[] parameterTags;
525    ClassDoc[] exceptions;
526    Classifier thrownException;
527
528    Dependency dependency;
529
530    Parameter parameter;
531    String parameterComment;
532
533    // create the construtor itself
534    result = factory.constructOperation( doc.name() );
535    result.setSpecification( false ); // assume the method exists, so it is not a specification
536    result.setQuery( false ); // the constructors are not read-only
537
538    factory.constructTagDocumentation( result, doc.commentText() );
539
540    // visiblity
541    if ( doc.isPublic() ) {
542      result.setVisibility( VisibilityKindEnum.VK_PUBLIC );
543    } else if ( doc.isPrivate() ) {
544      result.setVisibility( VisibilityKindEnum.VK_PRIVATE );
545
546    } else if ( doc.isProtected() ) {
547      result.setVisibility( VisibilityKindEnum.VK_PROTECTED );
548    }
549
550    // scope
551    if ( doc.isStatic() ) {
552      result.setOwnerScope( ScopeKindEnum.SK_CLASSIFIER );
553    } else {
554      result.setOwnerScope( ScopeKindEnum.SK_INSTANCE );
555    }
556
557    // inhertitance
558    result.setAbstract( false ); // the constructor cannot be abstract
559    result.setLeaf( false ); // the constructor cannot be final
560
561    // synchronization / concurrency
562    if ( doc.isSynchronized() ) {
563      result.setConcurrency( CallConcurrencyKindEnum.CCK_SEQUENTIAL );
564    } else {
565      result.setConcurrency( CallConcurrencyKindEnum.CCK_CONCURRENT );
566    }
567    // Skip specification
568    // result.setSpecification( "specification..." );
569
570    // signature
571    parameters = doc.parameters();
572    parameterTags = doc.paramTags();
573
574    // define each parameter
575    for (int i = 0; i < parameters.length; i++) {
576      parameter = createParameter( parameters[ i ].name(),
577                                   parameters[i].type(),
578                                   ParameterDirectionKindEnum.PDK_INOUT );
579
580      parameter.setBehavioralFeature( result );
581
582      // bind documentation
583      parameterComment = findComment( JAVADOC_RETURN_TAG_NAME, doc.tags() );
584      if ( parameterComment != null ) {
585        factory.constructTagDocumentation( parameter, parameterComment );
586      }
587    }
588
589    // return/result type
590    parameter = factory.constructParameter(RETURN_PARAMETER_NAME);
591
592    //  provide no name - as of the convention for result parameters
593    parameter.setType( thisClass ); // The type name incudes array dimensions
594    parameter.setKind( ParameterDirectionKindEnum.PDK_RETURN );
595    parameter.setVisibility( VisibilityKindEnum.VK_PUBLIC );
596    parameter.setBehavioralFeature( result );
597
598    // Bind method result (@return) documentation
599    parameterComment = findComment( JAVADOC_RETURN_TAG_NAME, parameterTags );
600    if ( parameterComment != null ) {
601      factory.constructTagDocumentation( parameter, parameterComment );
602    }
603
604    // Add throws clause
605    exceptions = doc.thrownExceptions();
606    for (int i = 0; i < exceptions.length; i++) {
607      thrownException = factory.constructException(exceptions[ i ].qualifiedName());
608
609      // bind operation -> exception in a dependency
610      dependency = factory.constructDependency( result, thrownException, "" );
611
612      // stereotype the dependency
613      factory.constructStereotype(dependency, Convention.STEREOTYPE_THROWS);
614    }
615    return result;
616  }
617
618
619  /**
620   * This method registers a model method with the documentation provided. The method is not bound
621   * to a class
622   *
623   * @param doc a non-null documentation of the method do be created
624   * @return a non null method created
625   */
626  private Operation createMethod(MethodDoc doc) {
627    Operation result;
628    com.sun.javadoc.Parameter[] parameters;
629    ParamTag[] parameterTags;
630    ClassDoc[] exceptions;
631//    Classifier thrownException;
632
633//    Stereotype throwsStereotype;
634//    Dependency dependency;
635
636    Parameter parameter;
637    String parameterComment;
638
639    Type parameterTypeDoc;
640
641    // create the constructor itself
642    result = factory.constructOperation( doc.name() );
643    result.setSpecification( false ); // assume the method exists, so it is not a specification
644    result.setQuery( false ); // assume the method is not read-only
645
646    factory.constructTagDocumentation( result, doc.commentText() );
647
648    // visibility
649    if ( doc.isPublic() ) {
650      result.setVisibility( VisibilityKindEnum.VK_PUBLIC );
651    } else if ( doc.isPrivate() ) {
652      result.setVisibility( VisibilityKindEnum.VK_PRIVATE );
653
654    } else if ( doc.isProtected() ) {
655      result.setVisibility( VisibilityKindEnum.VK_PROTECTED );
656    }
657
658    // scope
659    if ( doc.isStatic() ) {
660      result.setOwnerScope( ScopeKindEnum.SK_CLASSIFIER );
661    } else {
662      result.setOwnerScope( ScopeKindEnum.SK_INSTANCE );
663    }
664
665    // inhertitance
666    result.setAbstract( doc.isAbstract() );
667    result.setLeaf( doc.isFinal() );
668
669    // synchronization / concurrency
670    if ( doc.isSynchronized() ) {
671      result.setConcurrency( CallConcurrencyKindEnum.CCK_SEQUENTIAL );
672    } else {
673      result.setConcurrency( CallConcurrencyKindEnum.CCK_CONCURRENT );
674    }
675    // Skip specification
676    // result.setSpecification( "specification..." );
677
678    // signature
679    parameters = doc.parameters();
680    parameterTags = doc.paramTags();
681
682    // define each parameter
683    for (int i = 0; i < parameters.length; i++) {
684      parameter = createParameter( parameters[ i ].name(),
685                                   parameters[i].type(),
686                                   ParameterDirectionKindEnum.PDK_INOUT );
687      parameter.setBehavioralFeature( result );
688
689      // bind parameter documentation
690      parameterComment = findComment( parameters[ i ].name(), parameterTags );
691      if ( parameterComment != null ) {
692        factory.constructTagDocumentation( parameter, parameterComment );
693      }
694    }
695
696    // return/result type
697    parameterTypeDoc = doc.returnType();
698    if ( parameterTypeDoc != null ) {
699      parameter = createParameter( RETURN_PARAMETER_NAME,
700                                   doc.returnType(),
701                                   ParameterDirectionKindEnum.PDK_RETURN );
702
703      parameter.setBehavioralFeature( result );
704
705      // Bind method result (@return) documentation
706      parameterComment = findComment( JAVADOC_RETURN_TAG_NAME, doc.tags() );
707      if ( parameterComment != null ) {
708        factory.constructTagDocumentation( parameter, parameterComment );
709      }
710    }
711
712    // Add throws clause
713    exceptions = doc.thrownExceptions();
714    for (int i = 0; i < exceptions.length; i++) {
715//      thrownException =
716      factory.constructException( exceptions[ i ].qualifiedName());
717
718// "throws" dependency between operations and exceptions is not properly supported by Rose TODO check again
719//      throwsStereotype= modelManager.instantiateStereotype( MdrUml13Manager.STEREOTYPE_THROWS );
720//
721//      // bind operation -> exception in a dependency
722//      dependency = modelManager.instantiateDependency( result, thrownException, "" );
723//
724//      // stereotype the dependency
725//      throwsStereotype.getExtendedElement().add( dependency );
726    }
727    return result;
728  }
729
730
731  /**
732   * This method instantiates a new method parameter as specified by the parameter description
733   * received.
734   * Pre-condition: All interfaces/datatypes/classes subject of documentation have already been created
735   * @param parameterName is the parameter description
736   * @param parameterType is the documentation of this type
737   * @param kind IN/INOUT/OUT/RETURN
738   * @return the parameter created. It is not null. No documentation is bound.
739   */
740  private Parameter createParameter(String parameterName,
741                                    Type parameterType,
742                                    ParameterDirectionKindEnum kind) {
743    Parameter result;
744    Classifier typeClassifier;
745
746    result = factory.constructParameter(parameterName);
747
748    try {
749      typeClassifier = (Classifier) factory.locateModelElement( parameterType.qualifiedTypeName() );
750
751    } catch (IllegalArgumentException ex) { // this is an unknown still type, thus it has not been documented as a model class
752      typeClassifier = factory.constructDataType( parameterType.qualifiedTypeName());
753    }
754    result.setType( typeClassifier );
755    result.setKind( kind );
756
757    return result;
758  }
759
760
761  /**
762   * This method creates an attribute with the corresponding name, visibility, type and scope
763   * Pre-condition: All interfaces/datatypes/classes subject of documentation have already been created
764   *
765   * @param fieldDoc
766   * @return the attribute cteated
767   */
768  private Attribute createAttribute(FieldDoc fieldDoc) {
769    Type typeDoc;
770    Attribute result;
771    String initialValue;
772    Expression initialValueExpression;
773
774//    System.err.println("createAttribute: "+fieldDoc.name());
775
776    // create the attribute
777    result = factory.constructAttribute( fieldDoc.name() );
778    factory.constructTagDocumentation( result, fieldDoc.commentText() );
779
780    // visiblity
781    if ( fieldDoc.isPublic() ) {
782      result.setVisibility( VisibilityKindEnum.VK_PUBLIC );
783    } else if ( fieldDoc.isPrivate() ) {
784      result.setVisibility( VisibilityKindEnum.VK_PRIVATE );
785
786    } else if ( fieldDoc.isProtected() ) {
787      result.setVisibility( VisibilityKindEnum.VK_PROTECTED );
788    }
789
790    // scope
791    if ( fieldDoc.isStatic() ) {
792      result.setOwnerScope( ScopeKindEnum.SK_CLASSIFIER );
793    } else {
794      result.setOwnerScope( ScopeKindEnum.SK_INSTANCE );
795    }
796
797    // changeability
798    if ( fieldDoc.isFinal() ) {
799      result.setChangeability( ChangeableKindEnum.CK_FROZEN );
800    } else {
801      result.setChangeability( ChangeableKindEnum.CK_CHANGEABLE );
802    }
803
804    // type
805    typeDoc = fieldDoc.type();
806    assignTypeToAttribute( typeDoc, result );
807
808    // initial value
809    initialValue = fieldDoc.constantValueExpression();
810    if ( initialValue != null && !initialValue.equals("") ) {
811      initialValueExpression = factory.constructExpression( initialValue );
812      result.setInitialValue( initialValueExpression );
813    }
814    return result;
815  }
816
817
818  /**
819   * This method processes a type/class (of an attribute). It might be just
820   * a primitive type, a class, a collection or a generic class. Il looks up
821   * or creates the target type as an UML Class:<ul>
822   * <li> the data types are just instantiated as data types in UML
823   * <li> the classes are located as classes in the model. If not found, they are
824   *      instantiated as data types
825   * <li> the collections are represented:<ul>
826   *      <li> if the collection is raw, the type of the attribute becomes Object
827   *           and its multiplicity assigned is *
828   *      <li> if the collection is a generic, the type of the attribute becomes the type parameter
829   *           and the multiplicity is *
830   *      </ul>
831   * <li> the generic classes are represented as:<ul>
832   *      <li> a class named after the generic class + the parameter types
833   *      </ul>
834   * </ul>
835   * @param result
836   * @param typeDoc
837   * @returns a non-null representation of the class
838   */
839  private void assignTypeToAttribute(Type typeDoc, Attribute result) {
840    Classifier type;
841    int multiUpperBound;
842    Class declaredAttributeClass;
843    String associatedClassName;
844    ParameterizedType genericType;
845    Type[] arguments;
846
847    if ( typeDoc.isPrimitive() ) { // an attribute of primitive type
848      try {
849        type = (Classifier) factory.locateModelElement( typeDoc.simpleTypeName() ); // TODO CHECK AGAIN if qualified of simple name should be used
850
851      } catch (IllegalArgumentException ex) { // not found, this is an unknown primitive type
852        type = factory.constructDataType( typeDoc.simpleTypeName());
853      }
854      result.setType( type );
855
856      if ( isSingleValued( typeDoc ) ) {
857        multiUpperBound = 1;
858      } else { // an array of primitive values
859        multiUpperBound =  Convention.UNLIMITED_UPPER_MULTIPLICITY;
860      }
861    } else { // an attribute of a class - associative attribute
862
863      // load the type as a java class
864      try {
865        declaredAttributeClass = Class.forName( typeDoc.qualifiedTypeName() );
866
867      } catch (Exception ex) { // invalid/unknown class declaration
868        declaredAttributeClass = null;
869      } // declaredAttributeClass == null when the class is not known (=> non-standard),
870        // or the class of the attribute, otherwise
871
872      if ( declaredAttributeClass != null
873           && Collection.class.isAssignableFrom( declaredAttributeClass ) ) { // an association *-to-MANY
874        multiUpperBound =  Convention.UNLIMITED_UPPER_MULTIPLICITY;
875
876        // identify the associated class
877        genericType = typeDoc.asParameterizedType();
878        if ( genericType != null ) { // a generic collection, assumed with a single type parameter
879          arguments = genericType.typeArguments();
880          if ( arguments.length > 0 ) {
881            associatedClassName = arguments[0].qualifiedTypeName();
882
883          } else {
884//            System.err.println(" A generic type found with no actual arguments");
885            associatedClassName = Object.class.getName();
886          }
887        } else { // a raw collection - Objects are associated
888          associatedClassName = Object.class.getName();
889        }
890      } else { // an association *-to-ONE
891        multiUpperBound = 1;
892        associatedClassName = typeDoc.qualifiedTypeName();
893      }
894
895      try {
896        type = (Classifier) factory.locateModelElement( associatedClassName );
897
898      } catch (IllegalArgumentException ex) { // this is an unknown primitive type
899        type = factory.constructDataType( associatedClassName );
900      }
901      result.setType( type );
902    }
903
904    // multiplicity
905    // TODO consider adding (as a comment?) the  multiplicity expression
906    result.setMultiplicity( factory.constructMultiplicity(multiUpperBound ));
907  }
908
909
910  /**
911   * This method checks if the type describes a simple (single-value) variable (true) or it is an array.
912   * @param typeDoc a field/parameter type
913   * @return true if the type documentation describes a single-value field type, false if it is an array.
914   */
915  private boolean isSingleValued(Type typeDoc) {
916    return typeDoc.dimension() == null  || typeDoc.dimension().equals("");
917  }
918
919
920  /**
921   * This method finds the contents of a general tag with the name provided
922   *
923   * @param tagName is the non-null pagarameter name
924   * @param tags holds the comments in param tags
925   * @return null if nothing found
926   */
927  private String findComment(String tagName, Tag[] tags) {
928    String result = null;
929    int i = 0;
930    while ( result == null && i < tags.length ) {
931      if ( tagName.equals( tags[ i ].name() ) ) {
932        result = tags[ i ].text();
933      }
934      i++;
935    }
936    return result;
937  }
938
939
940  /**
941   * This method finds the method parameter comment among the param tags provided
942   *
943   * @param parameterName is the non-null pagarameter name
944   * @param parameterTags holds the comments in param tags
945   * @return null if nothing found
946   */
947  private String findComment(String parameterName, ParamTag[] parameterTags) {
948    String result = null;
949    int i = 0;
950    while ( result == null && i < parameterTags.length ) {
951      if ( parameterName.equals( parameterTags[ i ].parameterName() ) ) {
952        result = parameterTags[ i ].parameterComment();
953      }
954      i++;
955    }
956    return result;
957  }
958}