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 <<Interface>> 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}