001/* 002 * Copyright 2002-2013 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.factory.config; 018 019import org.springframework.beans.BeanMetadataElement; 020import org.springframework.util.Assert; 021import org.springframework.util.ClassUtils; 022import org.springframework.util.ObjectUtils; 023 024/** 025 * Holder for a typed String value. Can be added to bean definitions 026 * in order to explicitly specify a target type for a String value, 027 * for example for collection elements. 028 * 029 * <p>This holder will just store the String value and the target type. 030 * The actual conversion will be performed by the bean factory. 031 * 032 * @author Juergen Hoeller 033 * @since 1.2 034 * @see BeanDefinition#getPropertyValues 035 * @see org.springframework.beans.MutablePropertyValues#addPropertyValue 036 */ 037public class TypedStringValue implements BeanMetadataElement { 038 039 private String value; 040 041 private volatile Object targetType; 042 043 private Object source; 044 045 private String specifiedTypeName; 046 047 private volatile boolean dynamic; 048 049 050 /** 051 * Create a new {@link TypedStringValue} for the given String value. 052 * @param value the String value 053 */ 054 public TypedStringValue(String value) { 055 setValue(value); 056 } 057 058 /** 059 * Create a new {@link TypedStringValue} for the given String value 060 * and target type. 061 * @param value the String value 062 * @param targetType the type to convert to 063 */ 064 public TypedStringValue(String value, Class<?> targetType) { 065 setValue(value); 066 setTargetType(targetType); 067 } 068 069 /** 070 * Create a new {@link TypedStringValue} for the given String value 071 * and target type. 072 * @param value the String value 073 * @param targetTypeName the type to convert to 074 */ 075 public TypedStringValue(String value, String targetTypeName) { 076 setValue(value); 077 setTargetTypeName(targetTypeName); 078 } 079 080 081 /** 082 * Set the String value. 083 * <p>Only necessary for manipulating a registered value, 084 * for example in BeanFactoryPostProcessors. 085 * @see PropertyPlaceholderConfigurer 086 */ 087 public void setValue(String value) { 088 this.value = value; 089 } 090 091 /** 092 * Return the String value. 093 */ 094 public String getValue() { 095 return this.value; 096 } 097 098 /** 099 * Set the type to convert to. 100 * <p>Only necessary for manipulating a registered value, 101 * for example in BeanFactoryPostProcessors. 102 * @see PropertyPlaceholderConfigurer 103 */ 104 public void setTargetType(Class<?> targetType) { 105 Assert.notNull(targetType, "'targetType' must not be null"); 106 this.targetType = targetType; 107 } 108 109 /** 110 * Return the type to convert to. 111 */ 112 public Class<?> getTargetType() { 113 Object targetTypeValue = this.targetType; 114 if (!(targetTypeValue instanceof Class)) { 115 throw new IllegalStateException("Typed String value does not carry a resolved target type"); 116 } 117 return (Class<?>) targetTypeValue; 118 } 119 120 /** 121 * Specify the type to convert to. 122 */ 123 public void setTargetTypeName(String targetTypeName) { 124 Assert.notNull(targetTypeName, "'targetTypeName' must not be null"); 125 this.targetType = targetTypeName; 126 } 127 128 /** 129 * Return the type to convert to. 130 */ 131 public String getTargetTypeName() { 132 Object targetTypeValue = this.targetType; 133 if (targetTypeValue instanceof Class) { 134 return ((Class<?>) targetTypeValue).getName(); 135 } 136 else { 137 return (String) targetTypeValue; 138 } 139 } 140 141 /** 142 * Return whether this typed String value carries a target type . 143 */ 144 public boolean hasTargetType() { 145 return (this.targetType instanceof Class); 146 } 147 148 /** 149 * Determine the type to convert to, resolving it from a specified class name 150 * if necessary. Will also reload a specified Class from its name when called 151 * with the target type already resolved. 152 * @param classLoader the ClassLoader to use for resolving a (potential) class name 153 * @return the resolved type to convert to 154 * @throws ClassNotFoundException if the type cannot be resolved 155 */ 156 public Class<?> resolveTargetType(ClassLoader classLoader) throws ClassNotFoundException { 157 if (this.targetType == null) { 158 return null; 159 } 160 Class<?> resolvedClass = ClassUtils.forName(getTargetTypeName(), classLoader); 161 this.targetType = resolvedClass; 162 return resolvedClass; 163 } 164 165 166 /** 167 * Set the configuration source {@code Object} for this metadata element. 168 * <p>The exact type of the object will depend on the configuration mechanism used. 169 */ 170 public void setSource(Object source) { 171 this.source = source; 172 } 173 174 @Override 175 public Object getSource() { 176 return this.source; 177 } 178 179 /** 180 * Set the type name as actually specified for this particular value, if any. 181 */ 182 public void setSpecifiedTypeName(String specifiedTypeName) { 183 this.specifiedTypeName = specifiedTypeName; 184 } 185 186 /** 187 * Return the type name as actually specified for this particular value, if any. 188 */ 189 public String getSpecifiedTypeName() { 190 return this.specifiedTypeName; 191 } 192 193 /** 194 * Mark this value as dynamic, i.e. as containing an expression 195 * and hence not being subject to caching. 196 */ 197 public void setDynamic() { 198 this.dynamic = true; 199 } 200 201 /** 202 * Return whether this value has been marked as dynamic. 203 */ 204 public boolean isDynamic() { 205 return this.dynamic; 206 } 207 208 209 @Override 210 public boolean equals(Object other) { 211 if (this == other) { 212 return true; 213 } 214 if (!(other instanceof TypedStringValue)) { 215 return false; 216 } 217 TypedStringValue otherValue = (TypedStringValue) other; 218 return (ObjectUtils.nullSafeEquals(this.value, otherValue.value) && 219 ObjectUtils.nullSafeEquals(this.targetType, otherValue.targetType)); 220 } 221 222 @Override 223 public int hashCode() { 224 return ObjectUtils.nullSafeHashCode(this.value) * 29 + ObjectUtils.nullSafeHashCode(this.targetType); 225 } 226 227 @Override 228 public String toString() { 229 return "TypedStringValue: value [" + this.value + "], target type [" + this.targetType + "]"; 230 } 231 232}