001package org.andromda.metafacades.uml14; 002 003import java.util.Collection; 004import org.andromda.metafacades.uml.ClassifierFacade; 005import org.andromda.metafacades.uml.EnumerationFacade; 006import org.andromda.metafacades.uml.NameMasker; 007import org.andromda.metafacades.uml.TypeMappings; 008import org.andromda.metafacades.uml.UMLMetafacadeProperties; 009import org.andromda.metafacades.uml.UMLMetafacadeUtils; 010import org.andromda.metafacades.uml.UMLProfile; 011import org.andromda.utils.StringUtilsHelper; 012import org.apache.commons.lang.BooleanUtils; 013import org.apache.commons.lang.ObjectUtils; 014import org.apache.commons.lang.StringUtils; 015import org.omg.uml.foundation.core.Attribute; 016import org.omg.uml.foundation.core.Classifier; 017import org.omg.uml.foundation.datatypes.ChangeableKindEnum; 018import org.omg.uml.foundation.datatypes.Multiplicity; 019import org.omg.uml.foundation.datatypes.MultiplicityRange; 020import org.omg.uml.foundation.datatypes.OrderingKind; 021import org.omg.uml.foundation.datatypes.OrderingKindEnum; 022import org.omg.uml.foundation.datatypes.ScopeKindEnum; 023 024/** 025 * Metaclass facade implementation. 026 * @author Bob Fields 027 */ 028public class AttributeFacadeLogicImpl 029 extends AttributeFacadeLogic 030{ 031 private static final long serialVersionUID = 34L; 032 /** 033 * @param metaObject 034 * @param context 035 */ 036 public AttributeFacadeLogicImpl( 037 Attribute metaObject, 038 String context) 039 { 040 super(metaObject, context); 041 } 042 043 /** 044 * @see org.andromda.core.metafacade.MetafacadeBase#getValidationOwner() 045 public ClassifierFacade getValidationOwner() 046 { 047 return this.getOwner(); 048 } 049 */ 050 051 /** 052 * @see org.andromda.metafacades.uml.AttributeFacade#getGetterName() 053 */ 054 @Override 055 public String handleGetGetterName() 056 { 057 return UMLMetafacadeUtils.getGetterPrefix(this.getType()) + StringUtilsHelper.capitalize(this.getName()); 058 } 059 060 /** 061 * @see org.andromda.metafacades.uml.AttributeFacade#getSetterName() 062 */ 063 @Override 064 public String handleGetSetterName() 065 { 066 return "set" + StringUtils.capitalize(this.getName()); 067 } 068 069 /** 070 * @see org.andromda.metafacades.uml.AttributeFacade#getDefaultValue() 071 */ 072 @Override 073 public String handleGetDefaultValue() 074 { 075 String defaultValue = null; 076 if (this.metaObject.getInitialValue() != null) 077 { 078 defaultValue = this.metaObject.getInitialValue().getBody(); 079 } 080 // Put single or double quotes around default in case modeler forgot to do it. Most templates 081 // declare Type attribute = $attribute.defaultValue, requiring quotes around the value 082 if (StringUtils.isNotBlank(defaultValue) && !this.isMany() && this.metaObject.getType() != null && defaultValue != null) 083 { 084 String typeName = this.metaObject.getType().getName(); 085 if ("String".equals(typeName) && defaultValue.indexOf('"')<0) 086 { 087 defaultValue = '"' + defaultValue + '"'; 088 } 089 else if (("char".equals(typeName) || "Character".equals(typeName)) 090 && defaultValue.indexOf('\'')<0) 091 { 092 defaultValue = "'" + defaultValue.charAt(0) + '\''; 093 } 094 } 095 if (defaultValue==null) defaultValue=""; 096 return defaultValue; 097 } 098 099 /** 100 * @see org.andromda.metafacades.uml.AttributeFacade#isChangeable() 101 */ 102 @Override 103 public boolean handleIsChangeable() 104 { 105 return ChangeableKindEnum.CK_CHANGEABLE.equals(metaObject.getChangeability()); 106 } 107 108 /** 109 * @see org.andromda.metafacades.uml.AttributeFacade#isAddOnly() 110 */ 111 @Override 112 public boolean handleIsAddOnly() 113 { 114 return ChangeableKindEnum.CK_ADD_ONLY.equals(metaObject.getChangeability()); 115 } 116 117 /** 118 * @see org.andromda.metafacades.uml.AttributeFacade#getType() 119 */ 120 @Override 121 protected Classifier handleGetType() 122 { 123 return metaObject.getType(); 124 } 125 126 /** 127 * @see org.andromda.metafacades.uml.AttributeFacade#getOwner() 128 */ 129 @Override 130 public Classifier handleGetOwner() 131 { 132 return this.metaObject.getOwner(); 133 } 134 135 /** 136 * @see org.andromda.metafacades.uml.AttributeFacade#isReadOnly() 137 */ 138 @Override 139 public boolean handleIsReadOnly() 140 { 141 return ChangeableKindEnum.CK_FROZEN.equals(metaObject.getChangeability()); 142 } 143 144 /** 145 * @see org.andromda.metafacades.uml.AttributeFacade#isStatic() 146 */ 147 @Override 148 public boolean handleIsStatic() 149 { 150 return ScopeKindEnum.SK_CLASSIFIER.equals(this.metaObject.getOwnerScope()); 151 } 152 153 /** 154 * @see org.andromda.metafacades.uml.AttributeFacade#findTaggedValue(String, boolean) 155 */ 156 @Override 157 public Object handleFindTaggedValue( 158 String name, 159 boolean follow) 160 { 161 name = StringUtils.trimToEmpty(name); 162 Object value = findTaggedValue(name); 163 if (follow) 164 { 165 ClassifierFacade type = this.getType(); 166 while (value == null && type != null) 167 { 168 value = type.findTaggedValue(name); 169 type = (ClassifierFacade)type.getGeneralization(); 170 } 171 } 172 return value; 173 } 174 175 /** 176 * @see org.andromda.metafacades.uml.AttributeFacade#isRequired() 177 */ 178 @Override 179 public boolean handleIsRequired() 180 { 181 int lower = this.getMultiplicityRangeLower(); 182 return lower >= 1; 183 } 184 185 /** 186 * @see org.andromda.metafacades.uml.AttributeFacade#isMany() 187 */ 188 @Override 189 public boolean handleIsMany() 190 { 191 boolean isMany = false; 192 final Multiplicity multiplicity = this.metaObject.getMultiplicity(); 193 194 // assume no multiplicity is 1 195 if (multiplicity != null) 196 { 197 final Collection<MultiplicityRange> ranges = multiplicity.getRange(); 198 if (ranges != null && !ranges.isEmpty()) 199 { 200 for (MultiplicityRange multiplicityRange : ranges) 201 { 202 final int upper = multiplicityRange.getUpper(); 203 isMany = upper > 1 || upper < 0; 204 } 205 } 206 } 207 if (null!=this.getType() && !isMany) 208 { 209 // isCollectionType causes too many problems with metafacades 210 isMany = this.getType().isArrayType(); 211 } 212 return isMany; 213 } 214 215 /** 216 * Returns the lower range of the multiplicity for the passed in attribute. If not specified, default is based on 217 * primitive or wrappedPrimitive type, or the default multiplicity. 218 * 219 * @return int the lower range of the multiplicity or the default multiplicity or 1 if it isn't defined. 220 */ 221 private int getMultiplicityRangeLower() 222 { 223 Integer lower = null; 224 final Multiplicity multiplicity = metaObject.getMultiplicity(); 225 if (multiplicity != null) 226 { 227 final Collection<MultiplicityRange> ranges = multiplicity.getRange(); 228 if (ranges != null && !ranges.isEmpty()) 229 { 230 for (MultiplicityRange multiplicityRange : ranges) 231 { 232 lower = Integer.valueOf(multiplicityRange.getLower()); 233 } 234 } 235 } 236 if (lower == null) 237 { 238 if (this.getType().isPrimitive()) 239 { 240 lower = Integer.valueOf(1); 241 } 242 else if (this.getType().isWrappedPrimitive()) 243 { 244 lower = Integer.valueOf(0); 245 } 246 else 247 { 248 final String defaultMultiplicity = this.getDefaultMultiplicity(); 249 if (defaultMultiplicity.startsWith("0")) 250 { 251 lower = Integer.valueOf(0); 252 } 253 else 254 { 255 lower = Integer.valueOf(1); 256 } 257 } 258 } 259 return lower.intValue(); 260 } 261 262 /** 263 * Returns the upper range of the multiplicity for the passed in attribute 264 * 265 * @return int the upper range of the multiplicity or 1 if it isn't defined. 266 */ 267 private int getMultiplicityRangeUpper() 268 { 269 Integer upper = null; 270 final Multiplicity multiplicity = metaObject.getMultiplicity(); 271 if (multiplicity != null) 272 { 273 final Collection<MultiplicityRange> ranges = multiplicity.getRange(); 274 if (ranges != null && !ranges.isEmpty()) 275 { 276 for (MultiplicityRange multiplicityRange : ranges) 277 { 278 upper = Integer.valueOf(multiplicityRange.getUpper()); 279 } 280 } 281 } 282 if (upper == null) 283 { 284 upper = Integer.valueOf(1); 285 } 286 return upper.intValue(); 287 } 288 289 /** 290 * Gets the default multiplicity for this attribute (the 291 * multiplicity if none is defined). 292 * 293 * @return the default multiplicity as a String. 294 */ 295 private String getDefaultMultiplicity() 296 { 297 return ObjectUtils.toString(this.getConfiguredProperty(UMLMetafacadeProperties.DEFAULT_MULTIPLICITY)); 298 } 299 300 /** 301 * @see org.andromda.metafacades.uml.AttributeFacade#getEnumeration() 302 */ 303 @Override 304 protected EnumerationFacade handleGetEnumeration() 305 { 306 return (EnumerationFacade)(this.isEnumerationLiteral() ? this.getOwner() : null); 307 } 308 309 /** 310 * @see org.andromda.metafacades.uml.AttributeFacade#isEnumerationLiteral() 311 */ 312 @Override 313 protected boolean handleIsEnumerationLiteral() 314 { 315 final ClassifierFacade owner = this.getOwner(); 316 return (owner != null) && owner.isEnumeration(); 317 } 318 319 /** 320 * @see org.andromda.metafacades.uml.AttributeFacade#getEnumerationValue() 321 */ 322 @Override 323 protected String handleGetEnumerationValue() 324 { 325 String value = null; 326 if (this.isEnumerationLiteral()) 327 { 328 value = this.getDefaultValue(); 329 value = StringUtils.isEmpty(value) ? this.getName() : String.valueOf(value); 330 } 331 if (this.getType().isStringType() && value!=null && value.indexOf('"')<0) 332 { 333 value = '\"' + value + '\"'; 334 } 335 return value; 336 } 337 338 /** 339 * @see org.andromda.metafacades.uml.AttributeFacade#isEnumerationMember() 340 */ 341 @Override 342 protected boolean handleIsEnumerationMember() 343 { 344 boolean isMemberVariable = false; 345 final String isMemberVariableAsString = (String)this.findTaggedValue( 346 UMLProfile.TAGGEDVALUE_PERSISTENCE_ENUMERATION_MEMBER_VARIABLE); 347 if (StringUtils.isNotBlank(isMemberVariableAsString) && BooleanUtils.toBoolean(isMemberVariableAsString)) 348 { 349 isMemberVariable = true; 350 } 351 return isMemberVariable; 352 } 353 354 /** 355 * @see org.andromda.metafacades.uml.AttributeFacade#getEnumerationLiteralParameters() 356 */ 357 @Override 358 protected String handleGetEnumerationLiteralParameters() 359 { 360 return (String)this.findTaggedValue(UMLProfile.TAGGEDVALUE_PERSISTENCE_ENUMERATION_LITERAL_PARAMETERS); 361 } 362 363 /** 364 * @see org.andromda.metafacades.uml.AttributeFacade#isEnumerationLiteralParametersExist() 365 */ 366 @Override 367 protected boolean handleIsEnumerationLiteralParametersExist() 368 { 369 boolean parametersExist = false; 370 if (StringUtils.isNotBlank(this.getEnumerationLiteralParameters())) 371 { 372 parametersExist = true; 373 } 374 return parametersExist; 375 } 376 377 /** 378 * @see org.andromda.metafacades.uml.AttributeFacade#isDefaultValuePresent() 379 */ 380 @Override 381 public boolean handleIsDefaultValuePresent() 382 { 383 return StringUtils.isNotBlank(this.getDefaultValue()); 384 } 385 386 /** 387 * Overridden to provide different handling of the name if this attribute represents a literal. 388 * 389 * @see org.andromda.metafacades.uml.ModelElementFacade#getName() 390 */ 391 @Override 392 protected String handleGetName() 393 { 394 String name = null; 395 if (this.isEnumerationMember()) 396 { 397 name = super.handleGetName(); 398 } 399 else 400 { 401 final String mask = String.valueOf(this.getConfiguredProperty( 402 this.getOwner() instanceof EnumerationFacade 403 ? UMLMetafacadeProperties.ENUMERATION_LITERAL_NAME_MASK 404 : UMLMetafacadeProperties.CLASSIFIER_PROPERTY_NAME_MASK )); 405 406 name = NameMasker.mask(super.handleGetName(), mask); 407 final boolean templating = Boolean.parseBoolean(String.valueOf( 408 this.getConfiguredProperty(UMLMetafacadeProperties.ENABLE_TEMPLATING))); 409 // May be null during the validation process. 410 if (this.getType() != null) 411 { 412 final boolean arrayType = this.getType().isArrayType(); 413 if (this.isPluralizeAttributeNames() && ((this.isMany() && templating) || arrayType)) 414 { 415 name = StringUtilsHelper.pluralize(name); 416 } 417 } 418 } 419 420 return name; 421 } 422 423 /** 424 * Indicates whether or not we should pluralize association end names. 425 * 426 * @return true/false 427 */ 428 private boolean isPluralizeAttributeNames() 429 { 430 final Object value = this.getConfiguredProperty(UMLMetafacadeProperties.PLURALIZE_ATTRIBUTE_NAMES); 431 return value != null && Boolean.valueOf(String.valueOf(value)).booleanValue(); 432 } 433 434 /** 435 * UML2 Only: Returns false always. 436 * @return false 437 * @see org.andromda.metafacades.uml.AttributeFacade#isLeaf() 438 */ 439 @Override 440 public boolean handleIsLeaf() 441 { 442 return false; 443 } 444 445 /** 446 * @see org.andromda.metafacades.uml.AttributeFacade#isOrdered() 447 */ 448 @Override 449 public boolean handleIsOrdered() 450 { 451 boolean ordered = false; 452 453 final OrderingKind ordering = metaObject.getOrdering(); 454 455 // no ordering is 'unordered' 456 if (ordering != null) 457 { 458 ordered = ordering.equals(OrderingKindEnum.OK_ORDERED); 459 } 460 461 return ordered; 462 } 463 464 /** 465 * @return hasStereotype(UMLProfile.STEREOTYPE_UNIQUE) 466 * @see org.andromda.metafacades.uml.AttributeFacade#isUnique() 467 */ 468 protected boolean handleIsUnique() 469 { 470 return this.hasStereotype(UMLProfile.STEREOTYPE_UNIQUE); 471 } 472 473 /** 474 * @see org.andromda.metafacades.uml.AttributeFacade#getGetterSetterTypeName() 475 */ 476 @Override 477 public String handleGetGetterSetterTypeName() 478 { 479 String name = null; 480 // TODO: Change to check upperBound > 1, not isMany or Array/Collection types 481 if (this.isMany() && !this.getType().isArrayType() && !this.getType().isCollectionType()) 482 { 483 final TypeMappings mappings = this.getLanguageMappings(); 484 if (this.hasStereotype(UMLProfile.STEREOTYPE_UNIQUE)) 485 { 486 name = 487 isOrdered() ? mappings.getTo(UMLProfile.ORDERED_SET_TYPE_NAME) : mappings.getTo( 488 UMLProfile.SET_TYPE_NAME); 489 } 490 else 491 { 492 name = 493 isOrdered() ? mappings.getTo(UMLProfile.LIST_TYPE_NAME) : mappings.getTo( 494 UMLProfile.COLLECTION_TYPE_NAME); 495 } 496 497 // set this attribute's type as a template parameter if required 498 if (BooleanUtils.toBoolean( 499 ObjectUtils.toString(this.getConfiguredProperty(UMLMetafacadeProperties.ENABLE_TEMPLATING))) 500 && this.getType() != null) 501 { 502 String type = this.getType().getFullyQualifiedName(); 503 /*Collection<GeneralizableElementFacade> specializations = this.getType().getAllSpecializations(); 504 if ((specializations != null && !specializations.isEmpty())) 505 { 506 name += "<? extends " + type + '>'; 507 } 508 else 509 {*/ 510 name += '<' + type + '>'; 511 //} 512 } 513 } 514 if (name == null && this.getType() != null) 515 { 516 name = this.getType().getFullyQualifiedName(); 517 } 518 return name; 519 } 520 521 /** 522 * Get the UML upper multiplicity 523 */ 524 @Override 525 protected int handleGetUpper() 526 { 527 return this.getMultiplicityRangeUpper(); 528 } 529 530 /** 531 * Get the UML lower multiplicity 532 */ 533 @Override 534 protected int handleGetLower() 535 { 536 return this.getMultiplicityRangeLower(); 537 } 538 539 /** 540 * @see org.andromda.metafacades.uml.AttributeFacade#isDerived() 541 */ 542 @Override 543 protected boolean handleIsDerived() 544 { 545 // UML 1.4 does not have derived attribute. 546 return false; 547 } 548}