1 package com.fasterxml.jackson.databind.cfg;
2
3 import java.util.*;
4
5 /**
6 * Helper class used for storing and accessing per-call attributes.
7 * Storage is two-layered: at higher precedence, we have actual per-call
8 * attributes; and at lower precedence, default attributes that may be
9 * defined for Object readers and writers.
10 *<p>
11 * Note that the way mutability is implemented differs between kinds
12 * of attributes, to account for thread-safety: per-call attributes
13 * are handled assuming that instances are never shared, whereas
14 * changes to per-reader/per-writer attributes are made assuming
15 * sharing, by creating new copies instead of modifying state.
16 * This allows sharing of default values without per-call copying, but
17 * requires two-level lookup on access.
18 *
19 * @since 2.3
20 */
21 public abstract class ContextAttributes
22 {
23 public static ContextAttributes getEmpty() {
24 return Impl.getEmpty();
25 }
26
27 /*
28 /**********************************************************
29 /* Per-reader/writer access
30 /**********************************************************
31 */
32
33 public abstract ContextAttributes withSharedAttribute(Object key, Object value);
34
35 public abstract ContextAttributes withSharedAttributes(Map<?,?> attributes);
36
37 public abstract ContextAttributes withoutSharedAttribute(Object key);
38
39 /*
40 /**********************************************************
41 /* Per-operation (serialize/deserialize) access
42 /**********************************************************
43 */
44
45 /**
46 * Accessor for value of specified attribute
47 */
48 public abstract Object getAttribute(Object key);
49
50 /**
51 * Mutator used during call (via context) to set value of "non-shared"
52 * part of attribute set.
53 */
54 public abstract ContextAttributes withPerCallAttribute(Object key, Object value);
55
56 /*
57 /**********************************************************
58 /* Default implementation
59 /**********************************************************
60 */
61
62 public static class Impl extends ContextAttributes
63 implements java.io.Serializable // just so ObjectReader/ObjectWriter can retain configs
64 {
65 private static final long serialVersionUID = 1L;
66
67 protected final static Impl EMPTY = new Impl(Collections.emptyMap());
68
69 protected final static Object NULL_SURROGATE = new Object();
70
71 /**
72 * Shared attributes that we cannot modify in-place.
73 */
74 protected final Map<?,?> _shared;
75
76 /**
77 * Per-call attributes that we can directly modify, since they are not
78 * shared between threads.
79 *<p>
80 * NOTE: typed as Object-to-Object, unlike {@link #_shared}, because
81 * we need to be able to modify contents, and wildcard type would
82 * complicate that access.
83 */
84 protected transient Map<Object,Object> _nonShared;
85
86 /*
87 /**********************************************************
88 /* Construction, factory methods
89 /**********************************************************
90 */
91
92 protected Impl(Map<?,?> shared) {
93 _shared = shared;
94 _nonShared = null;
95 }
96
97 protected Impl(Map<?,?> shared, Map<Object,Object> nonShared) {
98 _shared = shared;
99 _nonShared = nonShared;
100 }
101
102 public static ContextAttributes getEmpty() {
103 return EMPTY;
104 }
105
106 /*
107 /**********************************************************
108 /* Per-reader/writer mutant factories
109 /**********************************************************
110 */
111
112 @Override
113 public ContextAttributes withSharedAttribute(Object key, Object value)
114 {
115 Map<Object,Object> m;
116 // need to cover one special case, since EMPTY uses Immutable map:
117 if (this == EMPTY) {
118 m = new HashMap<Object,Object>(8);
119 } else {
120 m = _copy(_shared);
121 }
122 m.put(key, value);
123 return new Impl(m);
124 }
125
126 @Override
127 public ContextAttributes withSharedAttributes(Map<?,?> shared) {
128 return new Impl(shared);
129 }
130
131 @Override
132 public ContextAttributes withoutSharedAttribute(Object key)
133 {
134 // first couple of trivial optimizations
135 if (_shared.isEmpty()) {
136 return this;
137 }
138 if (_shared.containsKey(key)) {
139 if (_shared.size() == 1) {
140 return EMPTY;
141 }
142 } else { // if we didn't have it anyway, return as-is
143 return this;
144 }
145 // otherwise make copy, modify
146 Map<Object,Object> m = _copy(_shared);
147 m.remove(key);
148 return new Impl(m);
149 }
150
151 /*
152 /**********************************************************
153 /* Per-call access
154 /**********************************************************
155 */
156
157 @Override
158 public Object getAttribute(Object key)
159 {
160 if (_nonShared != null) {
161 Object ob = _nonShared.get(key);
162 if (ob != null) {
163 if (ob == NULL_SURROGATE) {
164 return null;
165 }
166 return ob;
167 }
168 }
169 return _shared.get(key);
170 }
171
172 @Override
173 public ContextAttributes withPerCallAttribute(Object key, Object value)
174 {
175 // First: null value may need masking
176 if (value == null) {
177 // need to mask nulls to ensure default values won't be showing
178 if (_shared.containsKey(key)) {
179 value = NULL_SURROGATE;
180 } else if ((_nonShared == null) || !_nonShared.containsKey(key)) {
181 // except if non-mutable shared list has no entry, we don't care
182 return this;
183 } else {
184 _nonShared.remove(key);
185 return this;
186 }
187 }
188 // a special case: create non-shared instance if need be
189 if (_nonShared == null) {
190 return nonSharedInstance(key, value);
191 }
192 _nonShared.put(key, value);
193 return this;
194 }
195
196 /*
197 /**********************************************************
198 /* Internal methods
199 /**********************************************************
200 */
201
202 /**
203 * Overridable method that creates initial non-shared instance,
204 * with the first explicit set value.
205 */
206 protected ContextAttributes nonSharedInstance(Object key, Object value)
207 {
208 Map<Object,Object> m = new HashMap<Object,Object>();
209 if (value == null) {
210 value = NULL_SURROGATE;
211 }
212 m.put(key, value);
213 return new Impl(_shared, m);
214 }
215
216 private Map<Object,Object> _copy(Map<?,?> src)
217 {
218 return new HashMap<Object,Object>(src);
219 }
220 }
221 }
222