1 package com.fasterxml.jackson.core.json; 2 3 import com.fasterxml.jackson.core.*; 4 5 /** 6 * Extension of {@link JsonStreamContext}, which implements 7 * core methods needed, and also exposes 8 * more complete API to generator implementation classes. 9 */ 10 public class JsonWriteContext extends JsonStreamContext 11 { 12 // // // Return values for writeValue() 13 14 public final static int STATUS_OK_AS_IS = 0; 15 public final static int STATUS_OK_AFTER_COMMA = 1; 16 public final static int STATUS_OK_AFTER_COLON = 2; 17 public final static int STATUS_OK_AFTER_SPACE = 3; // in root context 18 public final static int STATUS_EXPECT_VALUE = 4; 19 public final static int STATUS_EXPECT_NAME = 5; 20 21 /** 22 * Parent context for this context; null for root context. 23 */ 24 protected final JsonWriteContext _parent; 25 26 // // // Optional duplicate detection 27 28 protected DupDetector _dups; 29 30 /* 31 /********************************************************** 32 /* Simple instance reuse slots; speed up things a bit (10-15%) 33 /* for docs with lots of small arrays/objects 34 /********************************************************** 35 */ 36 37 protected JsonWriteContext _child; 38 39 /* 40 /********************************************************** 41 /* Location/state information (minus source reference) 42 /********************************************************** 43 */ 44 45 /** 46 * Name of the field of which value is to be written; only 47 * used for OBJECT contexts 48 */ 49 protected String _currentName; 50 51 /** 52 * @since 2.5 53 */ 54 protected Object _currentValue; 55 56 /** 57 * Marker used to indicate that we just wrote a name, and 58 * now expect a value to write 59 */ 60 protected boolean _gotName; 61 62 /* 63 /********************************************************** 64 /* Life-cycle 65 /********************************************************** 66 */ 67 JsonWriteContext(int type, JsonWriteContext parent, DupDetector dups)68 protected JsonWriteContext(int type, JsonWriteContext parent, DupDetector dups) { 69 super(); 70 _type = type; 71 _parent = parent; 72 _dups = dups; 73 _index = -1; 74 } 75 76 /* @since 2.10 */ JsonWriteContext(int type, JsonWriteContext parent, DupDetector dups, Object currValue)77 protected JsonWriteContext(int type, JsonWriteContext parent, DupDetector dups, 78 Object currValue) { 79 super(); 80 _type = type; 81 _parent = parent; 82 _dups = dups; 83 _index = -1; 84 _currentValue = currValue; 85 } 86 reset(int type)87 protected JsonWriteContext reset(int type) { 88 _type = type; 89 _index = -1; 90 _currentName = null; 91 _gotName = false; 92 _currentValue = null; 93 if (_dups != null) { _dups.reset(); } 94 return this; 95 } 96 97 /* @since 2.10 */ reset(int type, Object currValue)98 protected JsonWriteContext reset(int type, Object currValue) { 99 _type = type; 100 _index = -1; 101 _currentName = null; 102 _gotName = false; 103 _currentValue = currValue; 104 if (_dups != null) { _dups.reset(); } 105 return this; 106 } 107 withDupDetector(DupDetector dups)108 public JsonWriteContext withDupDetector(DupDetector dups) { 109 _dups = dups; 110 return this; 111 } 112 113 @Override getCurrentValue()114 public Object getCurrentValue() { 115 return _currentValue; 116 } 117 118 @Override setCurrentValue(Object v)119 public void setCurrentValue(Object v) { 120 _currentValue = v; 121 } 122 123 /* 124 /********************************************************** 125 /* Factory methods 126 /********************************************************** 127 */ 128 129 /** 130 * @deprecated Since 2.3; use method that takes argument 131 */ 132 @Deprecated createRootContext()133 public static JsonWriteContext createRootContext() { return createRootContext(null); } 134 createRootContext(DupDetector dd)135 public static JsonWriteContext createRootContext(DupDetector dd) { 136 return new JsonWriteContext(TYPE_ROOT, null, dd); 137 } 138 createChildArrayContext()139 public JsonWriteContext createChildArrayContext() { 140 JsonWriteContext ctxt = _child; 141 if (ctxt == null) { 142 _child = ctxt = new JsonWriteContext(TYPE_ARRAY, this, 143 (_dups == null) ? null : _dups.child()); 144 return ctxt; 145 } 146 return ctxt.reset(TYPE_ARRAY); 147 } 148 149 /* @since 2.10 */ createChildArrayContext(Object currValue)150 public JsonWriteContext createChildArrayContext(Object currValue) { 151 JsonWriteContext ctxt = _child; 152 if (ctxt == null) { 153 _child = ctxt = new JsonWriteContext(TYPE_ARRAY, this, 154 (_dups == null) ? null : _dups.child(), currValue); 155 return ctxt; 156 } 157 return ctxt.reset(TYPE_ARRAY, currValue); 158 } 159 createChildObjectContext()160 public JsonWriteContext createChildObjectContext() { 161 JsonWriteContext ctxt = _child; 162 if (ctxt == null) { 163 _child = ctxt = new JsonWriteContext(TYPE_OBJECT, this, 164 (_dups == null) ? null : _dups.child()); 165 return ctxt; 166 } 167 return ctxt.reset(TYPE_OBJECT); 168 } 169 170 /* @since 2.10 */ createChildObjectContext(Object currValue)171 public JsonWriteContext createChildObjectContext(Object currValue) { 172 JsonWriteContext ctxt = _child; 173 if (ctxt == null) { 174 _child = ctxt = new JsonWriteContext(TYPE_OBJECT, this, 175 (_dups == null) ? null : _dups.child(), currValue); 176 return ctxt; 177 } 178 return ctxt.reset(TYPE_OBJECT, currValue); 179 } 180 getParent()181 @Override public final JsonWriteContext getParent() { return _parent; } getCurrentName()182 @Override public final String getCurrentName() { return _currentName; } 183 // @since 2.9 hasCurrentName()184 @Override public boolean hasCurrentName() { return _currentName != null; } 185 186 /** 187 * Method that can be used to both clear the accumulated references 188 * (specifically value set with {@link #setCurrentValue(Object)}) 189 * that should not be retained, and returns parent (as would 190 * {@link #getParent()} do). Typically called when closing the active 191 * context when encountering {@link JsonToken#END_ARRAY} or 192 * {@link JsonToken#END_OBJECT}. 193 * 194 * @since 2.7 195 */ clearAndGetParent()196 public JsonWriteContext clearAndGetParent() { 197 _currentValue = null; 198 // could also clear the current name, but seems cheap enough to leave? 199 return _parent; 200 } 201 getDupDetector()202 public DupDetector getDupDetector() { 203 return _dups; 204 } 205 206 /** 207 * Method that writer is to call before it writes a field name. 208 * 209 * @return Index of the field entry (0-based) 210 */ writeFieldName(String name)211 public int writeFieldName(String name) throws JsonProcessingException { 212 if ((_type != TYPE_OBJECT) || _gotName) { 213 return STATUS_EXPECT_VALUE; 214 } 215 _gotName = true; 216 _currentName = name; 217 if (_dups != null) { _checkDup(_dups, name); } 218 return (_index < 0) ? STATUS_OK_AS_IS : STATUS_OK_AFTER_COMMA; 219 } 220 _checkDup(DupDetector dd, String name)221 private final void _checkDup(DupDetector dd, String name) throws JsonProcessingException { 222 if (dd.isDup(name)) { 223 Object src = dd.getSource(); 224 throw new JsonGenerationException("Duplicate field '"+name+"'", 225 ((src instanceof JsonGenerator) ? ((JsonGenerator) src) : null)); 226 } 227 } 228 writeValue()229 public int writeValue() { 230 // Most likely, object: 231 if (_type == TYPE_OBJECT) { 232 if (!_gotName) { 233 return STATUS_EXPECT_NAME; 234 } 235 _gotName = false; 236 ++_index; 237 return STATUS_OK_AFTER_COLON; 238 } 239 240 // Ok, array? 241 if (_type == TYPE_ARRAY) { 242 int ix = _index; 243 ++_index; 244 return (ix < 0) ? STATUS_OK_AS_IS : STATUS_OK_AFTER_COMMA; 245 } 246 247 // Nope, root context 248 // No commas within root context, but need space 249 ++_index; 250 return (_index == 0) ? STATUS_OK_AS_IS : STATUS_OK_AFTER_SPACE; 251 } 252 } 253