001package org.andromda.metafacades.uml14; 002 003import java.util.ArrayList; 004import java.util.Collection; 005import java.util.Iterator; 006import java.util.LinkedHashMap; 007import java.util.List; 008import java.util.Map; 009import org.andromda.core.common.ExceptionUtils; 010import org.andromda.core.metafacade.MetafacadeConstants; 011import org.andromda.core.metafacade.MetafacadeFactory; 012import org.andromda.metafacades.uml.ActivityGraphFacade; 013import org.andromda.metafacades.uml.ClassifierFacade; 014import org.andromda.metafacades.uml.EventFacade; 015import org.andromda.metafacades.uml.MetafacadeUtils; 016import org.andromda.metafacades.uml.ModelElementFacade; 017import org.andromda.metafacades.uml.ParameterFacade; 018import org.andromda.metafacades.uml.UMLProfile; 019import org.andromda.metafacades.uml.UseCaseFacade; 020import org.apache.commons.collections.CollectionUtils; 021import org.apache.commons.collections.Predicate; 022import org.apache.commons.lang.StringUtils; 023import org.omg.uml.behavioralelements.activitygraphs.ActivityGraph; 024import org.omg.uml.behavioralelements.statemachines.Event; 025import org.omg.uml.behavioralelements.statemachines.FinalState; 026import org.omg.uml.behavioralelements.usecases.UseCase; 027import org.omg.uml.foundation.core.Attribute; 028import org.omg.uml.foundation.core.Classifier; 029import org.omg.uml.foundation.core.CorePackage; 030import org.omg.uml.foundation.core.ModelElement; 031import org.omg.uml.foundation.core.Parameter; 032import org.omg.uml.foundation.core.Stereotype; 033import org.omg.uml.foundation.core.TaggedValue; 034import org.omg.uml.foundation.core.UmlClass; 035import org.omg.uml.foundation.datatypes.VisibilityKind; 036import org.omg.uml.foundation.datatypes.VisibilityKindEnum; 037import org.omg.uml.modelmanagement.Model; 038import org.omg.uml.modelmanagement.UmlPackage; 039 040/** 041 * Utilities for dealing with UML 1.4 metafacades 042 * 043 * @author Chad Brandon 044 * @author Bob Fields 045 */ 046public class UML14MetafacadeUtils 047{ 048 /** 049 * Finds a given model element in the model having the specified 050 * <code>fullyQualifiedName</code>. If the model element can <strong>NOT 051 * </strong> be found, <code>null</code> will be returned instead. 052 * 053 * @param fullyQualifiedName the fully qualified name of the element to 054 * search for. 055 * @param separator the PSM separator used for qualifying the name (example 056 * "."). 057 * @param modelName a flag indicating whether or not a search shall be performed using 058 * the fully qualified model name or fully qualified PSM name. 059 * @return the found model element 060 */ 061 static Object findByFullyQualifiedName(final String fullyQualifiedName, final String separator, final boolean modelName) 062 { 063 Object modelElement; 064 Collection elements = ((org.omg.uml.UmlPackage)MetafacadeFactory.getInstance().getModel().getModel()).getCore() 065 .getModelElement() 066 .refAllOfType(); 067 modelElement = CollectionUtils.find(elements, new Predicate() 068 { 069 public boolean evaluate(Object object) 070 { 071 ModelElement element = (ModelElement)object; 072 StringBuilder fullName = new StringBuilder(getPackageName(element, separator, modelName)); 073 String name = element.getName(); 074 if (StringUtils.isNotBlank(name)) 075 { 076 String namespaceSeparator = MetafacadeConstants.NAMESPACE_SCOPE_OPERATOR; 077 if (!modelName) 078 { 079 namespaceSeparator = separator; 080 } 081 fullName.append(namespaceSeparator); 082 fullName.append(name); 083 } 084 return fullName.toString().equals(fullyQualifiedName); 085 } 086 }); 087 return modelElement; 088 } 089 090 private static String empty = ""; 091 /** 092 * Constructs the package name for the given <code>metaObject</code>, separating the package name by the given 093 * <code>separator</code>. 094 * 095 * @param metaObject the Model Element 096 * @param separator the PSM namespace separator 097 * @param modelName true/false on whether or not to get the model package name instead 098 * of the PSM package name. 099 * @return the package name. 100 */ 101 static String getPackageName(ModelElement metaObject, String separator, boolean modelName) 102 { 103 String packageName = empty; 104 for (ModelElement namespace = metaObject.getNamespace(); (namespace instanceof UmlPackage) && 105 !(namespace instanceof Model); namespace = namespace.getNamespace()) 106 { 107 packageName = packageName.equals(empty) ? namespace.getName() : namespace.getName() + separator + packageName; 108 } 109 if (modelName && StringUtils.isNotBlank(packageName)) 110 { 111 packageName = StringUtils.replace(packageName, separator, MetafacadeConstants.NAMESPACE_SCOPE_OPERATOR); 112 } 113 return packageName; 114 } 115 116 /** 117 * Basically just checks to make sure the <code>model</code> is of type <code>org.omg.uml.UmlPackage</code> and 118 * retrieves the <code>CorePackage</code> from it. 119 * 120 * @return the <code>model</code> as a <code>org.omg.uml.UmlPackage</code> 121 */ 122 static CorePackage getCorePackage() 123 { 124 return ((org.omg.uml.UmlPackage)MetafacadeFactory.getInstance().getModel().getModel()).getCore(); 125 } 126 127 /** 128 * Finds and returns the first model element having the given <code>name</code> in the <code>modelPackage</code>, 129 * returns <code>null</code> if not found. 130 * 131 * @param name the name to find. 132 * @return the found model element. 133 */ 134 static Object findByName(final String name) 135 { 136 Object modelElement = null; 137 if (StringUtils.isNotBlank(name)) 138 { 139 modelElement = CollectionUtils.find(getModel().getCore().getModelElement().refAllOfType(), new Predicate() 140 { 141 public boolean evaluate(Object object) 142 { 143 return StringUtils.trimToEmpty(((ModelElement)object).getName()).equals(name); 144 } 145 }); 146 } 147 return modelElement; 148 } 149 150 /** 151 * Gets the root package in the model. 152 * 153 * @return the root package as a UmlPackage. 154 */ 155 static UmlPackage getRootPackage() 156 { 157 Object result = null; 158 Collection rootPackages = UML14MetafacadeUtils.getModel().getModelManagement().getModel().refAllOfType(); 159 for (Object rootPackage : rootPackages) 160 { 161 // get the first package that's a ModelElement instance 162 // Note: UML2 allows top level ModelElement to be a Package. 163 if (rootPackage instanceof ModelElement) 164 { 165 result = rootPackage; 166 break; 167 } 168 } 169 return (UmlPackage)result; 170 } 171 172 /** 173 * Returns the entire model. 174 * 175 * @return org.omg.uml.UmlPackage model instance. 176 */ 177 static org.omg.uml.UmlPackage getModel() 178 { 179 return (org.omg.uml.UmlPackage)MetafacadeFactory.getInstance().getModel().getModel(); 180 } 181 182 /** 183 * Gets the correct meta model visibility kind for the given <code>visibility</code> string. 184 * 185 * @param visibility the visibility to retrieve. 186 * @return the VisibilityKind 187 */ 188 static VisibilityKind getVisibilityKind(String visibility) 189 { 190 VisibilityKind visibilityKind = null; 191 visibility = StringUtils.trimToEmpty(visibility); 192 if ("public".equals(visibility)) 193 { 194 visibilityKind = VisibilityKindEnum.VK_PUBLIC; 195 } 196 else if ("private".equals(visibility)) 197 { 198 visibilityKind = VisibilityKindEnum.VK_PRIVATE; 199 } 200 else if (StringUtils.isEmpty(visibility)) 201 { 202 visibilityKind = VisibilityKindEnum.VK_PACKAGE; 203 } 204 else if ("protected".equals(visibility)) 205 { 206 visibilityKind = VisibilityKindEnum.VK_PROTECTED; 207 } 208 return visibilityKind; 209 } 210 211 /** 212 * Creates an attribute having the give <code>name</code> and the type 213 * having the given <code>fullyQualifiedTypeName</code>, with the 214 * specified visibility, if no type can be found with the given name, no 215 * type is set. 216 * 217 * @param name the new name 218 * @param fullyQualifiedTypeName the name of the fully qualified type 219 * @param visibility the visibility name 220 * @param separator the separator used for qualifying the name. 221 * @return the new Attribute. 222 */ 223 static Attribute createAttribute(String name, String fullyQualifiedTypeName, String visibility, String separator) 224 { 225 Attribute attribute = UML14MetafacadeUtils.getCorePackage().getAttribute().createAttribute(); 226 attribute.setName(name); 227 attribute.setVisibility(UML14MetafacadeUtils.getVisibilityKind(visibility)); 228 Object type = UML14MetafacadeUtils.findByFullyQualifiedName(fullyQualifiedTypeName, separator, false); 229 if (type != null && Classifier.class.isAssignableFrom(type.getClass())) 230 { 231 attribute.setType((Classifier)type); 232 } 233 return attribute; 234 } 235 236 /** 237 * Indicates whether or not the attribute exists on the given </code>classifier</code>. 238 * 239 * @param classifier the classifier to check 240 * @param name the name of the attribute 241 * @return true/false 242 */ 243 static boolean attributeExists(Object classifier, String name) 244 { 245 boolean exists = false; 246 if (Classifier.class.isAssignableFrom(classifier.getClass())) 247 { 248 List features = ((Classifier)classifier).getFeature(); 249 if (features != null && !features.isEmpty()) 250 { 251 for (final Iterator featureIterator = features.iterator(); featureIterator.hasNext();) 252 { 253 Object feature = featureIterator.next(); 254 if (feature != null && Attribute.class.isAssignableFrom(feature.getClass())) 255 { 256 exists = StringUtils.trimToEmpty(((Attribute)feature).getName()).equals(name); 257 if(exists) 258 { 259 break; 260 } 261 } 262 } 263 } 264 } 265 return exists; 266 } 267 268 /** 269 * Finds or creates a stereotype with the given name. If the stereotype isn't found, it will be created. 270 * 271 * @param name the name of the stereotype. 272 * @return the new Stereotype. 273 */ 274 static Stereotype findOrCreateStereotype(String name) 275 { 276 Object stereotype = UML14MetafacadeUtils.findByName(name); 277 if (stereotype == null || !Stereotype.class.isAssignableFrom(stereotype.getClass())) 278 { 279 stereotype = UML14MetafacadeUtils.getCorePackage().getStereotype().createStereotype(); 280 ((Stereotype)stereotype).setName(name); 281 } 282 return (Stereotype)stereotype; 283 } 284 285 /** 286 * Returns the first use-case it can find with the given name. 287 * @param name 288 * @return findFirstUseCaseWithNameAndStereotype(name, null) 289 */ 290 static UseCase findFirstUseCaseWithName(String name) 291 { 292 return findFirstUseCaseWithNameAndStereotype(name, null); 293 } 294 295 /** 296 * Returns the first use-case it can find with the given name and stereotype, if the stereotype is not specified (it 297 * is null) it will be ignored and the returned use-case may have any arbitrary stereotype. 298 * @param name 299 * @param stereotypeName 300 * @return useCaseWithNameAndStereotype 301 */ 302 static UseCase findFirstUseCaseWithNameAndStereotype(String name, String stereotypeName) 303 { 304 UseCase useCaseWithNameAndStereotype = null; 305 306 Collection<UseCase> useCases = getModel().getUseCases().getUseCase().refAllOfType(); 307 for (final Iterator<UseCase> useCaseIterator = useCases.iterator(); useCaseIterator.hasNext() && useCaseWithNameAndStereotype == 308 null;) 309 { 310 UseCase useCase = useCaseIterator.next(); 311 if (name.equals(useCase.getName())) 312 { 313 if (stereotypeName == null || isStereotypePresent(useCase, stereotypeName)) 314 { 315 useCaseWithNameAndStereotype = useCase; 316 } 317 } 318 } 319 320 return useCaseWithNameAndStereotype; 321 } 322 323 /** 324 * Returns the first activity graph it can find with the given name. 325 * @param name 326 * @return findFirstActivityGraphWithNameAndStereotype(name, null) 327 */ 328 static ActivityGraph findFirstActivityGraphWithName(String name) 329 { 330 return findFirstActivityGraphWithNameAndStereotype(name, null); 331 } 332 333 /** 334 * Returns the first activity graph it can find with the given name and stereotype, if the stereotype is not 335 * specified (it is null) it will be ignored and the returned activity graph may have any arbitrary stereotype. 336 * @param name 337 * @param stereotypeName 338 * @return graphWithNameAndStereotype 339 */ 340 static ActivityGraph findFirstActivityGraphWithNameAndStereotype(String name, String stereotypeName) 341 { 342 ActivityGraph graphWithNameAndStereotype = null; 343 344 Collection<ActivityGraph> graphs = getModel().getActivityGraphs().getActivityGraph().refAllOfType(); 345 for (final Iterator<ActivityGraph> graphIterator = graphs.iterator(); 346 graphIterator.hasNext() && graphWithNameAndStereotype == null;) 347 { 348 ActivityGraph graph = graphIterator.next(); 349 if (name.equals(graph.getName())) 350 { 351 if (stereotypeName == null || isStereotypePresent(graph, stereotypeName)) 352 { 353 graphWithNameAndStereotype = graph; 354 } 355 } 356 } 357 358 return graphWithNameAndStereotype; 359 } 360 361 /** 362 * Returns true if the given model element has a tag with the given name and value, returns false otherwise. 363 * @param element 364 * @param tag 365 * @param value 366 * @return tagPresent 367 */ 368 static boolean isTagPresent(ModelElement element, String tag, Object value) 369 { 370 boolean tagPresent = false; 371 372 Collection<TaggedValue> taggedValues = element.getTaggedValue(); 373 for (final Iterator<TaggedValue> taggedValueIterator = taggedValues.iterator(); taggedValueIterator.hasNext() && !tagPresent;) 374 { 375 TaggedValue taggedValue = taggedValueIterator.next(); 376 // does this name match the argument tagged value name ? 377 // Check both the UML14 format name @andromda.value and EMF Format andromda_whatever 378 String tagName = taggedValue.getName(); 379 if (tag.equals(tagName) || MetafacadeUtils.getEmfTaggedValue(tag).equals(tagName) 380 || MetafacadeUtils.getUml14TaggedValue(tag).equals(tagName)) 381 { 382 for (final Iterator valueIterator = taggedValue.getDataValue().iterator(); valueIterator.hasNext() && 383 !tagPresent;) 384 { 385 Object dataValue = valueIterator.next(); 386 if (value.equals(dataValue)) 387 { 388 tagPresent = true; 389 } 390 } 391 for (final Iterator valueIterator = taggedValue.getReferenceValue().iterator(); valueIterator.hasNext() && 392 !tagPresent;) 393 { 394 Object referenceValue = valueIterator.next(); 395 if (value.equals(referenceValue)) 396 { 397 tagPresent = true; 398 } 399 } 400 } 401 } 402 return tagPresent; 403 } 404 405 /** 406 * Returns true if the given model element has a hyperlink with the given value, returns false otherwise. 407 * @param element 408 * @param value 409 * @return isTagPresent(element, "hyperlinkModel", value) 410 */ 411 static boolean isHyperlinkPresent(ModelElement element, Object value) 412 { 413 return isTagPresent(element, "hyperlinkModel", value); 414 } 415 416 /** 417 * @param element 418 * @param stereotypeName 419 * @return stereotypePresent 420 */ 421 static boolean isStereotypePresent(ModelElement element, String stereotypeName) 422 { 423 boolean stereotypePresent = false; 424 425 Collection<Stereotype> stereotypes = element.getStereotype(); 426 for (final Iterator<Stereotype> stereotypeIterator = stereotypes.iterator(); 427 stereotypeIterator.hasNext() && !stereotypePresent;) 428 { 429 Stereotype stereotype = stereotypeIterator.next(); 430 if (stereotypeName.equals(stereotype.getName())) 431 { 432 stereotypePresent = true; 433 } 434 } 435 return stereotypePresent; 436 } 437 438 /** 439 * Returns the first use-case this method can find with the given tagged value or hyperlink. Both arguments are used 440 * to look for the tagged value but only <code>value</code> is used to search for the hyperlink. 441 * @param tag 442 * @param value 443 * @return useCaseWithTaggedValue 444 */ 445 static UseCase findUseCaseWithTaggedValueOrHyperlink(String tag, String value) 446 { 447 UseCase useCaseWithTaggedValue = null; 448 449 Collection<UseCase> useCases = getModel().getUseCases().getUseCase().refAllOfType(); 450 for (final Iterator<UseCase> useCaseIterator = useCases.iterator(); useCaseIterator.hasNext() && useCaseWithTaggedValue == 451 null;) 452 { 453 // loop over all use-cases 454 UseCase useCase = useCaseIterator.next(); 455 if (isTagPresent(useCase, tag, value) || isHyperlinkPresent(useCase, value)) 456 { 457 useCaseWithTaggedValue = useCase; 458 } 459 } 460 461 return useCaseWithTaggedValue; 462 } 463 464 /** 465 * Returns the first class this method can find with the given tagged value or hyperlink. Both arguments are used to 466 * look for the tagged value but only <code>value</code> is used to search for the hyperlink. 467 * @param tag 468 * @param value 469 * @return classWithTaggedValue 470 */ 471 static UmlClass findClassWithTaggedValueOrHyperlink(String tag, String value) 472 { 473 UmlClass classWithTaggedValue = null; 474 475 Collection<UmlClass> classes = getModel().getCore().getUmlClass().refAllOfType(); 476 for (final Iterator<UmlClass> classIterator = classes.iterator(); classIterator.hasNext() && classWithTaggedValue == null;) 477 { 478 // loop over all classes 479 UmlClass clazz = classIterator.next(); 480 if (isTagPresent(clazz, tag, value) || isHyperlinkPresent(clazz, value)) 481 { 482 classWithTaggedValue = clazz; 483 } 484 } 485 486 return classWithTaggedValue; 487 } 488 489 /** 490 * @param useCase 491 * @return finalStates 492 */ 493 static Collection<FinalState> findFinalStatesWithNameOrHyperlink(UseCase useCase) 494 { 495 List finalStates = new ArrayList(); 496 497 if (useCase != null && useCase.getName() != null) 498 { 499 String useCaseName = useCase.getName(); 500 Collection<FinalState> allFinalStates = getModel().getStateMachines().getFinalState().refAllOfType(); 501 for (final Iterator<FinalState> iterator = allFinalStates.iterator(); iterator.hasNext();) 502 { 503 FinalState finalState = iterator.next(); 504 if (useCaseName != null) 505 { 506 if (useCaseName.equals(finalState.getName())) 507 { 508 finalStates.add(finalState); 509 } 510 else 511 { 512 if (isHyperlinkPresent(finalState, useCase)) 513 { 514 finalStates.add(finalState); 515 } 516 } 517 } 518 else 519 { 520 if (isHyperlinkPresent(finalState, useCase)) 521 { 522 finalStates.add(finalState); 523 } 524 } 525 } 526 } 527 528 return finalStates; 529 } 530 531 /** 532 * Finds the given metafacade class for the passed in <code>facade</code>. 533 * 534 * @param facade the model element facade for which to find the meta class. 535 * @return the meta model element 536 */ 537 static ActivityGraph getMetaClass(ActivityGraphFacade facade) 538 { 539 ActivityGraph activityGraph = null; 540 541 if (facade != null) 542 { 543 String id = facade.getId(); 544 Collection<ModelElement> graphs = getModel().getActivityGraphs().getActivityGraph().refAllOfType(); 545 for (final Iterator<ModelElement> iterator = graphs.iterator(); iterator.hasNext() && activityGraph == null;) 546 { 547 ModelElement element = iterator.next(); 548 if (id.equals(element.refMofId())) 549 { 550 activityGraph = (ActivityGraph)element; 551 } 552 } 553 } 554 return activityGraph; 555 } 556 557 /** 558 * Finds the given metafacade class for the passed in <code>facade</code>. 559 * 560 * @param facade the model element facade for which to find the meta class. 561 * @return the meta model element 562 */ 563 static UseCase getMetaClass(UseCaseFacade facade) 564 { 565 UseCase useCase = null; 566 567 if (facade != null) 568 { 569 String id = facade.getId(); 570 Collection<ModelElement> useCases = getModel().getUseCases().getUseCase().refAllOfType(); 571 for (final Iterator<ModelElement> iterator = useCases.iterator(); iterator.hasNext() && useCase == null;) 572 { 573 ModelElement element = iterator.next(); 574 if (id.equals(element.refMofId())) 575 { 576 useCase = (UseCase)element; 577 } 578 } 579 } 580 return useCase; 581 } 582 583 /** 584 * Finds the given metafacade class for the passed in <code>facade</code>. 585 * 586 * @param facade the model element facade for which to find the meta class. 587 * @return the meta model element 588 */ 589 static Parameter getMetaClass(ParameterFacade facade) 590 { 591 Parameter parameter = null; 592 593 if (facade != null) 594 { 595 String id = facade.getId(); 596 Collection<ModelElement> parameters = getModel().getCore().getParameter().refAllOfType(); 597 for (final Iterator<ModelElement> iterator = parameters.iterator(); iterator.hasNext() && parameter == null;) 598 { 599 ModelElement element = iterator.next(); 600 if (id.equals(element.refMofId())) 601 { 602 parameter = (Parameter)element; 603 } 604 } 605 } 606 return parameter; 607 } 608 609 /** 610 * Finds the given metafacade class for the passed in <code>facade</code>. 611 * 612 * @param facade the model element facade for which to find the meta class. 613 * @return the meta model element 614 */ 615 static Event getMetaClass(EventFacade facade) 616 { 617 Event event = null; 618 619 if (facade != null) 620 { 621 String id = facade.getId(); 622 Collection<ModelElement> events = getModel().getStateMachines().getEvent().refAllOfType(); 623 for (final Iterator<ModelElement> iterator = events.iterator(); iterator.hasNext() && event == null;) 624 { 625 ModelElement element = iterator.next(); 626 if (id.equals(element.refMofId())) 627 { 628 event = (Event)element; 629 } 630 } 631 } 632 return event; 633 } 634 635 /** 636 * Finds the given metafacade class for the passed in <code>facade</code>. 637 * 638 * @param facade the model element facade for which to find the meta class. 639 * @return the meta model element 640 */ 641 static ModelElement getMetaClass(ModelElementFacade facade) 642 { 643 ModelElement modelElement = null; 644 645 if (facade != null) 646 { 647 String id = facade.getId(); 648 Collection<ModelElement> modelElements = getModel().getCore().getModelElement().refAllOfType(); 649 for (final Iterator<ModelElement> iterator = modelElements.iterator(); iterator.hasNext() && modelElement == null;) 650 { 651 ModelElement element = iterator.next(); 652 if (id.equals(element.refMofId())) 653 { 654 modelElement = element; 655 } 656 } 657 } 658 return modelElement; 659 } 660 661 /** 662 * Retrieves the serial version UID by reading the tagged value 663 * {@link UMLProfile#TAGGEDVALUE_SERIALVERSION_UID} of the 664 * <code>classifier</code>. 665 * 666 * @param classifier the classifier to be inspected. 667 * @return the serial version UID of the classifier. Returns 668 * <code>null</code> if the tagged value cannot be found. 669 */ 670 public static String getSerialVersionUID(ClassifierFacade classifier) 671 { 672 ExceptionUtils.checkNull("classifer", classifier); 673 String serialVersionString = (String)classifier 674 .findTaggedValue(UMLProfile.TAGGEDVALUE_SERIALVERSION_UID); 675 return StringUtils.trimToNull(serialVersionString); 676 } 677 678 /** 679 * This method removes all duplicates within the <code>elements</code> collection while at the same 680 * time copying tagged values from duplicates to the one remaining element with the given name. 681 * 682 * @param elements the elements to remove duplicates and copy tagged values to. 683 * @return the elements with duplicates removed. 684 */ 685 public static List<ModelElementFacade> removeDuplicatesAndCopyTaggedValues(final Collection<ModelElementFacade> elements) 686 { 687 final Map<String, ModelElementFacade> map = new LinkedHashMap<String, ModelElementFacade>(); 688 if (elements != null) 689 { 690 for (final Iterator<ModelElementFacade> iterator = elements.iterator(); iterator.hasNext();) 691 { 692 ModelElementFacade element = iterator.next(); 693 final String name = element.getName(); 694 final ModelElementFacade existingVariable = map.get(name); 695 // - copy over any tagged values from the existing variable to the new one. 696 if (existingVariable != null) 697 { 698 element.copyTaggedValues(existingVariable); 699 } 700 map.put( 701 name, 702 element); 703 } 704 } 705 return new ArrayList<ModelElementFacade>(map.values()); 706 } 707}