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
60 public PropertyName(String simpleName) {
61 this(simpleName, null);
62 }
63
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
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 */
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
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
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 */
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 */
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
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 */
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
180 public String getNamespace() {
181 return _namespace;
182 }
183
184 public boolean hasSimpleName() {
185 return _simpleName.length() > 0;
186 }
187
188 /**
189 * @since 2.3
190 */
191 public boolean hasSimpleName(String str) {
192 // _simpleName never null so...
193 return _simpleName.equals(str);
194 }
195
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 */
208 public boolean isEmpty() {
209 return (_namespace == null) && (_simpleName.isEmpty());
210 }
211
212 /*
213 /**********************************************************
214 /* Std method overrides
215 /**********************************************************
216 */
217
218 @Override
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
244 public int hashCode() {
245 if (_namespace == null) {
246 return _simpleName.hashCode();
247 }
248 return _namespace.hashCode() ^ _simpleName.hashCode();
249 }
250
251 @Override
252 public String toString() {
253 if (_namespace == null) {
254 return _simpleName;
255 }
256 return "{"+_namespace + "}" + _simpleName;
257 }
258 }
259