001package org.andromda.cartridges.spring; 002 003import java.text.SimpleDateFormat; 004import java.util.ArrayList; 005import java.util.Collection; 006import java.util.Date; 007import java.util.Iterator; 008import java.util.LinkedHashMap; 009import java.util.LinkedHashSet; 010import java.util.List; 011import java.util.Map; 012import org.andromda.cartridges.spring.metafacades.SpringService; 013import org.andromda.metafacades.uml.AssociationEndFacade; 014import org.andromda.metafacades.uml.AttributeFacade; 015import org.andromda.metafacades.uml.ClassifierFacade; 016import org.andromda.metafacades.uml.Entity; 017import org.andromda.metafacades.uml.EntityAttribute; 018import org.andromda.metafacades.uml.EnumerationFacade; 019import org.andromda.metafacades.uml.EnumerationLiteralFacade; 020import org.andromda.metafacades.uml.GeneralizableElementFacade; 021import org.andromda.metafacades.uml.ModelElementFacade; 022import org.andromda.metafacades.uml.Role; 023import org.andromda.metafacades.uml.Service; 024import org.andromda.utils.StringUtilsHelper; 025import org.apache.commons.collections.Closure; 026import org.apache.commons.collections.CollectionUtils; 027import org.apache.commons.collections.Predicate; 028import org.apache.commons.lang.StringUtils; 029import org.apache.log4j.Logger; 030 031 032/** 033 * Contains utilities used within the Spring cartridge. 034 * 035 * @author Chad Brandon 036 * @author Joel Kozikowski 037 */ 038public class SpringUtils 039{ 040 /** 041 * The logger instance. 042 */ 043 private static final Logger logger = Logger.getLogger(SpringUtils.class); 044 045 /** 046 * Retrieves all roles from the given <code>services</code> collection. 047 * 048 * @param services the collection services. 049 * @return all roles from the collection. 050 */ 051 public Collection<Role> getAllRoles(Collection<Service> services) 052 { 053 final Collection<Role> allRoles = new LinkedHashSet<Role>(); 054 CollectionUtils.forAllDo( 055 services, 056 new Closure() 057 { 058 public void execute(Object object) 059 { 060 if (object != null && Service.class.isAssignableFrom(object.getClass())) 061 { 062 allRoles.addAll(((Service)object).getAllRoles()); 063 } 064 } 065 }); 066 return allRoles; 067 } 068 069 /** 070 * Indicates if any remote EJBs are present in the collection 071 * of services. 072 * 073 * @param services the collection of services to check. 074 * @return true/false. 075 */ 076 public boolean remoteEjbsPresent(final Collection<Service> services) 077 { 078 boolean present = services != null && !services.isEmpty(); 079 if (present) 080 { 081 present = 082 CollectionUtils.find( 083 services, 084 new Predicate() 085 { 086 public boolean evaluate(final Object object) 087 { 088 boolean valid = false; 089 if (object instanceof SpringService) 090 { 091 final SpringService service = (SpringService)object; 092 valid = service.isEjbRemoteView(); 093 } 094 return valid; 095 } 096 }) != null; 097 } 098 return present; 099 } 100 101 /** 102 * Indicates if any local EJBs are present in the collection 103 * of services. 104 * 105 * @param services the collection of services to check. 106 * @return true/false. 107 */ 108 public boolean localEjbsPresent(final Collection<Service> services) 109 { 110 boolean present = services != null && !services.isEmpty(); 111 if (present) 112 { 113 present = 114 CollectionUtils.find( 115 services, 116 new Predicate() 117 { 118 public boolean evaluate(final Object object) 119 { 120 boolean valid = false; 121 if (object instanceof SpringService) 122 { 123 final SpringService service = (SpringService)object; 124 valid = service.isEjbLocalView(); 125 } 126 return valid; 127 } 128 }) != null; 129 } 130 return present; 131 } 132 133 /** 134 * Indicates if any Spring remotable services are present. 135 * 136 * @param services the collection of services to check. 137 * @return true/false. 138 */ 139 public boolean remotableServicesPresent(final Collection<Service> services) 140 { 141 boolean present = services != null && !services.isEmpty(); 142 if (present) 143 { 144 present = 145 CollectionUtils.find( 146 services, 147 new Predicate() 148 { 149 public boolean evaluate(final Object object) 150 { 151 boolean valid = false; 152 if (object instanceof SpringService) 153 { 154 final SpringService service = (SpringService)object; 155 valid = service.isRemotable(); 156 } 157 return valid; 158 } 159 }) != null; 160 } 161 return present; 162 } 163 164 /** 165 * Indicates if any remotable services using Lingo are present. 166 * 167 * @param services the collection of services to check. 168 * @return true/false. 169 */ 170 public boolean lingoRemotableServicesPresent(final Collection<Service> services) 171 { 172 boolean present = services != null && !services.isEmpty(); 173 if (present) 174 { 175 present = 176 CollectionUtils.find( 177 services, 178 new Predicate() 179 { 180 public boolean evaluate(final Object object) 181 { 182 boolean valid = false; 183 if (object instanceof SpringService) 184 { 185 final SpringService service = (SpringService)object; 186 valid = service.isRemotingTypeLingo(); 187 } 188 return valid; 189 } 190 }) != null; 191 } 192 return present; 193 } 194 195 /** 196 * Indicates if any private services are present. 197 * 198 * @param services the collection of services to check. 199 * @return true/false. 200 */ 201 public boolean privateServicesPresent(final Collection<Service> services) 202 { 203 boolean present = services != null && !services.isEmpty(); 204 if (present) 205 { 206 present = 207 CollectionUtils.find( 208 services, 209 new Predicate() 210 { 211 public boolean evaluate(final Object object) 212 { 213 boolean valid = false; 214 if (object instanceof SpringService) 215 { 216 final SpringService service = (SpringService)object; 217 valid = service.isPrivate(); 218 } 219 return valid; 220 } 221 }) != null; 222 } 223 return present; 224 } 225 226 /** 227 * Indicates if any public (non private) services are present. 228 * 229 * @param services the collection of services to check. 230 * @return true/false. 231 */ 232 public boolean publicServicesPresent(final Collection<Service> services) 233 { 234 boolean present = services != null && !services.isEmpty(); 235 if (present) 236 { 237 present = 238 CollectionUtils.find( 239 services, 240 new Predicate() 241 { 242 public boolean evaluate(final Object object) 243 { 244 boolean valid = false; 245 if (object instanceof SpringService) 246 { 247 final SpringService service = (SpringService)object; 248 valid = !service.isPrivate(); 249 } 250 return valid; 251 } 252 }) != null; 253 } 254 return present; 255 } 256 257 /** 258 * Based on the given <code>value</code>, this method will return 259 * a formatted Spring property (including the handling of 'null'). 260 * 261 * @param value the value from which to create the spring value. 262 * @return the spring value. 263 */ 264 public String getSpringPropertyValue(final String value) 265 { 266 String propertyValue = ""; 267 if (value != null) 268 { 269 if ("null".equalsIgnoreCase(value)) 270 { 271 propertyValue = "<null/>"; 272 } 273 else 274 { 275 propertyValue = "<value>" + value + "</value>"; 276 } 277 } 278 return propertyValue; 279 } 280 281 /** 282 * Removes generics from string. Currently used to strip generics 283 * from ejb-jar.xml method parameters. 284 * @param parameter String containing generics 285 * @return String with generics stripped 286 */ 287 public String removeGenerics(final String parameter) 288 { 289 int position = parameter.indexOf('<'); 290 String result = parameter; 291 if(position != -1) 292 { 293 result = result.substring(0, position); 294 } 295 return result; 296 } 297 298 /** 299 * Are we generating code for a rich client? false. 300 */ 301 private boolean richClient = false; 302 303 304 /** 305 * Sets if code is being generated for a rich client. 306 * @param richClientProperty 307 */ 308 public void setRichClient(final boolean richClientProperty) 309 { 310 this.richClient = richClientProperty; 311 } 312 313 /** 314 * Returns TRUE if code is being generated for a rich client environment 315 * @return richClient 316 */ 317 public boolean isRichClient() 318 { 319 return this.richClient; 320 } 321 322 /** 323 * Returns the class name part of a fully qualified name 324 * @param fullyQualifiedName 325 * @return just the "class name" part of the fully qualified name 326 */ 327 public String getClassName(String fullyQualifiedName) 328 { 329 String className = null; 330 if (StringUtils.isNotBlank(fullyQualifiedName)) 331 { 332 int lastDot = fullyQualifiedName.lastIndexOf('.'); 333 if (lastDot >= 0) 334 { 335 className = fullyQualifiedName.substring(lastDot+1); 336 } 337 else 338 { 339 className = fullyQualifiedName; 340 } 341 } 342 else 343 { 344 className = ""; 345 } 346 347 return className; 348 } 349 350 351 /** 352 * Returns the package name part of a fully qualified name 353 * @param fullyQualifiedName 354 * @return just the "package" part of the fully qualified name 355 */ 356 public String getPackageName(String fullyQualifiedName) 357 { 358 String packageName = null; 359 if (StringUtils.isNotBlank(fullyQualifiedName)) 360 { 361 int lastDot = fullyQualifiedName.lastIndexOf('.'); 362 packageName = (lastDot >= 0 ? fullyQualifiedName.substring(0, lastDot) : ""); 363 } 364 else 365 { 366 packageName = ""; 367 } 368 369 return packageName; 370 } 371 372 /** 373 * Returns an ordered set containing the argument model elements, model elements with a name that is already 374 * used by another model element in the argument collection will not be returned. 375 * The first operation with a name not already encountered will be returned, the order inferred by the 376 * argument's iterator will determine the order of the returned list. 377 * 378 * @param modelElements a collection of model elements, elements that are not model elements will be ignored 379 * @return the argument model elements without, elements with a duplicate name will only be recorded once 380 */ 381 public List<ModelElementFacade> filterUniqueByName(Collection<ModelElementFacade> modelElements) 382 { 383 final Map<String, ModelElementFacade> filteredElements = new LinkedHashMap<String, ModelElementFacade>(); 384 385 for (final ModelElementFacade modelElement : modelElements) 386 { 387 /*final Object object = elementIterator.next(); 388 if (object instanceof ModelElementFacade) 389 {*/ 390 if (!filteredElements.containsKey(modelElement.getName())) 391 { 392 filteredElements.put(modelElement.getName(), modelElement); 393 } 394 /*}*/ 395 } 396 397 return new ArrayList<ModelElementFacade>(filteredElements.values()); 398 } 399 400 /** 401 * Formats the given type to the appropriate Hibernate query parameter value. 402 * 403 * @param type the type of the Hibernate query parameter. 404 * @param value the current value to format. 405 * @return the formatted value. 406 */ 407 public String formatHibernateQueryParameterValue(final ClassifierFacade type, String value) 408 { 409 if (type != null) 410 { 411 if (type.isPrimitive()) 412 { 413 value = "new " + type.getWrapperName() + '(' + value + ')'; 414 } 415 } 416 return value; 417 } 418 419 /** 420 * Takes the given <code>names</code> and concatenates them in camel case 421 * form. 422 * 423 * @param names the names to concatenate. 424 * @return the result of the concatenation 425 */ 426 public static String concatNamesCamelCase(final Collection<String> names) 427 { 428 String result = null; 429 if (names != null) 430 { 431 result = StringUtilsHelper.lowerCamelCaseName(StringUtils.join(names.iterator(), " ")); 432 } 433 return result; 434 } 435 436 /** 437 * Constructs the fully qualified class name from the packageName and name. 438 * @param packageName the package name to which the class belongs. 439 * @param name the name of the class. 440 * @return the fully qualified name. 441 */ 442 public static String getFullyQualifiedClassName(final String packageName, final String name) 443 { 444 final StringBuilder fullName = new StringBuilder(StringUtils.trimToEmpty(packageName)); 445 if (fullName.length() > 0) 446 { 447 fullName.append('.'); 448 } 449 fullName.append(name); 450 return fullName.toString(); 451 } 452 453 /** 454 * Constructs the fully qualified class definition from the facade. Used for 455 * ValueObject, EmbeddedValue 456 * @param facade the class to construct the roo field definition. 457 * @return the Roo class definition. 458 */ 459 public static List<String> getRooEnum(final EnumerationFacade facade) 460 { 461 List<String> results = new ArrayList<String>(); 462 String result = "enum type --class " + facade.getFullyQualifiedName() + " --permitReservedWords"; 463 results.add(result); 464 // Can't do for: because literal may be AttributeFacade or EnumerationLiteralFacade - ClassCastException 465 Iterator literals = facade.getLiterals().iterator(); 466 while (literals.hasNext()) 467 { 468 result = "enum constant --name " + ((ModelElementFacade)literals.next()).getName(); 469 results.add(result); 470 } 471 return results; 472 } 473 474 /** 475 * Constructs the fully qualified class definition from the facade. Used for 476 * ValueObject, EmbeddedValue 477 * @param facade the class to construct the roo field definition. 478 * @return the Roo class definition. 479 */ 480 public static List<String> getRooClass(final ClassifierFacade facade) 481 { 482 List<String> results = new ArrayList<String>(); 483 String result = null; 484 if (facade.isEmbeddedValue() || facade.hasStereotype("ValueObject")) 485 { 486 if (facade.isEmbeddedValue()) 487 { 488 result = "embeddable --class "; 489 } 490 else if (facade.hasStereotype("ValueObject")) 491 { 492 result = "class --class "; 493 } 494 result += facade.getFullyQualifiedName() + " --permitReservedWords"; 495 if (facade.isAbstract()) 496 { 497 result += " --abstract"; 498 } 499 if (facade.getGeneralization() != null) 500 { 501 result += " --extends " + facade.getGeneralization().getFullyQualifiedName(); 502 } 503 results.add(result); 504 for (AttributeFacade attr : facade.getAttributes()) 505 { 506 results.add(getRooField(attr)); 507 } 508 //results.add(""); 509 } 510 // Old style Java 1.4 enumeration class 511 /*else if (facade.hasStereotype("Enumeration")) 512 { 513 result = "enum type --class " + facade.getName() + " --permitReservedWords"; 514 results.add(result); 515 for (AttributeFacade literal : facade.getAttributes()) 516 { 517 result = "enum constant --name " + literal.getName(); 518 results.add(result); 519 } 520 results.add(""); 521 }*/ 522 return results; 523 } 524 525 /** 526 * Constructs the fully qualified class name from the packageName and name. 527 * Removes the words 'Test' and 'TestCase' because Roo cannot create tests for Entities 528 * with those names. 529 * @param entity the entity to construct the roo script definition. 530 * @return the Roo field definition. 531 */ 532 public static String getRooEntityName(final Entity entity) 533 { 534 return StringUtils.remove(entity.getFullyQualifiedName(), "Test"); 535 } 536 537 /** 538 * Constructs the fully qualified class name from the packageName and name. 539 * @param entity the entity to construct the roo script definition. 540 * @param recordType Either 'dao' or 'repository' 541 * @return the Roo field definition. 542 */ 543 public static List<String> getRooEntity(final Entity entity, String recordType) 544 { 545 List<String> results = new ArrayList<String>(); 546 Collection<ModelElementFacade> identifiers = entity.getIdentifiers(false); 547 String identifierLine = null; 548 // Keep track of entities already output, so that descendants are created after ancestors. 549 if (entity.isCompositeIdentifier()) 550 { 551 String line = "embeddable --class " + StringUtils.remove(entity.getFullyQualifiedIdentifierTypeName(), "Test") + " --serializable"; 552 //String line = "embeddable --class " + entity.getFullyQualifiedIdentifierTypeName() + " --serializable"; 553 results.add(line); 554 for (AssociationEndFacade associationEnd : entity.getIdentifierAssociationEnds()) 555 { 556 //results.add(SpringUtils.getRooField(associationEnd)); 557 if (associationEnd.isMany2One()) 558 { 559 line = "field other --fieldName " + associationEnd.getOtherEnd().getName() + " --type " + associationEnd.getOtherEnd().getType().getFullyQualifiedName(); 560 results.add(line); 561 } 562 } 563 for (ModelElementFacade identifier : identifiers) 564 { 565 logger.info("getRooField identifier: " + getRooField(identifier) + " for " + identifier); 566 results.add(SpringUtils.getRooField(identifier)); 567 } 568 identifierLine = " --identifierField " + entity.getIdentifierName() + " --identifierType " + StringUtils.remove(entity.getFullyQualifiedIdentifierTypeName(), "Test"); 569 } 570 // Hibernate cartridge automatically adds default identifier if none, spring cartridge does not 571 else if (entity.getIdentifiers(false).size()==0) 572 { 573 identifierLine = " --identifierField id --identifierType java.lang.Long --identifierColumn ID"; 574 } 575 else if (entity.getIdentifiers(false).size()==1) 576 { 577 ModelElementFacade id = entity.getIdentifiers(false).iterator().next(); 578 String identifierType = entity.getFullyQualifiedIdentifierTypeName(); 579 // Identifier properties can be either attribute or associationEnd. If end, associated class identifiers are added to this class identifiers. 580 if (id instanceof EntityAttribute) 581 { 582 ClassifierFacade type = ((EntityAttribute)id).getType(); 583 if (type.isPrimitive()) 584 { 585 // Primitive type not allowed for identifier in Spring Roo 586 identifierType = type.getWrapperName(); 587 } 588 } 589 // Some test models have 'Test' in entity/attribute names, conflicting with names created for test scaffolding 590 identifierLine = " --identifierField " + entity.getIdentifierName() + " --identifierType " + StringUtils.remove(identifierType, "Test"); 591 } 592 else 593 { 594 // Should never get to this place 595 } 596 // Identifiers: identifiers.size() 597 String activeRecord = " --activeRecord true"; 598 // false = Use Spring Data JPA, no DAOs. True = Dao helpers 599 if (recordType.equals("active")) 600 { 601 activeRecord = " --activeRecord false"; 602 } 603 String mappedSuperclass = ""; 604 if (entity.hasStereotype("MappedSuperclass")) 605 { 606 mappedSuperclass = " --mappedSuperclass"; 607 } 608 String extension = ""; 609 GeneralizableElementFacade general = entity.getGeneralization(); 610 if (general != null) 611 { 612 extension = " --extends " + general.getFullyQualifiedName(); 613 } 614 String isAbstract = ""; 615 if (entity.isAbstract()) 616 { 617 isAbstract = " --abstract"; 618 } 619 String schema = ""; 620 if (StringUtils.isNotBlank(entity.getSchema())) 621 { 622 schema = " --schema " + entity.getSchema(); 623 } 624 String version = ""; 625 String entityVersion = (String) entity.findTaggedValue("andromda_hibernate_version"); 626 for (AttributeFacade attr : entity.getAttributes()) 627 { 628 if (attr.hasStereotype("Version")) 629 { 630 version = " --versionField " + attr.getName() + " --versionColumn " + ((EntityAttribute)attr).getColumnName() + " --versionType " + attr.getType().getFullyQualifiedName(); 631 } 632 } 633 // TODO Check for global configured property "versionProperty" for adding version property to all entities 634 if (StringUtils.isNotBlank(entityVersion) && StringUtils.isBlank(version)) 635 { 636 // Add automatic version identifier to entity definition 637 version = " --versionField version --versionColumn VERSION --versionType java.lang.Integer"; 638 } 639 // TODO version*, inheritanceType, persistenceUnit, entityName, sequenceName 640 String line = "entity jpa --class " + StringUtils.remove(entity.getFullyQualifiedName(), "Test") + activeRecord + " --table " + entity.getTableName() + identifierLine + mappedSuperclass + extension + version + isAbstract + schema + " --equals --serializable --testAutomatically --permitReservedWords"; 641 results.add(StringUtils.replace(line, " ", " ")); 642 return results; 643 } 644 645 /** 646 * Constructs the fully qualified class name from the packageName and name. 647 * @param facade the property (attribute or associationEnd) to construct the roo field definition. 648 * @return the Roo field definition. 649 */ 650 public static String getRooField(final ModelElementFacade facade) 651 { 652 String result = " --fieldName " + facade.getName(); 653 if (facade instanceof AssociationEndFacade) 654 { 655 AssociationEndFacade end = (AssociationEndFacade)facade; 656 ClassifierFacade type = end.getOtherEnd().getType(); 657 String typeName = " --type " + type.getFullyQualifiedName(); 658 if (end.isMany2One()) 659 { 660 result = "field reference " + result + typeName; 661 } 662 else if (end.isOne2Many()) 663 { 664 result = "field set " + result + typeName; 665 } 666 else 667 { 668 result = "field other " + result + typeName; 669 } 670 //System.out.println(end.getBindedFullyQualifiedName(end) + " " + end.getQualifiedName()); 671 String owner = end.getFullyQualifiedName(); 672 if (owner.lastIndexOf('.') > 0) 673 { 674 owner = owner.substring(0, owner.lastIndexOf('.')); 675 result += " --class " + owner; 676 } 677 else 678 { 679 logger.error("getRooField invalid owner: " + owner + " for " + facade.getFullyQualifiedName()); 680 } 681 } 682 else if (facade instanceof AttributeFacade) 683 { 684 AttributeFacade attribute = (AttributeFacade)facade; 685 ClassifierFacade type = attribute.getType(); 686 String typeName = " --type " + type.getFullyQualifiedName(); 687 //logger.info("getRooField " + attribute.getFullyQualifiedName() + " type=" + type.getFullyQualifiedName() + " Integer=" + type.isIntegerType()); 688 if ((attribute.isMany() || attribute.getName().endsWith("[]")) && !type.isDataType()) 689 { 690 // The Many side of M:1 relationship, as an Attribute instead of an AssociationEnd 691 result = "field reference " + result + typeName; 692 } 693 // Version attribute is specified in the entity definition, skip this field definition 694 else if (type.getFullyQualifiedName().endsWith("[]") || attribute.hasStereotype("Version")) 695 { 696 // TODO: Convert DataType * to column Map with association. Punt for now. 697 result = ""; 698 } 699 else 700 { 701 String primitive = ""; 702 if (type.isPrimitive()) 703 { 704 primitive = " --primitive "; 705 } 706 if (type.isIntegerType() || type.isLongType() || type.isDoubleType() || type.isFloatType()) 707 { 708 result = "field number " + result + primitive + typeName; 709 } 710 else if (type.isBooleanType()) 711 { 712 result = "field boolean " + result + primitive; 713 } 714 else if (type.isStringType() || type.isCharacterType()) 715 { 716 result = "field string " + result; 717 } 718 else if (type.isDateType()) 719 { 720 result = "field date " + result + typeName; 721 } 722 // EmbeddedValue cannot have column, notNull, comment options in Roo 723 else if (type.isEmbeddedValue()) 724 { 725 result = "field embedded " + result + typeName; 726 } 727 else if (type.isEnumeration()) 728 { 729 result = "field enum " + result + typeName; 730 } 731 else if (type.isDateType()) 732 { 733 result = "field reference " + result + typeName; 734 } 735 else if (type.isDateType()) 736 { 737 result = "field set " + result + typeName; 738 } 739 else 740 { 741 result = "field other " + result + typeName; 742 } 743 if (attribute instanceof EntityAttribute && !type.isEmbeddedValue()) 744 { 745 String column = ((EntityAttribute)attribute).getColumnName(); 746 if (StringUtils.isNotBlank(column)) 747 { 748 result += " --column " + column; 749 } 750 } 751 else 752 { 753 //logger.error("getRooField facade should be EntityAttribute: " + attribute); 754 } 755 int lower = attribute.getLower(); 756 if (lower > 0 && !type.isEmbeddedValue()) 757 { 758 result += " --notNull "; 759 } 760 String comment = attribute.getDocumentation("", 9999, false); 761 if (StringUtils.isNotBlank(comment) && !type.isEmbeddedValue()) 762 { 763 result += " --comment \"" + comment + "\""; 764 } 765 } 766 } 767 else 768 { 769 throw new RuntimeException("getRooField facade must be Attribute or AssociationEnd: " + facade); 770 } 771 if (StringUtils.isNotBlank(result)) 772 { 773 result = StringUtils.replace(result + " --permitReservedWords", " ", " "); 774 } 775 return result; 776 } 777 private static final SimpleDateFormat DF = new SimpleDateFormat("MM/dd/yyyy HH:mm:ssZ"); 778 /** 779 * Returns the current Date in the specified format. $conversionUtils does not seem to work in vsl. 780 * 781 * @param format The format for the output date 782 * @return the current date in the specified format. 783 */ 784 public static String getDate(String format) 785 { 786 /*if (DF == null || !format.equals(DF.toLocalizedPattern())) 787 { 788 DF = new SimpleDateFormat(format); 789 }*/ 790 return DF.format(new Date()); 791 } 792 793 /** 794 * Returns the current Date in the specified format. 795 * 796 * @return the current date with the default format . 797 */ 798 public static String getDate() 799 { 800 return DF.format(new Date()); 801 } 802}