• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*******************************************************************************
2  * Copyright 2011 See AUTHORS file.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *   http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  ******************************************************************************/
16 
17 package com.badlogic.gdx.utils;
18 
19 import java.io.DataInputStream;
20 import java.io.IOException;
21 import java.io.InputStream;
22 import java.io.InputStreamReader;
23 
24 import com.badlogic.gdx.Gdx;
25 import com.badlogic.gdx.files.FileHandle;
26 import com.badlogic.gdx.utils.JsonWriter.OutputType;
27 
28 /** Lightweight UBJSON parser.<br>
29  * <br>
30  * The default behavior is to parse the JSON into a DOM containing {@link JsonValue} objects. Extend this class and override
31  * methods to perform event driven parsing. When this is done, the parse methods will return null. <br>
32  * @author Xoppa */
33 public class UBJsonReader implements BaseJsonReader {
34 	public boolean oldFormat = true;
35 
36 	/** Parses the UBJSON from the given stream. <br>
37 	 * For best performance you should provide buffered streams to this method! */
38 	@Override
parse(InputStream input)39 	public JsonValue parse (InputStream input) {
40 		DataInputStream din = null;
41 		try {
42 			din = new DataInputStream(input);
43 			return parse(din);
44 		} catch (IOException ex) {
45 			throw new SerializationException(ex);
46 		} finally {
47 			StreamUtils.closeQuietly(din);
48 		}
49 	}
50 
51 	@Override
parse(FileHandle file)52 	public JsonValue parse (FileHandle file) {
53 		try {
54 			return parse(file.read(8192));
55 		} catch (Exception ex) {
56 			throw new SerializationException("Error parsing file: " + file, ex);
57 		}
58 	}
59 
parse(final DataInputStream din)60 	public JsonValue parse (final DataInputStream din) throws IOException {
61 		try {
62 			return parse(din, din.readByte());
63 		} finally {
64 			StreamUtils.closeQuietly(din);
65 		}
66 	}
67 
parse(final DataInputStream din, final byte type)68 	protected JsonValue parse (final DataInputStream din, final byte type) throws IOException {
69 		if (type == '[')
70 			return parseArray(din);
71 		else if (type == '{')
72 			return parseObject(din);
73 		else if (type == 'Z')
74 			return new JsonValue(JsonValue.ValueType.nullValue);
75 		else if (type == 'T')
76 			return new JsonValue(true);
77 		else if (type == 'F')
78 			return new JsonValue(false);
79 		else if (type == 'B')
80 			return new JsonValue((long)readUChar(din));
81 		else if (type == 'U')
82 			return new JsonValue((long)readUChar(din));
83 		else if (type == 'i')
84 			return new JsonValue(oldFormat ? (long)din.readShort() : (long)din.readByte());
85 		else if (type == 'I')
86 			return new JsonValue(oldFormat ? (long)din.readInt() : (long)din.readShort());
87 		else if (type == 'l')
88 			return new JsonValue((long)din.readInt());
89 		else if (type == 'L')
90 			return new JsonValue(din.readLong());
91 		else if (type == 'd')
92 			return new JsonValue(din.readFloat());
93 		else if (type == 'D')
94 			return new JsonValue(din.readDouble());
95 		else if (type == 's' || type == 'S')
96 			return new JsonValue(parseString(din, type));
97 		else if (type == 'a' || type == 'A')
98 			return parseData(din, type);
99 		else if (type == 'C')
100 			return new JsonValue(din.readChar());
101 		else
102 			throw new GdxRuntimeException("Unrecognized data type");
103 	}
104 
parseArray(final DataInputStream din)105 	protected JsonValue parseArray (final DataInputStream din) throws IOException {
106 		JsonValue result = new JsonValue(JsonValue.ValueType.array);
107 		byte type = din.readByte();
108 		byte valueType = 0;
109 		if (type == '$') {
110 			valueType = din.readByte();
111 			type = din.readByte();
112 		}
113 		long size = -1;
114 		if (type == '#') {
115 			size = parseSize(din, false, -1);
116 			if (size < 0) throw new GdxRuntimeException("Unrecognized data type");
117 			if (size == 0) return result;
118 			type = valueType == 0 ? din.readByte() : valueType;
119 		}
120 		JsonValue prev = null;
121 		long c = 0;
122 		while (din.available() > 0 && type != ']') {
123 			final JsonValue val = parse(din, type);
124 			val.parent = result;
125 			if (prev != null) {
126 				val.prev = prev;
127 				prev.next = val;
128 				result.size++;
129 			} else {
130 				result.child = val;
131 				result.size = 1;
132 			}
133 			prev = val;
134 			if (size > 0 && ++c >= size) break;
135 			type = valueType == 0 ? din.readByte() : valueType;
136 		}
137 		return result;
138 	}
139 
parseObject(final DataInputStream din)140 	protected JsonValue parseObject (final DataInputStream din) throws IOException {
141 		JsonValue result = new JsonValue(JsonValue.ValueType.object);
142 		byte type = din.readByte();
143 		byte valueType = 0;
144 		if (type == '$') {
145 			valueType = din.readByte();
146 			type = din.readByte();
147 		}
148 		long size = -1;
149 		if (type == '#') {
150 			size = parseSize(din, false, -1);
151 			if (size < 0) throw new GdxRuntimeException("Unrecognized data type");
152 			if (size == 0) return result;
153 			type = din.readByte();
154 		}
155 		JsonValue prev = null;
156 		long c = 0;
157 		while (din.available() > 0 && type != '}') {
158 			final String key = parseString(din, true, type);
159 			final JsonValue child = parse(din, valueType == 0 ? din.readByte() : valueType);
160 			child.setName(key);
161 			child.parent = result;
162 			if (prev != null) {
163 				child.prev = prev;
164 				prev.next = child;
165 				result.size++;
166 			} else {
167 				result.child = child;
168 				result.size = 1;
169 			}
170 			prev = child;
171 			if (size > 0 && ++c >= size) break;
172 			type = din.readByte();
173 		}
174 		return result;
175 	}
176 
parseData(final DataInputStream din, final byte blockType)177 	protected JsonValue parseData (final DataInputStream din, final byte blockType) throws IOException {
178 		// FIXME: a/A is currently not following the specs because it lacks strong typed, fixed sized containers,
179 		// see: https://github.com/thebuzzmedia/universal-binary-json/issues/27
180 		final byte dataType = din.readByte();
181 		final long size = blockType == 'A' ? readUInt(din) : (long)readUChar(din);
182 		final JsonValue result = new JsonValue(JsonValue.ValueType.array);
183 		JsonValue prev = null;
184 		for (long i = 0; i < size; i++) {
185 			final JsonValue val = parse(din, dataType);
186 			val.parent = result;
187 			if (prev != null) {
188 				prev.next = val;
189 				result.size++;
190 			} else {
191 				result.child = val;
192 				result.size = 1;
193 			}
194 			prev = val;
195 		}
196 		return result;
197 	}
198 
parseString(final DataInputStream din, final byte type)199 	protected String parseString (final DataInputStream din, final byte type) throws IOException {
200 		return parseString(din, false, type);
201 	}
202 
parseString(final DataInputStream din, final boolean sOptional, final byte type)203 	protected String parseString (final DataInputStream din, final boolean sOptional, final byte type) throws IOException {
204 		long size = -1;
205 		if (type == 'S') {
206 			size = parseSize(din, true, -1);
207 		} else if (type == 's')
208 			size = (long)readUChar(din);
209 		else if (sOptional) size = parseSize(din, type, false, -1);
210 		if (size < 0) throw new GdxRuntimeException("Unrecognized data type, string expected");
211 		return size > 0 ? readString(din, size) : "";
212 	}
213 
parseSize(final DataInputStream din, final boolean useIntOnError, final long defaultValue)214 	protected long parseSize (final DataInputStream din, final boolean useIntOnError, final long defaultValue) throws IOException {
215 		return parseSize(din, din.readByte(), useIntOnError, defaultValue);
216 	}
217 
parseSize(final DataInputStream din, final byte type, final boolean useIntOnError, final long defaultValue)218 	protected long parseSize (final DataInputStream din, final byte type, final boolean useIntOnError, final long defaultValue)
219 		throws IOException {
220 		if (type == 'i') return (long)readUChar(din);
221 		if (type == 'I') return (long)readUShort(din);
222 		if (type == 'l') return (long)readUInt(din);
223 		if (type == 'L') return din.readLong();
224 		if (useIntOnError) {
225 			long result = (long)((short)type & 0xFF) << 24;
226 			result |= (long)((short)din.readByte() & 0xFF) << 16;
227 			result |= (long)((short)din.readByte() & 0xFF) << 8;
228 			result |= (long)((short)din.readByte() & 0xFF);
229 			return result;
230 		}
231 		return defaultValue;
232 	}
233 
readUChar(final DataInputStream din)234 	protected short readUChar (final DataInputStream din) throws IOException {
235 		return (short)((short)din.readByte() & 0xFF);
236 	}
237 
readUShort(final DataInputStream din)238 	protected int readUShort (final DataInputStream din) throws IOException {
239 		return ((int)din.readShort() & 0xFFFF);
240 	}
241 
readUInt(final DataInputStream din)242 	protected long readUInt (final DataInputStream din) throws IOException {
243 		return ((long)din.readInt() & 0xFFFFFFFF);
244 	}
245 
readString(final DataInputStream din, final long size)246 	protected String readString (final DataInputStream din, final long size) throws IOException {
247 		final byte data[] = new byte[(int)size];
248 		din.readFully(data);
249 		return new String(data, "UTF-8");
250 	}
251 }
252