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() &lt;&lt; !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