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