1
11 package com.thoughtworks.xstream.io.json;
12
13 import java.io.Externalizable;
14 import java.math.BigDecimal;
15 import java.math.BigInteger;
16 import java.util.Arrays;
17 import java.util.Collection;
18 import java.util.HashSet;
19 import java.util.Map;
20 import java.util.Set;
21
22 import com.thoughtworks.xstream.converters.ConversionException;
23 import com.thoughtworks.xstream.core.util.FastStack;
24 import com.thoughtworks.xstream.io.AbstractWriter;
25 import com.thoughtworks.xstream.io.naming.NameCoder;
26 import com.thoughtworks.xstream.io.naming.NoNameCoder;
27 import com.thoughtworks.xstream.mapper.Mapper;
28
29
30
38 public abstract class AbstractJsonWriter extends AbstractWriter {
39
64 public static final int DROP_ROOT_MODE = 1;
65
87 public static final int STRICT_MODE = 2;
88
120 public static final int EXPLICIT_MODE = 4;
121
137 public static final int IEEE_754_MODE = 8;
138
139 public static class Type {
140 public static Type NULL = new Type();
141 public static Type STRING = new Type();
142 public static Type NUMBER = new Type();
143 public static Type BOOLEAN = new Type();
144 }
145
146 private static class StackElement {
147 final Class type;
148 int status;
149 public StackElement(Class type, int status) {
150 this.type = type;
151 this.status = status;
152 }
153 }
154
155 private static class IllegalWriterStateException extends IllegalStateException {
156 public IllegalWriterStateException(int from, int to, String element) {
157 super("Cannot turn from state " + getState(from) + " into state " + getState(to)
158 + (element == null ? "" : " for property " + element));
159 }
160 private static String getState(int state) {
161 switch (state) {
162 case STATE_ROOT: return "ROOT";
163 case STATE_END_OBJECT: return "END_OBJECT";
164 case STATE_START_OBJECT: return "START_OBJECT";
165 case STATE_START_ATTRIBUTES: return "START_ATTRIBUTES";
166 case STATE_NEXT_ATTRIBUTE: return "NEXT_ATTRIBUTE";
167 case STATE_END_ATTRIBUTES: return "END_ATTRIBUTES";
168 case STATE_START_ELEMENTS: return "START_ELEMENTS";
169 case STATE_NEXT_ELEMENT: return "NEXT_ELEMENT";
170 case STATE_END_ELEMENTS: return "END_ELEMENTS";
171 case STATE_SET_VALUE: return "SET_VALUE";
172 default: throw new IllegalArgumentException("Unknown state provided: " + state
173 + ", cannot create message for IllegalWriterStateException");
174 }
175 }
176 }
177
178 private static final int STATE_ROOT = 1 << 0;
179 private static final int STATE_END_OBJECT = 1 << 1;
180 private static final int STATE_START_OBJECT = 1 << 2;
181 private static final int STATE_START_ATTRIBUTES = 1 << 3;
182 private static final int STATE_NEXT_ATTRIBUTE = 1 << 4;
183 private static final int STATE_END_ATTRIBUTES = 1 << 5;
184 private static final int STATE_START_ELEMENTS = 1 << 6;
185 private static final int STATE_NEXT_ELEMENT = 1 << 7;
186 private static final int STATE_END_ELEMENTS = 1 << 8;
187 private static final int STATE_SET_VALUE = 1 << 9;
188
189 private static final Set NUMBER_TYPES = new HashSet(Arrays.asList(new Class[]{
190 byte.class, Byte.class, short.class, Short.class, int.class, Integer.class, long.class,
191 Long.class, float.class, Float.class, double.class, Double.class, BigInteger.class,
192 BigDecimal.class}));
193 private int mode;
194 private FastStack stack = new FastStack(16);
195 private int expectedStates;
196
197
202 public AbstractJsonWriter() {
203 this(new NoNameCoder());
204 }
205
206
212 public AbstractJsonWriter(int mode) {
213 this(mode, new NoNameCoder());
214 }
215
216
222 public AbstractJsonWriter(NameCoder nameCoder) {
223 this(0, nameCoder);
224 }
225
226
233 public AbstractJsonWriter(int mode, NameCoder nameCoder) {
234 super(nameCoder);
235 this.mode = (mode & EXPLICIT_MODE) > 0 ? EXPLICIT_MODE : mode;
236 stack.push(new StackElement(null, STATE_ROOT));
237 expectedStates = STATE_START_OBJECT;
238 }
239
240 public void startNode(String name, Class clazz) {
241 if (name == null) {
242 throw new NullPointerException("name");
243 }
244 stack.push(new StackElement(clazz, ((StackElement)stack.peek()).status));
245 handleCheckedStateTransition(STATE_START_OBJECT, name, null);
246 expectedStates = STATE_SET_VALUE | STATE_NEXT_ATTRIBUTE | STATE_START_OBJECT | STATE_NEXT_ELEMENT | STATE_ROOT;
247 }
248
249 public void startNode(String name) {
250 startNode(name, null);
251 }
252
253 public void addAttribute(String name, String value) {
254 handleCheckedStateTransition(STATE_NEXT_ATTRIBUTE, name, value);
255 expectedStates = STATE_SET_VALUE | STATE_NEXT_ATTRIBUTE | STATE_START_OBJECT | STATE_NEXT_ELEMENT | STATE_ROOT;
256 }
257
258 public void setValue(String text) {
259 Class type = ((StackElement)stack.peek()).type;
260 if ((type == Character.class || type == Character.TYPE) && "".equals(text)) {
261 text = "\u0000";
262 }
263 handleCheckedStateTransition(STATE_SET_VALUE, null, text);
264 expectedStates = STATE_NEXT_ELEMENT | STATE_ROOT;
265 }
266
267 public void endNode() {
268 int size = stack.size();
269 int nextState = size > 2 ? STATE_NEXT_ELEMENT : STATE_ROOT;
270 handleCheckedStateTransition(nextState, null, null);
271 stack.pop();
272 ((StackElement)stack.peek()).status = nextState;
273 expectedStates = STATE_START_OBJECT;
274 if (size > 2) {
275 expectedStates |= STATE_NEXT_ELEMENT | STATE_ROOT;
276 }
277 }
278
279 private void handleCheckedStateTransition(final int requiredState, final String elementToAdd, final String valueToAdd)
280 {
281 final StackElement stackElement = (StackElement)stack.peek();
282 if ((expectedStates & requiredState) == 0) {
283 throw new IllegalWriterStateException(stackElement.status, requiredState, elementToAdd);
284 }
285 int currentState = handleStateTransition(stackElement.status, requiredState, elementToAdd, valueToAdd);
286 stackElement.status = currentState;
287 }
288
289 private int handleStateTransition(int currentState, final int requiredState, final String elementToAdd, final String valueToAdd)
290 {
291 int size = stack.size();
292 Class currentType = ((StackElement)stack.peek()).type;
293 boolean isArray = size > 1 && isArray(currentType);
294 boolean isArrayElement = size > 1 && isArray(((StackElement)stack.get(size-2)).type);
295 switch(currentState) {
296 case STATE_ROOT:
297 if (requiredState == STATE_START_OBJECT) {
298 currentState = handleStateTransition(STATE_START_ELEMENTS, STATE_START_OBJECT, elementToAdd, null);
299 return requiredState;
300 }
301 throw new IllegalWriterStateException(currentState, requiredState, elementToAdd);
302
303 case STATE_END_OBJECT:
304 switch(requiredState) {
305 case STATE_START_OBJECT:
306 currentState = handleStateTransition(currentState, STATE_NEXT_ELEMENT, null, null);
307 currentState = handleStateTransition(currentState, STATE_START_OBJECT, elementToAdd, null);
308 return requiredState;
309 case STATE_NEXT_ELEMENT:
310 nextElement();
311 return requiredState;
312 case STATE_ROOT:
313 if (((mode & DROP_ROOT_MODE) == 0 || size > 2) && (mode & EXPLICIT_MODE) == 0) {
314 endObject();
315 }
316 return requiredState;
317 default:
318 throw new IllegalWriterStateException(currentState, requiredState, elementToAdd);
319 }
320
321 case STATE_START_OBJECT:
322 switch(requiredState) {
323 case STATE_SET_VALUE:
324 case STATE_START_OBJECT:
325 case STATE_ROOT:
326 case STATE_NEXT_ELEMENT:
327 if (!isArrayElement || (mode & EXPLICIT_MODE) != 0) {
328 currentState = handleStateTransition(currentState, STATE_START_ATTRIBUTES, null, null);
329 currentState = handleStateTransition(currentState, STATE_END_ATTRIBUTES, null, null);
330 }
331 currentState = STATE_START_ELEMENTS;
332
333 switch(requiredState) {
334 case STATE_SET_VALUE:
335 currentState = handleStateTransition(currentState, STATE_SET_VALUE, null, valueToAdd);
336 break;
337 case STATE_START_OBJECT:
338 currentState = handleStateTransition(currentState, STATE_START_OBJECT, elementToAdd, null);
339 break;
340 case STATE_ROOT:
341 case STATE_NEXT_ELEMENT:
342 currentState = handleStateTransition(currentState, STATE_SET_VALUE, null, null);
343 currentState = handleStateTransition(currentState, requiredState, null, null);
344 break;
345 }
346 return requiredState;
347 case STATE_START_ATTRIBUTES:
348 if ((mode & EXPLICIT_MODE) != 0) {
349 startArray();
350 }
351 return requiredState;
352 case STATE_NEXT_ATTRIBUTE:
353 if ((mode & EXPLICIT_MODE) != 0 || !isArray) {
354 currentState = handleStateTransition(currentState, STATE_START_ATTRIBUTES, null, null);
355 currentState = handleStateTransition(currentState, STATE_NEXT_ATTRIBUTE, elementToAdd, valueToAdd);
356 return requiredState;
357 } else {
358 return STATE_START_OBJECT;
359 }
360 default:
361 throw new IllegalWriterStateException(currentState, requiredState, elementToAdd);
362 }
363
364 case STATE_NEXT_ELEMENT:
365 switch(requiredState) {
366 case STATE_START_OBJECT:
367 nextElement();
368 if (!isArrayElement && (mode & EXPLICIT_MODE) == 0) {
369 addLabel(encodeNode(elementToAdd));
370 if ((mode & EXPLICIT_MODE) == 0 && isArray) {
371 startArray();
372 }
373 return requiredState;
374 }
375 break;
376 case STATE_ROOT:
377 currentState = handleStateTransition(currentState, STATE_END_OBJECT, null, null);
378 currentState = handleStateTransition(currentState, STATE_ROOT, null, null);
379 return requiredState;
380 case STATE_NEXT_ELEMENT:
381 case STATE_END_OBJECT:
382 currentState = handleStateTransition(currentState, STATE_END_ELEMENTS, null, null);
383 currentState = handleStateTransition(currentState, STATE_END_OBJECT, null, null);
384 if ((mode & EXPLICIT_MODE) == 0 && !isArray) {
385 endObject();
386 }
387 return requiredState;
388 case STATE_END_ELEMENTS:
389 if ((mode & EXPLICIT_MODE) == 0 && isArray) {
390 endArray();
391 }
392 return requiredState;
393 default:
394 throw new IllegalWriterStateException(currentState, requiredState, elementToAdd);
395 }
396
397 case STATE_START_ELEMENTS:
398 switch(requiredState) {
399 case STATE_START_OBJECT:
400 if ((mode & DROP_ROOT_MODE) == 0 || size > 2) {
401 if (!isArrayElement || (mode & EXPLICIT_MODE) != 0) {
402 if (!"".equals(valueToAdd)) {
403 startObject();
404 }
405 addLabel(encodeNode(elementToAdd));
406 }
407 if ((mode & EXPLICIT_MODE) != 0) {
408 startArray();
409 }
410 }
411 if ((mode & EXPLICIT_MODE) == 0) {
412 if (isArray) {
413 startArray();
414 }
415 }
416 return requiredState;
417 case STATE_SET_VALUE:
418 if ((mode & STRICT_MODE) != 0 && size == 2) {
419 throw new ConversionException("Single value cannot be root element");
420 }
421 if (valueToAdd == null) {
422 if (currentType == Mapper.Null.class) {
423 addValue("null", Type.NULL);
424 } else if ((mode & EXPLICIT_MODE) == 0 && !isArray) {
425 startObject();
426 endObject();
427 }
428 } else {
429 if (((mode & IEEE_754_MODE) != 0)
430 && (currentType == long.class || currentType == Long.class)) {
431 long longValue = Long.parseLong(valueToAdd);
432
433 if (longValue > 9007199254740992L || longValue < -9007199254740992L) {
434 addValue(valueToAdd, Type.STRING);
435 } else {
436 addValue(valueToAdd, getType(currentType));
437 }
438 } else {
439 addValue(valueToAdd, getType(currentType));
440 }
441 }
442 return requiredState;
443 case STATE_END_ELEMENTS:
444 case STATE_NEXT_ELEMENT:
445 if ((mode & EXPLICIT_MODE) == 0) {
446 if (isArray) {
447 endArray();
448 } else {
449 endObject();
450 }
451 }
452 return requiredState;
453 default:
454 throw new IllegalWriterStateException(currentState, requiredState, elementToAdd);
455 }
456
457 case STATE_END_ELEMENTS:
458 switch(requiredState) {
459 case STATE_END_OBJECT:
460 if ((mode & EXPLICIT_MODE) != 0) {
461 endArray();
462 endArray();
463 endObject();
464 }
465 return requiredState;
466 default:
467 throw new IllegalWriterStateException(currentState, requiredState, elementToAdd);
468 }
469
470 case STATE_START_ATTRIBUTES:
471 switch(requiredState) {
472 case STATE_NEXT_ATTRIBUTE:
473 if (elementToAdd != null) {
474 String name = ((mode & EXPLICIT_MODE) == 0 ? "@" : "" ) + elementToAdd;
475 startObject();
476 addLabel(encodeAttribute(name));
477 addValue(valueToAdd, Type.STRING);
478 }
479 return requiredState;
480 }
481
482 case STATE_NEXT_ATTRIBUTE:
483 switch(requiredState) {
484 case STATE_END_ATTRIBUTES:
485 if ((mode & EXPLICIT_MODE) != 0) {
486 if (currentState == STATE_NEXT_ATTRIBUTE) {
487 endObject();
488 }
489 endArray();
490 nextElement();
491 startArray();
492 }
493 return requiredState;
494 case STATE_NEXT_ATTRIBUTE:
495 if (!isArray || (mode & EXPLICIT_MODE) != 0) {
496 nextElement();
497 String name = ((mode & EXPLICIT_MODE) == 0 ? "@" : "" ) + elementToAdd;
498 addLabel(encodeAttribute(name));
499 addValue(valueToAdd, Type.STRING);
500 }
501 return requiredState;
502 case STATE_SET_VALUE:
503 case STATE_START_OBJECT:
504 currentState = handleStateTransition(currentState, STATE_END_ATTRIBUTES, null, null);
505 currentState = handleStateTransition(currentState, STATE_START_ELEMENTS, null, null);
506 switch (requiredState) {
507 case STATE_SET_VALUE:
508 if ((mode & EXPLICIT_MODE) == 0) {
509 addLabel(encodeNode("$"));
510 }
511 currentState = handleStateTransition(currentState, STATE_SET_VALUE, null, valueToAdd);
512 if ((mode & EXPLICIT_MODE) == 0) {
513 endObject();
514 }
515 break;
516 case STATE_START_OBJECT:
517 currentState = handleStateTransition(currentState, STATE_START_OBJECT, elementToAdd, (mode & EXPLICIT_MODE) == 0 ? "" : null);
518 break;
519 case STATE_END_OBJECT:
520 currentState = handleStateTransition(currentState, STATE_SET_VALUE, null, null);
521 currentState = handleStateTransition(currentState, STATE_END_OBJECT, null, null);
522 break;
523 }
524 return requiredState;
525 case STATE_NEXT_ELEMENT:
526 currentState = handleStateTransition(currentState, STATE_END_ATTRIBUTES, null, null);
527 currentState = handleStateTransition(currentState, STATE_END_OBJECT, null, null);
528 return requiredState;
529 case STATE_ROOT:
530 currentState = handleStateTransition(currentState, STATE_END_ATTRIBUTES, null, null);
531 currentState = handleStateTransition(currentState, STATE_END_OBJECT, null, null);
532 currentState = handleStateTransition(currentState, STATE_ROOT, null, null);
533 return requiredState;
534 default:
535 throw new IllegalWriterStateException(currentState, requiredState, elementToAdd);
536 }
537
538 case STATE_END_ATTRIBUTES:
539 switch(requiredState) {
540 case STATE_START_ELEMENTS:
541 if ((mode & EXPLICIT_MODE) == 0) {
542 nextElement();
543 }
544 break;
545 case STATE_END_OBJECT:
546 currentState = handleStateTransition(STATE_START_ELEMENTS, STATE_END_ELEMENTS, null, null);
547 currentState = handleStateTransition(currentState, STATE_END_OBJECT, null, null);
548 break;
549 default:
550 throw new IllegalWriterStateException(currentState, requiredState, elementToAdd);
551 }
552 return requiredState;
553
554 case STATE_SET_VALUE:
555 switch(requiredState) {
556 case STATE_END_ELEMENTS:
557 if ((mode & EXPLICIT_MODE) == 0 && isArray) {
558 endArray();
559 }
560 return requiredState;
561 case STATE_NEXT_ELEMENT:
562 currentState = handleStateTransition(currentState, STATE_END_ELEMENTS, null, null);
563 currentState = handleStateTransition(currentState, STATE_END_OBJECT, null, null);
564 return requiredState;
565 case STATE_ROOT:
566 currentState = handleStateTransition(currentState, STATE_END_ELEMENTS, null, null);
567 currentState = handleStateTransition(currentState, STATE_END_OBJECT, null, null);
568 currentState = handleStateTransition(currentState, STATE_ROOT, null, null);
569 return requiredState;
570 default:
571 throw new IllegalWriterStateException(currentState, requiredState, elementToAdd);
572 }
573 }
574
575 throw new IllegalWriterStateException(currentState, requiredState, elementToAdd);
576 }
577
578
585 protected Type getType(Class clazz) {
586 return clazz == Mapper.Null.class
587 ? Type.NULL
588 : (clazz == Boolean.class || clazz == Boolean.TYPE)
589 ? Type.BOOLEAN
590 : NUMBER_TYPES.contains(clazz)
591 ? Type.NUMBER
592 : Type.STRING;
593 }
594
595
602 protected boolean isArray(Class clazz) {
603 return clazz != null && (clazz.isArray()
604 || Collection.class.isAssignableFrom(clazz)
605 || Externalizable.class.isAssignableFrom(clazz)
606 || Map.class.isAssignableFrom(clazz)
607 || Map.Entry.class.isAssignableFrom(clazz));
608 }
609
610
615 protected abstract void startObject();
616
617
623 protected abstract void addLabel(String name);
624
625
632 protected abstract void addValue(String value, Type type);
633
634
639 protected abstract void startArray();
640
641
646 protected abstract void nextElement();
647
648
653 protected abstract void endArray();
654
655
660 protected abstract void endObject();
661 }
662