001package org.andromda.cartridges.ejb3.metafacades; 002 003import java.util.ArrayList; 004import java.util.Collection; 005import java.util.Iterator; 006import java.util.List; 007import org.andromda.cartridges.ejb3.EJB3Globals; 008import org.andromda.cartridges.ejb3.EJB3Profile; 009import org.andromda.core.common.ExceptionUtils; 010import org.andromda.metafacades.uml.AttributeFacade; 011import org.andromda.metafacades.uml.ClassifierFacade; 012import org.andromda.metafacades.uml.EntityMetafacadeUtils; 013import org.andromda.metafacades.uml.ModelElementFacade; 014import org.andromda.metafacades.uml.OperationFacade; 015import org.andromda.metafacades.uml.UMLProfile; 016import org.apache.commons.collections.CollectionUtils; 017import org.apache.commons.collections.Predicate; 018import org.apache.commons.lang.StringUtils; 019 020/** 021 * Contains utilities for use with EJB metafacades. 022 * 023 * @author Chad Brandon 024 * @author Vance Karimi 025 */ 026class EJB3MetafacadeUtils 027{ 028 /** 029 * Gets all create methods for the given <code>classifier</code>. 030 * 031 * @param classifier The classifier from which to retries the create methods 032 * @param follow if true, all super type create methods are also retrieved 033 * @return collection of create methods found. 034 */ 035 static Collection<OperationFacade> getCreateMethods( 036 ClassifierFacade classifier, 037 boolean follow) 038 { 039 ExceptionUtils.checkNull("classifer", classifier); 040 Collection<OperationFacade> retval = new ArrayList<OperationFacade>(); 041 ClassifierFacade entity = classifier; 042 do 043 { 044 for (final OperationFacade op : entity.getOperations()) 045 { 046 if (op.hasStereotype(EJB3Profile.STEREOTYPE_CREATE_METHOD)) 047 { 048 retval.add(op); 049 } 050 } 051 if (follow) 052 { 053 entity = (ClassifierFacade)entity.getGeneralization(); 054 } 055 } 056 while (follow && entity != null); 057 return retval; 058 } 059 060 /** 061 * Gets the interface name for the passed in <code>classifier</code>. Returns 'LocalHome' if the mode element has 062 * the entity stereotype, returns 'Home' otherwise. 063 * @param classifier 064 * @return the interface name. 065 */ 066 static String getHomeInterfaceName(ClassifierFacade classifier) 067 { 068 ExceptionUtils.checkNull("classifer", classifier); 069 String homeInterfaceName; 070 if (classifier.hasStereotype(UMLProfile.STEREOTYPE_ENTITY)) 071 { 072 homeInterfaceName = classifier.getName() + "LocalHome"; 073 } 074 else 075 { 076 homeInterfaceName = classifier.getName() + "Home"; 077 } 078 return homeInterfaceName; 079 } 080 081 /** 082 * Gets the view type for the passed in <code>classifier</code>. If no 083 * view type can be retrieved from the <code>classifier</code>, then the 084 * <code>defaultViewType</code> is returned. 085 * 086 * If session ejb pojo then checks the ejb tagged value and if there is 087 * no value defined, returns 'remote'. 088 * 089 * @param classifier The classifier to lookup the view type tagged value 090 * @param defaultViewType The default view type if one is not found 091 * @return String the view type name. 092 */ 093 static String getViewType( 094 ClassifierFacade classifier, 095 String defaultViewType) 096 { 097 ExceptionUtils.checkNull("classifer", classifier); 098 String viewType = (String)classifier.findTaggedValue(EJB3Profile.TAGGEDVALUE_EJB_VIEWTYPE); 099 if (StringUtils.isBlank(viewType)) 100 { 101 if (classifier.hasStereotype(EJB3Profile.STEREOTYPE_SERVICE)) 102 { 103 // if the view type wasn't found, search all super classes 104 if (StringUtils.isBlank(viewType)) 105 { 106 viewType = (String)CollectionUtils.find( 107 classifier.getAllGeneralizations(), 108 new Predicate() 109 { 110 public boolean evaluate(Object object) 111 { 112 return ((ModelElementFacade)object).findTaggedValue( 113 EJB3Profile.TAGGEDVALUE_EJB_VIEWTYPE) != null; 114 } 115 }); 116 } 117 if (StringUtils.isBlank(viewType)) 118 { 119 viewType = (StringUtils.isNotBlank(defaultViewType) ? 120 defaultViewType : EJB3Globals.VIEW_TYPE_REMOTE); 121 } 122 } 123 } 124 return viewType.toLowerCase(); 125 } 126 127 /** 128 * Gets all the inherited instance attributes, excluding the instance attributes directory from this 129 * <code>classifier</code>. 130 * 131 * @param classifier the ClassifierFacade from which to retrieve the inherited attributes. 132 * @return a list of ordered attributes. 133 */ 134 static List<AttributeFacade> getInheritedInstanceAttributes(ClassifierFacade classifier) 135 { 136 ExceptionUtils.checkNull("classifer", classifier); 137 ClassifierFacade current = (ClassifierFacade)classifier.getGeneralization(); 138 if (current == null) 139 { 140 return new ArrayList<AttributeFacade>(); 141 } 142 List<AttributeFacade> retval = getInheritedInstanceAttributes(current); 143 144 if (current.getInstanceAttributes() != null) 145 { 146 retval.addAll(current.getInstanceAttributes()); 147 } 148 return retval; 149 } 150 151 /** 152 * Gets all instance attributes including those instance attributes belonging to the 153 * <code>classifier</code> and any inherited ones. 154 * 155 * @param classifier the ClassifierFacade from which to retrieve the instance attributes. 156 * @return the list of all instance attributes. 157 */ 158 static List<AttributeFacade> getAllInstanceAttributes(ClassifierFacade classifier) 159 { 160 ExceptionUtils.checkNull("classifer", classifier); 161 List<AttributeFacade> retval = getInheritedInstanceAttributes(classifier); 162 retval.addAll(classifier.getInstanceAttributes()); 163 return retval; 164 } 165 166 /** 167 * Gets all environment entries for the specified <code>classifier</code>. If <code>follow</code> is true, then a 168 * search up the inheritance hierarchy will be performed and all super type environment entries will also be 169 * retrieved. 170 * 171 * @param classifier the classifier from which to retrieve the env-entries 172 * @param follow true/false on whether or not to 'follow' the inheritance hierarchy when retrieving the 173 * env-entries. 174 * @return the collection of environment entries 175 */ 176 static Collection<AttributeFacade> getEnvironmentEntries( 177 ClassifierFacade classifier, 178 boolean follow) 179 { 180 ExceptionUtils.checkNull("classifer", classifier); 181 182 Collection<AttributeFacade> attributes = classifier.getStaticAttributes(); 183 184 if (follow) 185 { 186 for (classifier = (ClassifierFacade)classifier.getGeneralization(); 187 classifier != null; classifier = (ClassifierFacade)classifier.getGeneralization()) 188 { 189 attributes.addAll(classifier.getStaticAttributes()); 190 } 191 } 192 193 CollectionUtils.filter(attributes, new Predicate() 194 { 195 public boolean evaluate(Object object) 196 { 197 return ((AttributeFacade)object).hasStereotype(EJB3Profile.STEREOTYPE_ENV_ENTRY); 198 } 199 }); 200 201 return attributes; 202 } 203 204 /** 205 * Returns the transaction type for the specified <code>classifier</code>. 206 * 207 * @param classifier the classifier from which to retrieve the transaction type. 208 * @param defaultTransactionType the default transaction type if no tagged value is specified. 209 * @return the transaction type as a String. 210 */ 211 static String getTransactionType(ClassifierFacade classifier, String defaultTransactionType) 212 { 213 ExceptionUtils.checkNull("classifer", classifier); 214 215 String transactionType = (String)classifier.findTaggedValue(EJB3Profile.TAGGEDVALUE_EJB_TRANSACTION_TYPE); 216 if (StringUtils.isNotBlank(transactionType)) 217 { 218 transactionType = convertTransactionType(transactionType); 219 } 220 else 221 { 222 transactionType = defaultTransactionType; 223 } 224 return transactionType; 225 } 226 227 /** 228 * Convert the transaction type from lower casing to upper casing. 229 * This maintains reusable tagged value enumeration from EJB 230 * implementation. 231 * 232 * @param transType 233 * @return transactionType 234 */ 235 static String convertTransactionType(String transType) 236 { 237 ExceptionUtils.checkNull("transType", transType); 238 239 String type = null; 240 if (StringUtils.equalsIgnoreCase(transType, EJB3Globals.TRANSACTION_TYPE_MANDATORY)) 241 { 242 type = "MANDATORY"; 243 } 244 else if (StringUtils.equalsIgnoreCase(transType, EJB3Globals.TRANSACTION_TYPE_NEVER)) 245 { 246 type = "NEVER"; 247 } 248 else if (StringUtils.equalsIgnoreCase(transType, EJB3Globals.TRANSACTION_TYPE_NOT_SUPPORTED)) 249 { 250 type = "NOT_SUPPORTED"; 251 } 252 else if (StringUtils.equalsIgnoreCase(transType, EJB3Globals.TRANSACTION_TYPE_REQUIRED)) 253 { 254 type = "REQUIRED"; 255 } 256 else if (StringUtils.equalsIgnoreCase(transType, EJB3Globals.TRANSACTION_TYPE_REQUIRES_NEW)) 257 { 258 type = "REQUIRES_NEW"; 259 } 260 else if (StringUtils.equalsIgnoreCase(transType, EJB3Globals.TRANSACTION_TYPE_SUPPORTS)) 261 { 262 type = "SUPPORTS"; 263 } 264 return type; 265 } 266 267 /** 268 * Gets all constants for the specified <code>classifier</code>. 269 * If <code>follow</code> is true, then a search up 270 * the inheritance hierarchy will be performed and all super 271 * type constants will also be retrieved. 272 * 273 * @param classifier the classifier from which to retrieve the constants 274 * @param follow true/false on whether or not to 'follow' the inheritance hierarchy when retrieving the 275 * constants. 276 * @return the collection of environment entries 277 */ 278 static Collection<AttributeFacade> getConstants( 279 ClassifierFacade classifier, 280 boolean follow) 281 { 282 ExceptionUtils.checkNull("classifer", classifier); 283 284 Collection<AttributeFacade> attributes = classifier.getStaticAttributes(); 285 286 if (follow) 287 { 288 for (classifier = (ClassifierFacade)classifier.getGeneralization(); 289 classifier != null; classifier = (ClassifierFacade)classifier.getGeneralization()) 290 { 291 attributes.addAll(classifier.getStaticAttributes()); 292 } 293 } 294 295 CollectionUtils.filter(attributes, new Predicate() 296 { 297 public boolean evaluate(Object object) 298 { 299 return !((AttributeFacade)object).hasStereotype(EJB3Profile.STEREOTYPE_ENV_ENTRY); 300 } 301 }); 302 303 return attributes; 304 } 305 306 /** 307 * Returns true/false based on whether or not synthetic or auto generated create methods should be allowed. 308 * 309 * @param classifier The entity or session bean. 310 * @return true/false 311 */ 312 static boolean allowSyntheticCreateMethod(ClassifierFacade classifier) 313 { 314 ExceptionUtils.checkNull("classifer", classifier); 315 return !classifier.isAbstract() && classifier.findTaggedValue( 316 EJB3Profile.TAGGEDVALUE_EJB_NO_SYNTHETIC_CREATE_METHOD) == null; 317 } 318 319 /** 320 * Creates a fully qualified name from the given <code>packageName</code>, 321 * <code>name</code>, and <code>suffix</code>. 322 * 323 * @param packageName the name of the model element package. 324 * @param name the name of the model element. 325 * @param suffix the suffix to append. 326 * @return the new fully qualified name. 327 */ 328 static String getFullyQualifiedName( 329 String packageName, 330 String name, 331 String suffix) 332 { 333 StringBuilder fullyQualifiedName = new StringBuilder(StringUtils.trimToEmpty(packageName)); 334 if (StringUtils.isNotBlank(packageName)) 335 { 336 fullyQualifiedName.append('.'); 337 } 338 fullyQualifiedName.append(StringUtils.trimToEmpty(name)); 339 fullyQualifiedName.append(StringUtils.trimToEmpty(suffix)); 340 return fullyQualifiedName.toString(); 341 } 342 343 /** 344 * Returns true if the Seam stereotype is modeled on the class. 345 * 346 * @param classifier The classifier to lookup if the stereotype is modeled 347 * @return True is stereotype exists, false otherwise 348 */ 349 static boolean isSeamComponent(ClassifierFacade classifier) 350 { 351 boolean isSeamComponent = false; 352 if (classifier.hasStereotype(EJB3Profile.STEREOTYPE_SEAM_COMPONENT)) 353 { 354 isSeamComponent = true; 355 } 356 return isSeamComponent; 357 } 358 359 /** 360 * Gets the Seam component scope type for Entity and Session beans. 361 * If no scope has been specified: 362 * If the Classifier is a stateless session bean, then returns STATELESS 363 * If the Classifier is an entity or stateful session bean, then returns CONVERSATION 364 * 365 * @param classifier The classifier to lookup the scope type tagged value 366 * @param stateless Whether the classifier is a stateless session bean 367 * @return The scope type as a String 368 */ 369 static String getSeamComponentScopeType(ClassifierFacade classifier, boolean stateless) 370 { 371 ExceptionUtils.checkNull("classifer", classifier); 372 String scopeType = (String)classifier.findTaggedValue(EJB3Profile.TAGGEDVALUE_SEAM_SCOPE_TYPE); 373 if (StringUtils.isBlank(scopeType)) 374 { 375 if (stateless) 376 { 377 scopeType = EJB3Globals.SEAM_COMPONENT_SCOPE_STATELESS; 378 } 379 else 380 { 381 scopeType = EJB3Globals.SEAM_COMPONENT_SCOPE_CONVERSATION; 382 } 383 } 384 return scopeType; 385 } 386 387 /** 388 * Returns the Seam component name. Can override using tagged value, otherwise just the 389 * class name. 390 * 391 * @param classifier The classifier to get the tagged value or the name from. 392 * @return The Seam component name 393 */ 394 static String getSeamComponentName(ClassifierFacade classifier) 395 { 396 ExceptionUtils.checkNull("classifer", classifier); 397 String componentName = (String)classifier.findTaggedValue(EJB3Profile.TAGGEDVALUE_SEAM_COMPONENT_NAME); 398 if (StringUtils.isBlank(componentName)) 399 { 400 componentName = StringUtils.uncapitalize(classifier.getName()); 401 } 402 return componentName; 403 } 404 405 /** 406 * Builds an annotation parameter line 407 * @param parameters The parameters 408 * @return The parameter line 409 */ 410 static String buildAnnotationParameters(Collection<String> parameters) 411 { 412 StringBuilder buf = new StringBuilder(); 413 if(!parameters.isEmpty()) 414 { 415 buf.append("("); 416 Iterator it = parameters.iterator(); 417 while(it.hasNext()) 418 { 419 String option = (String) it.next(); 420 buf.append(option); 421 if(it.hasNext()) 422 { 423 buf.append(", "); 424 } 425 } 426 buf.append(")"); 427 return buf.toString(); 428 } 429 else 430 { 431 return null; 432 } 433 } 434 435 /** 436 * Builds a multi valued parameter string 437 * @param name The name of the parameter 438 * @param values The values for the parameters 439 * @return The parameter string 440 */ 441 static String buildAnnotationMultivalueParameter(String name, Collection<String> values) 442 { 443 return buildAnnotationMultivalueParameter(name, values, true); 444 } 445 446 /** 447 * Builds a multi valued parameter string 448 * @param name The name of the parameter 449 * @param values The values for the parameters 450 * @param areStrings If the values are strings then surround with " sign 451 * @return The parameter string 452 */ 453 static String buildAnnotationMultivalueParameter(String name, Collection<String> values, boolean areStrings) 454 { 455 return buildAnnotationMultivalueParameter(name, values, areStrings, null); 456 } 457 458 /** 459 * Builds a multi option string 460 * Builds a multi valued parameter string 461 * @param name The name of the parameter 462 * @param values The values for the parameters 463 * @param areStrings If the values are strings then surround with " sign 464 * @param suffix Any suffix to add to the values 465 * @return The parameter string 466 */ 467 static String buildAnnotationMultivalueParameter(String name, Collection<String> values, boolean areStrings, String suffix) 468 { 469 if(values.isEmpty()) 470 { 471 return null; 472 } 473 else 474 { 475 StringBuilder buf = new StringBuilder(); 476 buf.append(name + " = {"); 477 478 Iterator it = values.iterator(); 479 while(it.hasNext()) 480 { 481 String parameter = (String) it.next(); 482 if(areStrings) 483 { 484 buf.append("\""); 485 } 486 buf.append(parameter); 487 if((suffix != null) && !parameter.endsWith(suffix)) 488 { 489 buf.append(suffix); 490 } 491 if(areStrings) 492 { 493 buf.append("\""); 494 } 495 if(it.hasNext()) 496 { 497 buf.append(", "); 498 } 499 } 500 buf.append("}"); 501 return buf.toString(); 502 } 503 } 504 505 /** 506 * Gets the SQL name. (i.e. column name, table name, etc.). If it can't find 507 * the corresponding tagged value with the specified <code>name</code>, 508 * then it uses the element name by default and just returns that. 509 * 510 * @param prefix the optional prefix to add to the sql name (i.e. table name 511 * prefix, etc.). 512 * @param element from which to retrieve the SQL name. 513 * @param name the name of the tagged value. 514 * @param nameMaxLength if this is not null, then the name returned will be 515 * trimmed to this length (if it happens to be longer). 516 * @param suffix the optional suffix to add to the sql name (i.e. foreign 517 * key suffix, etc.) 518 * @param separator character used to separate words 519 * @param shortenSqlNameMethod The method used to shorten the name, i.e. removeVowels 520 * @return the SQL name as a String. 521 */ 522 public static String getSqlNameFromTaggedValue( 523 String prefix, 524 final EJB3AssociationFacade element, 525 String name, 526 final Short nameMaxLength, 527 String suffix, 528 final Object separator, 529 final Object shortenSqlNameMethod) 530 { 531 if (element != null) 532 { 533 Object value = element.findTaggedValue(name); 534 StringBuilder buffer = new StringBuilder(StringUtils.trimToEmpty((String)value)); 535 if (StringUtils.isBlank(buffer.toString())) 536 { 537 // if we can't find the tagValue then use the 538 // element name for the name 539 buffer = new StringBuilder( 540 EntityMetafacadeUtils.toSqlName( 541 element.getName(), 542 separator)); 543 544 suffix = StringUtils.trimToEmpty(suffix); 545 prefix = StringUtils.trimToEmpty(prefix); 546 if (nameMaxLength != null) 547 { 548 final short maxLength = (short)(nameMaxLength.shortValue() - suffix.length() - prefix.length()); 549 buffer = 550 new StringBuilder( 551 EntityMetafacadeUtils.ensureMaximumNameLength( 552 buffer.toString(), 553 new Short(maxLength), 554 (String)shortenSqlNameMethod)); 555 } 556 if (StringUtils.isNotBlank(prefix)) 557 { 558 buffer.insert( 559 0, 560 prefix); 561 } 562 if (StringUtils.isNotBlank(suffix)) 563 { 564 buffer.append(suffix); 565 } 566 } 567 name = buffer.toString(); 568 } 569 return name; 570 } 571}