292 lines
		
	
	
		
			6.6 KiB
		
	
	
	
		
			Haxe
		
	
	
	
	
	
		
		
			
		
	
	
			292 lines
		
	
	
		
			6.6 KiB
		
	
	
	
		
			Haxe
		
	
	
	
	
	
| 
								 | 
							
								/*
							 | 
						||
| 
								 | 
							
								 * Copyright (C)2005-2019 Haxe Foundation
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 * Permission is hereby granted, free of charge, to any person obtaining a
							 | 
						||
| 
								 | 
							
								 * copy of this software and associated documentation files (the "Software"),
							 | 
						||
| 
								 | 
							
								 * to deal in the Software without restriction, including without limitation
							 | 
						||
| 
								 | 
							
								 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
							 | 
						||
| 
								 | 
							
								 * and/or sell copies of the Software, and to permit persons to whom the
							 | 
						||
| 
								 | 
							
								 * Software is furnished to do so, subject to the following conditions:
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 * The above copyright notice and this permission notice shall be included in
							 | 
						||
| 
								 | 
							
								 * all copies or substantial portions of the Software.
							 | 
						||
| 
								 | 
							
								 *
							 | 
						||
| 
								 | 
							
								 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
							 | 
						||
| 
								 | 
							
								 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
							 | 
						||
| 
								 | 
							
								 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
							 | 
						||
| 
								 | 
							
								 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
							 | 
						||
| 
								 | 
							
								 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
							 | 
						||
| 
								 | 
							
								 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
							 | 
						||
| 
								 | 
							
								 * DEALINGS IN THE SOFTWARE.
							 | 
						||
| 
								 | 
							
								 */
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								package haxe.format;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								/**
							 | 
						||
| 
								 | 
							
									An implementation of JSON printer in Haxe.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									This class is used by `haxe.Json` when native JSON implementation
							 | 
						||
| 
								 | 
							
									is not available.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									@see https://haxe.org/manual/std-Json-encoding.html
							 | 
						||
| 
								 | 
							
								**/
							 | 
						||
