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