forked from LeenkxTeam/LNXSDK
		
	
		
			
				
	
	
		
			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
 | |
| }
 |