001/* 002 * Copyright 2002-2016 the original author or authors. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * https://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017package org.springframework.beans; 018 019import java.io.Serializable; 020 021import org.springframework.util.Assert; 022import org.springframework.util.ObjectUtils; 023 024/** 025 * Object to hold information and value for an individual bean property. 026 * Using an object here, rather than just storing all properties in 027 * a map keyed by property name, allows for more flexibility, and the 028 * ability to handle indexed properties etc in an optimized way. 029 * 030 * <p>Note that the value doesn't need to be the final required type: 031 * A {@link BeanWrapper} implementation should handle any necessary conversion, 032 * as this object doesn't know anything about the objects it will be applied to. 033 * 034 * @author Rod Johnson 035 * @author Rob Harrop 036 * @author Juergen Hoeller 037 * @since 13 May 2001 038 * @see PropertyValues 039 * @see BeanWrapper 040 */ 041@SuppressWarnings("serial") 042public class PropertyValue extends BeanMetadataAttributeAccessor implements Serializable { 043 044 private final String name; 045 046 private final Object value; 047 048 private boolean optional = false; 049 050 private boolean converted = false; 051 052 private Object convertedValue; 053 054 /** Package-visible field that indicates whether conversion is necessary */ 055 volatile Boolean conversionNecessary; 056 057 /** Package-visible field for caching the resolved property path tokens */ 058 transient volatile Object resolvedTokens; 059 060 061 /** 062 * Create a new PropertyValue instance. 063 * @param name the name of the property (never {@code null}) 064 * @param value the value of the property (possibly before type conversion) 065 */ 066 public PropertyValue(String name, Object value) { 067 this.name = name; 068 this.value = value; 069 } 070 071 /** 072 * Copy constructor. 073 * @param original the PropertyValue to copy (never {@code null}) 074 */ 075 public PropertyValue(PropertyValue original) { 076 Assert.notNull(original, "Original must not be null"); 077 this.name = original.getName(); 078 this.value = original.getValue(); 079 this.optional = original.isOptional(); 080 this.converted = original.converted; 081 this.convertedValue = original.convertedValue; 082 this.conversionNecessary = original.conversionNecessary; 083 this.resolvedTokens = original.resolvedTokens; 084 setSource(original.getSource()); 085 copyAttributesFrom(original); 086 } 087 088 /** 089 * Constructor that exposes a new value for an original value holder. 090 * The original holder will be exposed as source of the new holder. 091 * @param original the PropertyValue to link to (never {@code null}) 092 * @param newValue the new value to apply 093 */ 094 public PropertyValue(PropertyValue original, Object newValue) { 095 Assert.notNull(original, "Original must not be null"); 096 this.name = original.getName(); 097 this.value = newValue; 098 this.optional = original.isOptional(); 099 this.conversionNecessary = original.conversionNecessary; 100 this.resolvedTokens = original.resolvedTokens; 101 setSource(original); 102 copyAttributesFrom(original); 103 } 104 105 106 /** 107 * Return the name of the property. 108 */ 109 public String getName() { 110 return this.name; 111 } 112 113 /** 114 * Return the value of the property. 115 * <p>Note that type conversion will <i>not</i> have occurred here. 116 * It is the responsibility of the BeanWrapper implementation to 117 * perform type conversion. 118 */ 119 public Object getValue() { 120 return this.value; 121 } 122 123 /** 124 * Return the original PropertyValue instance for this value holder. 125 * @return the original PropertyValue (either a source of this 126 * value holder or this value holder itself). 127 */ 128 public PropertyValue getOriginalPropertyValue() { 129 PropertyValue original = this; 130 Object source = getSource(); 131 while (source instanceof PropertyValue && source != original) { 132 original = (PropertyValue) source; 133 source = original.getSource(); 134 } 135 return original; 136 } 137 138 /** 139 * Set whether this is an optional value, that is, to be ignored 140 * when no corresponding property exists on the target class. 141 * @since 3.0 142 */ 143 public void setOptional(boolean optional) { 144 this.optional = optional; 145 } 146 147 /** 148 * Return whether this is an optional value, that is, to be ignored 149 * when no corresponding property exists on the target class. 150 * @since 3.0 151 */ 152 public boolean isOptional() { 153 return this.optional; 154 } 155 156 /** 157 * Return whether this holder contains a converted value already ({@code true}), 158 * or whether the value still needs to be converted ({@code false}). 159 */ 160 public synchronized boolean isConverted() { 161 return this.converted; 162 } 163 164 /** 165 * Set the converted value of the constructor argument, 166 * after processed type conversion. 167 */ 168 public synchronized void setConvertedValue(Object value) { 169 this.converted = true; 170 this.convertedValue = value; 171 } 172 173 /** 174 * Return the converted value of the constructor argument, 175 * after processed type conversion. 176 */ 177 public synchronized Object getConvertedValue() { 178 return this.convertedValue; 179 } 180 181 182 @Override 183 public boolean equals(Object other) { 184 if (this == other) { 185 return true; 186 } 187 if (!(other instanceof PropertyValue)) { 188 return false; 189 } 190 PropertyValue otherPv = (PropertyValue) other; 191 return (this.name.equals(otherPv.name) && 192 ObjectUtils.nullSafeEquals(this.value, otherPv.value) && 193 ObjectUtils.nullSafeEquals(getSource(), otherPv.getSource())); 194 } 195 196 @Override 197 public int hashCode() { 198 return this.name.hashCode() * 29 + ObjectUtils.nullSafeHashCode(this.value); 199 } 200 201 @Override 202 public String toString() { 203 return "bean property '" + this.name + "'"; 204 } 205 206}