Exemplo n.º 1
0
  @SuppressWarnings("unchecked")
  private void setPropertyValue(PropertyTokenHolder tokens, PropertyValue pv)
      throws BeansException {
    String propertyName = tokens.canonicalName;
    String actualName = tokens.actualName;

    if (tokens.keys != null) {
      // Apply indexes and map keys: fetch value for all keys but the last one.
      PropertyTokenHolder getterTokens = new PropertyTokenHolder();
      getterTokens.canonicalName = tokens.canonicalName;
      getterTokens.actualName = tokens.actualName;
      getterTokens.keys = new String[tokens.keys.length - 1];
      System.arraycopy(tokens.keys, 0, getterTokens.keys, 0, tokens.keys.length - 1);
      Object propValue;
      try {
        propValue = getPropertyValue(getterTokens);
      } catch (NotReadablePropertyException ex) {
        throw new NotWritablePropertyException(
            getRootClass(),
            this.nestedPath + propertyName,
            "Cannot access indexed value in property referenced "
                + "in indexed property path '"
                + propertyName
                + "'",
            ex);
      }
      // Set value for last key.
      String key = tokens.keys[tokens.keys.length - 1];
      if (propValue == null) {
        // null map value case
        if (this.autoGrowNestedPaths) {
          // TODO: cleanup, this is pretty hacky
          int lastKeyIndex = tokens.canonicalName.lastIndexOf('[');
          getterTokens.canonicalName = tokens.canonicalName.substring(0, lastKeyIndex);
          propValue = setDefaultValue(getterTokens);
        } else {
          throw new NullValueInNestedPathException(
              getRootClass(),
              this.nestedPath + propertyName,
              "Cannot access indexed value in property referenced "
                  + "in indexed property path '"
                  + propertyName
                  + "': returned null");
        }
      }
      if (propValue.getClass().isArray()) {
        PropertyDescriptor pd = getCachedIntrospectionResults().getPropertyDescriptor(actualName);
        Class requiredType = propValue.getClass().getComponentType();
        int arrayIndex = Integer.parseInt(key);
        Object oldValue = null;
        try {
          if (isExtractOldValueForEditor() && arrayIndex < Array.getLength(propValue)) {
            oldValue = Array.get(propValue, arrayIndex);
          }
          Object convertedValue =
              convertIfNecessary(
                  propertyName,
                  oldValue,
                  pv.getValue(),
                  requiredType,
                  TypeDescriptor.nested(property(pd), tokens.keys.length));
          Array.set(propValue, arrayIndex, convertedValue);
        } catch (IndexOutOfBoundsException ex) {
          throw new InvalidPropertyException(
              getRootClass(),
              this.nestedPath + propertyName,
              "Invalid array index in property path '" + propertyName + "'",
              ex);
        }
      } else if (propValue instanceof List) {
        PropertyDescriptor pd = getCachedIntrospectionResults().getPropertyDescriptor(actualName);
        Class requiredType =
            GenericCollectionTypeResolver.getCollectionReturnType(
                pd.getReadMethod(), tokens.keys.length);
        List list = (List) propValue;
        int index = Integer.parseInt(key);
        int size = list.size();
        Object oldValue = null;
        if (isExtractOldValueForEditor() && index < size) {
          oldValue = list.get(index);
        }
        Object convertedValue =
            convertIfNecessary(
                propertyName,
                oldValue,
                pv.getValue(),
                requiredType,
                TypeDescriptor.nested(property(pd), tokens.keys.length));
        if (index >= size && index < this.autoGrowCollectionLimit) {
          for (int i = size; i < index; i++) {
            try {
              list.add(null);
            } catch (NullPointerException ex) {
              throw new InvalidPropertyException(
                  getRootClass(),
                  this.nestedPath + propertyName,
                  "Cannot set element with index "
                      + index
                      + " in List of size "
                      + size
                      + ", accessed using property path '"
                      + propertyName
                      + "': List does not support filling up gaps with null elements");
            }
          }
          list.add(convertedValue);
        } else {
          try {
            list.set(index, convertedValue);
          } catch (IndexOutOfBoundsException ex) {
            throw new InvalidPropertyException(
                getRootClass(),
                this.nestedPath + propertyName,
                "Invalid list index in property path '" + propertyName + "'",
                ex);
          }
        }
      } else if (propValue instanceof Map) {
        PropertyDescriptor pd = getCachedIntrospectionResults().getPropertyDescriptor(actualName);
        Class mapKeyType =
            GenericCollectionTypeResolver.getMapKeyReturnType(
                pd.getReadMethod(), tokens.keys.length);
        Class mapValueType =
            GenericCollectionTypeResolver.getMapValueReturnType(
                pd.getReadMethod(), tokens.keys.length);
        Map map = (Map) propValue;
        // IMPORTANT: Do not pass full property name in here - property editors
        // must not kick in for map keys but rather only for map values.
        TypeDescriptor typeDescriptor =
            mapKeyType != null
                ? TypeDescriptor.valueOf(mapKeyType)
                : TypeDescriptor.valueOf(Object.class);
        Object convertedMapKey = convertIfNecessary(null, null, key, mapKeyType, typeDescriptor);
        Object oldValue = null;
        if (isExtractOldValueForEditor()) {
          oldValue = map.get(convertedMapKey);
        }
        // Pass full property name and old value in here, since we want full
        // conversion ability for map values.
        Object convertedMapValue =
            convertIfNecessary(
                propertyName,
                oldValue,
                pv.getValue(),
                mapValueType,
                TypeDescriptor.nested(property(pd), tokens.keys.length));
        map.put(convertedMapKey, convertedMapValue);
      } else {
        throw new InvalidPropertyException(
            getRootClass(),
            this.nestedPath + propertyName,
            "Property referenced in indexed property path '"
                + propertyName
                + "' is neither an array nor a List nor a Map; returned value was ["
                + pv.getValue()
                + "]");
      }
    } else {
      PropertyDescriptor pd = pv.resolvedDescriptor;
      if (pd == null || !pd.getWriteMethod().getDeclaringClass().isInstance(this.object)) {
        pd = getCachedIntrospectionResults().getPropertyDescriptor(actualName);
        if (pd == null || pd.getWriteMethod() == null) {
          if (pv.isOptional()) {
            logger.debug(
                "Ignoring optional value for property '"
                    + actualName
                    + "' - property not found on bean class ["
                    + getRootClass().getName()
                    + "]");
            return;
          } else {
            PropertyMatches matches = PropertyMatches.forProperty(propertyName, getRootClass());
            throw new NotWritablePropertyException(
                getRootClass(),
                this.nestedPath + propertyName,
                matches.buildErrorMessage(),
                matches.getPossibleMatches());
          }
        }
        pv.getOriginalPropertyValue().resolvedDescriptor = pd;
      }

      Object oldValue = null;
      try {
        Object originalValue = pv.getValue();
        Object valueToApply = originalValue;
        if (!Boolean.FALSE.equals(pv.conversionNecessary)) {
          if (pv.isConverted()) {
            valueToApply = pv.getConvertedValue();
          } else {
            if (isExtractOldValueForEditor() && pd.getReadMethod() != null) {
              final Method readMethod = pd.getReadMethod();
              if (!Modifier.isPublic(readMethod.getDeclaringClass().getModifiers())
                  && !readMethod.isAccessible()) {
                if (System.getSecurityManager() != null) {
                  AccessController.doPrivileged(
                      new PrivilegedAction<Object>() {
                        public Object run() {
                          readMethod.setAccessible(true);
                          return null;
                        }
                      });
                } else {
                  readMethod.setAccessible(true);
                }
              }
              try {
                if (System.getSecurityManager() != null) {
                  oldValue =
                      AccessController.doPrivileged(
                          new PrivilegedExceptionAction<Object>() {
                            public Object run() throws Exception {
                              return readMethod.invoke(object);
                            }
                          },
                          acc);
                } else {
                  oldValue = readMethod.invoke(object);
                }
              } catch (Exception ex) {
                if (ex instanceof PrivilegedActionException) {
                  ex = ((PrivilegedActionException) ex).getException();
                }
                if (logger.isDebugEnabled()) {
                  logger.debug(
                      "Could not read previous value of property '"
                          + this.nestedPath
                          + propertyName
                          + "'",
                      ex);
                }
              }
            }
            valueToApply = convertForProperty(propertyName, oldValue, originalValue, pd);
          }
          pv.getOriginalPropertyValue().conversionNecessary = (valueToApply != originalValue);
        }
        final Method writeMethod =
            (pd instanceof GenericTypeAwarePropertyDescriptor
                ? ((GenericTypeAwarePropertyDescriptor) pd).getWriteMethodForActualAccess()
                : pd.getWriteMethod());
        if (!Modifier.isPublic(writeMethod.getDeclaringClass().getModifiers())
            && !writeMethod.isAccessible()) {
          if (System.getSecurityManager() != null) {
            AccessController.doPrivileged(
                new PrivilegedAction<Object>() {
                  public Object run() {
                    writeMethod.setAccessible(true);
                    return null;
                  }
                });
          } else {
            writeMethod.setAccessible(true);
          }
        }
        final Object value = valueToApply;
        if (System.getSecurityManager() != null) {
          try {
            AccessController.doPrivileged(
                new PrivilegedExceptionAction<Object>() {
                  public Object run() throws Exception {
                    writeMethod.invoke(object, value);
                    return null;
                  }
                },
                acc);
          } catch (PrivilegedActionException ex) {
            throw ex.getException();
          }
        } else {
          writeMethod.invoke(this.object, value);
        }
      } catch (TypeMismatchException ex) {
        throw ex;
      } catch (InvocationTargetException ex) {
        PropertyChangeEvent propertyChangeEvent =
            new PropertyChangeEvent(
                this.rootObject, this.nestedPath + propertyName, oldValue, pv.getValue());
        if (ex.getTargetException() instanceof ClassCastException) {
          throw new TypeMismatchException(
              propertyChangeEvent, pd.getPropertyType(), ex.getTargetException());
        } else {
          throw new MethodInvocationException(propertyChangeEvent, ex.getTargetException());
        }
      } catch (Exception ex) {
        PropertyChangeEvent pce =
            new PropertyChangeEvent(
                this.rootObject, this.nestedPath + propertyName, oldValue, pv.getValue());
        throw new MethodInvocationException(pce, ex);
      }
    }
  }