001package org.andromda.metafacades.uml; 002 003import java.util.ArrayList; 004import java.util.Collection; 005import java.util.Date; 006import java.util.HashMap; 007import java.util.Iterator; 008import java.util.List; 009import java.util.Map; 010import java.util.regex.Matcher; 011import java.util.regex.Pattern; 012import org.andromda.core.metafacade.MetafacadeConstants; 013import org.apache.commons.collections.CollectionUtils; 014import org.apache.commons.collections.Predicate; 015import org.apache.commons.lang.BooleanUtils; 016import org.apache.commons.lang.ObjectUtils; 017import org.apache.commons.lang.StringUtils; 018import org.apache.commons.lang.time.FastDateFormat; 019import org.apache.log4j.Logger; 020 021/** 022 * Contains utilities that are common to the UML metafacades. 023 * 024 * @author Chad Brandon 025 * @author Bob Fields 026 */ 027public class UMLMetafacadeUtils 028{ 029 /** 030 * The logger instance. 031 */ 032 private static final Logger logger = Logger.getLogger(UMLMetafacadeUtils.class); 033 034 /** 035 * Returns true or false depending on whether or not this Classifier or any of its specializations is of the given 036 * type having the specified <code>typeName</code> 037 * @param classifier 038 * @param typeName the name of the type (i.e. datatype::Collection) 039 * @return true/false 040 */ 041 public static boolean isType(ClassifierFacade classifier, String typeName) 042 { 043 boolean isType = false; 044 if (classifier != null && typeName != null) 045 { 046 final String type = StringUtils.trimToEmpty(typeName); 047 String name = StringUtils.trimToEmpty(classifier.getFullyQualifiedName(true)); 048 isType = name.equals(type); 049 // if this isn't a type defined by typeName, see if we can find any 050 // types that inherit from the type. 051 if (!isType) 052 { 053 isType = CollectionUtils.find(classifier.getAllGeneralizations(), new Predicate() 054 { 055 public boolean evaluate(Object object) 056 { 057 String name = StringUtils.trimToEmpty( 058 ((ModelElementFacade)object).getFullyQualifiedName(true)); 059 return name.equals(type); 060 } 061 }) != null; 062 } 063 if (!isType) 064 { 065 // Match=true if the classifier name is in a different package than datatype::, i.e. PrimitiveTypes:: 066 // or the name is the same. Allows using Java, UML Standard types instead of AndroMDA types 067 String lastType = typeName; 068 if (!StringUtils.isBlank(StringUtils.substringAfterLast(typeName, ":"))) 069 { 070 lastType = StringUtils.substringAfterLast(typeName, ":"); 071 } 072 String classifierType = classifier.getFullyQualifiedName(); 073 if (!StringUtils.isBlank(StringUtils.substringAfterLast(classifierType, ":"))) 074 { 075 classifierType = StringUtils.substringAfterLast(classifierType, ":"); 076 } 077 // If FQN class name is the same as the mapped implementation Class Name 078 name = StringUtils.trimToEmpty(classifier.getFullyQualifiedName(true)); 079 // IgnoreCase allows primitive and wrapped types to both return true 080 if (lastType.equalsIgnoreCase(classifierType) 081 || lastType.equalsIgnoreCase(name) || lastType.equalsIgnoreCase(classifier.getFullyQualifiedName())) 082 { 083 isType = true; 084 } 085 } 086 } 087 return isType; 088 } 089 090 // TODO: Move this to an external configuration. Distinguish between Java, C# reserved words. 091 private static List<String> reservedWords = new ArrayList<String>(); 092 private static void populateReservedWords() 093 { 094 synchronized (reservedWords) 095 { 096 if (reservedWords.isEmpty()) 097 { 098 reservedWords.add("abstract"); 099 reservedWords.add("as"); 100 reservedWords.add("assert"); 101 reservedWords.add("auto"); 102 reservedWords.add("bool"); 103 reservedWords.add("boolean"); 104 reservedWords.add("break"); 105 reservedWords.add("byte"); 106 reservedWords.add("case"); 107 reservedWords.add("catch"); 108 reservedWords.add("char"); 109 reservedWords.add("checked"); 110 reservedWords.add("class"); 111 reservedWords.add("const"); 112 reservedWords.add("continue"); 113 reservedWords.add("decimal"); 114 reservedWords.add("default"); 115 reservedWords.add("delegate"); 116 reservedWords.add("delete"); 117 reservedWords.add("deprecated"); 118 reservedWords.add("do"); 119 reservedWords.add("double"); 120 reservedWords.add("else"); 121 reservedWords.add("enum"); 122 reservedWords.add("event"); 123 reservedWords.add("explicit"); 124 reservedWords.add("export"); 125 reservedWords.add("extends"); 126 reservedWords.add("extern"); 127 reservedWords.add("false"); 128 reservedWords.add("final"); 129 reservedWords.add("finally"); 130 reservedWords.add("fixed"); 131 reservedWords.add("float"); 132 reservedWords.add("foreach"); 133 reservedWords.add("for"); 134 reservedWords.add("function"); 135 reservedWords.add("goto"); 136 reservedWords.add("if"); 137 reservedWords.add("implements"); 138 reservedWords.add("implicit"); 139 reservedWords.add("import"); 140 reservedWords.add("in"); 141 reservedWords.add("inline"); 142 reservedWords.add("instanceof"); 143 reservedWords.add("int"); 144 reservedWords.add("interface"); 145 reservedWords.add("internal"); 146 reservedWords.add("is"); 147 reservedWords.add("lock"); 148 reservedWords.add("long"); 149 reservedWords.add("namespace"); 150 reservedWords.add("native"); 151 reservedWords.add("new"); 152 reservedWords.add("null"); 153 reservedWords.add("object"); 154 reservedWords.add("operator"); 155 reservedWords.add("out"); 156 reservedWords.add("override"); 157 reservedWords.add("package"); 158 reservedWords.add("params"); 159 reservedWords.add("private"); 160 reservedWords.add("property"); 161 reservedWords.add("protected"); 162 reservedWords.add("public"); 163 reservedWords.add("readonly"); 164 reservedWords.add("ref"); 165 reservedWords.add("register"); 166 reservedWords.add("return"); 167 reservedWords.add("sbyte"); 168 reservedWords.add("sealed"); 169 reservedWords.add("short"); 170 reservedWords.add("signed"); 171 reservedWords.add("sizeof"); 172 reservedWords.add("static"); 173 reservedWords.add("strictfp"); 174 reservedWords.add("shring"); 175 reservedWords.add("struct"); 176 reservedWords.add("super"); 177 reservedWords.add("switch"); 178 reservedWords.add("synchronized"); 179 reservedWords.add("this"); 180 reservedWords.add("thread"); 181 reservedWords.add("throw"); 182 reservedWords.add("throws"); 183 reservedWords.add("transient"); 184 reservedWords.add("true"); 185 reservedWords.add("try"); 186 reservedWords.add("typedef"); 187 reservedWords.add("typeof"); 188 reservedWords.add("uint"); 189 reservedWords.add("ulong"); 190 reservedWords.add("unchecked"); 191 reservedWords.add("union"); 192 reservedWords.add("unsafe"); 193 reservedWords.add("unsigned"); 194 reservedWords.add("ushort"); 195 reservedWords.add("using"); 196 reservedWords.add("virtual"); 197 reservedWords.add("union"); 198 reservedWords.add("unsigned"); 199 reservedWords.add("uuid"); 200 reservedWords.add("var"); 201 reservedWords.add("void"); 202 reservedWords.add("volatile"); 203 reservedWords.add("while"); 204 } 205 } 206 } 207 208 /** 209 * Returns true if the value is a reserved keyword in Java or C#, or cannot be used as a name 210 * @param name the String to check if a keyword 211 * @return true/false 212 */ 213 public static boolean isReservedWord(String name) 214 { 215 boolean reserved = false; 216 populateReservedWords(); 217 if (StringUtils.isNotBlank(name) && reservedWords.contains(name.toLowerCase())) 218 { 219 reserved = true; 220 } 221 return reserved; 222 } 223 224 /** 225 * Gets the getter prefix for a getter operation given the <code>type</code>. 226 * 227 * @param type the type from which to determine the prefix. 228 * @return the getter prefix. 229 */ 230 public static String getGetterPrefix(final ClassifierFacade type) 231 { 232 return type != null && type.isBooleanType() && type.isPrimitive() ? "is" : "get"; 233 } 234 235 /** 236 * Gets the getter prefix for a getter operation given the <code>type</code>, 237 * taking multiplicity into account for booleans 238 * 239 * @param type the type from which to determine the prefix. 240 * @param lowerBound If > 0 then type is not optional, thus primitive isBoolean() 241 * @return the getter prefix. 242 */ 243 public static String getGetterPrefix(final ClassifierFacade type, int lowerBound) 244 { 245 // Automatically converted to primitive type or wrapped type based on lowerBound 246 return type != null && type.isBooleanType() && (lowerBound > 0 || type.isPrimitive()) ? "is" : "get"; 247 } 248 249 /** 250 * Returns true if the passed in constraint <code>expression</code> is of type <code>kind</code>, false otherwise. 251 * 252 * @param expression the expression to check. 253 * @param kind the constraint kind (i.e. <em>inv</em>,<em>pre</em>, <em>body</em>, etc). 254 * @return true/false 255 */ 256 public static boolean isConstraintKind(String expression, String kind) 257 { 258 Pattern pattern = Pattern.compile(".*\\s*" + StringUtils.trimToEmpty(kind) + "\\s*\\w*\\s*:.*", Pattern.DOTALL); 259 Matcher matcher = pattern.matcher(StringUtils.trimToEmpty(expression)); 260 return matcher.matches(); 261 } 262 263 // TODO extract the mappings into the configurable metafacade namespace in UMLProfile 264 private static Map<String, String> implCollection = new HashMap<String, String>(); 265 /** 266 * Transforms the declared type to implementation type for a declared Collection. 267 * Default: Collection=LinkedList, List=ArrayList, Set=HashSet, SortedSet=TreeSet. 268 * Retains the generics and package in the template declaration, if any 269 * 270 * @param input the declared Collection type to be transformed into an implementation type 271 * @return the Collection implementation declaration. 272 */ 273 public static String getImplCollection(final String input) 274 { 275 synchronized (implCollection) 276 { 277 // Populate collection map based on profile.xml settings 278 // TODO Use mapped implementation type instead of model types 279 if (implCollection.isEmpty()) 280 { 281 // Put all mappings into Map, removing the initial 'datatype::' 282 //implCollection.put("List", "ArrayList"); 283 //implCollection.put("Set", "HashSet"); 284 //implCollection.put("SortedSet", "TreeSet"); 285 //implCollection.put("Map", "HashMap"); 286 //implCollection.put("SortedMap", "TreeMap"); 287 implCollection.put(UMLProfile.COLLECTION_TYPE_NAME.substring( 288 UMLProfile.COLLECTION_TYPE_NAME.lastIndexOf(':')+1), 289 UMLProfile.COLLECTION_IMPL_TYPE_NAME.substring( 290 UMLProfile.COLLECTION_IMPL_TYPE_NAME.lastIndexOf(':')+1)); 291 implCollection.put(UMLProfile.LIST_TYPE_NAME.substring( 292 UMLProfile.LIST_TYPE_NAME.lastIndexOf(':')+1), 293 UMLProfile.LIST_IMPL_TYPE_NAME.substring( 294 UMLProfile.LIST_IMPL_TYPE_NAME.lastIndexOf(':')+1)); 295 implCollection.put(UMLProfile.MAP_TYPE_NAME.substring( 296 UMLProfile.MAP_TYPE_NAME.lastIndexOf(':')+1), 297 UMLProfile.MAP_IMPL_TYPE_NAME.substring( 298 UMLProfile.MAP_IMPL_TYPE_NAME.lastIndexOf(':')+1)); 299 implCollection.put(UMLProfile.ORDERED_MAP_TYPE_NAME.substring( 300 UMLProfile.ORDERED_MAP_TYPE_NAME.lastIndexOf(':')+1), 301 UMLProfile.ORDERED_MAP_IMPL_TYPE_NAME.substring( 302 UMLProfile.ORDERED_MAP_IMPL_TYPE_NAME.lastIndexOf(':')+1)); 303 implCollection.put(UMLProfile.ORDERED_SET_TYPE_NAME.substring( 304 UMLProfile.ORDERED_SET_TYPE_NAME.lastIndexOf(':')+1), 305 UMLProfile.ORDERED_SET_IMPL_TYPE_NAME.substring( 306 UMLProfile.ORDERED_SET_IMPL_TYPE_NAME.lastIndexOf(':')+1)); 307 implCollection.put(UMLProfile.SET_TYPE_NAME.substring( 308 UMLProfile.SET_TYPE_NAME.lastIndexOf(':')+1), 309 UMLProfile.SET_IMPL_TYPE_NAME.substring( 310 UMLProfile.SET_IMPL_TYPE_NAME.lastIndexOf(':')+1)); 311 } 312 } 313 String collectionImpl = input; 314 // No transformation if no package on fullyQualifiedName 315 if (input.indexOf('.') > 0) 316 { 317 String collectionType = null; 318 String genericType = null; 319 String pkg = null; 320 if (input.indexOf('<') > 0) 321 { 322 collectionType = input.substring(0, input.indexOf('<')); 323 genericType = input.substring(input.indexOf('<')); 324 if (genericType.startsWith("<? extends ")) 325 { 326 // Implementation collection type cannot declare 'extends' 327 genericType = '<' + genericType.substring(11); 328 } 329 } 330 else 331 { 332 collectionType = input; 333 genericType = ""; 334 } 335 if (collectionType.indexOf('.') > 0) 336 { 337 pkg = collectionType.substring(0, collectionType.lastIndexOf('.')+1); 338 collectionType = collectionType.substring(collectionType.lastIndexOf('.')+1); 339 } 340 else 341 { 342 pkg = "java.util."; 343 logger.warn("UMLMetafacadeUtils pkg not found for " + collectionType); 344 } 345 String implType = implCollection.get(collectionType); 346 if (implType == null) 347 { 348 logger.warn("UMLMetafacadeUtils colectionImpl not found for " + collectionType); 349 collectionImpl = pkg + "ArrayList" + genericType; 350 } 351 else 352 { 353 //logger.info("UMLMetafacadeUtils translated from " + collectionType + " to " + implType); 354 collectionImpl = pkg + implType + genericType; 355 } 356 } 357 return collectionImpl; 358 } 359 360 /** 361 * Determines if the class/package should be generated. Will not be generated if it has any 362 * stereotypes: documentation, docOnly, Future, Ignore, analysis, perspective, 363 * or any invalid package identifier characters ` ~!@#%^&*()-+={}[]:;<>,?/| 364 * @param mef ModelElementFacade class to check for stereotypes. 365 * @return false if it has any Stereotypes DocOnly, Future, Ignore configured in UMLProfile 366 */ 367 public static boolean shouldOutput(ModelElementFacade mef) 368 { 369 boolean rtn = true; 370 if (mef!=null) 371 { 372 try 373 { 374 PackageFacade pkg = (PackageFacade) mef.getPackage(); 375 if (mef instanceof PackageFacade) 376 { 377 pkg = (PackageFacade) mef; 378 } 379 if (mef.hasStereotype(UMLProfile.STEREOTYPE_DOC_ONLY) || 380 mef.hasStereotype(UMLProfile.STEREOTYPE_FUTURE) || 381 mef.hasStereotype(UMLProfile.STEREOTYPE_IGNORE)) 382 { 383 rtn = false; 384 } 385 if (pkg != null && 386 ( pkg.hasStereotype(UMLProfile.STEREOTYPE_DOC_ONLY) || 387 pkg.hasStereotype(UMLProfile.STEREOTYPE_FUTURE) || 388 pkg.hasStereotype(UMLProfile.STEREOTYPE_IGNORE) || 389 pkg.hasStereotype("analysis") || 390 pkg.hasStereotype("perspective") || 391 // Verify package does not have any Java disallowed characters 392 StringUtils.containsAny(pkg.getName(), " `~!@#%^&*()-+={}[]:;<>,?/|") || 393 "PrimitiveTypes".equals(pkg.getName()) || 394 "datatype".equals(pkg.getName()))) 395 { 396 rtn = false; 397 } 398 } 399 catch (Exception ex) 400 { 401 // Output=true anyway just in case we want this output 402 logger.error("UMLMetafacadeUtils.shouldOutput for " + mef.toString() + ' ' + ex.getClass().getName() + ": "+ ex.getMessage()); 403 } 404 } 405 return rtn; 406 } 407 /** 408 * Get the classname without the package name and without additional template<> parameters. 409 * 410 * @param facade 411 * @param enableTemplating 412 * @return getNameWithoutPackage 413 */ 414 // TODO This should really be a method on ModelElementFacade 415 public static String getClassDeclaration(ModelElementFacade facade, boolean enableTemplating) 416 { 417 return UMLMetafacadeUtils.getClassDeclaration(facade, facade.getName(), enableTemplating); 418 } 419 420 private static final String namespaceScopeOperator = "."; 421 private static final String COMMA = ", "; 422 private static final String LT = "<"; 423 private static final String GT = ">"; 424 /** 425 * Get the classname without the package name and without additional template<> parameters. 426 * 427 * @param facade 428 * @param className Class name to use in the class declaration, overrides facade.getName() 429 * @param enableTemplating Whether template declaration should be created. 430 * @return getNameWithoutPackage 431 */ 432 // TODO This should really be a method on ModelElementFacade 433 public static String getClassDeclaration(ModelElementFacade facade, String className, boolean enableTemplating) 434 { 435 if (StringUtils.isBlank(className)) 436 { 437 className = facade.getName(); 438 } 439 String fullName = StringUtils.trimToEmpty(className); 440 final String packageName = facade.getPackageName(true); 441 final String metafacadeNamespaceScopeOperator = MetafacadeConstants.NAMESPACE_SCOPE_OPERATOR; 442 if (StringUtils.isNotBlank(packageName)) 443 { 444 fullName = packageName + metafacadeNamespaceScopeOperator + fullName; 445 } 446 final TypeMappings languageMappings = facade.getLanguageMappings(); 447 if (languageMappings != null) 448 { 449 fullName = StringUtils.trimToEmpty(languageMappings.getTo(fullName)); 450 451 // now replace the metafacade scope operators 452 // with the mapped scope operators 453 fullName = StringUtils.replace( 454 fullName, 455 metafacadeNamespaceScopeOperator, 456 namespaceScopeOperator); 457 } 458 // remove the package qualifier 459 if (fullName.indexOf('.')>-1) 460 { 461 fullName = fullName.substring(fullName.lastIndexOf('.')+1); 462 } 463 464 if (facade.isTemplateParametersPresent() && enableTemplating) 465 { 466 // we'll be constructing the parameter list in this buffer 467 final StringBuilder buffer = new StringBuilder(); 468 469 // add the name we've constructed so far 470 buffer.append(fullName); 471 472 // start the parameter list 473 buffer.append(LT); 474 475 // loop over the parameters, we are so to have at least one (see 476 // outer condition) 477 int size = facade.getTemplateParameters().size(); 478 int i = 1; 479 for (TemplateParameterFacade parameter : facade.getTemplateParameters()) 480 { 481 if (parameter != null) 482 { 483 /*String name = parameter.getValidationName(); 484 if (name==null && parameter.getParameter() != null) 485 { 486 name = parameter.getParameter().getName(); 487 } 488 buffer.append(name);*/ 489 buffer.append(parameter.getName()); 490 if (i < size) 491 { 492 buffer.append(COMMA); 493 i++; 494 } 495 } 496 } 497 498 // we're finished listing the parameters 499 buffer.append(GT); 500 501 // we have constructed the full name in the buffer 502 fullName = buffer.toString(); 503 } 504 505 return fullName; 506 } 507 508 private static final String QUESTION = "?"; 509 /** 510 * Get the generic template<?, ?> declaration. 511 * 512 * @param facade 513 * @param enableTemplating 514 * @return getGenericTemplate 515 */ 516 // TODO This should really be a method on ModelElementFacade 517 public static String getGenericTemplate(ModelElementFacade facade, boolean enableTemplating) 518 { 519 String fullName = ""; 520 if (facade != null && facade.isTemplateParametersPresent() && enableTemplating) 521 { 522 // we'll be constructing the parameter list in this buffer 523 final StringBuilder buffer = new StringBuilder(); 524 525 // start the parameter list 526 buffer.append(LT); 527 528 // loop over the parameters, we are so to have at least one (see 529 // outer condition) 530 for (Iterator<TemplateParameterFacade> parameterIterator = 531 facade.getTemplateParameters().iterator(); parameterIterator.hasNext();) 532 { 533 parameterIterator.next(); 534 buffer.append(QUESTION); 535 if (parameterIterator.hasNext()) 536 { 537 buffer.append(COMMA); 538 } 539 } 540 541 // we're finished listing the parameters 542 buffer.append(GT); 543 544 // we have constructed the full name in the buffer 545 fullName = buffer.toString(); 546 } 547 548 return fullName; 549 } 550 551 /** 552 * Get the fully-qualified classname without the additional template<> parameters. 553 * 554 * @param facade 555 * @return getFQNameWithoutTemplate 556 */ 557 // TODO This should really be a method on ModelElementFacade 558 public static String getFQNameWithoutTemplate(ModelElementFacade facade) 559 { 560 String fullName = StringUtils.trimToEmpty(facade.getName()); 561 final String packageName = facade.getPackageName(true); 562 final String metafacadeNamespaceScopeOperator = MetafacadeConstants.NAMESPACE_SCOPE_OPERATOR; 563 if (StringUtils.isNotBlank(packageName)) 564 { 565 fullName = packageName + metafacadeNamespaceScopeOperator + fullName; 566 } 567 final TypeMappings languageMappings = facade.getLanguageMappings(); 568 if (languageMappings != null) 569 { 570 fullName = StringUtils.trimToEmpty(languageMappings.getTo(fullName)); 571 fullName = StringUtils.replace( 572 fullName, 573 metafacadeNamespaceScopeOperator, 574 namespaceScopeOperator); 575 } 576 return fullName; 577 } 578 579 /** 580 * Returns the number of methods without stereotypes or with SimpleClass stereotype. . 581 * @param mef ModelElementFacade class to check for stereotypes. 582 * @param outletFile Name of output file currently being processed. How do we get this in template? 583 * @param refOutput Should .ref files be output? 584 * @return false if it has any Stereotypes DocOnly, Future, Ignore configured in UMLProfile 585 */ 586 public static boolean shouldOutput(ModelElementFacade mef, String outletFile, boolean refOutput) 587 { 588 boolean rtn = true; 589 if (outletFile==null) 590 { 591 return rtn; 592 } 593 if (outletFile.endsWith(".ref") && !refOutput) 594 { 595 rtn = false; 596 } 597 else 598 { 599 rtn = shouldOutput(mef); 600 } 601 return rtn; 602 } 603 604 /** 605 * Supplies a result for type = <new value>; initialization for all types 606 * @param facade Type to create default object for 607 * @return Constructor String with facade name 608 */ 609 public String createConstructor(ModelElementFacade facade) 610 { 611 return createConstructor(facade, false); 612 } 613 614 /** 615 * Supplies a result for type = <new value>; initialization for all types 616 * @param facade Type to create default object for 617 * @param useMany Return constructor with multiplicity type instead of underlying type 618 * @return Constructor String with facade name 619 */ 620 public String createConstructor(ModelElementFacade facade, boolean useMany) 621 { 622 return createConstructor(facade, useMany, null); 623 } 624 625 /** 626 * Supplies a result for type = <new value>; initialization for all types 627 * @param facade Type to create default object for 628 * @param useMany Return constructor with multiplicity type instead of underlying type 629 * @param parent Object containing this facade, which may have an attribute named dependency to a different type 630 * @return Constructor String with facade name 631 */ 632 public String createConstructor(ModelElementFacade facade, boolean useMany, ModelElementFacade parent) 633 { 634 if (facade==null) 635 { 636 return "facade was null"; 637 } 638 String rtn = ""; 639 String toString = ""; 640 ClassifierFacade type = null; 641 String typeName = facade.getFullyQualifiedName(); 642 String name = facade.getName(); 643 String defaultValue = ""; 644 // TODO: Default collection type from properties 645 String collectionType = "java.util.ArrayList"; 646 Boolean isMany = null; 647 boolean isEnumeration = false; 648 int maxLength = 9999; 649 /*if (parent != null) 650 { 651 // See if a named dependency exists with the same facadeName 652 for (final DependencyFacade dependency : parent.getSourceDependencies()) 653 { 654 if (dependency.getName().equals(facade.getName()) && dependency instanceof DependencyFacade) 655 { 656 facade = ((DependencyFacade)dependency).getTargetElement(); 657 toString = ".toString()"; 658 break; 659 } 660 } 661 }*/ 662 try { 663 if (logger.isDebugEnabled()) 664 { 665 logger.debug("name=" + name + " typeName=" + typeName + " useMany=" + useMany + " facade=" + facade + " parent=" + parent); 666 } 667 // Use persistence or validation annotations to limit the created value length 668 String length = (String)facade.findTaggedValue("andromda_persistence_column_length"); 669 if (length != null && length.length()>0 && StringUtils.isNumeric(length)) 670 { 671 maxLength = Integer.parseInt(length); 672 } 673 else 674 { 675 length = (String)facade.findTaggedValue("andromda_persistence_column_precision"); 676 if (length != null && length.length()>0 && StringUtils.isNumeric(length)) 677 { 678 maxLength = Integer.parseInt(length); 679 } 680 else 681 { 682 length = (String)facade.findTaggedValue("andromda_validation_length"); 683 if (length != null && length.length()>0 && StringUtils.isNumeric(length)) 684 { 685 maxLength = Integer.parseInt(length); 686 } 687 else 688 { 689 length = (String)facade.findTaggedValue("andromda_validation_precision"); 690 if (length != null && length.length()>0 && StringUtils.isNumeric(length)) 691 { 692 maxLength = Integer.parseInt(length); 693 } 694 } 695 } 696 } 697 if (facade instanceof ClassifierFacade) 698 { 699 ClassifierFacade classifier = (ClassifierFacade) facade; 700 type = classifier; 701 typeName = classifier.getFullyQualifiedName(); 702 } 703 if (facade instanceof AttributeFacade) 704 { 705 AttributeFacade attr = (AttributeFacade) facade; 706 defaultValue = attr.getDefaultValue(); 707 type = attr.getType(); 708 if (useMany) 709 { 710 typeName = attr.getGetterSetterTypeName(); 711 } 712 else 713 { 714 typeName = type.getFullyQualifiedName(); 715 } 716 if (attr.getUpper()>1 || attr.getUpper()==-1) 717 { 718 isMany = true; 719 } 720 } 721 else if (facade instanceof ParameterFacade) 722 { 723 ParameterFacade attr = (ParameterFacade) facade; 724 defaultValue = attr.getDefaultValue(); 725 type = attr.getType(); 726 typeName = type.getFullyQualifiedName(); 727 if (type.isEnumeration()) 728 { 729 facade = type; 730 } 731 else if (useMany) 732 { 733 typeName = collectionType + '<' + type.getFullyQualifiedName() + '>'; 734 } 735 else 736 { 737 typeName = type.getFullyQualifiedName(); 738 } 739 if (attr.getUpper()>1 || attr.getUpper()==-1) 740 { 741 isMany = true; 742 } 743 } 744 if (facade instanceof AssociationEndFacade) 745 { 746 AssociationEndFacade attr = (AssociationEndFacade) facade; 747 type = attr.getType(); 748 if (useMany) 749 { 750 typeName = attr.getGetterSetterTypeName(); 751 } 752 else 753 { 754 typeName = type.getFullyQualifiedName(); 755 } 756 if (attr.getUpper()>1 || attr.getUpper()==-1) 757 { 758 isMany = true; 759 } 760 facade = attr.getType(); 761 } 762 // TODO: Make this work for attribute types other than String. 763 if (parent != null && StringUtils.isEmpty(defaultValue) && ("String".equals(typeName) || "java.lang.String".equals(typeName))) 764 { 765 // See if a named dependency exists with the same facadeName 766 for (final DependencyFacade dependency : parent.getSourceDependencies()) 767 { 768 if (dependency.getName().equals(facade.getName())) 769 { 770 facade = dependency.getTargetElement(); 771 // DependencyFacade type comes back empty for UML2::Integer 772 // Need to get metaObject Name property and verify it is not null. 773 if (facade instanceof ClassifierFacade) 774 { 775 type = (ClassifierFacade) facade; 776 } 777 typeName = facade.getFullyQualifiedName(); 778 toString = ".toString()"; 779 if (logger.isDebugEnabled()) 780 { 781 logger.debug(parent + " " + facade + " = " 782 + dependency + " type=" + type + " typeName=" 783 + typeName); 784 } 785 break; 786 } 787 } 788 } 789 if (type instanceof EnumerationFacade) 790 { 791 EnumerationFacade enumer = (EnumerationFacade) type; 792 //type = enumer.getLiteralType().getFullyQualifiedName(); 793 Collection<AttributeFacade> literals = enumer.getLiterals(); 794 if (StringUtils.isEmpty(defaultValue) && !literals.isEmpty()) 795 { 796 // Just get the first enumeration literal 797 Object literal = literals.iterator().next(); 798 if (literal instanceof EnumerationLiteralFacade) 799 { 800 EnumerationLiteralFacade enumLiteral = (EnumerationLiteralFacade) literal; 801 defaultValue = enumLiteral.getValue(); 802 } 803 else if (literal instanceof AttributeFacade) 804 { 805 AttributeFacade attrib = (AttributeFacade) literal; 806 defaultValue = attrib.getEnumerationValue(); 807 if (defaultValue==null) 808 { 809 defaultValue = attrib.getDefaultValue(); 810 } 811 } 812 // Literal value is always a String. Remove quotes if part of default (i.e. class attribute). 813 defaultValue = StringUtils.remove(defaultValue, "\""); 814 defaultValue = enumer.getFullyQualifiedName() + ".fromValue(\"" + defaultValue + "\")"; 815 } 816 else 817 { 818 defaultValue = enumer.getName() + '.' + defaultValue; 819 } 820 isEnumeration = true; 821 if (logger.isDebugEnabled()) 822 { 823 logger.debug("EnumerationFacade=" + facade + " type=" + type + " literals=" + literals.size() + " default=" + defaultValue); 824 } 825 } 826 if (type != null && type.findTaggedValue("andromda_persistence_lob_type")!=null) 827 { 828 typeName = String.valueOf(type.findTaggedValue("andromda_persistence_lob_type")); 829 // LOB Types have a different datatype than the underlying declared type 830 } 831 if (useMany && (isMany==null || isMany.booleanValue()) && !typeName.endsWith("[]")) 832 { 833 typeName = UMLMetafacadeUtils.getImplCollection(typeName); 834 if (!typeName.startsWith("java.util") && type != null) 835 { 836 if (type.equals("java.util.Collection") || typeName.equals("java.util.List")) 837 { 838 rtn = "new " + collectionType + "<" + typeName + ">()"; 839 } 840 else if (typeName.equals("java.util.Set")) 841 { 842 rtn = "new java.util.HashSet<" + typeName + ">()"; 843 } 844 else if (typeName.equals("java.util.Map")) 845 { 846 rtn = "new java.util.HashMap<" + typeName + ">()"; 847 } 848 else 849 { 850 rtn = "new " + collectionType + '<' + typeName + ">()"; 851 } 852 } 853 else 854 { 855 // Assume array or type Collection<type> 856 rtn = "new " + typeName + "()"; 857 } 858 } 859 else if ("String".equals(typeName) || "java.lang.String".equals(typeName)) 860 { 861 if (defaultValue != null && defaultValue.trim().length() > 0) 862 { 863 if (defaultValue.startsWith("\"") || defaultValue.startsWith("'")) 864 { 865 defaultValue = defaultValue.substring(1, defaultValue.length()-1); 866 } 867 if (defaultValue.endsWith("\"") || defaultValue.endsWith("'")) 868 { 869 defaultValue = defaultValue.substring(0, defaultValue.length()-2); 870 } 871 if (defaultValue.trim().length() > maxLength) 872 { 873 logger.warn("Attribute default for " + facade.getFullyQualifiedName() + " is longer than max column length " + maxLength); 874 defaultValue = defaultValue.substring(0, maxLength-1); 875 } 876 } 877 rtn = '\"' + (StringUtils.isNotBlank(defaultValue) ? defaultValue : name) + '\"'; 878 } 879 else if ("Boolean".equals(typeName) || "java.lang.Boolean".equals(typeName)) 880 { 881 rtn = (StringUtils.isNotBlank(defaultValue) ? "Boolean." + defaultValue.toUpperCase() : "Boolean.TRUE"); 882 } 883 else if ("boolean".equals(typeName)) 884 { 885 rtn = (StringUtils.isNotBlank(defaultValue) ? defaultValue : "true"); 886 } 887 else if ("int".equals(typeName) || "short".equals(typeName) || "long".equals(typeName) 888 || "byte".equals(typeName) || "float".equals(typeName) || "double".equals(typeName)) 889 { 890 rtn = (StringUtils.isNotBlank(defaultValue) ? defaultValue : "1"); 891 } 892 else if ("java.util.Date".equals(typeName)) 893 { 894 rtn = "new " + typeName + "()"; 895 } 896 else if ("java.sql.Timestamp".equals(typeName)) 897 { 898 rtn = "new java.sql.Timestamp(System.currentTimeMillis())"; 899 } 900 else if ("java.util.Calendar".equals(typeName)) 901 { 902 rtn = "java.util.Calendar.getInstance()"; 903 } 904 else if ("org.joda.time.LocalTime".equals(typeName)) 905 { 906 rtn = "new org.joda.time.LocalTime(1, 1)"; 907 } 908 else if ("char".equals(typeName)) 909 { 910 rtn = "'" + (StringUtils.isNotEmpty(defaultValue) ? defaultValue : name.substring(0, 1)) + "'"; 911 } 912 else if ("Character".equals(typeName)) 913 { 914 rtn = "new Character('" + (StringUtils.isNotEmpty(defaultValue) ? "new Character(" + defaultValue : name.substring(0, 1)) + "')"; 915 } 916 else if ("Byte".equals(typeName) || "java.lang.Byte".equals(typeName)) 917 { 918 rtn = "new Byte(\"" + facade.getName() + "\")"; 919 } 920 else if ("Short".equals(typeName) || "java.lang.Short".equals(typeName) 921 || "Integer".equals(typeName) || "java.lang.Integer".equals(typeName) 922 || "Long".equals(typeName) || "java.lang.Long".equals(typeName) 923 || "Float".equals(typeName) || "java.lang.Float".equals(typeName) 924 || "Double".equals(typeName) || "java.lang.Double".equals(typeName) 925 || "java.math.BigDecimal".equals(typeName)) 926 { 927 rtn = (!StringUtils.isEmpty(defaultValue) ? typeName + ".valueOf(" + defaultValue + ")" : typeName + ".valueOf(1)"); 928 } 929 else if ("java.math.BigInteger".equals(typeName)) 930 { 931 rtn = (!StringUtils.isEmpty(defaultValue) ? "java.math.BigInteger.valueOf(" + defaultValue + ')' : "java.math.BigInteger.valueOf(1)"); 932 } 933 else if ("byte[]".equals(typeName)) 934 { 935 rtn = (StringUtils.isNotBlank(defaultValue) ? defaultValue : '\"' + name + '\"') + ".getBytes()"; 936 } 937 else if ("char[]".equals(typeName)) 938 { 939 String value = StringUtils.isNotBlank(defaultValue) ? defaultValue : name; 940 if (!value.startsWith("\"")) 941 { 942 value = "\"" + value; 943 } 944 if (!value.endsWith("\"")) 945 { 946 value = value + "\""; 947 } 948 rtn = value + ".toCharArray()"; 949 } 950 else if ("String[]".equals(typeName)) 951 { 952 rtn = "new String[] { " + (StringUtils.isNotBlank(defaultValue) ? defaultValue : '\"' + name + '\"') + " }"; 953 } 954 else if (isEnumeration) 955 { 956 if (useMany) 957 { 958 rtn = collectionType + '<' + defaultValue + '>'; 959 } 960 else 961 { 962 rtn = defaultValue; 963 } 964 } 965 else if (!StringUtils.isEmpty(defaultValue)) 966 { 967 rtn = "new " + typeName + '(' + defaultValue + ')'; 968 } 969 else if (type != null && type.hasStereotype("EmbeddedValue")) 970 { 971 // EmbeddedValue classes will always be abstract with Impl generated classes. 972 rtn = "new " + typeName + "Impl()"; 973 } 974 else if (type instanceof GeneralizableElementFacade) 975 { 976 // If type has a descendant with name <typeName>Impl, assume typeNameImpl must be instantiated instead of typeName 977 if (typeName.endsWith("[]")) 978 { 979 rtn = "{ new " + typeName.substring(0, typeName.length()-2) + "() }"; 980 } 981 else 982 { 983 rtn = "new " + typeName + "()"; 984 } 985 //if (facade instanceof ClassifierFacade) 986 //{ 987 //ClassifierFacade classifier = (ClassifierFacade)facade; 988 // If type is abstract, choose Impl descendant if exists, or the last descendant 989 if (type.isAbstract()) 990 { 991 // Can't instantiate abstract class - pick some descendant 992 for (GeneralizableElementFacade spec : type.getSpecializations()) 993 { 994 if (spec.getName().equals(type.getName() + "Impl")) 995 { 996 rtn = '(' + type.getName() + ")new " + typeName + "Impl()"; 997 break; 998 } 999 rtn = '(' + type.getName() + ")new " + spec.getFullyQualifiedName() + "()"; 1000 } 1001 } 1002 //} 1003 GeneralizableElementFacade generalization = (GeneralizableElementFacade)type; 1004 for (GeneralizableElementFacade spec : generalization.getSpecializations()) 1005 { 1006 if (spec.getName().equals(type.getName() + "Impl")) 1007 { 1008 rtn = '(' + type.getName() + ")new " + spec.getFullyQualifiedName() + "Impl()"; 1009 } 1010 } 1011 } 1012 else if (typeName.endsWith("[]")) 1013 { 1014 rtn = "new " + typeName + " { new " + typeName.substring(0, typeName.length()-2) + "() }"; 1015 } 1016 else 1017 { 1018 rtn = "new " + typeName + "()"; 1019 } 1020 rtn = StringUtils.replace(rtn, "java.util.Collection", "java.util.ArrayList") + toString; 1021 rtn = StringUtils.replace(rtn, "java.util.Set", "java.util.HashSet") + toString; 1022 if (logger.isDebugEnabled()) 1023 { 1024 logger.debug("facade=" + facade + " facadeName=" + facade.getName() + " type=" + type + " typeName=" + typeName + " name=" + name + " isMany=" + isMany + " defaultValue=" + defaultValue + " rtn=" + rtn); 1025 } 1026 } catch (RuntimeException e) { 1027 logger.error(e + " facade=" + facade + " facadeName=" + facade.getName() + " parent=" + parent + " type=" + type + " typeName=" + typeName + " name=" + name + " isMany=" + isMany + " defaultValue=" + defaultValue); 1028 e.printStackTrace(); 1029 } 1030 return rtn; 1031 } 1032 1033 /** 1034 * TODO Reference this logic from AssociationEnd 1035 * Determine if this association end owns the relationship. i.e. if the associationEnd property 1036 * belonging to the Entity on the opposite end owns the relationship. Based on tagged value, 1037 * multiplicity, aggregation/composition. If all else fails, the longest name owns the 1038 * association, or else the alphabetically first name. One side of a relationship must 1039 * always own the association and be created and deleted first. 1040 * @param associationEnd the association end 1041 * @return true if the associationEnd (property of the entity on the other end) is owned by the entity on the other end 1042 */ 1043 public static boolean isOwningEnd(AssociationEndFacade associationEnd) 1044 { 1045 boolean owning = false; 1046 AssociationEndFacade otherEnd = associationEnd.getOtherEnd(); 1047 //String assoc = ((Entity)otherEnd.getValidationOwner()).getName() + "." + associationEnd.getName() + " -> " + associationEnd.getType().getName() + " "; 1048 if (BooleanUtils.toBoolean( 1049 ObjectUtils.toString(otherEnd.findTaggedValue( 1050 "andromda_persistence_associationEnd_primary")))) 1051 { 1052 owning = true; 1053 } 1054 // See if this end or the other end is tagged as the association owner 1055 else if (BooleanUtils.toBoolean( 1056 ObjectUtils.toString(associationEnd.findTaggedValue( 1057 "andromda_persistence_associationEnd_primary")))) 1058 { 1059 owning = false; 1060 } 1061 // Navigable side always owns the relationship 1062 else if (associationEnd.isNavigable() && !otherEnd.isNavigable()) 1063 { 1064 owning = true; 1065 //LOGGER.info("Owning=true: " + assoc + "nav=" + associationEnd.isNavigable() + " Onav=" + otherEnd.isNavigable()); 1066 } 1067 else if (!associationEnd.isNavigable() && otherEnd.isNavigable()) 1068 { 1069 owning = false; 1070 //LOGGER.info("Owning=false: " + assoc + "nav=" + associationEnd.isNavigable() + " Onav=" + otherEnd.isNavigable()); 1071 } 1072 // Other side: aggregation/composition side does not own the bidirectional relationship 1073 else if (otherEnd.isAggregation() || otherEnd.isComposition()) 1074 { 1075 owning = false; 1076 //LOGGER.info("Owning=true: " + assoc + "Oagg=" + otherEnd.isAggregation() + " Ocomp=" + otherEnd.isComposition()); 1077 } 1078 else if (associationEnd.isAggregation() || associationEnd.isComposition()) 1079 { 1080 owning = true; 1081 //LOGGER.info("Owning=false: " + assoc + "Oagg=" + associationEnd.isAggregation() + " Ocomp=" + otherEnd.isComposition()); 1082 } 1083 // The many side of 1:M owns the bidirectional relationship 1084 else if (!associationEnd.isMany() && otherEnd.isMany()) 1085 { 1086 owning = true; 1087 //LOGGER.info("Owning=true: " + assoc + "many=" + associationEnd.isMany() + " Omany=" + otherEnd.isMany()); 1088 } 1089 // Other side: the many side of 1:M owns the bidirectional relationship if no composition/aggregation 1090 else if (associationEnd.isMany() && !otherEnd.isMany()) 1091 { 1092 owning = false; 1093 //LOGGER.info("Owning=false: " + assoc + "many=" + associationEnd.isMany() + " Omany=" + otherEnd.isMany()); 1094 } 1095 // The optional side of 1:1 or M:M owns the bidirectional relationship 1096 else if (associationEnd.getLower() > 0 && otherEnd.getLower() == 0) 1097 { 1098 owning = true; 1099 //LOGGER.info("Owning=true: " + assoc + "many=" + associationEnd.isMany() + " Omany=" + otherEnd.isMany()); 1100 } 1101 // If bidirectional 1:1 or M:M, choose the side with the longest type name because it typically indicates a composition relationship 1102 /*else if (this.getOtherEnd().getType().getName().length() 1103 > this.getType().getName().length())*/ 1104 else if (associationEnd.getName().length() 1105 < otherEnd.getName().length()) 1106 { 1107 owning = (associationEnd.getName().length() < otherEnd.getName().length()); 1108 //LOGGER.info("Owning=true: " + assoc + "endLength=" + associationEnd.getName().length() + " Olength=" + otherEnd.getName().length()); 1109 } 1110 // If length is the same, alphabetically earliest is the owner 1111 else if (associationEnd.getName().compareTo( 1112 otherEnd.getName()) < 0) 1113 { 1114 owning = (associationEnd.getName().compareTo(otherEnd.getName()) < 0); 1115 //LOGGER.info("Owning=true: " + assoc + "name=" + associationEnd.getName() + " < OName=" + otherEnd.getName()); 1116 } 1117 /*LOGGER.info(((Entity)associationEnd.getOtherEnd().getValidationOwner()).getName() 1118 + "." + associationEnd.getName() +" IsOwningEnd=" + owning + " for " 1119 + ((Entity)associationEnd.getOtherEnd().getValidationOwner()).getName() 1120 + " OName=" + otherEnd.getName() + " Aggregation=" + associationEnd.isAggregation() 1121 + " Composition=" + associationEnd.isComposition() + " Navigable=" + associationEnd.isNavigable() 1122 + " !Navigable=" + !otherEnd.isNavigable() + " Many=" + associationEnd.isMany() 1123 + " OMany=" + otherEnd.isMany() + " Upper=" + associationEnd.getUpper() 1124 + " OUpper=" + otherEnd.getUpper() + " OAggregation=" + otherEnd.isAggregation() 1125 + " OComposition=" + otherEnd.isComposition() + " ONavigable=" + otherEnd.isNavigable() 1126 + " otherEnd=" + otherEnd.getFullyQualifiedName());*/ 1127 return owning; 1128 } 1129 1130 private static FastDateFormat df = FastDateFormat.getInstance("MM/dd/yyyy HH:mm:ss"); 1131 1132 /** 1133 * Returns the current Date in the specified format. 1134 * 1135 * @param format The format for the output date 1136 * @return the current date in the specified format. 1137 */ 1138 public static String getDate(String format) 1139 { 1140 if (df == null || !format.equals(df.getPattern())) 1141 { 1142 df = FastDateFormat.getInstance(format); 1143 } 1144 return df.format(new Date()); 1145 } 1146 1147 /** 1148 * Returns the current Date in the specified format. 1149 * 1150 * @return the current date with the default format . 1151 */ 1152 public static String getDate() 1153 { 1154 return df.format(new Date()); 1155 } 1156}