| 
								 | 
							
								class JsonPrinter {
							 | 
						||
| 
								 | 
							
									/**
							 | 
						||
| 
								 | 
							
										Encodes `o`'s value and returns the resulting JSON string.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										If `replacer` is given and is not null, it is used to retrieve
							 | 
						||
| 
								 | 
							
										actual object to be encoded. The `replacer` function takes two parameters,
							 | 
						||
| 
								 | 
							
										the key and the value being encoded. Initial key value is an empty string.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										If `space` is given and is not null, the result will be pretty-printed.
							 | 
						||
| 
								 | 
							
										Successive levels will be indented by this string.
							 | 
						||
| 
								 | 
							
									**/
							 | 
						||
| 
								 | 
							
									static public function print(o:Dynamic, ?replacer:(key:Dynamic, value:Dynamic) -> Dynamic, ?space:String):String {
							 | 
						||
| 
								 | 
							
										var printer = new JsonPrinter(replacer, space);
							 | 
						||
| 
								 | 
							
										printer.write("", o);
							 | 
						||
| 
								 | 
							
										return printer.buf.toString();
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									var buf:#if flash flash.utils.ByteArray #else StringBuf #end;
							 | 
						||
| 
								 | 
							
									var replacer:(key:Dynamic, value:Dynamic) -> Dynamic;
							 | 
						||
| 
								 | 
							
									var indent:String;
							 | 
						||
| 
								 | 
							
									var pretty:Bool;
							 | 
						||
| 
								 | 
							
									var nind:Int;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									function new(replacer:(key:Dynamic, value:Dynamic) -> Dynamic, space:String) {
							 | 
						||
| 
								 | 
							
										this.replacer = replacer;
							 | 
						||
| 
								 | 
							
										this.indent = space;
							 | 
						||
| 
								 | 
							
										this.pretty = space != null;
							 | 
						||
| 
								 | 
							
										this.nind = 0;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										#if flash
							 | 
						||
| 
								 | 
							
										buf = new flash.utils.ByteArray();
							 | 
						||
| 
								 | 
							
										buf.endian = flash.utils.Endian.BIG_ENDIAN;
							 | 
						||
| 
								 | 
							
										buf.position = 0;
							 | 
						||
| 
								 | 
							
										#else
							 | 
						||
| 
								 | 
							
										buf = new StringBuf();
							 | 
						||
| 
								 | 
							
										#end
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									inline function ipad():Void {
							 | 
						||
| 
								 | 
							
										if (pretty)
							 | 
						||
| 
								 | 
							
											add(StringTools.lpad('', indent, nind * indent.length));
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									inline function newl():Void {
							 | 
						||
| 
								 | 
							
										if (pretty)
							 | 
						||
| 
								 | 
							
											addChar('\n'.code);
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									function write(k:Dynamic, v:Dynamic) {
							 | 
						||
| 
								 | 
							
										if (replacer != null)
							 | 
						||
| 
								 | 
							
											v = replacer(k, v);
							 | 
						||
| 
								 | 
							
										switch (Type.typeof(v)) {
							 | 
						||
| 
								 | 
							
											case TUnknown:
							 | 
						||
| 
								 | 
							
												add('"???"');
							 | 
						||
| 
								 | 
							
											case TObject:
							 | 
						||
| 
								 | 
							
												objString(v);
							 | 
						||
| 
								 | 
							
											case TInt:
							 | 
						||
| 
								 | 
							
												add(#if (jvm || hl) Std.string(v) #else v #end);
							 | 
						||
| 
								 | 
							
											case TFloat:
							 | 
						||
| 
								 | 
							
												add(Math.isFinite(v) ? Std.string(v) : 'null');
							 | 
						||
| 
								 | 
							
											case TFunction:
							 | 
						||
| 
								 | 
							
												add('"<fun>"');
							 | 
						||
| 
								 | 
							
											case TClass(c):
							 | 
						||
| 
								 | 
							
												if (c == String)
							 | 
						||
| 
								 | 
							
													quote(v);
							 | 
						||
| 
								 | 
							
												else if (c == Array) {
							 | 
						||
| 
								 | 
							
													var v:Array<Dynamic> = v;
							 | 
						||
| 
								 | 
							
													addChar('['.code);
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
													var len = v.length;
							 | 
						||
| 
								 | 
							
													var last = len - 1;
							 | 
						||
| 
								 | 
							
													for (i in 0...len) {
							 | 
						||
| 
								 | 
							
														if (i > 0)
							 | 
						||
| 
								 | 
							
															addChar(','.code)
							 | 
						||
| 
								 | 
							
														else
							 | 
						||
| 
								 | 
							
															nind++;
							 | 
						||
| 
								 | 
							
														newl();
							 | 
						||
| 
								 | 
							
														ipad();
							 | 
						||
| 
								 | 
							
														write(i, v[i]);
							 | 
						||
| 
								 | 
							
														if (i == last) {
							 | 
						||
| 
								 | 
							
															nind--;
							 | 
						||
| 
								 | 
							
															newl();
							 | 
						||
| 
								 | 
							
															ipad();
							 | 
						||
| 
								 | 
							
														}
							 | 
						||
| 
								 | 
							
													}
							 | 
						||
| 
								 | 
							
													addChar(']'.code);
							 | 
						||
| 
								 | 
							
												} else if (c == haxe.ds.StringMap) {
							 | 
						||
| 
								 | 
							
													var v:haxe.ds.StringMap<Dynamic> = v;
							 | 
						||
| 
								 | 
							
													var o = {};
							 | 
						||
| 
								 | 
							
													for (k in v.keys())
							 | 
						||
| 
								 | 
							
														Reflect.setField(o, k, v.get(k));
							 | 
						||
| 
								 | 
							
													objString(o);
							 | 
						||
| 
								 | 
							
												} else if (c == Date) {
							 | 
						||
| 
								 | 
							
													var v:Date = v;
							 | 
						||
| 
								 | 
							
													quote(v.toString());
							 | 
						||
| 
								 | 
							
												} else
							 | 
						||
| 
								 | 
							
													classString(v);
							 | 
						||
| 
								 | 
							
											case TEnum(_):
							 | 
						||
| 
								 | 
							
												var i:Dynamic = Type.enumIndex(v);
							 | 
						||
| 
								 | 
							
												add(i);
							 | 
						||
| 
								 | 
							
											case TBool:
							 | 
						||
| 
								 | 
							
												add(#if (php || jvm || hl) (v ? 'true' : 'false') #else v #end);
							 | 
						||
| 
								 | 
							
											case TNull:
							 | 
						||
| 
								 | 
							
												add('null');
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									extern inline function addChar(c:Int) {
							 | 
						||
| 
								 | 
							
										#if flash
							 | 
						||
| 
								 | 
							
										buf.writeByte(c);
							 | 
						||
| 
								 | 
							
										#else
							 | 
						||
| 
								 | 
							
										buf.addChar(c);
							 | 
						||
| 
								 | 
							
										#end
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									extern inline function add(v:String) {
							 | 
						||
| 
								 | 
							
										#if flash
							 | 
						||
| 
								 | 
							
										// argument is not always a string but will be automatically casted
							 | 
						||
| 
								 | 
							
										buf.writeUTFBytes(v);
							 | 
						||
| 
								 | 
							
										#else
							 | 
						||
| 
								 | 
							
										buf.add(v);
							 | 
						||
| 
								 | 
							
										#end
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									function classString(v:Dynamic) {
							 | 
						||
| 
								 | 
							
										fieldsString(v, Type.getInstanceFields(Type.getClass(v)));
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									inline function objString(v:Dynamic) {
							 | 
						||
| 
								 | 
							
										fieldsString(v, Reflect.fields(v));
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									function fieldsString(v:Dynamic, fields:Array<String>) {
							 | 
						||
| 
								 | 
							
										addChar('{'.code);
							 | 
						||
| 
								 | 
							
										var len = fields.length;
							 | 
						||
| 
								 | 
							
										var last = len - 1;
							 | 
						||
| 
								 | 
							
										var first = true;
							 | 
						||
| 
								 | 
							
										for (i in 0...len) {
							 | 
						||
| 
								 | 
							
											var f = fields[i];
							 | 
						||
| 
								 | 
							
											var value = Reflect.field(v, f);
							 | 
						||
| 
								 | 
							
											if (Reflect.isFunction(value))
							 | 
						||
| 
								 | 
							
												continue;
							 | 
						||
| 
								 | 
							
											if (first) {
							 | 
						||
| 
								 | 
							
												nind++;
							 | 
						||
| 
								 | 
							
												first = false;
							 | 
						||
| 
								 | 
							
											} else
							 | 
						||
| 
								 | 
							
												addChar(','.code);
							 | 
						||
| 
								 | 
							
											newl();
							 | 
						||
| 
								 | 
							
											ipad();
							 | 
						||
| 
								 | 
							
											quote(f);
							 | 
						||
| 
								 | 
							
											addChar(':'.code);
							 | 
						||
| 
								 | 
							
											if (pretty)
							 | 
						||
| 
								 | 
							
												addChar(' '.code);
							 | 
						||
| 
								 | 
							
											write(f, value);
							 | 
						||
| 
								 | 
							
											if (i == last) {
							 | 
						||
| 
								 | 
							
												nind--;
							 | 
						||
| 
								 | 
							
												newl();
							 | 
						||
| 
								 | 
							
												ipad();
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
										addChar('}'.code);
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									function quote(s:String) {
							 | 
						||
| 
								 | 
							
										#if neko
							 | 
						||
| 
								 | 
							
										if (s.length != neko.Utf8.length(s)) {
							 | 
						||
| 
								 | 
							
											quoteUtf8(s);
							 | 
						||
| 
								 | 
							
											return;
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
										#end
							 | 
						||
| 
								 | 
							
										addChar('"'.code);
							 | 
						||
| 
								 | 
							
										var i = 0;
							 | 
						||
| 
								 | 
							
										var length = s.length;
							 | 
						||
| 
								 | 
							
										#if hl
							 | 
						||
| 
								 | 
							
										var prev = -1;
							 | 
						||
| 
								 | 
							
										#end
							 | 
						||
| 
								 | 
							
										while (i < length) {
							 | 
						||
| 
								 | 
							
											var c = StringTools.unsafeCodeAt(s, i++);
							 | 
						||
| 
								 | 
							
											switch (c) {
							 | 
						||
| 
								 | 
							
												case '"'.code:
							 | 
						||
| 
								 | 
							
													add('\\"');
							 | 
						||
| 
								 | 
							
												case '\\'.code:
							 | 
						||
| 
								 | 
							
													add('\\\\');
							 | 
						||
| 
								 | 
							
												case '\n'.code:
							 | 
						||
| 
								 | 
							
													add('\\n');
							 | 
						||
| 
								 | 
							
												case '\r'.code:
							 | 
						||
| 
								 | 
							
													add('\\r');
							 | 
						||
| 
								 | 
							
												case '\t'.code:
							 | 
						||
| 
								 | 
							
													add('\\t');
							 | 
						||
| 
								 | 
							
												case 8:
							 | 
						||
| 
								 | 
							
													add('\\b');
							 | 
						||
| 
								 | 
							
												case 12:
							 | 
						||
| 
								 | 
							
													add('\\f');
							 | 
						||
| 
								 | 
							
												default:
							 | 
						||
| 
								 | 
							
													#if flash
							 | 
						||
| 
								 | 
							
													if (c >= 128)
							 | 
						||
| 
								 | 
							
														add(String.fromCharCode(c))
							 | 
						||
| 
								 | 
							
													else
							 | 
						||
| 
								 | 
							
														addChar(c);
							 | 
						||
| 
								 | 
							
													#elseif hl
							 | 
						||
| 
								 | 
							
													if (prev >= 0) {
							 | 
						||
| 
								 | 
							
														if (c >= 0xD800 && c <= 0xDFFF) {
							 | 
						||
| 
								 | 
							
															addChar((((prev - 0xD800) << 10) | (c - 0xDC00)) + 0x10000);
							 | 
						||
| 
								 | 
							
															prev = -1;
							 | 
						||
| 
								 | 
							
														} else {
							 | 
						||
| 
								 | 
							
															addChar("□".code);
							 | 
						||
| 
								 | 
							
															prev = c;
							 | 
						||
| 
								 | 
							
														}
							 | 
						||
| 
								 | 
							
													} else {
							 | 
						||
| 
								 | 
							
														if (c >= 0xD800 && c <= 0xDFFF)
							 | 
						||
| 
								 | 
							
															prev = c;
							 | 
						||
| 
								 | 
							
														else
							 | 
						||
| 
								 | 
							
															addChar(c);
							 | 
						||
| 
								 | 
							
													}
							 | 
						||
| 
								 | 
							
													#else
							 | 
						||
| 
								 | 
							
													addChar(c);
							 | 
						||
| 
								 | 
							
													#end
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
										#if hl
							 | 
						||
| 
								 | 
							
										if (prev >= 0)
							 | 
						||
| 
								 | 
							
											addChar("□".code);
							 | 
						||
| 
								 | 
							
										#end
							 | 
						||
| 
								 | 
							
										addChar('"'.code);
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									#if neko
							 | 
						||
| 
								 | 
							
									function quoteUtf8(s:String) {
							 | 
						||
| 
								 | 
							
										var u = new neko.Utf8();
							 | 
						||
| 
								 | 
							
										neko.Utf8.iter(s, function(c) {
							 | 
						||
| 
								 | 
							
											switch (c) {
							 | 
						||
| 
								 | 
							
												case '\\'.code, '"'.code:
							 | 
						||
| 
								 | 
							
													u.addChar('\\'.code);
							 | 
						||
| 
								 | 
							
													u.addChar(c);
							 | 
						||
| 
								 | 
							
												case '\n'.code:
							 | 
						||
| 
								 | 
							
													u.addChar('\\'.code);
							 | 
						||
| 
								 | 
							
													u.addChar('n'.code);
							 | 
						||
| 
								 | 
							
												case '\r'.code:
							 | 
						||
| 
								 | 
							
													u.addChar('\\'.code);
							 | 
						||
| 
								 | 
							
													u.addChar('r'.code);
							 | 
						||
| 
								 | 
							
												case '\t'.code:
							 | 
						||
| 
								 | 
							
													u.addChar('\\'.code);
							 | 
						||
| 
								 | 
							
													u.addChar('t'.code);
							 | 
						||
| 
								 | 
							
												case 8:
							 | 
						||
| 
								 | 
							
													u.addChar('\\'.code);
							 | 
						||
| 
								 | 
							
													u.addChar('b'.code);
							 | 
						||
| 
								 | 
							
												case 12:
							 | 
						||
| 
								 | 
							
													u.addChar('\\'.code);
							 | 
						||
| 
								 | 
							
													u.addChar('f'.code);
							 | 
						||
| 
								 | 
							
												default:
							 | 
						||
| 
								 | 
							
													u.addChar(c);
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
										});
							 | 
						||
| 
								 | 
							
										buf.add('"');
							 | 
						||
| 
								 | 
							
										buf.add(u.toString());
							 | 
						||
| 
								 | 
							
										buf.add('"');
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
									#end
							 | 
						||
| 
								 | 
							
								}
							 |