/**
  * Evaluate the given value as an expression, if necessary.
  *
  * @param value the candidate value (may be an expression)
  * @return the resolved value
  */
 protected Object evaluate(TypedStringValue value) {
   Object result = doEvaluate(value.getValue());
   if (!ObjectUtils.nullSafeEquals(result, value.getValue())) {
     value.setDynamic();
   }
   return result;
 }
  private RootBeanDefinition parseAttributeSource(Element attrEle, ParserContext parserContext) {
    List<Element> methods = DomUtils.getChildElementsByTagName(attrEle, METHOD_ELEMENT);
    ManagedMap<TypedStringValue, RuleBasedTransactionAttribute> transactionAttributeMap =
        new ManagedMap<TypedStringValue, RuleBasedTransactionAttribute>(methods.size());
    transactionAttributeMap.setSource(parserContext.extractSource(attrEle));

    for (Element methodEle : methods) {
      String name = methodEle.getAttribute(METHOD_NAME_ATTRIBUTE);
      TypedStringValue nameHolder = new TypedStringValue(name);
      nameHolder.setSource(parserContext.extractSource(methodEle));

      RuleBasedTransactionAttribute attribute = new RuleBasedTransactionAttribute();
      String propagation = methodEle.getAttribute(PROPAGATION_ATTRIBUTE);
      String isolation = methodEle.getAttribute(ISOLATION_ATTRIBUTE);
      String timeout = methodEle.getAttribute(TIMEOUT_ATTRIBUTE);
      String readOnly = methodEle.getAttribute(READ_ONLY_ATTRIBUTE);
      if (StringUtils.hasText(propagation)) {
        attribute.setPropagationBehaviorName(
            RuleBasedTransactionAttribute.PREFIX_PROPAGATION + propagation);
      }
      if (StringUtils.hasText(isolation)) {
        attribute.setIsolationLevelName(RuleBasedTransactionAttribute.PREFIX_ISOLATION + isolation);
      }
      if (StringUtils.hasText(timeout)) {
        try {
          attribute.setTimeout(Integer.parseInt(timeout));
        } catch (NumberFormatException ex) {
          parserContext
              .getReaderContext()
              .error("Timeout must be an integer value: [" + timeout + "]", methodEle);
        }
      }
      if (StringUtils.hasText(readOnly)) {
        attribute.setReadOnly(Boolean.valueOf(methodEle.getAttribute(READ_ONLY_ATTRIBUTE)));
      }

      List<RollbackRuleAttribute> rollbackRules = new LinkedList<RollbackRuleAttribute>();
      if (methodEle.hasAttribute(ROLLBACK_FOR_ATTRIBUTE)) {
        String rollbackForValue = methodEle.getAttribute(ROLLBACK_FOR_ATTRIBUTE);
        addRollbackRuleAttributesTo(rollbackRules, rollbackForValue);
      }
      if (methodEle.hasAttribute(NO_ROLLBACK_FOR_ATTRIBUTE)) {
        String noRollbackForValue = methodEle.getAttribute(NO_ROLLBACK_FOR_ATTRIBUTE);
        addNoRollbackRuleAttributesTo(rollbackRules, noRollbackForValue);
      }
      attribute.setRollbackRules(rollbackRules);

      transactionAttributeMap.put(nameHolder, attribute);
    }

    RootBeanDefinition attributeSourceDefinition =
        new RootBeanDefinition(NameMatchTransactionAttributeSource.class);
    attributeSourceDefinition.setSource(parserContext.extractSource(attrEle));
    attributeSourceDefinition.getPropertyValues().add("nameMap", transactionAttributeMap);
    return attributeSourceDefinition;
  }
 /**
  * Resolve the target type in the given TypedStringValue.
  *
  * @param value the TypedStringValue to resolve
  * @return the resolved target type (or {@code null} if none specified)
  * @throws ClassNotFoundException if the specified type cannot be resolved
  * @see TypedStringValue#resolveTargetType
  */
 protected Class<?> resolveTargetType(TypedStringValue value) throws ClassNotFoundException {
   if (value.hasTargetType()) {
     return value.getTargetType();
   }
   return value.resolveTargetType(this.beanFactory.getBeanClassLoader());
 }