• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 {
getEmpty()23     public static ContextAttributes getEmpty() {
24         return Impl.getEmpty();
25     }
26 
27     /*
28     /**********************************************************
29     /* Per-reader/writer access
30     /**********************************************************
31      */
32 
withSharedAttribute(Object key, Object value)33     public abstract ContextAttributes withSharedAttribute(Object key, Object value);
34 
withSharedAttributes(Map<?,?> attributes)35     public abstract ContextAttributes withSharedAttributes(Map<?,?> attributes);
36 
withoutSharedAttribute(Object key)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      */
getAttribute(Object key)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      */
withPerCallAttribute(Object key, Object value)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 
Impl(Map<?,?> shared)92         protected Impl(Map<?,?> shared) {
93             _shared = shared;
94             _nonShared = null;
95         }
96 
Impl(Map<?,?> shared, Map<Object,Object> nonShared)97         protected Impl(Map<?,?> shared, Map<Object,Object> nonShared) {
98             _shared = shared;
99             _nonShared = nonShared;
100         }
101 
getEmpty()102         public static ContextAttributes getEmpty() {
103             return EMPTY;
104         }
105 
106         /*
107         /**********************************************************
108         /* Per-reader/writer mutant factories
109         /**********************************************************
110          */
111 
112         @Override
withSharedAttribute(Object key, Object value)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
withSharedAttributes(Map<?,?> shared)127         public ContextAttributes withSharedAttributes(Map<?,?> shared) {
128             return new Impl(shared);
129         }
130 
131         @Override
withoutSharedAttribute(Object key)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
getAttribute(Object key)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
withPerCallAttribute(Object key, Object value)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          */
nonSharedInstance(Object key, Object value)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 
_copy(Map<?,?> src)216         private Map<Object,Object> _copy(Map<?,?> src)
217         {
218             return new HashMap<Object,Object>(src);
219         }
220     }
221 }
222