001package org.andromda.metafacades.uml14; 002 003import java.util.ArrayList; 004import java.util.Collection; 005import java.util.Iterator; 006import java.util.LinkedHashSet; 007import org.andromda.metafacades.uml.ClassifierFacade; 008import org.andromda.metafacades.uml.ConstraintFacade; 009import org.andromda.metafacades.uml.DependencyFacade; 010import org.andromda.metafacades.uml.MetafacadeUtils; 011import org.andromda.metafacades.uml.ModelElementFacade; 012import org.andromda.metafacades.uml.NameMasker; 013import org.andromda.metafacades.uml.OperationFacade; 014import org.andromda.metafacades.uml.ParameterFacade; 015import org.andromda.metafacades.uml.TypeMappings; 016import org.andromda.metafacades.uml.UMLMetafacadeProperties; 017import org.andromda.metafacades.uml.UMLProfile; 018import org.andromda.translation.ocl.ExpressionKinds; 019import org.andromda.utils.StringUtilsHelper; 020import org.apache.commons.collections.CollectionUtils; 021import org.apache.commons.collections.Predicate; 022import org.apache.commons.collections.Transformer; 023import org.apache.commons.lang.StringUtils; 024import org.apache.log4j.Logger; 025import org.omg.uml.foundation.core.Classifier; 026import org.omg.uml.foundation.core.Operation; 027import org.omg.uml.foundation.core.Parameter; 028import org.omg.uml.foundation.datatypes.CallConcurrencyKind; 029import org.omg.uml.foundation.datatypes.CallConcurrencyKindEnum; 030import org.omg.uml.foundation.datatypes.ParameterDirectionKindEnum; 031import org.omg.uml.foundation.datatypes.ScopeKindEnum; 032 033/** 034 * Metaclass facade implementation. 035 * @author Bob Fields 036 */ 037public class OperationFacadeLogicImpl 038 extends OperationFacadeLogic 039{ 040 private static final long serialVersionUID = 34L; 041 /** 042 * @param metaObject 043 * @param context 044 */ 045 public OperationFacadeLogicImpl(Operation metaObject, String context) 046 { 047 super(metaObject, context); 048 } 049 050 /** 051 * The logger instance. 052 */ 053 private static final Logger logger = Logger.getLogger(OperationFacadeLogicImpl.class); 054 055 /** 056 * Overridden to provide name masking. 057 * 058 * @see org.andromda.metafacades.uml.ModelElementFacade#getName() 059 */ 060 @Override 061 protected String handleGetName() 062 { 063 final String nameMask = String.valueOf(this.getConfiguredProperty(UMLMetafacadeProperties.OPERATION_NAME_MASK)); 064 String name = super.handleGetName(); 065 if (this.isMany() && this.isPluralizeAssociationEndNames()) 066 { 067 name = StringUtilsHelper.pluralize(name); 068 } 069 return NameMasker.mask(name, nameMask); 070 // UML14 does not support multiplicity > 1 on operation return parameter 071 //return NameMasker.mask(super.handleGetName(), nameMask); 072 } 073 074 /** 075 * @see org.andromda.core.metafacade.MetafacadeBase#getValidationOwner() 076 */ 077 public ClassifierFacade getValidationOwner() 078 { 079 return this.getOwner(); 080 } 081 082 /** 083 * @see org.andromda.metafacades.uml.OperationFacade#getSignature() 084 */ 085 @Override 086 protected String handleGetSignature() 087 { 088 return this.getSignature(true); 089 } 090 091 /** 092 * @see org.andromda.metafacades.uml.OperationFacade#getSignature(boolean) 093 */ 094 @Override 095 protected String handleGetSignature(boolean withArgumentNames) 096 { 097 return MetafacadeUtils.getSignature(this.getName(), this.getArguments(), withArgumentNames, null); 098 } 099 100 /** 101 * @see org.andromda.metafacades.uml.OperationFacade#getTypedArgumentList() 102 */ 103 @Override 104 protected String handleGetTypedArgumentList() 105 { 106 return this.getTypedArgumentList(true); 107 } 108 109 private String getTypedArgumentList(boolean withArgumentNames) 110 { 111 return this.getTypedArgumentList(withArgumentNames, null); 112 } 113 114 /** 115 * @see org.andromda.metafacades.uml.OperationFacade#getCall() 116 */ 117 @Override 118 protected String handleGetCall() 119 { 120 return this.getCall(this.getName()); 121 } 122 123 /** 124 * Constructs the operation call with the given <code>name</code> 125 * 126 * @param name the name form which to construct the operation call. 127 * @return the operation call. 128 */ 129 private String getCall(String name) 130 { 131 StringBuilder buffer = new StringBuilder(); 132 buffer.append(name); 133 buffer.append('('); 134 buffer.append(this.getArgumentNames()); 135 buffer.append(')'); 136 return buffer.toString(); 137 } 138 139 /** 140 * @see org.andromda.metafacades.uml.OperationFacade#getArgumentNames() 141 */ 142 @Override 143 protected String handleGetArgumentNames() 144 { 145 StringBuilder buffer = new StringBuilder(); 146 147 Iterator iterator = metaObject.getParameter().iterator(); 148 149 boolean commaNeeded = false; 150 while (iterator.hasNext()) 151 { 152 Parameter parameter = (Parameter)iterator.next(); 153 154 if (!ParameterDirectionKindEnum.PDK_RETURN.equals(parameter.getKind())) 155 { 156 if (commaNeeded) 157 { 158 buffer.append(", "); 159 } 160 ParameterFacade facade = (ParameterFacade)this.shieldedElement(parameter); 161 buffer.append(facade.getName()); 162 commaNeeded = true; 163 } 164 } 165 return buffer.toString(); 166 } 167 168 /** 169 * @see org.andromda.metafacades.uml.OperationFacade#getArgumentTypeNames() 170 */ 171 @Override 172 protected String handleGetArgumentTypeNames() 173 { 174 StringBuilder buffer = new StringBuilder(); 175 176 Iterator iterator = metaObject.getParameter().iterator(); 177 178 boolean commaNeeded = false; 179 while (iterator.hasNext()) 180 { 181 Parameter parameter = (Parameter)iterator.next(); 182 183 if (!ParameterDirectionKindEnum.PDK_RETURN.equals(parameter.getKind())) 184 { 185 if (commaNeeded) 186 { 187 buffer.append(", "); 188 } 189 ParameterFacade facade = (ParameterFacade)shieldedElement(parameter); 190 buffer.append(facade.getType().getFullyQualifiedName()); 191 commaNeeded = true; 192 } 193 } 194 return buffer.toString(); 195 } 196 197 /** 198 * @return fully qualified return type, including multiplicity 199 * @see org.andromda.metafacades.uml.OperationFacade#getGetterSetterReturnTypeName() 200 */ 201 @Override 202 protected String handleGetGetterSetterReturnTypeName() 203 { 204 if (this.getReturnType()==null) 205 { 206 return ""; 207 } 208 else 209 { 210 // Multiplicity in return type is only supported in UML2 211 return getReturnType().getFullyQualifiedName(); 212 } 213 } 214 215 /** 216 * @see org.andromda.metafacades.uml.OperationFacade#getReturnType() 217 */ 218 @Override 219 protected Classifier handleGetReturnType() 220 { 221 Classifier type = null; 222 final Collection<Parameter> parms = metaObject.getParameter(); 223 for (final Parameter parameter : parms) 224 { 225 if (ParameterDirectionKindEnum.PDK_RETURN.equals(parameter.getKind())) 226 { 227 type = parameter.getType(); 228 break; 229 } 230 } 231 return type; 232 } 233 234 /** 235 * @see org.andromda.metafacades.uml.OperationFacade#getArguments() 236 */ 237 @Override 238 protected Collection<Parameter> handleGetArguments() 239 { 240 final Collection<Parameter> arguments = new ArrayList<Parameter>(metaObject.getParameter()); 241 CollectionUtils.filter(arguments, new Predicate() 242 { 243 public boolean evaluate(Object object) 244 { 245 return !ParameterDirectionKindEnum.PDK_RETURN.equals(((Parameter)object).getKind()); 246 } 247 }); 248 return arguments; 249 } 250 251 /** 252 * Not yet implemented, always returns null. To implement: walk through the 253 * related elements from the Sequence Diagram in the UML model to produce compilable code. 254 * @return method body 255 * @see org.andromda.metafacades.uml.OperationFacade#getMethodBody() 256 */ 257 @Override 258 protected String handleGetMethodBody() 259 { 260 return null; 261 } 262 263 /** 264 * @see org.andromda.metafacades.uml.OperationFacade#getOwner() 265 */ 266 @Override 267 protected Classifier handleGetOwner() 268 { 269 return this.metaObject.getOwner(); 270 } 271 272 /** 273 * @see org.andromda.metafacades.uml.OperationFacade#getParameters() 274 */ 275 @Override 276 protected Collection<Parameter> handleGetParameters() 277 { 278 return metaObject.getParameter(); 279 } 280 281 /** 282 * @see org.andromda.metafacades.uml.OperationFacade#findTaggedValue(String, boolean) 283 */ 284 @Override 285 protected Object handleFindTaggedValue(String name, boolean follow) 286 { 287 name = StringUtils.trimToEmpty(name); 288 Object value = findTaggedValue(name); 289 if (follow) 290 { 291 ClassifierFacade type = this.getReturnType(); 292 while (value == null && type != null) 293 { 294 value = type.findTaggedValue(name); 295 type = (ClassifierFacade)type.getGeneralization(); 296 } 297 } 298 return value; 299 } 300 301 /** 302 * @see org.andromda.metafacades.uml.OperationFacade#isStatic() 303 */ 304 @Override 305 protected boolean handleIsStatic() 306 { 307 return ScopeKindEnum.SK_CLASSIFIER.equals(this.metaObject.getOwnerScope()); 308 } 309 310 /** 311 * @see org.andromda.metafacades.uml.OperationFacade#isAbstract() 312 */ 313 @Override 314 protected boolean handleIsAbstract() 315 { 316 return metaObject.isAbstract(); 317 } 318 319 /** 320 * @return metaObject.isLeaf() 321 * @see org.andromda.metafacades.uml.OperationFacade#isLeaf() 322 */ 323 @Override 324 protected boolean handleIsLeaf() 325 { 326 return metaObject.isLeaf(); 327 } 328 329 /** 330 * @return false always 331 * @see org.andromda.metafacades.uml.OperationFacade#isMany() 332 */ 333 //@Override 334 protected boolean handleIsMany() 335 { 336 boolean isMany = false; 337 if (null!=this.getReturnParameter()) 338 { 339 isMany = this.getReturnParameter().isMany(); 340 } 341 return isMany; 342 } 343 344 /** 345 * @return false always 346 * @see org.andromda.metafacades.uml.OperationFacade#isOrdered() 347 */ 348 //@Override 349 protected boolean handleIsOrdered() 350 { 351 return false; 352 } 353 354 /** 355 * @return false always 356 * @see org.andromda.metafacades.uml.OperationFacade#isOrdered() 357 */ 358 //@Override 359 protected boolean handleIsUnique() 360 { 361 return this.hasStereotype(UMLProfile.STEREOTYPE_UNIQUE); 362 } 363 364 /** 365 * @see org.andromda.metafacades.uml.OperationFacade#isQuery() 366 */ 367 @Override 368 protected boolean handleIsQuery() 369 { 370 return metaObject.isQuery(); 371 } 372 373 /** 374 * @see org.andromda.metafacades.uml.OperationFacade#isExceptionsPresent() 375 */ 376 @Override 377 protected boolean handleIsExceptionsPresent() 378 { 379 return !this.getExceptions().isEmpty(); 380 } 381 382 /** 383 * @see org.andromda.metafacades.uml.OperationFacade#getExceptions() 384 */ 385 @Override 386 protected Collection<ModelElementFacade> handleGetExceptions() 387 { 388 Collection <ModelElementFacade> exceptions = new LinkedHashSet<ModelElementFacade>(); 389 390 // finds both exceptions and exception references 391 final class ExceptionFilter 392 implements Predicate 393 { 394 public boolean evaluate(Object object) 395 { 396 boolean hasException = object instanceof DependencyFacade; 397 if (hasException) 398 { 399 DependencyFacade dependency = (DependencyFacade)object; 400 // first check for exception references 401 hasException = dependency.hasStereotype(UMLProfile.STEREOTYPE_EXCEPTION_REF); 402 403 // if there wasn't any exception reference 404 // now check for actual exceptions 405 if (!hasException) 406 { 407 ModelElementFacade targetElement = dependency.getTargetElement(); 408 hasException = targetElement != null && targetElement.hasStereotype( 409 UMLProfile.STEREOTYPE_EXCEPTION); 410 } 411 } 412 return hasException; 413 } 414 } 415 416 // first get any dependencies on this operation's 417 // owner (because these will represent the default exception(s)) 418 final Collection<DependencyFacade> ownerDependencies = new ArrayList<DependencyFacade>(this.getOwner().getSourceDependencies()); 419 if (!ownerDependencies.isEmpty()) 420 { 421 CollectionUtils.filter(ownerDependencies, new ExceptionFilter()); 422 exceptions.addAll(ownerDependencies); 423 } 424 425 final Collection<DependencyFacade> operationDependencies = new ArrayList<DependencyFacade>(this.getSourceDependencies()); 426 // now get any exceptions directly on the operation 427 if (!operationDependencies.isEmpty()) 428 { 429 CollectionUtils.filter(operationDependencies, new ExceptionFilter()); 430 exceptions.addAll(operationDependencies); 431 } 432 433 // now transform the dependency(s) to the actual exception(s) 434 CollectionUtils.transform(exceptions, new Transformer() 435 { 436 public ModelElementFacade transform(Object object) 437 { 438 return ((DependencyFacade)object).getTargetElement(); 439 } 440 }); 441 return exceptions; 442 } 443 444 /** 445 * @see org.andromda.metafacades.uml.OperationFacade#getExceptionList() 446 */ 447 @Override 448 protected String handleGetExceptionList() 449 { 450 return this.getExceptionList(null); 451 } 452 453 /** 454 * @see org.andromda.metafacades.uml.OperationFacade#isReturnTypePresent() 455 */ 456 @Override 457 protected boolean handleIsReturnTypePresent() 458 { 459 boolean hasReturnType = false; 460 if (this.getReturnType() != null) 461 { 462 hasReturnType = !("void".equalsIgnoreCase(StringUtils.trimToEmpty( 463 this.getReturnType().getFullyQualifiedName())) 464 || StringUtils.trimToEmpty( 465 this.getReturnType().getFullyQualifiedName(true)).equals(UMLProfile.VOID_TYPE_NAME)); 466 } 467 return hasReturnType; 468 } 469 470 /** 471 * @see org.andromda.metafacades.uml.OperationFacade#getExceptionList(String) 472 */ 473 @Override 474 protected String handleGetExceptionList(String initialExceptions) 475 { 476 initialExceptions = StringUtils.trimToEmpty(initialExceptions); 477 StringBuilder exceptionList = new StringBuilder(initialExceptions); 478 Collection exceptions = this.getExceptions(); 479 if (exceptions != null && !exceptions.isEmpty()) 480 { 481 if (StringUtils.isNotBlank(initialExceptions)) 482 { 483 exceptionList.append(", "); 484 } 485 Iterator exceptionIt = exceptions.iterator(); 486 while (exceptionIt.hasNext()) 487 { 488 ModelElementFacade exception = (ModelElementFacade)exceptionIt.next(); 489 exceptionList.append(exception.getFullyQualifiedName()); 490 if (exceptionIt.hasNext()) 491 { 492 exceptionList.append(", "); 493 } 494 } 495 } 496 497 return exceptionList.toString(); 498 } 499 500 /** 501 * @see org.andromda.metafacades.uml.OperationFacade#getTypedArgumentList(String) 502 */ 503 @Override 504 protected String handleGetTypedArgumentList(String modifier) 505 { 506 return this.getTypedArgumentList(true, modifier); 507 } 508 509 /** 510 * @see org.andromda.metafacades.uml.OperationFacade#getSignature(String) 511 */ 512 @Override 513 protected String handleGetSignature(String argumentModifier) 514 { 515 return MetafacadeUtils.getSignature(this.getName(), this.getArguments(), true, argumentModifier); 516 } 517 518 private String getTypedArgumentList(boolean withArgumentNames, String modifier) 519 { 520 final StringBuilder buffer = new StringBuilder(); 521 final Iterator parameterIterator = metaObject.getParameter().iterator(); 522 523 boolean commaNeeded = false; 524 while (parameterIterator.hasNext()) 525 { 526 Parameter paramter = (Parameter)parameterIterator.next(); 527 528 if (!ParameterDirectionKindEnum.PDK_RETURN.equals(paramter.getKind())) 529 { 530 String type = null; 531 if (paramter.getType() == null) 532 { 533 OperationFacadeLogicImpl.logger.error( 534 "ERROR! No type specified for parameter --> '" + paramter.getName() + 535 "' on operation --> '" + 536 this.getName() + 537 "', please check your model"); 538 } 539 else 540 { 541 type = ((ClassifierFacade)this.shieldedElement(paramter.getType())).getFullyQualifiedName(); 542 } 543 544 if (commaNeeded) 545 { 546 buffer.append(", "); 547 } 548 if (StringUtils.isNotBlank(modifier)) 549 { 550 buffer.append(modifier); 551 buffer.append(' '); 552 } 553 buffer.append(type); 554 if (withArgumentNames) 555 { 556 buffer.append(' '); 557 buffer.append(paramter.getName()); 558 } 559 commaNeeded = true; 560 } 561 } 562 return buffer.toString(); 563 } 564 565 /** 566 * @see org.andromda.metafacades.uml.OperationFacade#getConcurrency() 567 */ 568 @Override 569 protected String handleGetConcurrency() 570 { 571 String concurrency; 572 573 final CallConcurrencyKind concurrencyKind = metaObject.getConcurrency(); 574 if (concurrencyKind == null || CallConcurrencyKindEnum.CCK_CONCURRENT.equals(concurrencyKind)) 575 { 576 concurrency = "concurrent"; 577 } 578 else if (CallConcurrencyKindEnum.CCK_GUARDED.equals(concurrencyKind)) 579 { 580 concurrency = "guarded"; 581 } 582 else // CallConcurrencyKindEnum.CCK_SEQUENTIAL 583 { 584 concurrency = "sequential"; 585 } 586 587 final TypeMappings languageMappings = this.getLanguageMappings(); 588 if (languageMappings != null) 589 { 590 concurrency = languageMappings.getTo(concurrency); 591 } 592 593 return concurrency; 594 } 595 596 /** 597 * @see org.andromda.metafacades.uml.OperationFacade#getPreconditionName() 598 */ 599 @Override 600 protected String handleGetPreconditionName() 601 { 602 return this.getPreconditionPattern().replaceAll("\\{0\\}", this.getName()); 603 } 604 605 /** 606 * @see org.andromda.metafacades.uml.OperationFacade#getPostconditionName() 607 */ 608 @Override 609 protected String handleGetPostconditionName() 610 { 611 return this.getPostconditionPattern().replaceAll("\\{0\\}", this.getName()); 612 } 613 614 /** 615 * @see org.andromda.metafacades.uml.OperationFacade#getPreconditionSignature() 616 */ 617 @Override 618 protected String handleGetPreconditionSignature() 619 { 620 return MetafacadeUtils.getSignature(this.getPreconditionName(), this.getArguments(), true, null); 621 } 622 623 /** 624 * @see org.andromda.metafacades.uml.OperationFacade#getPreconditionCall() 625 */ 626 @Override 627 protected String handleGetPreconditionCall() 628 { 629 return this.getCall(this.getPreconditionName()); 630 } 631 632 /** 633 * Gets the pattern for constructing the precondition name. 634 * 635 * @return the precondition pattern. 636 */ 637 private String getPreconditionPattern() 638 { 639 return String.valueOf(this.getConfiguredProperty(UMLMetafacadeProperties.PRECONDITION_NAME_PATTERN)); 640 } 641 642 /** 643 * Gets the pattern for constructing the postcondition name. 644 * 645 * @return the postcondition pattern. 646 */ 647 private String getPostconditionPattern() 648 { 649 return String.valueOf(this.getConfiguredProperty(UMLMetafacadeProperties.POSTCONDITION_NAME_PATTERN)); 650 } 651 652 /** 653 * @see org.andromda.metafacades.uml.OperationFacade#isPreconditionsPresent() 654 */ 655 @Override 656 protected boolean handleIsPreconditionsPresent() 657 { 658 final Collection<ConstraintFacade> preconditions = this.getPreconditions(); 659 return preconditions != null && !preconditions.isEmpty(); 660 } 661 662 /** 663 * @see org.andromda.metafacades.uml.OperationFacade#isPostconditionsPresent() 664 */ 665 @Override 666 protected boolean handleIsPostconditionsPresent() 667 { 668 final Collection<ConstraintFacade> postconditions = this.getPostconditions(); 669 return postconditions != null && !postconditions.isEmpty(); 670 } 671 672 /** 673 * @see org.andromda.metafacades.uml.OperationFacade#getPreconditions() 674 */ 675 @Override 676 protected Collection<ConstraintFacade> handleGetPreconditions() 677 { 678 return this.getConstraints(ExpressionKinds.PRE); 679 } 680 681 /** 682 * @see org.andromda.metafacades.uml.OperationFacade#getPostconditions() 683 */ 684 @Override 685 protected Collection<ConstraintFacade> handleGetPostconditions() 686 { 687 return this.getConstraints(ExpressionKinds.POST); 688 } 689 690 /** 691 * @see org.andromda.metafacades.uml.OperationFacade#findParameter(String) 692 */ 693 @Override 694 protected ParameterFacade handleFindParameter(final String name) 695 { 696 return (ParameterFacade)CollectionUtils.find( 697 this.getParameters(), 698 new Predicate() 699 { 700 public boolean evaluate(Object object) 701 { 702 final ParameterFacade parameter = (ParameterFacade)object; 703 return StringUtils.trimToEmpty(parameter.getName()).equals(name); 704 } 705 }); 706 } 707 708 /** 709 * Get the UML upper multiplicity 710 * @return -1 (UnlimitedNatural) is isMany, otherwise 1 711 */ 712 @Override 713 protected int handleGetUpper() 714 { 715 if (this.isMany()) 716 { 717 return -1; 718 } 719 return 1; 720 } 721 722 /** 723 * Get the UML lower multiplicity 724 * @return 1 if primitive, otherwise 0 725 */ 726 @Override 727 protected int handleGetLower() 728 { 729 if (!this.getReturnParameter().hasStereotype("Nullable") 730 && this.getReturnType().isPrimitive()) 731 { 732 return 1; 733 } 734 return 0; 735 } 736 737 /** 738 * @see org.andromda.metafacades.uml14.OperationFacadeLogic#handleGetReturnParameter() 739 */ 740 @Override 741 public ParameterFacade handleGetReturnParameter() 742 { 743 //throw new UnsupportedOperationException("ReturnResults is not a UML1.4 feature"); 744 ParameterFacade facade = null; 745 final Collection<Parameter> parms = metaObject.getParameter(); 746 for (final Parameter parameter : parms) 747 { 748 if (ParameterDirectionKindEnum.PDK_RETURN.equals(parameter.getKind())) 749 { 750 facade = (ParameterFacade)shieldedElement(parameter); 751 break; 752 } 753 } 754 return facade; 755 } 756 757 /** 758 * @see org.andromda.metafacades.uml14.OperationFacadeLogic#handleIsOverriding() 759 */ 760 protected boolean handleIsOverriding() 761 { 762 return this.getOverriddenOperation() != null; 763 } 764 765 /** 766 * @see org.andromda.metafacades.uml14.OperationFacadeLogic#handleGetOverriddenOperation() 767 */ 768 protected OperationFacade handleGetOverriddenOperation() 769 { 770 OperationFacade overriddenOperation = null; 771 772 final String signature = this.getSignature(false); 773 774 ClassifierFacade ancestor = this.getOwner().getSuperClass(); 775 while (overriddenOperation == null && ancestor != null) 776 { 777 for (Iterator operationIterator = ancestor.getOperations().iterator(); 778 overriddenOperation == null && operationIterator.hasNext();) 779 { 780 final OperationFacade ancestorOperation = (OperationFacade)operationIterator.next(); 781 if (signature.equals(ancestorOperation.getSignature(false))) 782 { 783 overriddenOperation = ancestorOperation; 784 } 785 } 786 787 ancestor = ancestor.getSuperClass(); 788 } 789 790 return overriddenOperation; 791 } 792 793 /** 794 * Indicates whether or not we should pluralize association end names. 795 * 796 * @return true/false 797 */ 798 //@SuppressWarnings("unused") 799 private boolean isPluralizeAssociationEndNames() 800 { 801 final Object value = this.getConfiguredProperty(UMLMetafacadeProperties.PLURALIZE_ASSOCIATION_END_NAMES); 802 return value != null && Boolean.valueOf(String.valueOf(value)); 803 } 804}