1 package com.fasterxml.jackson.databind; 2 3 import com.fasterxml.jackson.core.SerializableString; 4 import com.fasterxml.jackson.core.io.SerializedString; 5 import com.fasterxml.jackson.core.util.InternCache; 6 import com.fasterxml.jackson.databind.cfg.MapperConfig; 7 import com.fasterxml.jackson.databind.util.ClassUtil; 8 9 /** 10 * Simple value class used for containing names of properties as defined 11 * by annotations (and possibly other configuration sources). 12 * 13 * @since 2.1 14 */ 15 public class PropertyName 16 implements java.io.Serializable 17 { 18 private static final long serialVersionUID = 1L; // 2.5 19 20 private final static String _USE_DEFAULT = ""; 21 private final static String _NO_NAME = ""; 22 23 /** 24 * Special placeholder value that indicates that name to use should be 25 * based on the standard heuristics. This can be different from returning 26 * null, as null means "no information available, whereas this value 27 * indicates explicit defaulting. 28 */ 29 public final static PropertyName USE_DEFAULT = new PropertyName(_USE_DEFAULT, null); 30 31 /** 32 * Special placeholder value that indicates that there is no name associated. 33 * Exact semantics to use (if any) depend on actual annotation in use, but 34 * commonly this value disables behavior for which name would be needed. 35 */ 36 public final static PropertyName NO_NAME = new PropertyName(new String(_NO_NAME), null); 37 38 /** 39 * Basic name of the property. 40 */ 41 protected final String _simpleName; 42 43 /** 44 * Additional namespace, for formats that have such concept (JSON 45 * does not, XML does, for example). 46 */ 47 protected final String _namespace; 48 49 /** 50 * Lazily-constructed efficient representation of the simple name. 51 *<p> 52 * NOTE: not defined as volatile to avoid performance problem with 53 * concurrent access in multi-core environments; due to statelessness 54 * of {@link SerializedString} at most leads to multiple instantiations. 55 * 56 * @since 2.4 57 */ 58 protected SerializableString _encodedSimple; 59 PropertyName(String simpleName)60 public PropertyName(String simpleName) { 61 this(simpleName, null); 62 } 63 PropertyName(String simpleName, String namespace)64 public PropertyName(String simpleName, String namespace) 65 { 66 _simpleName = ClassUtil.nonNullString(simpleName); 67 _namespace = namespace; 68 } 69 70 // To support JDK serialization, recovery of Singleton instance readResolve()71 protected Object readResolve() { 72 if (_namespace == null) { 73 if (_simpleName == null || _USE_DEFAULT.equals(_simpleName)) { 74 return USE_DEFAULT; 75 } 76 // 30-Oct-2016, tatu: I don't see how this could ever occur... 77 // or how to distinguish USE_DEFAULT/NO_NAME from serialized 78 /* 79 if (_simpleName.equals(_NO_NAME)) { 80 return NO_NAME; 81 } 82 */ 83 } 84 return this; 85 } 86 87 /** 88 * @since 2.6 89 */ construct(String simpleName)90 public static PropertyName construct(String simpleName) 91 { 92 if (simpleName == null || simpleName.length() == 0) { 93 return USE_DEFAULT; 94 } 95 return new PropertyName(InternCache.instance.intern(simpleName), null); 96 } 97 construct(String simpleName, String ns)98 public static PropertyName construct(String simpleName, String ns) 99 { 100 if (simpleName == null) { 101 simpleName = ""; 102 } 103 if (ns == null && simpleName.length() == 0) { 104 return USE_DEFAULT; 105 } 106 return new PropertyName(InternCache.instance.intern(simpleName), ns); 107 } 108 internSimpleName()109 public PropertyName internSimpleName() 110 { 111 if (_simpleName.length() == 0) { // empty String is canonical already 112 return this; 113 } 114 String interned = InternCache.instance.intern(_simpleName); 115 if (interned == _simpleName) { // was already interned 116 return this; 117 } 118 return new PropertyName(interned, _namespace); 119 } 120 121 /** 122 * Fluent factory method for constructing an instance with different 123 * simple name. 124 */ withSimpleName(String simpleName)125 public PropertyName withSimpleName(String simpleName) 126 { 127 if (simpleName == null) { 128 simpleName = ""; 129 } 130 if (simpleName.equals(_simpleName)) { 131 return this; 132 } 133 return new PropertyName(simpleName, _namespace); 134 } 135 136 /** 137 * Fluent factory method for constructing an instance with different 138 * namespace. 139 */ withNamespace(String ns)140 public PropertyName withNamespace(String ns) { 141 if (ns == null) { 142 if (_namespace == null) { 143 return this; 144 } 145 } else if (ns.equals(_namespace)) { 146 return this; 147 } 148 return new PropertyName(_simpleName, ns); 149 } 150 151 /* 152 /********************************************************** 153 /* Accessors 154 /********************************************************** 155 */ 156 getSimpleName()157 public String getSimpleName() { 158 return _simpleName; 159 } 160 161 /** 162 * Accessor that may be used to get lazily-constructed efficient 163 * representation of the simple name. 164 * 165 * @since 2.4 166 */ simpleAsEncoded(MapperConfig<?> config)167 public SerializableString simpleAsEncoded(MapperConfig<?> config) { 168 SerializableString sstr = _encodedSimple; 169 if (sstr == null) { 170 if (config == null) { 171 sstr = new SerializedString(_simpleName); 172 } else { 173 sstr = config.compileString(_simpleName); 174 } 175 _encodedSimple = sstr; 176 } 177 return sstr; 178 } 179 getNamespace()180 public String getNamespace() { 181 return _namespace; 182 } 183 hasSimpleName()184 public boolean hasSimpleName() { 185 return _simpleName.length() > 0; 186 } 187 188 /** 189 * @since 2.3 190 */ hasSimpleName(String str)191 public boolean hasSimpleName(String str) { 192 // _simpleName never null so... 193 return _simpleName.equals(str); 194 } 195 hasNamespace()196 public boolean hasNamespace() { 197 return _namespace != null; 198 } 199 200 /** 201 * Method that is basically equivalent of: 202 *<pre> 203 * !hasSimpleName() << !hasNamespace(); 204 *</pre> 205 * 206 * @since 2.4 207 */ isEmpty()208 public boolean isEmpty() { 209 return (_namespace == null) && (_simpleName.isEmpty()); 210 } 211 212 /* 213 /********************************************************** 214 /* Std method overrides 215 /********************************************************** 216 */ 217 218 @Override equals(Object o)219 public boolean equals(Object o) 220 { 221 if (o == this) return true; 222 if (o == null) return false; 223 /* 13-Nov-2012, tatu: by default, require strict type equality. 224 * Re-evaluate if this becomes an issue. 225 */ 226 if (o.getClass() != getClass()) return false; 227 // 13-Nov-2012, tatu: Should we have specific rules on matching USE_DEFAULT? 228 // (like, it only ever matching exact instance) 229 // If we did, would need to check symmetrically; that is, if either 'this' 230 // or 'o' was USE_DEFAULT, both would have to be. 231 PropertyName other = (PropertyName) o; 232 if (_simpleName == null) { 233 if (other._simpleName != null) return false; 234 } else if (!_simpleName.equals(other._simpleName)) { 235 return false; 236 } 237 if (_namespace == null) { 238 return (null == other._namespace); 239 } 240 return _namespace.equals(other._namespace); 241 } 242 243 @Override hashCode()244 public int hashCode() { 245 if (_namespace == null) { 246 return _simpleName.hashCode(); 247 } 248 return _namespace.hashCode() ^ _simpleName.hashCode(); 249 } 250 251 @Override toString()252 public String toString() { 253 if (_namespace == null) { 254 return _simpleName; 255 } 256 return "{"+_namespace + "}" + _simpleName; 257 } 258 } 259