/** * * Copyright (c) 2006-2017, Speedment, Inc. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); You may not * use this file except in compliance with the License. You may obtain a copy of * the License at: * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations under * the License. */ package com.speedment.common.json.internal; import java.io.IOException; import java.io.OutputStream; import java.util.List; import java.util.Map; import static java.util.Objects.requireNonNull; /** * An internal class that can serialize java objects into JSON code. * * @author Emil Forslund * @since 1.0.0 */ public final class JsonSerializer { private final OutputStream out; private final boolean pretty; private final int tabSize; private int level = 0; public JsonSerializer(OutputStream out, boolean pretty) { this.out = requireNonNull(out); this.pretty = pretty; this.tabSize = pretty ? PRETTY_TAB_SIZE : 0; } public void print(Object unknown) throws IOException { if (unknown == null) { printNull(); } else { final Class<?> type = unknown.getClass(); if (type == String.class) { printString((String) unknown); } else if (List.class.isAssignableFrom(type)) { @SuppressWarnings("unchecked") final List<Object> list = (List<Object>) unknown; printList(list); } else if (Map.class.isAssignableFrom(type)) { @SuppressWarnings("unchecked") final Map<String, Object> map = (Map<String, Object>) unknown; printMap(map); } else if (type == Double.class) { printDouble((Double) unknown); } else if (type == Float.class) { printDouble(((Float) unknown).doubleValue()); } else if (type == Long.class) { printLong((Long) unknown); } else if (type == Integer.class) { printLong(((Integer) unknown).longValue()); } else if (type == Short.class) { printLong(((Short) unknown).longValue()); } else if (type == Byte.class) { printLong(((Byte) unknown).longValue()); } else if (type == Boolean.class) { printBoolean((Boolean) unknown); } else { throw new IllegalArgumentException( "Can't parse unsupported type '" + type + "' into JSON." ); } } } private void printMap(Map<String, Object> map) throws IOException { out.write(BEGIN_OBJECT); // (begin the map) level++; boolean first = true; for (final Map.Entry<String, Object> entry : map.entrySet()) { if (first) { first = false; } else { out.write(SEPARATOR); } if (pretty) { out.write(NEW_LINE); printIndent(); } printString(entry.getKey()); if (pretty) { out.write(SPACE); } out.write(ASSIGN); if (pretty) { out.write(SPACE); } print(entry.getValue()); } level--; if (pretty && !first) { out.write(NEW_LINE); printIndent(); } out.write(END_OBJECT); // (close the map) } private void printList(List<Object> list) throws IOException { out.write(BEGIN_ARRAY); // (begin the list) level++; boolean first = true; for (final Object object : list) { if (first) { first = false; } else { out.write(SEPARATOR); } if (pretty) { out.write(NEW_LINE); printIndent(); } print(object); } level--; if (pretty && !first) { out.write(NEW_LINE); printIndent(); } out.write(END_ARRAY); // (close the list) } private void printString(String string) throws IOException { out.write(BEGIN_STRING); out.write(string.replace("\"", "\\\"").getBytes()); out.write(END_STRING); } private void printDouble(Double number) throws IOException { out.write(Double.toString(number).getBytes()); } private void printLong(Long number) throws IOException { out.write(Long.toString(number).getBytes()); } private void printBoolean(Boolean bool) throws IOException { if (bool) { out.write("true".getBytes()); } else { out.write("false".getBytes()); } } private void printNull() throws IOException { out.write(0x6E); // n out.write(0x75); // u out.write(0x6C); // l out.write(0x6C); // l } @SuppressWarnings("fallthrough") private void printIndent() throws IOException { switch (tabSize * level) { case 16 : out.write(SPACE); case 15 : out.write(SPACE); case 14 : out.write(SPACE); case 13 : out.write(SPACE); case 12 : out.write(SPACE); case 11 : out.write(SPACE); case 10 : out.write(SPACE); case 9 : out.write(SPACE); case 8 : out.write(SPACE); case 7 : out.write(SPACE); case 6 : out.write(SPACE); case 5 : out.write(SPACE); case 4 : out.write(SPACE); case 3 : out.write(SPACE); case 2 : out.write(SPACE); case 1 : out.write(SPACE); case 0 : return; default : final int count = tabSize * level; for (int i = 0; i < count; i++) { out.write(SPACE); } } } private final static int BEGIN_OBJECT = 0x7B, // { END_OBJECT = 0x7D, // } BEGIN_ARRAY = 0x5B, // [ END_ARRAY = 0x5D, // ] BEGIN_STRING = 0x22, // " END_STRING = 0x22, // " ASSIGN = 0x3A, // : SEPARATOR = 0x2C, // , NEW_LINE = 0x0A, // new-line SPACE = 0x20; // space private final static int PRETTY_TAB_SIZE = 2; }