⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 beanwrapperimpl.java

📁 Java/J2EE application framework based on [Expert One-on-One J2EE Design and Development] by Rod John
💻 JAVA
📖 第 1 页 / 共 3 页
字号:
				case PROPERTY_KEY_PREFIX_CHAR:
				case PROPERTY_KEY_SUFFIX_CHAR:
					inKey = !inKey;
					break;
				case NESTED_PROPERTY_SEPARATOR_CHAR:
					if (!inKey) {
						return i;
					}
			}
			if (last) i--; else i++;
		}
		return -1;
	}

	/**
	 * Get the last component of the path. Also works if not nested.
	 * @param bw BeanWrapper to work on
	 * @param nestedPath property path we know is nested
	 * @return last component of the path (the property on the target bean)
	 */
	private String getFinalPath(BeanWrapper bw, String nestedPath) {
		if (bw == this) {
			return nestedPath;
		}
		return nestedPath.substring(getNestedPropertySeparatorIndex(nestedPath, true) + 1);
	}

	/**
	 * Recursively navigate to return a BeanWrapper for the nested property path.
	 * @param propertyPath property property path, which may be nested
	 * @return a BeanWrapper for the target bean
	 */
	protected BeanWrapperImpl getBeanWrapperForPropertyPath(String propertyPath) throws BeansException {
		int pos = getNestedPropertySeparatorIndex(propertyPath, false);
		// handle nested properties recursively
		if (pos > -1) {
			String nestedProperty = propertyPath.substring(0, pos);
			String nestedPath = propertyPath.substring(pos + 1);
			BeanWrapperImpl nestedBw = getNestedBeanWrapper(nestedProperty);
			return nestedBw.getBeanWrapperForPropertyPath(nestedPath);
		}
		else {
			return this;
		}
	}

	/**
	 * Retrieve a BeanWrapper for the given nested property.
	 * Create a new one if not found in the cache.
	 * <p>Note: Caching nested BeanWrappers is necessary now,
	 * to keep registered custom editors for nested properties.
	 * @param nestedProperty property to create the BeanWrapper for
	 * @return the BeanWrapper instance, either cached or newly created
	 */
	private BeanWrapperImpl getNestedBeanWrapper(String nestedProperty) throws BeansException {
		if (this.nestedBeanWrappers == null) {
			this.nestedBeanWrappers = new HashMap();
		}
		// get value of bean property
		String[] tokens = getPropertyNameTokens(nestedProperty);
		Object propertyValue = getPropertyValue(tokens[0], tokens[1], tokens[2]);
		String canonicalName = tokens[0];
		String propertyName = tokens[1];
		if (propertyValue == null) {
			throw new NullValueInNestedPathException(getWrappedClass(), this.nestedPath + canonicalName);
		}

		// lookup cached sub-BeanWrapper, create new one if not found
		BeanWrapperImpl nestedBw = (BeanWrapperImpl) this.nestedBeanWrappers.get(canonicalName);
		if (nestedBw == null || nestedBw.getWrappedInstance() != propertyValue) {
			if (logger.isDebugEnabled()) {
				logger.debug("Creating new nested BeanWrapper for property '" + canonicalName + "'");
			}
			nestedBw = new BeanWrapperImpl(
					propertyValue, this.nestedPath + canonicalName + NESTED_PROPERTY_SEPARATOR, this);
			// inherit all type-specific PropertyEditors
			if (this.customEditors != null) {
				for (Iterator it = this.customEditors.entrySet().iterator(); it.hasNext();) {
					Map.Entry entry = (Map.Entry) it.next();
					if (entry.getKey() instanceof Class) {
						Class requiredType = (Class) entry.getKey();
						PropertyEditor editor = (PropertyEditor) entry.getValue();
						nestedBw.registerCustomEditor(requiredType, editor);
					}
					else if (entry.getKey() instanceof String) {
						String editorPath = (String) entry.getKey();
						int pos = getNestedPropertySeparatorIndex(editorPath, false);
						if (pos != -1) {
							String editorNestedProperty = editorPath.substring(0, pos);
							String editorNestedPath = editorPath.substring(pos + 1);
							if (editorNestedProperty.equals(canonicalName) || editorNestedProperty.equals(propertyName)) {
								CustomEditorHolder editorHolder = (CustomEditorHolder) entry.getValue();
								nestedBw.registerCustomEditor(
										editorHolder.getRegisteredType(), editorNestedPath, editorHolder.getPropertyEditor());
							}
						}
					}
				}
			}
			this.nestedBeanWrappers.put(canonicalName, nestedBw);
		}
		else {
			if (logger.isDebugEnabled()) {
				logger.debug("Using cached nested BeanWrapper for property '" + canonicalName + "'");
			}
		}
		return nestedBw;
	}

	private String[] getPropertyNameTokens(String propertyName) {
		String actualName = propertyName;
		String key = null;
		int keyStart = propertyName.indexOf(PROPERTY_KEY_PREFIX);
		if (keyStart != -1 && propertyName.endsWith(PROPERTY_KEY_SUFFIX)) {
			actualName = propertyName.substring(0, keyStart);
			key = propertyName.substring(keyStart + 1, propertyName.length() - 1);
			if (key.startsWith("'") && key.endsWith("'")) {
				key = key.substring(1, key.length() - 1);
			}
			else if (key.startsWith("\"") && key.endsWith("\"")) {
				key = key.substring(1, key.length() - 1);
			}
		}
		String canonicalName = actualName;
		if (key != null) {
			canonicalName += PROPERTY_KEY_PREFIX + key + PROPERTY_KEY_SUFFIX;
		}
		return new String[] {canonicalName, actualName, key};
	}


	public Object getPropertyValue(String propertyName) throws BeansException {
		BeanWrapperImpl nestedBw = getBeanWrapperForPropertyPath(propertyName);
		String[] tokens = getPropertyNameTokens(getFinalPath(nestedBw, propertyName));
		return nestedBw.getPropertyValue(tokens[0], tokens[1], tokens[2]);
	}

	protected Object getPropertyValue(String propertyName, String actualName, String key) throws BeansException {
		PropertyDescriptor pd = getPropertyDescriptorInternal(actualName);
		if (pd == null || pd.getReadMethod() == null) {
			throw new NotReadablePropertyException(getWrappedClass(), this.nestedPath + propertyName);
		}
		if (logger.isDebugEnabled())
			logger.debug("About to invoke read method [" + pd.getReadMethod() + "] on object of class [" +
					this.object.getClass().getName() + "]");
		try {
			Object value = pd.getReadMethod().invoke(this.object, null);
			if (key != null) {
				if (value == null) {
					throw new NullValueInNestedPathException(
							getWrappedClass(), this.nestedPath + propertyName,
							"Cannot access indexed value of property referenced in indexed " +
							"property path '" + propertyName + "': returned null");
				}
				else if (value.getClass().isArray()) {
					return Array.get(value, Integer.parseInt(key));
				}
				else if (value instanceof List) {
					List list = (List) value;
					return list.get(Integer.parseInt(key));
				}
				else if (value instanceof Set) {
					// apply index to Iterator in case of a Set
					Set set = (Set) value;
					int index = Integer.parseInt(key);
					Iterator it = set.iterator();
					for (int i = 0; it.hasNext(); i++) {
						Object elem = it.next();
						if (i == index) {
							return elem;
						}
					}
					throw new InvalidPropertyException(
							getWrappedClass(), this.nestedPath + propertyName,
							"Cannot get element with index " + index + " from Set of size " +
							set.size() + ", accessed using property path '" + propertyName + "'");
				}
				else if (value instanceof Map) {
					Map map = (Map) value;
					return map.get(key);
				}
				else {
					throw new InvalidPropertyException(
							getWrappedClass(), this.nestedPath + propertyName,
							"Property referenced in indexed property path '" + propertyName +
							"' is neither an array nor a List nor a Map; returned value was [" + value + "]");
				}
			}
			else {
				return value;
			}
		}
		catch (InvocationTargetException ex) {
			throw new InvalidPropertyException(
					getWrappedClass(), this.nestedPath + propertyName,
					"Getter for property '" + actualName + "' threw exception", ex);
		}
		catch (IllegalAccessException ex) {
			throw new InvalidPropertyException(
					getWrappedClass(), this.nestedPath + propertyName,
					"Illegal attempt to get property '" + actualName + "' threw exception", ex);
		}
		catch (IndexOutOfBoundsException ex) {
			throw new InvalidPropertyException(
					getWrappedClass(), this.nestedPath + propertyName,
					"Index of out of bounds in property path '" + propertyName + "'", ex);
		}
		catch (NumberFormatException ex) {
			throw new InvalidPropertyException(
					getWrappedClass(), this.nestedPath + propertyName,
					"Invalid index in property path '" + propertyName + "'", ex);
		}
	}

	public void setPropertyValue(String propertyName, Object value) throws BeansException {
		BeanWrapperImpl nestedBw = null;
		try {
			nestedBw = getBeanWrapperForPropertyPath(propertyName);
		}
		catch (NotReadablePropertyException ex) {
			throw new NotWritablePropertyException(
					getWrappedClass(), this.nestedPath + propertyName,
					"Nested property in path '" + propertyName + "' does not exist", ex);
		}
		String[] tokens = getPropertyNameTokens(getFinalPath(nestedBw, propertyName));
		nestedBw.setPropertyValue(tokens[0], tokens[1], tokens[2], value);
	}

	protected void setPropertyValue(String propertyName, String actualName, String key, Object value)
			throws BeansException {

		if (key != null) {
			Object propValue = null;
			try {
				propValue = getPropertyValue(actualName);
			}
			catch (NotReadablePropertyException ex) {
				throw new NotWritablePropertyException(
						getWrappedClass(), this.nestedPath + propertyName,
						"Cannot access indexed value in property referenced " +
						"in indexed property path '" + propertyName + "'", ex);
			}
			if (propValue == null) {
				throw new NullValueInNestedPathException(
						getWrappedClass(), this.nestedPath + propertyName,
						"Cannot access indexed value in property referenced " +
						"in indexed property path '" + propertyName + "': returned null");
			}
			else if (propValue.getClass().isArray()) {
				Class requiredType = propValue.getClass().getComponentType();
				Object newValue = doTypeConversionIfNecessary(propertyName, propertyName, null, value, requiredType);
				try {
					Array.set(propValue, Integer.parseInt(key), newValue);
				}
				catch (IllegalArgumentException ex) {
					PropertyChangeEvent pce = new PropertyChangeEvent(
							this.object, this.nestedPath + propertyName, null, newValue);
					throw new TypeMismatchException(pce, requiredType, ex);
				}
				catch (IndexOutOfBoundsException ex) {
					throw new InvalidPropertyException(
							getWrappedClass(), this.nestedPath + propertyName,
							"Invalid array index in property path '" + propertyName + "'", ex);
				}
			}
			else if (propValue instanceof List) {
				Object newValue = doTypeConversionIfNecessary(propertyName, propertyName, null, value, null);
				List list = (List) propValue;
				int index = Integer.parseInt(key);
				if (index < list.size()) {
					list.set(index, newValue);
				}
				else if (index >= list.size()) {
					for (int i = list.size(); i < index; i++) {
						try {
							list.add(null);
						}
						catch (NullPointerException ex) {
							throw new InvalidPropertyException(
									getWrappedClass(), this.nestedPath + propertyName,
									"Cannot set element with index " + index + " in List of size " +
									list.size() + ", accessed using property path '" + propertyName +
									"': List does not support filling up gaps with null elements");
						}
					}
					list.add(newValue);
				}
			}
			else if (propValue instanceof Map) {
				Object newValue = doTypeConversionIfNecessary(propertyName, propertyName, null, value, null);
				Map map = (Map) propValue;
				map.put(key, newValue);
			}
			else {
				throw new InvalidPropertyException(
						getWrappedClass(), this.nestedPath + propertyName,
						"Property referenced in indexed property path '" + propertyName +
						"' is neither an array nor a List nor a Map; returned value was [" + value + "]");
			}
		}

		else {
			if (!isWritableProperty(propertyName)) {
				throw new NotWritablePropertyException(getWrappedClass(), this.nestedPath + propertyName);
			}
			PropertyDescriptor pd = getPropertyDescriptor(propertyName);
			Method writeMethod = pd.getWriteMethod();
			Object newValue = null;
			try {
				// old value may still be null
				newValue = doTypeConversionIfNecessary(propertyName, propertyName, null, value, pd.getPropertyType());

				if (pd.getPropertyType().isPrimitive() && (newValue == null || "".equals(newValue))) {
					throw new IllegalArgumentException("Invalid value [" + value + "] for property '" +
								pd.getName() + "' of primitive type [" + pd.getPropertyType() + "]");
				}

				if (logger.isDebugEnabled()) {
					logger.debug("About to invoke write method [" + writeMethod + "] on object of class [" +
							this.object.getClass().getName() + "]");
				}
				writeMethod.invoke(this.object, new Object[] { newValue });
				if (logger.isDebugEnabled()) {
					String msg = "Invoked write method [" + writeMethod + "] with value ";
					// only cause toString invocation of new value in case of simple property
					if (newValue == null || BeanUtils.isSimpleProperty(pd.getPropertyType())) {
						logger.debug(msg + PROPERTY_KEY_PREFIX + newValue + PROPERTY_KEY_SUFFIX);
					}
					else {
						logger.debug(msg + "of type [" + pd.getPropertyType().getName() + "]");
					}
				}
			}
			catch (InvocationTargetException ex) {
				PropertyChangeEvent propertyChangeEvent =
						new PropertyChangeEvent(this.object, this.nestedPath + propertyName, null, value);
				if (ex.getTargetException() instanceof ClassCastException) {
					throw new TypeMismatchException(propertyChangeEvent, pd.getPropertyType(), ex.getTargetException());
				}
				else {
					throw new MethodInvocationException(propertyChangeEvent, ex.getTargetException());
				}
			}
			catch (IllegalArgumentException ex) {
				PropertyChangeEvent pce =
						new PropertyChangeEvent(this.object, this.nestedPath + propertyName, null, value);
				throw new TypeMismatchException(pce, pd.getPropertyType(), ex);
			}
			catch (IllegalAccessException ex) {
				PropertyChangeEvent pce =
						new PropertyChangeEvent(this.object, this.nestedPath + propertyName, null, value);
				throw new MethodInvocationException(pce, ex);
			}
		}
	}

	public void setPropertyValue(PropertyValue pv) throws BeansException {
		setPropertyValue(pv.getName(), pv.getValue());
	}

	/**
	 * Bulk update from a Map.

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -